linny-r 1.4.3 → 1.4.4

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.
Files changed (49) hide show
  1. package/README.md +102 -48
  2. package/package.json +1 -1
  3. package/server.js +31 -6
  4. package/static/images/check-off-not-same-changed.png +0 -0
  5. package/static/images/check-off-not-same-not-changed.png +0 -0
  6. package/static/images/check-off-same-changed.png +0 -0
  7. package/static/images/check-off-same-not-changed.png +0 -0
  8. package/static/images/check-on-not-same-changed.png +0 -0
  9. package/static/images/check-on-not-same-not-changed.png +0 -0
  10. package/static/images/check-on-same-changed.png +0 -0
  11. package/static/images/check-on-same-not-changed.png +0 -0
  12. package/static/images/eq-not-same-changed.png +0 -0
  13. package/static/images/eq-not-same-not-changed.png +0 -0
  14. package/static/images/eq-same-changed.png +0 -0
  15. package/static/images/eq-same-not-changed.png +0 -0
  16. package/static/images/ne-not-same-changed.png +0 -0
  17. package/static/images/ne-not-same-not-changed.png +0 -0
  18. package/static/images/ne-same-changed.png +0 -0
  19. package/static/images/ne-same-not-changed.png +0 -0
  20. package/static/images/sort-asc-lead.png +0 -0
  21. package/static/images/sort-asc.png +0 -0
  22. package/static/images/sort-desc-lead.png +0 -0
  23. package/static/images/sort-desc.png +0 -0
  24. package/static/images/sort-not.png +0 -0
  25. package/static/index.html +51 -35
  26. package/static/linny-r.css +167 -53
  27. package/static/scripts/linny-r-gui-actor-manager.js +340 -0
  28. package/static/scripts/linny-r-gui-chart-manager.js +944 -0
  29. package/static/scripts/linny-r-gui-constraint-editor.js +681 -0
  30. package/static/scripts/linny-r-gui-controller.js +4005 -0
  31. package/static/scripts/linny-r-gui-dataset-manager.js +1176 -0
  32. package/static/scripts/linny-r-gui-documentation-manager.js +739 -0
  33. package/static/scripts/linny-r-gui-equation-manager.js +307 -0
  34. package/static/scripts/linny-r-gui-experiment-manager.js +1944 -0
  35. package/static/scripts/linny-r-gui-expression-editor.js +449 -0
  36. package/static/scripts/linny-r-gui-file-manager.js +392 -0
  37. package/static/scripts/linny-r-gui-finder.js +727 -0
  38. package/static/scripts/linny-r-gui-model-autosaver.js +230 -0
  39. package/static/scripts/linny-r-gui-monitor.js +448 -0
  40. package/static/scripts/linny-r-gui-paper.js +2789 -0
  41. package/static/scripts/linny-r-gui-receiver.js +323 -0
  42. package/static/scripts/linny-r-gui-repository-browser.js +819 -0
  43. package/static/scripts/linny-r-gui-scale-unit-manager.js +244 -0
  44. package/static/scripts/linny-r-gui-sensitivity-analysis.js +778 -0
  45. package/static/scripts/linny-r-gui-undo-redo.js +560 -0
  46. package/static/scripts/linny-r-model.js +24 -11
  47. package/static/scripts/linny-r-utils.js +10 -0
  48. package/static/scripts/linny-r-vm.js +21 -12
  49. package/static/scripts/linny-r-gui.js +0 -16908
@@ -0,0 +1,244 @@
1
+ /*
2
+ Linny-R is an executable graphical specification language for (mixed integer)
3
+ linear programming (MILP) problems, especially unit commitment problems (UCP).
4
+ The Linny-R language and tool have been developed by Pieter Bots at Delft
5
+ University of Technology, starting in 2009. The project to develop a browser-
6
+ based version started in 2017. See https://linny-r.org for more information.
7
+
8
+ This JavaScript file (linny-r-gui-scale-unit-manager.js) provides the GUI
9
+ functionality for the Linny-R Scale Unit Manager dialog.
10
+
11
+ */
12
+
13
+ /*
14
+ Copyright (c) 2017-2023 Delft University of Technology
15
+
16
+ Permission is hereby granted, free of charge, to any person obtaining a copy
17
+ of this software and associated documentation files (the "Software"), to deal
18
+ in the Software without restriction, including without limitation the rights to
19
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
20
+ of the Software, and to permit persons to whom the Software is furnished to do
21
+ so, subject to the following conditions:
22
+
23
+ The above copyright notice and this permission notice shall be included in
24
+ all copies or substantial portions of the Software.
25
+
26
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
29
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
31
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32
+ SOFTWARE.
33
+ */
34
+
35
+ // CLASS ScaleUnitManager (modal dialog!)
36
+ class ScaleUnitManager {
37
+ constructor() {
38
+ // Add the scale units modal
39
+ this.dialog = new ModalDialog('scale-units');
40
+ this.dialog.close.addEventListener('click',
41
+ () => SCALE_UNIT_MANAGER.dialog.hide());
42
+ // Make the add, edit and delete buttons of this modal responsive
43
+ this.dialog.element('new-btn').addEventListener('click',
44
+ () => SCALE_UNIT_MANAGER.promptForScaleUnit());
45
+ this.dialog.element('edit-btn').addEventListener('click',
46
+ () => SCALE_UNIT_MANAGER.editScaleUnit());
47
+ this.dialog.element('delete-btn').addEventListener('click',
48
+ () => SCALE_UNIT_MANAGER.deleteScaleUnit());
49
+ // Add the scale unit definition modal
50
+ this.new_scale_unit_modal = new ModalDialog('new-scale-unit');
51
+ this.new_scale_unit_modal.ok.addEventListener(
52
+ 'click', () => SCALE_UNIT_MANAGER.addNewScaleUnit());
53
+ this.new_scale_unit_modal.cancel.addEventListener(
54
+ 'click', () => SCALE_UNIT_MANAGER.new_scale_unit_modal.hide());
55
+ this.scroll_area = this.dialog.element('scroll-area');
56
+ this.table = this.dialog.element('table');
57
+ }
58
+
59
+ get selectedUnitIsBaseUnit() {
60
+ // Returns TRUE iff selected unit is used as base unit for some unit
61
+ for(let u in this.scale_units) if(this.scale_units.hasOwnProperty(u)) {
62
+ if(this.scale_units[u].base_unit === this.selected_unit) return true;
63
+ }
64
+ return false;
65
+ }
66
+
67
+ show() {
68
+ // Show the user-defined scale units for the current model
69
+ // NOTE: add/edit/delete actions operate on this list, so changes
70
+ // take immediate effect
71
+ MODEL.cleanUpScaleUnits();
72
+ // NOTE: unit name is key in the scale units object
73
+ this.selected_unit = '';
74
+ this.last_time_selected = 0;
75
+ this.updateDialog();
76
+ this.dialog.show();
77
+ }
78
+
79
+ updateDialog() {
80
+ // Create the HTML for the scale units table and update the state
81
+ // of the action buttons
82
+ if(!MODEL.scale_units.hasOwnProperty(this.selected_unit)) {
83
+ this.selected_unit = '';
84
+ }
85
+ const
86
+ keys = Object.keys(MODEL.scale_units).sort(ciCompare),
87
+ sl = [],
88
+ ss = this.selected_unit;
89
+ let ssid = 'scntr';
90
+ if(keys.length <= 1) {
91
+ // Only one key => must be the default '1'
92
+ sl.push('<tr><td><em>No units defined</em></td></tr>');
93
+ } else {
94
+ for(let i = 1; i < keys.length; i++) {
95
+ const
96
+ s = keys[i],
97
+ clk = '" onclick="SCALE_UNIT_MANAGER.selectScaleUnit(event, \'' +
98
+ s + '\'';
99
+ if(s === ss) ssid += i;
100
+ sl.push(['<tr id="scntr', i, '" class="dataset-modif',
101
+ (s === ss ? ' sel-set' : ''),
102
+ '"><td class="dataset-selector', clk, ');">',
103
+ s, '</td><td class="dataset-selector', clk, ', \'scalar\');">',
104
+ MODEL.scale_units[s].scalar, '</td><td class="dataset-selector',
105
+ clk, ', \'base\');">', MODEL.scale_units[s].base_unit,
106
+ '</td></tr>'].join(''));
107
+ }
108
+ }
109
+ this.table.innerHTML = sl.join('');
110
+ if(ss) UI.scrollIntoView(document.getElementById(ssid));
111
+ let btns = 'scale-units-edit';
112
+ if(!this.selectedUnitIsBaseUnit) btns += ' scale-units-delete';
113
+ if(ss) {
114
+ UI.enableButtons(btns);
115
+ } else {
116
+ UI.disableButtons(btns);
117
+ }
118
+ }
119
+
120
+ selectScaleUnit(event, symbol, focus) {
121
+ // Select scale unit, and when double-clicked, allow to edit it
122
+ const
123
+ ss = this.selected_unit,
124
+ now = Date.now(),
125
+ dt = now - this.last_time_selected,
126
+ // NOTE: Alt-click and double-click indicate: edit
127
+ // Consider click to be "double" if the same modifier was clicked
128
+ // less than 300 ms ago
129
+ edit = event.altKey || (symbol === ss && dt < 300);
130
+ this.selected_unit = symbol;
131
+ this.last_time_selected = now;
132
+ if(edit) {
133
+ this.last_time_selected = 0;
134
+ this.promptForScaleUnit('Edit', focus);
135
+ return;
136
+ }
137
+ this.updateDialog();
138
+ }
139
+
140
+ promptForScaleUnit(action='Define new', focus='name') {
141
+ // Show the Add/Edit scale unit dialog for the indicated action
142
+ const md = this.new_scale_unit_modal;
143
+ // NOTE: by default, let name and base unit be empty strings, not '1'
144
+ let sv = {name: '', scalar: '1', base_unit: '' };
145
+ if(action === 'Edit' && this.selected_unit) {
146
+ sv = MODEL.scale_units[this.selected_unit];
147
+ }
148
+ md.element('action').innerText = action;
149
+ md.element('name').value = sv.name;
150
+ md.element('scalar').value = sv.scalar;
151
+ md.element('base').value = sv.base_unit;
152
+ UI.updateScaleUnitList();
153
+ this.new_scale_unit_modal.show(focus);
154
+ }
155
+
156
+ addNewScaleUnit() {
157
+ // Add the new scale unit or update the one being edited
158
+ const
159
+ md = this.new_scale_unit_modal,
160
+ edited = md.element('action').innerText === 'Edit',
161
+ // NOTE: unit name cannot contain single quotes
162
+ s = UI.cleanName(md.element('name').value).replace("'", ''),
163
+ v = md.element('scalar').value.trim(),
164
+ // NOTE: accept empty base unit to denote '1'
165
+ b = md.element('base').value.trim() || '1';
166
+ if(!s) {
167
+ // Do not accept empty string as name
168
+ UI.warn('Scale unit must have a name');
169
+ md.element('name').focus();
170
+ return;
171
+ }
172
+ if(MODEL.scale_units.hasOwnProperty(s) && !edited) {
173
+ // Do not accept existing unit as name for new unit
174
+ UI.warn(`Scale unit "${s}" is already defined`);
175
+ md.element('name').focus();
176
+ return;
177
+ }
178
+ if(b !== s && !MODEL.scale_units.hasOwnProperty(b)) {
179
+ UI.warn(`Base unit "${b}" is undefined`);
180
+ md.element('base').focus();
181
+ return;
182
+ }
183
+ if(UI.validNumericInput('new-scale-unit-scalar', 'scalar')) {
184
+ const ucs = Math.abs(safeStrToFloat(v));
185
+ if(ucs < VM.NEAR_ZERO) {
186
+ UI.warn(`Unit conversion scalar cannot be zero`);
187
+ md.element('scalar').focus();
188
+ return;
189
+ }
190
+ if(b === s && ucs !== 1) {
191
+ UI.warn(`When base unit = scale unit, scalar must equal 1`);
192
+ md.element('scalar').focus();
193
+ return;
194
+ }
195
+ const selu = this.selected_unit;
196
+ if(edited && b !== s) {
197
+ // Prevent inconsistencies across scalars
198
+ const cr = MODEL.scale_units[b].conversionRates();
199
+ if(cr.hasOwnProperty(s)) {
200
+ UI.warn(`Defining ${s} in terms of ${b} introduces a circular reference`);
201
+ md.element('base').focus();
202
+ return;
203
+ }
204
+ }
205
+ if(edited && s !== selu) {
206
+ // First rename base units
207
+ for(let u in MODEL.scale_units) if(MODEL.scale_units.hasOwnProperty(u)) {
208
+ if(MODEL.scale_units[u].base_unit === selu) {
209
+ MODEL.scale_units[u].base_unit = s;
210
+ }
211
+ }
212
+ // NOTE: renameScaleUnit replaces references to `s`, not the entry
213
+ MODEL.renameScaleUnit(selu, s);
214
+ delete MODEL.scale_units[this.selected_unit];
215
+ }
216
+ MODEL.scale_units[s] = new ScaleUnit(s, v, b);
217
+ MODEL.selected_unit = s;
218
+ this.new_scale_unit_modal.hide();
219
+ UI.updateScaleUnitList();
220
+ this.updateDialog();
221
+ }
222
+ }
223
+
224
+ editScaleUnit() {
225
+ // Allow user to edit name and/or value
226
+ if(this.selected_unit) this.promptForScaleUnit('Edit', 'scalar');
227
+ }
228
+
229
+ deleteScaleUnit() {
230
+ // Allow user to delete
231
+ // @@@TO DO: check whether scale unit is used in the model
232
+ if(this.selected_unit && !this.selectedUnitIsBaseUnit) {
233
+ delete MODEL.scale_units[this.selected_unit];
234
+ this.updateDialog();
235
+ }
236
+ }
237
+
238
+ updateScaleUnits() {
239
+ // Replace scale unit definitions of model by the new definitions
240
+ UI.updateScaleUnitList();
241
+ this.dialog.hide();
242
+ }
243
+
244
+ } // END of class ScaleUnitManager