linny-r 1.9.2 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +4 -4
- package/package.json +1 -1
- package/server.js +1 -1
- package/static/images/eq-negated.png +0 -0
- package/static/images/power.png +0 -0
- package/static/images/tex.png +0 -0
- package/static/index.html +225 -10
- package/static/linny-r.css +458 -8
- package/static/scripts/linny-r-ctrl.js +6 -4
- package/static/scripts/linny-r-gui-actor-manager.js +1 -1
- package/static/scripts/linny-r-gui-chart-manager.js +20 -13
- package/static/scripts/linny-r-gui-constraint-editor.js +410 -50
- package/static/scripts/linny-r-gui-controller.js +127 -12
- package/static/scripts/linny-r-gui-dataset-manager.js +28 -20
- package/static/scripts/linny-r-gui-documentation-manager.js +11 -3
- package/static/scripts/linny-r-gui-equation-manager.js +1 -1
- package/static/scripts/linny-r-gui-experiment-manager.js +1 -1
- package/static/scripts/linny-r-gui-expression-editor.js +7 -1
- package/static/scripts/linny-r-gui-file-manager.js +31 -13
- package/static/scripts/linny-r-gui-finder.js +1 -1
- package/static/scripts/linny-r-gui-model-autosaver.js +1 -1
- package/static/scripts/linny-r-gui-monitor.js +1 -1
- package/static/scripts/linny-r-gui-paper.js +108 -25
- package/static/scripts/linny-r-gui-power-grid-manager.js +529 -0
- package/static/scripts/linny-r-gui-receiver.js +1 -1
- package/static/scripts/linny-r-gui-repository-browser.js +1 -1
- package/static/scripts/linny-r-gui-scale-unit-manager.js +1 -1
- package/static/scripts/linny-r-gui-sensitivity-analysis.js +1 -1
- package/static/scripts/linny-r-gui-tex-manager.js +110 -0
- package/static/scripts/linny-r-gui-undo-redo.js +1 -1
- package/static/scripts/linny-r-milp.js +1 -1
- package/static/scripts/linny-r-model.js +1016 -155
- package/static/scripts/linny-r-utils.js +3 -3
- package/static/scripts/linny-r-vm.js +714 -248
- package/static/show-diff.html +1 -1
- package/static/show-png.html +1 -1
@@ -13,7 +13,7 @@ handler functions.
|
|
13
13
|
*/
|
14
14
|
|
15
15
|
/*
|
16
|
-
Copyright (c) 2017-
|
16
|
+
Copyright (c) 2017-2024 Delft University of Technology
|
17
17
|
|
18
18
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
19
19
|
of this software and associated documentation files (the "Software"), to deal
|
@@ -421,7 +421,7 @@ class GUIController extends Controller {
|
|
421
421
|
this.edit_btns = ['clone', 'paste', 'delete', 'undo', 'redo'];
|
422
422
|
this.model_btns = ['settings', 'save', 'repository', 'actors',
|
423
423
|
'dataset', 'equation', 'chart', 'sensitivity', 'experiment',
|
424
|
-
'diagram', 'savediagram', 'finder', 'monitor', 'solve'];
|
424
|
+
'diagram', 'savediagram', 'finder', 'monitor', 'tex', 'solve'];
|
425
425
|
this.other_btns = ['new', 'load', 'receiver', 'documentation',
|
426
426
|
'parent', 'lift', 'solve', 'stop', 'reset', 'zoomin', 'zoomout',
|
427
427
|
'stepback', 'stepforward', 'autosave', 'recall'];
|
@@ -569,6 +569,7 @@ class GUIController extends Controller {
|
|
569
569
|
this.buttons.finder.addEventListener('click', tdf);
|
570
570
|
this.buttons.monitor.addEventListener('click', tdf);
|
571
571
|
this.buttons.documentation.addEventListener('click', tdf);
|
572
|
+
this.buttons.tex.addEventListener('click', tdf);
|
572
573
|
// Cluster navigation elements:
|
573
574
|
this.focal_name.addEventListener('click',
|
574
575
|
() => UI.showClusterPropertiesDialog(MODEL.focal_cluster));
|
@@ -717,6 +718,10 @@ class GUIController extends Controller {
|
|
717
718
|
() => SCALE_UNIT_MANAGER.show());
|
718
719
|
this.modals.settings.element('solver-prefs-btn').addEventListener('click',
|
719
720
|
() => UI.showSolverPreferencesDialog());
|
721
|
+
this.modals.settings.element('power').addEventListener('click',
|
722
|
+
() => UI.togglePowerGridButton());
|
723
|
+
this.modals.settings.element('power-btn').addEventListener('click',
|
724
|
+
() => POWER_GRID_MANAGER.show());
|
720
725
|
// Make solver modal elements responsive.
|
721
726
|
this.modals.solver.ok.addEventListener('click',
|
722
727
|
() => UI.updateSolverPreferences());
|
@@ -773,7 +778,12 @@ class GUIController extends Controller {
|
|
773
778
|
this.modals.process.element('UB-x').addEventListener('click', eoxedit);
|
774
779
|
this.modals.process.element('IL-x').addEventListener('click', eoxedit);
|
775
780
|
this.modals.process.element('LCF-x').addEventListener('click', eoxedit);
|
776
|
-
|
781
|
+
// Processes can represent power grid elements.
|
782
|
+
this.modals.process.element('grid-plate').addEventListener(
|
783
|
+
'mouseenter', () => UI.showGridPlateMenu('process'));
|
784
|
+
// Make the grid plate menu responsive.
|
785
|
+
this.modals.process.element('grid-plate-menu').addEventListener(
|
786
|
+
'mouseleave', () => UI.hideGridPlateMenu('process'));
|
777
787
|
this.modals.product.ok.addEventListener('click',
|
778
788
|
() => UI.updateProductProperties());
|
779
789
|
this.modals.product.cancel.addEventListener('click',
|
@@ -2242,8 +2252,9 @@ class GUIController extends Controller {
|
|
2242
2252
|
while(i < inp.length && inp[i].disabled) i++;
|
2243
2253
|
if(i < inp.length) {
|
2244
2254
|
inp[i].focus();
|
2245
|
-
} else if('constraint-modal
|
2246
|
-
|
2255
|
+
} else if(['constraint-modal', 'boundline-data-modal',
|
2256
|
+
'xp-clusters-modal'].indexOf(topmod.id) >= 0) {
|
2257
|
+
// NOTE: Constraint modal, boundline data modal and "ignore clusters" modal must NOT close
|
2247
2258
|
// when Enter is pressed, but only de-focus the input field.
|
2248
2259
|
e.target.blur();
|
2249
2260
|
} else {
|
@@ -2763,8 +2774,8 @@ class GUIController extends Controller {
|
|
2763
2774
|
}
|
2764
2775
|
|
2765
2776
|
hideStayOnTopDialogs() {
|
2766
|
-
// Hide and reset all stay-on-top dialogs (even when not showing)
|
2767
|
-
// NOTE:
|
2777
|
+
// Hide and reset all stay-on-top dialogs (even when not showing).
|
2778
|
+
// NOTE: This routine is called when a new model is loaded.
|
2768
2779
|
DATASET_MANAGER.dialog.style.display = 'none';
|
2769
2780
|
this.buttons.dataset.classList.remove('stay-activ');
|
2770
2781
|
DATASET_MANAGER.reset();
|
@@ -2786,6 +2797,9 @@ class GUIController extends Controller {
|
|
2786
2797
|
DOCUMENTATION_MANAGER.dialog.style.display = 'none';
|
2787
2798
|
this.buttons.documentation.classList.remove('stay-activ');
|
2788
2799
|
DOCUMENTATION_MANAGER.reset();
|
2800
|
+
TEX_MANAGER.dialog.style.display = 'none';
|
2801
|
+
this.buttons.tex.classList.remove('stay-activ');
|
2802
|
+
TEX_MANAGER.reset();
|
2789
2803
|
FINDER.dialog.style.display = 'none';
|
2790
2804
|
this.buttons.finder.classList.remove('stay-activ');
|
2791
2805
|
FINDER.reset();
|
@@ -3558,10 +3572,13 @@ console.log('HERE name conflicts', name_conflicts, mapping);
|
|
3558
3572
|
this.setBox('settings-decimal-comma', model.decimal_comma);
|
3559
3573
|
this.setBox('settings-align-to-grid', model.align_to_grid);
|
3560
3574
|
this.setBox('settings-block-arrows', model.show_block_arrows);
|
3561
|
-
this.setBox('settings-diagnose',
|
3575
|
+
this.setBox('settings-diagnose', model.always_diagnose);
|
3576
|
+
this.setBox('settings-power', model.with_power_flow);
|
3562
3577
|
this.setBox('settings-cost-prices', model.infer_cost_prices);
|
3563
3578
|
this.setBox('settings-report-results', model.report_results);
|
3564
3579
|
this.setBox('settings-encrypt', model.encrypt);
|
3580
|
+
const pg_btn = md.element('power-btn');
|
3581
|
+
pg_btn.style.display = (model.with_power_flow ? 'inline-block' : 'none');
|
3565
3582
|
md.show('name');
|
3566
3583
|
}
|
3567
3584
|
|
@@ -3616,9 +3633,9 @@ console.log('HERE name conflicts', name_conflicts, mapping);
|
|
3616
3633
|
model.report_results = UI.boxChecked('settings-report-results');
|
3617
3634
|
model.encrypt = UI.boxChecked('settings-encrypt');
|
3618
3635
|
model.decimal_comma = UI.boxChecked('settings-decimal-comma');
|
3619
|
-
|
3636
|
+
model.always_diagnose = this.boxChecked('settings-diagnose');
|
3620
3637
|
// Notify modeler that diagnosis changes the value of +INF.
|
3621
|
-
if(
|
3638
|
+
if(model.always_diagnose) {
|
3622
3639
|
UI.notify('To diagnose unbounded problems, values beyond 1e+10 ' +
|
3623
3640
|
'are considered as infinite (\u221E)');
|
3624
3641
|
}
|
@@ -3627,6 +3644,9 @@ console.log('HERE name conflicts', name_conflicts, mapping);
|
|
3627
3644
|
redraw = !model.align_to_grid && cb;
|
3628
3645
|
model.align_to_grid = cb;
|
3629
3646
|
model.grid_pixels = Math.floor(px);
|
3647
|
+
cb = UI.boxChecked('settings-power');
|
3648
|
+
redraw = redraw || cb !== model.with_power_flow;
|
3649
|
+
model.with_power_flow = cb;
|
3630
3650
|
cb = UI.boxChecked('settings-cost-prices');
|
3631
3651
|
redraw = redraw || cb !== model.infer_cost_prices;
|
3632
3652
|
model.infer_cost_prices = cb;
|
@@ -3674,6 +3694,20 @@ console.log('HERE name conflicts', name_conflicts, mapping);
|
|
3674
3694
|
if(redraw) this.drawDiagram(model);
|
3675
3695
|
}
|
3676
3696
|
|
3697
|
+
togglePowerGridButton() {
|
3698
|
+
// Responds to clicking the "power grid options" checkbox by toggling
|
3699
|
+
// the "View/edit power grids" button.
|
3700
|
+
const
|
3701
|
+
cb = this.modals.settings.element('power'),
|
3702
|
+
pb = this.modals.settings.element('power-btn');
|
3703
|
+
// NOTE: When clicked, state has not been updated yet.
|
3704
|
+
if(cb.classList.contains('clear')) {
|
3705
|
+
pb.style.display = 'inline-block';
|
3706
|
+
} else {
|
3707
|
+
pb.style.display = 'none';
|
3708
|
+
}
|
3709
|
+
}
|
3710
|
+
|
3677
3711
|
// Solver preferences modal
|
3678
3712
|
|
3679
3713
|
showSolverPreferencesDialog() {
|
@@ -3749,6 +3783,17 @@ console.log('HERE name conflicts', name_conflicts, mapping);
|
|
3749
3783
|
md.group = group;
|
3750
3784
|
md.element('name').value = p.name;
|
3751
3785
|
md.element('actor').value = (p.hasActor ? p.actor.name : '');
|
3786
|
+
md.element('length').value = p.length_in_km;
|
3787
|
+
md.grid_id = (p.grid ? p.grid.id : '');
|
3788
|
+
this.hideGridPlateMenu('process');
|
3789
|
+
this.updateGridFields();
|
3790
|
+
const tex = md.element('tex-id');
|
3791
|
+
tex.value = p.TEX_id;
|
3792
|
+
if(TEX_MANAGER.visible) {
|
3793
|
+
tex.style.display = 'block';
|
3794
|
+
} else {
|
3795
|
+
tex.style.display = 'none';
|
3796
|
+
}
|
3752
3797
|
// Focus on lower bound when showing the dialog for a group.
|
3753
3798
|
if(group.length > 0) {
|
3754
3799
|
attr = 'LB';
|
@@ -3766,6 +3811,61 @@ console.log('HERE name conflicts', name_conflicts, mapping);
|
|
3766
3811
|
md.element(attr + '-x').dispatchEvent(new Event('click'));
|
3767
3812
|
}
|
3768
3813
|
}
|
3814
|
+
|
3815
|
+
showGridPlateMenu(modal) {
|
3816
|
+
const md = this.modals[modal];
|
3817
|
+
POWER_GRID_MANAGER.updateGridMenu(modal);
|
3818
|
+
md.element('grid-plate-menu').style.display = 'block';
|
3819
|
+
}
|
3820
|
+
|
3821
|
+
hideGridPlateMenu(modal) {
|
3822
|
+
const md = this.modals[modal];
|
3823
|
+
md.element('grid-plate-menu').style.display = 'none';
|
3824
|
+
}
|
3825
|
+
|
3826
|
+
setGridPlate(div) {
|
3827
|
+
const
|
3828
|
+
parts = div.id.split('-'),
|
3829
|
+
modal = parts[0],
|
3830
|
+
md = this.modals[modal],
|
3831
|
+
id = parts.pop(),
|
3832
|
+
grid = MODEL.powerGridByID(id);
|
3833
|
+
// NOTE: Store power grid identifier as property of the modal.
|
3834
|
+
md.grid_id = (grid ? id : '');
|
3835
|
+
this.updateGridFields();
|
3836
|
+
}
|
3837
|
+
|
3838
|
+
updateGridFields() {
|
3839
|
+
// Adjust the powergrid-related elements of the dialog according to
|
3840
|
+
// the value of the `grid_id` property of the modal.
|
3841
|
+
const
|
3842
|
+
md = this.modals.process,
|
3843
|
+
plate = md.element('grid-plate'),
|
3844
|
+
overlay = md.element('grid-overlay'),
|
3845
|
+
notab = ['LB', 'IL', 'LCF'],
|
3846
|
+
pg = MODEL.powerGridByID(md.grid_id);
|
3847
|
+
if(pg) {
|
3848
|
+
plate.className = 'grid-kV-plate';
|
3849
|
+
plate.style.backgroundColor = pg.color;
|
3850
|
+
plate.innerHTML = pg.voltage;
|
3851
|
+
overlay.style.display = 'block';
|
3852
|
+
// Disable tab stop for the properties that are now not shown.
|
3853
|
+
for(let i = 0; i < notab.length; i++) {
|
3854
|
+
md.element(notab[i]).tabIndex = -1;
|
3855
|
+
}
|
3856
|
+
} else {
|
3857
|
+
plate.innerHTML = '(↯)';
|
3858
|
+
plate.className = 'no-grid-plate';
|
3859
|
+
overlay.style.display = 'none';
|
3860
|
+
// Enable tab stop for the properties that are now not shown.
|
3861
|
+
for(let i = 0; i < notab.length; i++) {
|
3862
|
+
md.element(notab[i]).tabIndex = 0;
|
3863
|
+
}
|
3864
|
+
}
|
3865
|
+
this.hideGridPlateMenu('process');
|
3866
|
+
// Show plate "button" only when power grids option is set for model.
|
3867
|
+
plate.style.display = (MODEL.with_power_flow ? 'block' : 'none');
|
3868
|
+
}
|
3769
3869
|
|
3770
3870
|
updateProcessProperties() {
|
3771
3871
|
// Validates process properties, and only updates the edited process
|
@@ -3826,6 +3926,10 @@ console.log('HERE name conflicts', name_conflicts, mapping);
|
|
3826
3926
|
p.integer_level = this.boxChecked('process-integer');
|
3827
3927
|
p.level_to_zero = this.boxChecked('process-shut-down');
|
3828
3928
|
p.collapsed = this.boxChecked('process-collapsed');
|
3929
|
+
p.power_grid = MODEL.powerGridByID(md.grid_id);
|
3930
|
+
p.length_in_km = safeStrToFloat(md.element('length').value, 0);
|
3931
|
+
p.TEX_id = md.element('tex-id').value;
|
3932
|
+
if(TEX_MANAGER.visible) TEX_MANAGER.update(p);
|
3829
3933
|
if(md.group.length > 1) {
|
3830
3934
|
// Redraw the entire diagram, as multiple processes may have changed.
|
3831
3935
|
md.updateModifiedProperties(p);
|
@@ -3852,6 +3956,13 @@ console.log('HERE name conflicts', name_conflicts, mapping);
|
|
3852
3956
|
md.element('P-unit').innerHTML =
|
3853
3957
|
(p.scale_unit === '1' ? '' : '/' + p.scale_unit);
|
3854
3958
|
md.element('currency').innerHTML = MODEL.currency_unit;
|
3959
|
+
const tex = md.element('tex-id');
|
3960
|
+
tex.value = p.TEX_id;
|
3961
|
+
if(TEX_MANAGER.visible) {
|
3962
|
+
tex.style.display = 'block';
|
3963
|
+
} else {
|
3964
|
+
tex.style.display = 'none';
|
3965
|
+
}
|
3855
3966
|
// NOTE: IO parameter status is not "group-edited"!
|
3856
3967
|
this.setImportExportBox('product', MODEL.ioType(p));
|
3857
3968
|
// Focus on lower bound when showing the dialog for a group.
|
@@ -3924,9 +4035,11 @@ console.log('HERE name conflicts', name_conflicts, mapping);
|
|
3924
4035
|
p.upper_bound)) return false;
|
3925
4036
|
if(p.name.startsWith('$')) {
|
3926
4037
|
// NOTE: For actor cash flow data products, price and initial
|
3927
|
-
// level must remain blank
|
4038
|
+
// level must remain blank...
|
3928
4039
|
md.element('P').value = '';
|
3929
4040
|
md.element('IL').value = '';
|
4041
|
+
// ... and the unit must be the model's currency unit.
|
4042
|
+
md.element('unit').value = MODEL.currency_unit;
|
3930
4043
|
}
|
3931
4044
|
if(!this.updateExpressionInput('product-IL', 'initial level',
|
3932
4045
|
p.initial_level)) return false;
|
@@ -3943,10 +4056,10 @@ console.log('HERE name conflicts', name_conflicts, mapping);
|
|
3943
4056
|
}
|
3944
4057
|
// At this point, all input has been validated, so entity properties
|
3945
4058
|
// can be modified.
|
4059
|
+
p.changeScaleUnit(md.element('unit').value);
|
3946
4060
|
if(!p.name.startsWith('$')) {
|
3947
4061
|
// NOTE: For actor cash flow data products, these properties must
|
3948
4062
|
// also retain their initial value.
|
3949
|
-
p.changeScaleUnit(md.element('unit').value);
|
3950
4063
|
p.is_source = this.boxChecked('product-source');
|
3951
4064
|
p.is_sink = this.boxChecked('product-sink');
|
3952
4065
|
// NOTE: Do not unset `is_data` if product has ingoing data arrows.
|
@@ -3964,6 +4077,8 @@ console.log('HERE name conflicts', name_conflicts, mapping);
|
|
3964
4077
|
p.no_links = this.boxChecked('product-no-links');
|
3965
4078
|
let must_redraw = (pnl !== p.no_links);
|
3966
4079
|
MODEL.ioUpdate(p, this.getImportExportBox('product'));
|
4080
|
+
p.TEX_id = md.element('tex-id').value;
|
4081
|
+
if(TEX_MANAGER.visible) TEX_MANAGER.update(p);
|
3967
4082
|
// If a group was edited, update all entities in this group.
|
3968
4083
|
if(md.group.length > 0) md.updateModifiedProperties(p);
|
3969
4084
|
if(must_redraw || md.group.length > 1) {
|
@@ -11,7 +11,7 @@ for the Linny-R Dataset Manager dialog.
|
|
11
11
|
*/
|
12
12
|
|
13
13
|
/*
|
14
|
-
Copyright (c) 2017-
|
14
|
+
Copyright (c) 2017-2024 Delft University of Technology
|
15
15
|
|
16
16
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
17
17
|
of this software and associated documentation files (the "Software"), to deal
|
@@ -577,8 +577,8 @@ class GUIDatasetManager extends DatasetManager {
|
|
577
577
|
}
|
578
578
|
|
579
579
|
selectModifier(event, id, x=true) {
|
580
|
-
// Select modifier, or when double-clicked, edit its expression
|
581
|
-
// name of the modifier
|
580
|
+
// Select modifier, or when double-clicked, edit its expression when
|
581
|
+
// x = TRUE, or the name of the modifier when x = FALSE.
|
582
582
|
this.focal_table = this.modifier_table;
|
583
583
|
if(this.selected_dataset) {
|
584
584
|
const m = this.selected_dataset.modifiers[UI.nameToID(id)],
|
@@ -781,13 +781,17 @@ class GUIDatasetManager extends DatasetManager {
|
|
781
781
|
if(this.selected_modifier) ms = this.selected_modifier.selector;
|
782
782
|
md = this.rename_selector_modal;
|
783
783
|
}
|
784
|
+
md.element('type').innerText = 'dataset modifier';
|
784
785
|
md.element('name').value = ms;
|
785
786
|
md.show('name');
|
786
787
|
}
|
787
788
|
|
788
789
|
newModifier() {
|
790
|
+
const md = this.new_selector_modal;
|
791
|
+
// NOTE: Selector modal is also used by constraint editor.
|
792
|
+
if(md.element('type').innerText !== 'dataset modifier') return;
|
789
793
|
const
|
790
|
-
sel =
|
794
|
+
sel = md.element('name').value,
|
791
795
|
m = this.selected_dataset.addModifier(sel);
|
792
796
|
if(m) {
|
793
797
|
this.selected_modifier = m;
|
@@ -795,72 +799,76 @@ class GUIDatasetManager extends DatasetManager {
|
|
795
799
|
// (ignoring those with wildcards)
|
796
800
|
const sl = this.selected_dataset.plainSelectors;
|
797
801
|
if(sl.length > 1) MODEL.expandDimension(sl);
|
798
|
-
|
802
|
+
md.hide();
|
799
803
|
this.updateModifiers();
|
800
804
|
}
|
801
805
|
}
|
802
806
|
|
803
807
|
renameModifier() {
|
804
808
|
if(!this.selected_modifier) return;
|
809
|
+
const md = this.rename_selector_modal;
|
810
|
+
// NOTE: Selector modal is also used by constraint editor.
|
811
|
+
if(md.element('type').innerText !== 'dataset modifier') return;
|
805
812
|
const
|
806
813
|
wild = this.selected_modifier.hasWildcards,
|
807
|
-
sel =
|
808
|
-
// NOTE:
|
814
|
+
sel = md.element('name').value,
|
815
|
+
// NOTE: Normal dataset selector, so remove all invalid characters.
|
809
816
|
clean_sel = sel.replace(/[^a-zA-z0-9\%\+\-\?\*]/g, ''),
|
810
817
|
// Keep track of old name
|
811
818
|
oldm = this.selected_modifier,
|
812
|
-
// NOTE: addModifier returns existing one if selector not changed
|
819
|
+
// NOTE: addModifier returns existing one if selector not changed.
|
813
820
|
m = this.selected_dataset.addModifier(clean_sel);
|
814
821
|
// NULL can result when new name is invalid
|
815
822
|
if(!m) return;
|
816
|
-
// If selected modifier was the dataset default selector, update it
|
823
|
+
// If selected modifier was the dataset default selector, update it.
|
817
824
|
if(oldm.selector === this.selected_dataset.default_selector) {
|
818
825
|
this.selected_dataset.default_selector = m.selector;
|
819
826
|
}
|
820
827
|
MODEL.renameSelectorInExperiments(oldm.selector, clean_sel);
|
821
|
-
// If only case has changed, just update the selector
|
828
|
+
// If only case has changed, just update the selector.
|
822
829
|
if(m === oldm) {
|
823
830
|
m.selector = clean_sel;
|
824
831
|
this.updateDialog();
|
825
|
-
|
832
|
+
md.hide();
|
826
833
|
return;
|
827
834
|
}
|
828
|
-
// Rest is needed only when a new modifier has been added
|
835
|
+
// Rest is needed only when a new modifier has been added.
|
829
836
|
m.expression = oldm.expression;
|
830
837
|
if(wild) {
|
831
|
-
// Wildcard selector means: recompile the modifier expression
|
838
|
+
// Wildcard selector means: recompile the modifier expression.
|
832
839
|
m.expression.attribute = m.selector;
|
833
840
|
m.expression.compile();
|
834
841
|
}
|
835
842
|
this.deleteModifier();
|
836
843
|
this.selected_modifier = m;
|
837
|
-
// Update all chartvariables referencing this dataset + old selector
|
844
|
+
// Update all chartvariables referencing this dataset + old selector.
|
838
845
|
const vl = MODEL.datasetVariables;
|
839
846
|
let cv_cnt = 0;
|
840
847
|
for(let i = 0; i < vl.length; i++) {
|
848
|
+
const v = vl[i];
|
841
849
|
if(v.object === this.selected_dataset && v.attribute === oldm.selector) {
|
842
850
|
v.attribute = m.selector;
|
843
851
|
cv_cnt++;
|
844
852
|
}
|
845
853
|
}
|
846
|
-
// Also replace old selector in all expressions (count these as well)
|
854
|
+
// Also replace old selector in all expressions (count these as well).
|
847
855
|
const xr_cnt = MODEL.replaceAttributeInExpressions(
|
848
856
|
oldm.dataset.name + '|' + oldm.selector, m.selector);
|
849
|
-
// Notify modeler of changes (if any)
|
857
|
+
// Notify modeler of changes (if any).
|
850
858
|
const msg = [];
|
851
859
|
if(cv_cnt) msg.push(pluralS(cv_cnt, ' chart variable'));
|
852
860
|
if(xr_cnt) msg.push(pluralS(xr_cnt, ' expression variable'));
|
853
861
|
if(msg.length) {
|
854
862
|
UI.notify('Updated ' + msg.join(' and '));
|
855
863
|
// Also update these stay-on-top dialogs, as they may display a
|
856
|
-
// variable name for this dataset + modifier
|
864
|
+
// variable name for this dataset + modifier.
|
857
865
|
UI.updateControllerDialogs('CDEFJX');
|
858
866
|
}
|
859
|
-
// NOTE:
|
860
|
-
// (ignoring those with wildcards)
|
867
|
+
// NOTE: Update dimensions only if dataset now has 2 or more modifiers
|
868
|
+
// (ignoring those with wildcards).
|
861
869
|
const sl = this.selected_dataset.plainSelectors;
|
862
870
|
if(sl.length > 1) MODEL.expandDimension(sl);
|
863
|
-
|
871
|
+
md.hide();
|
864
872
|
this.updateModifiers();
|
865
873
|
}
|
866
874
|
|
@@ -12,7 +12,7 @@ viewing and editing documentation text for model entities.
|
|
12
12
|
*/
|
13
13
|
|
14
14
|
/*
|
15
|
-
Copyright (c) 2017-
|
15
|
+
Copyright (c) 2017-2024 Delft University of Technology
|
16
16
|
|
17
17
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
18
18
|
of this software and associated documentation files (the "Software"), to deal
|
@@ -336,8 +336,14 @@ class DocumentationManager {
|
|
336
336
|
this.markup = (e.comments ? e.comments : '');
|
337
337
|
this.editor.value = this.markup;
|
338
338
|
this.viewer.innerHTML = this.markdown;
|
339
|
-
|
340
|
-
|
339
|
+
if(e.grid && MODEL.solved && MODEL.with_power_flow) {
|
340
|
+
// Show cycle flows instead of comments.
|
341
|
+
const cf = POWER_GRID_MANAGER.allCycleFlows(e);
|
342
|
+
if(cf) this.viewer.innerHTML = cf;
|
343
|
+
} else {
|
344
|
+
this.edit_btn.classList.remove('disab');
|
345
|
+
this.edit_btn.classList.add('enab');
|
346
|
+
}
|
341
347
|
// NOTE: Permit documentation of the model by raising the dialog.
|
342
348
|
if(this.entity === MODEL) this.dialog.style.zIndex = 101;
|
343
349
|
} else if(e instanceof DatasetModifier) {
|
@@ -354,6 +360,8 @@ class DocumentationManager {
|
|
354
360
|
}
|
355
361
|
}
|
356
362
|
}
|
363
|
+
// When TEX renderer is visible, also update it.
|
364
|
+
if(TEX_MANAGER.visible) TEX_MANAGER.update(e, shift);
|
357
365
|
}
|
358
366
|
|
359
367
|
rewrite(str) {
|
@@ -11,7 +11,7 @@ for the Linny-R Equation Manager dialog.
|
|
11
11
|
*/
|
12
12
|
|
13
13
|
/*
|
14
|
-
Copyright (c) 2017-
|
14
|
+
Copyright (c) 2017-2024 Delft University of Technology
|
15
15
|
|
16
16
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
17
17
|
of this software and associated documentation files (the "Software"), to deal
|
@@ -11,7 +11,7 @@ for the Linny-R Experiment Manager dialog.
|
|
11
11
|
*/
|
12
12
|
|
13
13
|
/*
|
14
|
-
Copyright (c) 2017-
|
14
|
+
Copyright (c) 2017-2024 Delft University of Technology
|
15
15
|
|
16
16
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
17
17
|
of this software and associated documentation files (the "Software"), to deal
|
@@ -11,7 +11,7 @@ functionality for the Linny-R Expression Editor dialog.
|
|
11
11
|
*/
|
12
12
|
|
13
13
|
/*
|
14
|
-
Copyright (c) 2017-
|
14
|
+
Copyright (c) 2017-2024 Delft University of Technology
|
15
15
|
|
16
16
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
17
17
|
of this software and associated documentation files (the "Software"), to deal
|
@@ -263,6 +263,9 @@ NOTE: Grouping groups results in a single group, e.g., (1;2);(3;4;5) evaluates a
|
|
263
263
|
} else if(!this.edited_input_id && EQUATION_MANAGER.edited_expression) {
|
264
264
|
own = MODEL.equations_dataset;
|
265
265
|
sel = EQUATION_MANAGER.selected_modifier.selector;
|
266
|
+
} else if(!this.edited_input_id && CONSTRAINT_EDITOR.edited_expression) {
|
267
|
+
own = CONSTRAINT_EDITOR.selected;
|
268
|
+
sel = CONSTRAINT_EDITOR.selected_selector;
|
266
269
|
} else {
|
267
270
|
own = UI.edited_object;
|
268
271
|
sel = this.edited_input_id.split('-').pop();
|
@@ -292,6 +295,9 @@ NOTE: Grouping groups results in a single group, e.g., (1;2);(3;4;5) evaluates a
|
|
292
295
|
DATASET_MANAGER.modifyExpression(xp.expr);
|
293
296
|
} else if(EQUATION_MANAGER.edited_expression) {
|
294
297
|
EQUATION_MANAGER.modifyEquation(xp.expr);
|
298
|
+
} else if(CONSTRAINT_EDITOR.edited_expression) {
|
299
|
+
// NOTE: Boundline selector expressions may result in a grouping.
|
300
|
+
CONSTRAINT_EDITOR.modifyExpression(xp.expr, xp.concatenating);
|
295
301
|
}
|
296
302
|
UI.modals.expression.hide();
|
297
303
|
return true;
|
@@ -11,7 +11,7 @@ functionality for the Linny-R File Manager.
|
|
11
11
|
*/
|
12
12
|
|
13
13
|
/*
|
14
|
-
Copyright (c) 2017-
|
14
|
+
Copyright (c) 2017-2024 Delft University of Technology
|
15
15
|
|
16
16
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
17
17
|
of this software and associated documentation files (the "Software"), to deal
|
@@ -49,6 +49,14 @@ class GUIFileManager {
|
|
49
49
|
getRemoteData(dataset, url) {
|
50
50
|
// Gets data from a URL, or from a file on the local host
|
51
51
|
if(url === '') return;
|
52
|
+
if(url.indexOf('%') >= 0) {
|
53
|
+
// Expand %i, %j and %k if used in the URL.
|
54
|
+
const letters = ['i', 'j', 'k'];
|
55
|
+
for(let i = 0; i < letters.length; i++) {
|
56
|
+
const l = letters[i];
|
57
|
+
url = url.replaceAll('%' + l, valueOfIndexVariable(l));
|
58
|
+
}
|
59
|
+
}
|
52
60
|
// NOTE: add this dataset to the "loading" list...
|
53
61
|
addDistinct(dataset, MODEL.loading_datasets);
|
54
62
|
// ... and allow for 3 more seconds (6 times 500 ms) to complete
|
@@ -63,19 +71,29 @@ class GUIFileManager {
|
|
63
71
|
})
|
64
72
|
.then((data) => {
|
65
73
|
if(data !== '' && UI.postResponseOK(data)) {
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
data
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
// Show data in text area when the SERIES dialog is visible
|
75
|
-
if(!UI.hidden('series-modal')) {
|
76
|
-
DATASET_MANAGER.series_data.value = data.split(';').join('\n');
|
74
|
+
if(dataset instanceof BoundLine) {
|
75
|
+
// Server must return semicolon-separated list of white-
|
76
|
+
// space-separated list of numbers.
|
77
|
+
dataset.unpackPointDataString(data);
|
78
|
+
// Show data in boundline data modal when it is visible.
|
79
|
+
if(!UI.hidden('boundline-data-modal')) {
|
80
|
+
CONSTRAINT_EDITOR.stopEditing(false);
|
81
|
+
}
|
77
82
|
} else {
|
78
|
-
|
83
|
+
// Server must return either semicolon-separated or
|
84
|
+
// newline-separated string of numbers
|
85
|
+
if(data.indexOf(';') < 0) {
|
86
|
+
// If no semicolon found, replace newlines by semicolons
|
87
|
+
data = data.trim().split('\n').join(';');
|
88
|
+
}
|
89
|
+
// Remove all white space
|
90
|
+
data = data.replace(/\s+/g, '');
|
91
|
+
// Show data in text area when the SERIES dialog is visible
|
92
|
+
if(!UI.hidden('series-modal')) {
|
93
|
+
DATASET_MANAGER.series_data.value = data.split(';').join('\n');
|
94
|
+
} else {
|
95
|
+
dataset.unpackDataString(data);
|
96
|
+
}
|
79
97
|
}
|
80
98
|
// NOTE: remove dataset from the "loading" list
|
81
99
|
const i = MODEL.loading_datasets.indexOf(dataset);
|
@@ -13,7 +13,7 @@ model.
|
|
13
13
|
*/
|
14
14
|
|
15
15
|
/*
|
16
|
-
Copyright (c) 2017-
|
16
|
+
Copyright (c) 2017-2024 Delft University of Technology
|
17
17
|
|
18
18
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
19
19
|
of this software and associated documentation files (the "Software"), to deal
|
@@ -12,7 +12,7 @@ dialogs, the main drawing canvas, and event handler functions.
|
|
12
12
|
*/
|
13
13
|
|
14
14
|
/*
|
15
|
-
Copyright (c) 2017-
|
15
|
+
Copyright (c) 2017-2024 Delft University of Technology
|
16
16
|
|
17
17
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
18
18
|
of this software and associated documentation files (the "Software"), to deal
|
@@ -11,7 +11,7 @@ for the Linny-R Monitor dialog.
|
|
11
11
|
*/
|
12
12
|
|
13
13
|
/*
|
14
|
-
Copyright (c) 2017-
|
14
|
+
Copyright (c) 2017-2024 Delft University of Technology
|
15
15
|
|
16
16
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
17
17
|
of this software and associated documentation files (the "Software"), to deal
|