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.
- package/README.md +102 -48
- package/package.json +1 -1
- package/server.js +31 -6
- package/static/images/check-off-not-same-changed.png +0 -0
- package/static/images/check-off-not-same-not-changed.png +0 -0
- package/static/images/check-off-same-changed.png +0 -0
- package/static/images/check-off-same-not-changed.png +0 -0
- package/static/images/check-on-not-same-changed.png +0 -0
- package/static/images/check-on-not-same-not-changed.png +0 -0
- package/static/images/check-on-same-changed.png +0 -0
- package/static/images/check-on-same-not-changed.png +0 -0
- package/static/images/eq-not-same-changed.png +0 -0
- package/static/images/eq-not-same-not-changed.png +0 -0
- package/static/images/eq-same-changed.png +0 -0
- package/static/images/eq-same-not-changed.png +0 -0
- package/static/images/ne-not-same-changed.png +0 -0
- package/static/images/ne-not-same-not-changed.png +0 -0
- package/static/images/ne-same-changed.png +0 -0
- package/static/images/ne-same-not-changed.png +0 -0
- package/static/images/sort-asc-lead.png +0 -0
- package/static/images/sort-asc.png +0 -0
- package/static/images/sort-desc-lead.png +0 -0
- package/static/images/sort-desc.png +0 -0
- package/static/images/sort-not.png +0 -0
- package/static/index.html +51 -35
- package/static/linny-r.css +167 -53
- package/static/scripts/linny-r-gui-actor-manager.js +340 -0
- package/static/scripts/linny-r-gui-chart-manager.js +944 -0
- package/static/scripts/linny-r-gui-constraint-editor.js +681 -0
- package/static/scripts/linny-r-gui-controller.js +4005 -0
- package/static/scripts/linny-r-gui-dataset-manager.js +1176 -0
- package/static/scripts/linny-r-gui-documentation-manager.js +739 -0
- package/static/scripts/linny-r-gui-equation-manager.js +307 -0
- package/static/scripts/linny-r-gui-experiment-manager.js +1944 -0
- package/static/scripts/linny-r-gui-expression-editor.js +449 -0
- package/static/scripts/linny-r-gui-file-manager.js +392 -0
- package/static/scripts/linny-r-gui-finder.js +727 -0
- package/static/scripts/linny-r-gui-model-autosaver.js +230 -0
- package/static/scripts/linny-r-gui-monitor.js +448 -0
- package/static/scripts/linny-r-gui-paper.js +2789 -0
- package/static/scripts/linny-r-gui-receiver.js +323 -0
- package/static/scripts/linny-r-gui-repository-browser.js +819 -0
- package/static/scripts/linny-r-gui-scale-unit-manager.js +244 -0
- package/static/scripts/linny-r-gui-sensitivity-analysis.js +778 -0
- package/static/scripts/linny-r-gui-undo-redo.js +560 -0
- package/static/scripts/linny-r-model.js +24 -11
- package/static/scripts/linny-r-utils.js +10 -0
- package/static/scripts/linny-r-vm.js +21 -12
- 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
|