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.
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 +450 -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 +34 -15
  47. package/static/scripts/linny-r-utils.js +11 -1
  48. package/static/scripts/linny-r-vm.js +21 -12
  49. 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&#8320;)">t</code>,
66
+ <code title="Relative time step (t &minus; t&#8320; + 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 &pi; = ${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 &lsquo;unbounded&rsquo; 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(&lambda;) distribution">exponential</code>,
113
+ <code title="normal(X;Y) evaluates as a random number from the Normal(&mu;,&sigma;) distribution">normal</code>,
114
+ <code title="poisson(X) evaluates as a random number from the Poisson(&lambda;) 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(&lambda;,k) distribution">weibull</code>,
118
+ <code title="max(X1;&hellip;;Xn) evaluates as the highest value of X1, &hellip;, Xn">max</code>,
119
+ <code title="min(X1;&hellip;;Xn) evaluates as the lowest value of X1, &hellip;, 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., &Sigma; CF/(1+r)\u2071 for i=0, &hellip;, N-1.
122
+ NOTE: When the grouping contains more than 3 arguments, npv(R;X0;&hellip;;Xn)
123
+ considers X0, &hellip;, 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 &minus; 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 &lt;&gt; Y evaluates as 1 if X does NOT equal Y (otherwise 0)">&lt;&gt;</code>
137
+ or <code title="Alternative notation for X &lt;&gt; Y">!=</code>,
138
+ <code title="X &lt; Y evaluates as 1 if X is less than Y (otherwise 0)">&lt;</code>,
139
+ <code title="X &lt;= Y evaluates as 1 if X is less than or equal to Y (otherwise 0)">&lt;=</code>,
140
+ <code title="X &gt;= Y evaluates as 1 if X is greater than or equal to Y (otherwise 0)">&gt;=</code>,
141
+ <code title="X &gt; Y evaluates as 1 if X is greater than Y (otherwise 0)">&gt;</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 = &#x2047; <strong>then</strong> Y <strong>else</strong> X)<br>
154
+
155
+ <em>Grouping:</em>
156
+ <code title="X ; Y evaluates as a group or &ldquo;tuple&rdquo; (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('&amp;', '&'));
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 = '&nbsp;';
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