linny-r 1.4.3 → 1.4.5
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 +450 -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 +34 -15
- package/static/scripts/linny-r-utils.js +11 -1
- package/static/scripts/linny-r-vm.js +21 -12
- package/static/scripts/linny-r-gui.js +0 -16908
@@ -0,0 +1,450 @@
|
|
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-expression-editor.js) provides the GUI
|
9
|
+
functionality for the Linny-R Expression Editor 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 ExpressionEditor
|
36
|
+
class ExpressionEditor {
|
37
|
+
constructor() {
|
38
|
+
this.dataset_dot_option = '. (this dataset)';
|
39
|
+
this.edited_input_id = '';
|
40
|
+
this.edited_expression = null;
|
41
|
+
// Dialog DOM elements
|
42
|
+
this.property = document.getElementById('expression-property');
|
43
|
+
this.text = document.getElementById('expression-text');
|
44
|
+
this.status = document.getElementById('expression-status');
|
45
|
+
this.info = document.getElementById('expression-info');
|
46
|
+
// The DOM elements for the "insert variable" bar
|
47
|
+
this.obj = document.getElementById('variable-obj');
|
48
|
+
this.name = document.getElementById('variable-name');
|
49
|
+
this.attr = document.getElementById('variable-attr');
|
50
|
+
// The quick guide to Linny-R expressions
|
51
|
+
this.info.innerHTML = `
|
52
|
+
<h3>Linny-R expressions</h3>
|
53
|
+
<p><em>NOTE: Move cursor over a</em> <code>symbol</code>
|
54
|
+
<em>for explanation.</em>
|
55
|
+
<p>
|
56
|
+
<h4>Variables</h4>
|
57
|
+
<p>
|
58
|
+
Attributes of <em
|
59
|
+
title="i.e., processes, products, links, clusters, actors and datasets"
|
60
|
+
>entities</em> are enclosed by brackets, with a vertical bar between
|
61
|
+
entity name and <em>property selector</em>, e.g.,
|
62
|
+
<code title="NOTE: Entity names are not sensitive to case or spacing.
|
63
|
+
Attributes, however, are case sensitive!">[Actor X|CF]</code> for cash flow.
|
64
|
+
Solver properties
|
65
|
+
(<code title="Absolute time step (starts at t₀)">t</code>,
|
66
|
+
<code title="Relative time step (t − t₀ + 1)">rt</code>,
|
67
|
+
<code title="Number of current block">b</code>,
|
68
|
+
<code title="Time step within current block">bt</code>,
|
69
|
+
<code title="Duration of 1 time step (in hours)">dt</code>,
|
70
|
+
<code title="Run length (# time steps)">N</code>,
|
71
|
+
<code title="Block length (# time steps)">n</code>,
|
72
|
+
<code title="Look-ahead (# time steps)">l</code>,
|
73
|
+
<code title="Number of current round (1=a, 2=b, etc.)">r</code>,
|
74
|
+
<code title="Number of last round in the sequence (1=a, 2=b, etc.)">lr</code>,
|
75
|
+
<code title="Number of rounds in the sequence">nr</code>,
|
76
|
+
<code title="Number of current experiment run (starts at 0)">x</code>,
|
77
|
+
<code title="Number of runs in the experiment">nx</code>,
|
78
|
+
<span title="Index variables of iterator dimensions)">
|
79
|
+
<code>i</code>, <code>j</code>, <code>k</code>,
|
80
|
+
</span>
|
81
|
+
<code title="Number of time steps in 1 year)">yr</code>,
|
82
|
+
<code title="Number of time steps in 1 week)">wk</code>,
|
83
|
+
<code title="Number of time steps in 1 day)">d</code>,
|
84
|
+
<code title="Number of time steps in 1 hour)">h</code>,
|
85
|
+
<code title="Number of time steps in 1 minute)">m</code>,
|
86
|
+
<code title="Number of time steps in 1 second)">s</code>,
|
87
|
+
<code title="A random number from the uniform distribution U(0, 1)">random</code>),
|
88
|
+
constants (<code title="Mathematical constant π = ${Math.PI}">pi</code>,
|
89
|
+
<code title="Logical constant true = 1
|
90
|
+
NOTE: any non-zero value evaluates as true">true</code>,
|
91
|
+
<code title="Logical constant false = 0">false</code>,
|
92
|
+
<code title="The value used for ‘unbounded’ variables (` +
|
93
|
+
VM.PLUS_INFINITY.toExponential() + `)">infinity</code>) and scale units
|
94
|
+
are <strong><em>not</em></strong> enclosed by brackets. Scale units
|
95
|
+
may be enclosed by single quotes.
|
96
|
+
</p>
|
97
|
+
<h4>Operators</h4>
|
98
|
+
<p><em>Monadic:</em>
|
99
|
+
<code title="-X evaluates as minus X">-</code>,
|
100
|
+
<code title="not X evaluates as 1 if X equals 0 (otherwise 0)">not</code>,
|
101
|
+
<code title="abs X evaluates as the absolute value of X">abs</code>,
|
102
|
+
<code title="int X evaluates as the integer part of X">int</code>,
|
103
|
+
<code title="fract X evaluates as the decimal fraction of X">fract</code>,
|
104
|
+
<code title="round X evaluates as X rounded to the nearest integer">round</code>,
|
105
|
+
<code title="sqrt X evaluates as the square root of X">sqrt</code>,
|
106
|
+
<code title="ln X evaluates as the natural logarithm of X">ln</code>,
|
107
|
+
<code title="exp X evaluates as \u{1D452} raised to the power of X">exp</code>,
|
108
|
+
<code title="sin X evaluates as the sine of X">sin</code>,
|
109
|
+
<code title="cos X evaluates as the cosine of X">cos</code>,
|
110
|
+
<code title="atan X evaluates as the inverse tangent of X">atan</code>,
|
111
|
+
<code title="binomial X evaluates as a random number from the Binomial(N, p) distribution">binomial</code>,
|
112
|
+
<code title="exponential X evaluates as a random number from the Exponential(λ) distribution">exponential</code>,
|
113
|
+
<code title="normal(X;Y) evaluates as a random number from the Normal(μ,σ) distribution">normal</code>,
|
114
|
+
<code title="poisson(X) evaluates as a random number from the Poisson(λ) distribution">poisson</code>,
|
115
|
+
<code title="triangular(X;Y;Z) evaluates as a random number from the Triangular(a,b,c) distribution
|
116
|
+
NOTE: When omitted, the third parameter c defaults to (a+b)/2">triangular</code>,
|
117
|
+
<code title="weibull(X;Y) evaluates as a random number from the Weibull(λ,k) distribution">weibull</code>,
|
118
|
+
<code title="max(X1;…;Xn) evaluates as the highest value of X1, …, Xn">max</code>,
|
119
|
+
<code title="min(X1;…;Xn) evaluates as the lowest value of X1, …, Xn">min</code>,
|
120
|
+
<code title="npv(R;N;CF) evaluates as the net present value of a constant cash flow of CF
|
121
|
+
for a period of N time steps with a discount rate R, i.e., Σ CF/(1+r)\u2071 for i=0, …, N-1.
|
122
|
+
NOTE: When the grouping contains more than 3 arguments, npv(R;X0;…;Xn)
|
123
|
+
considers X0, …, Xn as a variable cash flow time series.">npv</code><br>
|
124
|
+
|
125
|
+
<em>Arithmetic:</em>
|
126
|
+
<code title="X + Y = sum of X and Y">+</code>,
|
127
|
+
<code title="X − Y = difference between X and Y">-</code>,
|
128
|
+
<code title="X * Y = product of X and Y">*</code>,
|
129
|
+
<code title="X / Y = division of X by Y">/</code>,
|
130
|
+
<code title="X % Y = the remainder of X divided by Y">%</code>,
|
131
|
+
<code title="X ^ Y = X raised to the power of Y">^</code>,
|
132
|
+
<code title="X log Y = base X logarithm of Y">log</code><br>
|
133
|
+
|
134
|
+
<em>Comparison:</em>
|
135
|
+
<code title="X = Y evaluates as 1 if X equals Y (otherwise 0)">=</code>,
|
136
|
+
<code title="X <> Y evaluates as 1 if X does NOT equal Y (otherwise 0)"><></code>
|
137
|
+
or <code title="Alternative notation for X <> Y">!=</code>,
|
138
|
+
<code title="X < Y evaluates as 1 if X is less than Y (otherwise 0)"><</code>,
|
139
|
+
<code title="X <= Y evaluates as 1 if X is less than or equal to Y (otherwise 0)"><=</code>,
|
140
|
+
<code title="X >= Y evaluates as 1 if X is greater than or equal to Y (otherwise 0)">>=</code>,
|
141
|
+
<code title="X > Y evaluates as 1 if X is greater than Y (otherwise 0)">></code><br>
|
142
|
+
|
143
|
+
<em>Logical:</em>
|
144
|
+
<code title="X and Y evaluates as 1 if X and Y are both non-zero (otherwise 0)">and</code>,
|
145
|
+
<code title="X or Y evaluates as 1 unless X and Y are both zero (otherwise 0)">or</code><br>
|
146
|
+
|
147
|
+
<em>Conditional:</em>
|
148
|
+
<code title="X ? Y : Z evaluates as Y if X is non-zero, and otherwise as Z">X ? Y : Z</code>
|
149
|
+
(can be read as <strong>if</strong> X <strong>then</strong> Y <strong>else</strong> Z)<br>
|
150
|
+
|
151
|
+
<em>Resolving undefined values:</em>
|
152
|
+
<code title="X | Y evaluates as Y if X is undefined, and otherwise as X">X | Y</code>
|
153
|
+
(can be read as <strong>if</strong> X = ⁇ <strong>then</strong> Y <strong>else</strong> X)<br>
|
154
|
+
|
155
|
+
<em>Grouping:</em>
|
156
|
+
<code title="X ; Y evaluates as a group or “tuple” (X, Y)
|
157
|
+
NOTE: Grouping groups results in a single group, e.g., (1;2);(3;4;5) evaluates as (1;2;3;4;5)">X ; Y</code>
|
158
|
+
(use only in combination with <code>max</code>, <code>min</code>, <code>npv</code>
|
159
|
+
and probabilistic operators)<br>
|
160
|
+
</p>
|
161
|
+
<p>
|
162
|
+
Monadic operators take precedence over dyadic operators.
|
163
|
+
Use parentheses to override the default evaluation precedence.
|
164
|
+
</p>`;
|
165
|
+
// Add listeners to the GUI elements
|
166
|
+
const md = UI.modals.expression;
|
167
|
+
md.ok.addEventListener('click', () => X_EDIT.parseExpression());
|
168
|
+
md.cancel.addEventListener('click', () => X_EDIT.cancel());
|
169
|
+
// NOTE: this modal also has an information button in its header
|
170
|
+
md.info.addEventListener(
|
171
|
+
'click', () => X_EDIT.toggleExpressionInfo());
|
172
|
+
document.getElementById('variable-obj').addEventListener(
|
173
|
+
'change', () => X_EDIT.updateVariableBar());
|
174
|
+
document.getElementById('variable-name').addEventListener(
|
175
|
+
'change', () => X_EDIT.updateAttributeSelector());
|
176
|
+
document.getElementById('variable-insert').addEventListener(
|
177
|
+
'click', () => X_EDIT.insertVariable());
|
178
|
+
}
|
179
|
+
|
180
|
+
editExpression(event) {
|
181
|
+
// Infers which entity property expression is to edited from the button
|
182
|
+
// that was clicked, and then opens the dialog
|
183
|
+
const
|
184
|
+
btn = event.target,
|
185
|
+
ids = btn.id.split('-'), // 3-tuple [entity type, attribute, 'x']
|
186
|
+
prop = btn.title.substring(20); // trim "Edit expression for "
|
187
|
+
if(ids[0] === 'note') {
|
188
|
+
UI.edited_object = UI.dbl_clicked_node;
|
189
|
+
this.edited_input_id = 'note-C';
|
190
|
+
if(UI.edited_object) {
|
191
|
+
this.edited_expression = UI.edited_object.color;
|
192
|
+
} else {
|
193
|
+
this.edited_expression = null;
|
194
|
+
}
|
195
|
+
} else {
|
196
|
+
let n = '',
|
197
|
+
a = '';
|
198
|
+
if(ids[0] === 'link') {
|
199
|
+
n = document.getElementById('link-from-name').innerHTML + UI.LINK_ARROW +
|
200
|
+
document.getElementById('link-to-name').innerHTML;
|
201
|
+
} else {
|
202
|
+
n = document.getElementById(ids[0] + '-name').value;
|
203
|
+
if(ids[0] === 'process') {
|
204
|
+
a = document.getElementById('process-actor').value.trim();
|
205
|
+
}
|
206
|
+
}
|
207
|
+
if(a) n += ` (${a})`;
|
208
|
+
// NOTE: Names may contain ampersand as HTML entity.
|
209
|
+
UI.edited_object = MODEL.objectByName(n.replace('&', '&'));
|
210
|
+
this.edited_input_id = UI.edited_object.type.toLowerCase() + '-' + ids[1];
|
211
|
+
this.edited_expression = UI.edited_object.attributeExpression(ids[1]);
|
212
|
+
}
|
213
|
+
const md = UI.modals.expression;
|
214
|
+
md.element('property').innerHTML = prop;
|
215
|
+
md.element('text').value = document.getElementById(
|
216
|
+
this.edited_input_id).value.trim();
|
217
|
+
document.getElementById('variable-obj').value = 0;
|
218
|
+
this.updateVariableBar();
|
219
|
+
this.clearStatusBar();
|
220
|
+
md.show('text');
|
221
|
+
}
|
222
|
+
|
223
|
+
cancel() {
|
224
|
+
// Closes the expression editor dialog
|
225
|
+
UI.modals.expression.hide();
|
226
|
+
// Clear the "shortcut flag" that may be set by Shift-clicking the
|
227
|
+
// "add chart variable" button in the chart dialog
|
228
|
+
EQUATION_MANAGER.add_to_chart = false;
|
229
|
+
}
|
230
|
+
|
231
|
+
parseExpression() {
|
232
|
+
// Parses the contents of the expression editor
|
233
|
+
let xt = this.text.value;
|
234
|
+
// NOTE: the Insert button is quite close to the OK button, and often
|
235
|
+
// the modeler clicks OK before Insert, leaving the expression empty;
|
236
|
+
// hence assume that modeler meant to insert a variable if text is empty,
|
237
|
+
// but all three variable components have been selected
|
238
|
+
if(xt === '') {
|
239
|
+
const
|
240
|
+
n = this.name.options[this.name.selectedIndex].innerHTML,
|
241
|
+
a = this.attr.options[this.attr.selectedIndex].innerHTML;
|
242
|
+
if(n && a) xt = `[${n}${UI.OA_SEPARATOR}${a}]`;
|
243
|
+
}
|
244
|
+
// NOTE: If the expression is a dataset modifier or an equation, pass
|
245
|
+
// the dataset and the selector as extra parameters for the parser
|
246
|
+
let own = null,
|
247
|
+
sel = '';
|
248
|
+
if(!this.edited_input_id && DATASET_MANAGER.edited_expression) {
|
249
|
+
own = DATASET_MANAGER.selected_dataset;
|
250
|
+
sel = DATASET_MANAGER.selected_modifier.selector;
|
251
|
+
} else if(!this.edited_input_id && EQUATION_MANAGER.edited_expression) {
|
252
|
+
own = MODEL.equations_dataset;
|
253
|
+
sel = EQUATION_MANAGER.selected_modifier.selector;
|
254
|
+
} else {
|
255
|
+
own = UI.edited_object;
|
256
|
+
sel = this.edited_input_id.split('-').pop();
|
257
|
+
}
|
258
|
+
const xp = new ExpressionParser(xt, own, sel);
|
259
|
+
if(xp.error) {
|
260
|
+
this.status.innerHTML = xp.error;
|
261
|
+
this.status.style.backgroundColor = 'Yellow';
|
262
|
+
SOUNDS.warning.play();
|
263
|
+
this.text.focus();
|
264
|
+
this.text.selectionStart = xp.pit - xp.los;
|
265
|
+
this.text.selectionEnd = xp.pit;
|
266
|
+
return false;
|
267
|
+
} else {
|
268
|
+
if(this.edited_input_id) {
|
269
|
+
document.getElementById(this.edited_input_id).value = xp.expr;
|
270
|
+
// NOTE: entity properties must be exogenous parameters
|
271
|
+
const eo = UI.edited_object;
|
272
|
+
if(eo && xp.is_level_based &&
|
273
|
+
!(eo instanceof Dataset || eo instanceof Note)) {
|
274
|
+
UI.warn(['Expression for', this.property.innerHTML,
|
275
|
+
'of<strong>', eo.displayName,
|
276
|
+
'</strong>contains a solution-dependent variable'].join(' '));
|
277
|
+
}
|
278
|
+
this.edited_input_id = '';
|
279
|
+
} else if(DATASET_MANAGER.edited_expression) {
|
280
|
+
DATASET_MANAGER.modifyExpression(xp.expr);
|
281
|
+
} else if(EQUATION_MANAGER.edited_expression) {
|
282
|
+
EQUATION_MANAGER.modifyEquation(xp.expr);
|
283
|
+
}
|
284
|
+
UI.modals.expression.hide();
|
285
|
+
return true;
|
286
|
+
}
|
287
|
+
}
|
288
|
+
|
289
|
+
clearStatusBar() {
|
290
|
+
this.status.style.backgroundColor = UI.color.dialog_background;
|
291
|
+
this.status.innerHTML = ' ';
|
292
|
+
}
|
293
|
+
|
294
|
+
namesByType(type) {
|
295
|
+
// Returns a list of entity names of the specified types
|
296
|
+
// (used only to generate the options of SELECT elements)
|
297
|
+
// NOTE: When editing a dataset modifier expression, start the list of
|
298
|
+
// datasets with the edited dataset (denoted by a dot) while omitting the
|
299
|
+
// name of that dataset from the list
|
300
|
+
let e,
|
301
|
+
l = MODEL.setByType(type),
|
302
|
+
n = [],
|
303
|
+
dsn = null;
|
304
|
+
if(type === 'Dataset' && DATASET_MANAGER.edited_expression) {
|
305
|
+
dsn = DATASET_MANAGER.selected_dataset.name;
|
306
|
+
}
|
307
|
+
if(dsn) n.push(this.dot_option);
|
308
|
+
for(e in l) if(l.hasOwnProperty(e) && e !== dsn &&
|
309
|
+
// NOTE: do not display the equations dataset or "black-boxed" datasets
|
310
|
+
!(e === UI.EQUATIONS_DATASET_ID || e.startsWith(UI.BLACK_BOX))) {
|
311
|
+
n.push(l[e].displayName);
|
312
|
+
}
|
313
|
+
return n;
|
314
|
+
}
|
315
|
+
|
316
|
+
updateVariableBar(prefix='') {
|
317
|
+
// NOTE: this method is also called by the add-variable dialog of the
|
318
|
+
// Chart Manager AND of the Sensitivity Analysis; in these cases, `prefix`
|
319
|
+
// is passed to differentiate between the DOM elements to be used
|
320
|
+
const
|
321
|
+
type = document.getElementById(prefix + 'variable-obj').value,
|
322
|
+
n_list = this.namesByType(VM.object_types[type]).sort(
|
323
|
+
(a, b) => UI.compareFullNames(a, b)),
|
324
|
+
vn = document.getElementById(prefix + 'variable-name'),
|
325
|
+
options = [];
|
326
|
+
// Add "empty" as first and initial option, but disable it.
|
327
|
+
options.push('<option selected disabled value="-1"></option>');
|
328
|
+
if(VM.object_types[type] === 'Equation') {
|
329
|
+
// Hide the variable name, as this is the Equations Dataset
|
330
|
+
vn.style.display = 'none';
|
331
|
+
} else {
|
332
|
+
for(let i = 0; i < n_list.length; i++) {
|
333
|
+
// NOTE: no "dot option" when adding a chart variable or SA variable
|
334
|
+
if(!(prefix && n_list[i] === this.dataset_dot_option)) {
|
335
|
+
options.push(`<option value="${i}">${n_list[i]}</option>`);
|
336
|
+
}
|
337
|
+
}
|
338
|
+
vn.innerHTML = options.join('');
|
339
|
+
vn.value = -1;
|
340
|
+
vn.style.display = 'inline-block';
|
341
|
+
}
|
342
|
+
this.updateAttributeSelector(prefix);
|
343
|
+
}
|
344
|
+
|
345
|
+
updateAttributeSelector(prefix='') {
|
346
|
+
// Updates the attribute list -- only if a dataset has been selected.
|
347
|
+
// NOTE: this method is also called by the add-variable dialog of the
|
348
|
+
// Chart Manager AND of the Sensitivity Analysis; in these cases, `prefix`
|
349
|
+
// is passed to differentiate between the DOM elements to be used
|
350
|
+
const
|
351
|
+
type = document.getElementById(prefix + 'variable-obj').value,
|
352
|
+
vn = document.getElementById(prefix + 'variable-name'),
|
353
|
+
va = document.getElementById(prefix + 'variable-attr'),
|
354
|
+
options = [];
|
355
|
+
if(VM.object_types[type] === 'Equation') {
|
356
|
+
// Add "empty" as first and initial option, but disable it
|
357
|
+
options.push('<option selected disabled value="-1"></option>');
|
358
|
+
const d = MODEL.equations_dataset;
|
359
|
+
if(d) {
|
360
|
+
const slist = [];
|
361
|
+
for(let m in d.modifiers) if(d.modifiers.hasOwnProperty(m)) {
|
362
|
+
slist.push(d.modifiers[m].selector);
|
363
|
+
}
|
364
|
+
// Sort to present equations in alphabetical order
|
365
|
+
slist.sort((a, b) => UI.compareFullNames(a, b));
|
366
|
+
for(let i = 0; i < slist.length; i++) {
|
367
|
+
options.push(`<option value="${slist[i]}">${slist[i]}</option>`);
|
368
|
+
}
|
369
|
+
}
|
370
|
+
va.innerHTML = options.join('');
|
371
|
+
// NOTE: Chart Manager variable dialog is 60px wider
|
372
|
+
va.style.width = (prefix ? 'calc(100% - 82px)' : 'calc(100% - 142px)');
|
373
|
+
return;
|
374
|
+
}
|
375
|
+
// Add "empty" as first and initial option, as it denotes "use default"
|
376
|
+
va.style.width = '65px';
|
377
|
+
options.push('<option value="-1" selected></option>');
|
378
|
+
if(VM.object_types[type] === 'Dataset') {
|
379
|
+
let d = null,
|
380
|
+
v = vn.options[vn.selectedIndex].innerHTML;
|
381
|
+
if(v === this.dataset_dot_option) {
|
382
|
+
d = DATASET_MANAGER.selected_dataset;
|
383
|
+
} else if(v) {
|
384
|
+
d = MODEL.datasetByID(UI.nameToID(v));
|
385
|
+
}
|
386
|
+
if(d) {
|
387
|
+
for(let m in d.modifiers) if(d.modifiers.hasOwnProperty(m)) {
|
388
|
+
const s = d.modifiers[m].selector;
|
389
|
+
options.push(`<option value="${s}">${s}</option>`);
|
390
|
+
}
|
391
|
+
}
|
392
|
+
} else {
|
393
|
+
const
|
394
|
+
vt = document.getElementById('add-sa-variable-type'),
|
395
|
+
a_list = VM.type_attributes[type];
|
396
|
+
for(let i = 0; i < a_list.length; i++) {
|
397
|
+
const att = a_list[i];
|
398
|
+
// NOTE: for SA parameters, only show expression attributes
|
399
|
+
if(!vt || vt.innerHTML !== 'parameter' ||
|
400
|
+
VM.expression_attr.indexOf(att) >= 0) {
|
401
|
+
options.push('<option value="', i, '" title="',
|
402
|
+
VM.attribute_names[att], '">', att, '</option>');
|
403
|
+
}
|
404
|
+
}
|
405
|
+
}
|
406
|
+
va.innerHTML = options.join('');
|
407
|
+
}
|
408
|
+
|
409
|
+
insertVariable() {
|
410
|
+
const type = this.obj.value;
|
411
|
+
let n = this.name.options[this.name.selectedIndex].text,
|
412
|
+
a = this.attr.options[this.attr.selectedIndex].text;
|
413
|
+
if(VM.object_types[type] === 'Equation') {
|
414
|
+
n = a;
|
415
|
+
a = '';
|
416
|
+
}
|
417
|
+
if(n) {
|
418
|
+
if(n === this.dataset_dot_option) n = '.';
|
419
|
+
if(a) n += UI.OA_SEPARATOR + a;
|
420
|
+
let p = this.text.selectionStart;
|
421
|
+
const
|
422
|
+
v = this.text.value,
|
423
|
+
tb = v.substring(0, p),
|
424
|
+
ta = v.substring(p, v.length);
|
425
|
+
this.text.value = `${tb}[${n}]${ta}`;
|
426
|
+
p += n.length + 2;
|
427
|
+
this.text.setSelectionRange(p, p);
|
428
|
+
}
|
429
|
+
this.text.focus();
|
430
|
+
}
|
431
|
+
|
432
|
+
toggleExpressionInfo() {
|
433
|
+
// Show/hide information pane with information on expression notation,
|
434
|
+
// meanwhile changing the dialog buttons: when guide is showing, only
|
435
|
+
// display a "close" button, otherwise info, OK and cancel
|
436
|
+
const md = UI.modals.expression;
|
437
|
+
if(window.getComputedStyle(this.info).display !== 'none') {
|
438
|
+
this.info.style.display = 'none';
|
439
|
+
md.ok.style.display = 'block';
|
440
|
+
md.cancel.style.display = 'block';
|
441
|
+
md.info.src = 'images/info.png';
|
442
|
+
} else {
|
443
|
+
this.info.style.display = 'block';
|
444
|
+
md.ok.style.display = 'none';
|
445
|
+
md.cancel.style.display = 'none';
|
446
|
+
md.info.src = 'images/close.png';
|
447
|
+
}
|
448
|
+
}
|
449
|
+
|
450
|
+
} // END of class ExpressionEditor
|