linny-r 2.0.8 → 2.0.10
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 +3 -40
- package/package.json +6 -2
- package/server.js +19 -157
- package/static/images/solve-not-same-changed.png +0 -0
- package/static/images/solve-not-same-not-changed.png +0 -0
- package/static/images/solve-same-changed.png +0 -0
- package/static/images/solve-same-not-changed.png +0 -0
- package/static/index.html +137 -20
- package/static/linny-r.css +260 -23
- package/static/scripts/iro.min.js +7 -7
- package/static/scripts/linny-r-ctrl.js +126 -85
- package/static/scripts/linny-r-gui-actor-manager.js +23 -33
- package/static/scripts/linny-r-gui-chart-manager.js +56 -53
- package/static/scripts/linny-r-gui-constraint-editor.js +10 -14
- package/static/scripts/linny-r-gui-controller.js +644 -260
- package/static/scripts/linny-r-gui-dataset-manager.js +64 -66
- package/static/scripts/linny-r-gui-documentation-manager.js +11 -17
- package/static/scripts/linny-r-gui-equation-manager.js +22 -22
- package/static/scripts/linny-r-gui-experiment-manager.js +124 -141
- package/static/scripts/linny-r-gui-expression-editor.js +26 -12
- package/static/scripts/linny-r-gui-file-manager.js +42 -48
- package/static/scripts/linny-r-gui-finder.js +294 -55
- package/static/scripts/linny-r-gui-model-autosaver.js +2 -4
- package/static/scripts/linny-r-gui-monitor.js +35 -41
- package/static/scripts/linny-r-gui-paper.js +42 -70
- package/static/scripts/linny-r-gui-power-grid-manager.js +31 -34
- package/static/scripts/linny-r-gui-receiver.js +1 -2
- package/static/scripts/linny-r-gui-repository-browser.js +44 -46
- package/static/scripts/linny-r-gui-scale-unit-manager.js +32 -32
- package/static/scripts/linny-r-gui-sensitivity-analysis.js +61 -67
- package/static/scripts/linny-r-gui-undo-redo.js +94 -95
- package/static/scripts/linny-r-milp.js +20 -24
- package/static/scripts/linny-r-model.js +1932 -2274
- package/static/scripts/linny-r-utils.js +27 -27
- package/static/scripts/linny-r-vm.js +900 -998
- package/static/show-png.html +0 -113
@@ -32,7 +32,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
32
32
|
SOFTWARE.
|
33
33
|
*/
|
34
34
|
|
35
|
-
// CLASS GUIDatasetManager provides the dataset dialog functionality
|
35
|
+
// CLASS GUIDatasetManager provides the dataset dialog functionality.
|
36
36
|
class GUIDatasetManager extends DatasetManager {
|
37
37
|
constructor() {
|
38
38
|
super();
|
@@ -44,7 +44,7 @@ class GUIDatasetManager extends DatasetManager {
|
|
44
44
|
'click', (event) => UI.toggleDialog(event));
|
45
45
|
document.getElementById('ds-new-btn').addEventListener(
|
46
46
|
// Shift-click on New button => add prefix of selected dataset
|
47
|
-
// (if any) to the name field of the dialog
|
47
|
+
// (if any) to the name field of the dialog.
|
48
48
|
'click', () => DATASET_MANAGER.promptForDataset(event.shiftKey));
|
49
49
|
document.getElementById('ds-data-btn').addEventListener(
|
50
50
|
'click', () => DATASET_MANAGER.editData());
|
@@ -58,14 +58,14 @@ class GUIDatasetManager extends DatasetManager {
|
|
58
58
|
'click', () => DATASET_MANAGER.deleteDataset());
|
59
59
|
document.getElementById('ds-filter-btn').addEventListener(
|
60
60
|
'click', () => DATASET_MANAGER.toggleFilter());
|
61
|
-
// Update when filter input text changes
|
61
|
+
// Update when filter input text changes.
|
62
62
|
this.filter_text = document.getElementById('ds-filter-text');
|
63
63
|
this.filter_text.addEventListener(
|
64
64
|
'input', () => DATASET_MANAGER.changeFilter());
|
65
65
|
this.dataset_table = document.getElementById('dataset-table');
|
66
|
-
// Data properties pane
|
66
|
+
// Data properties pane.
|
67
67
|
this.properties = document.getElementById('dataset-properties');
|
68
|
-
// Toggle buttons at bottom of dialog
|
68
|
+
// Toggle buttons at bottom of dialog.
|
69
69
|
this.blackbox = document.getElementById('dataset-blackbox');
|
70
70
|
this.blackbox.addEventListener(
|
71
71
|
'click', () => DATASET_MANAGER.toggleBlackBox());
|
@@ -75,7 +75,7 @@ class GUIDatasetManager extends DatasetManager {
|
|
75
75
|
this.io_box = document.getElementById('dataset-io');
|
76
76
|
this.io_box.addEventListener(
|
77
77
|
'click', () => DATASET_MANAGER.toggleImportExport());
|
78
|
-
// Modifier pane buttons
|
78
|
+
// Modifier pane buttons.
|
79
79
|
document.getElementById('ds-add-modif-btn').addEventListener(
|
80
80
|
'click', () => DATASET_MANAGER.promptForSelector('new'));
|
81
81
|
document.getElementById('ds-rename-modif-btn').addEventListener(
|
@@ -86,9 +86,9 @@ class GUIDatasetManager extends DatasetManager {
|
|
86
86
|
'click', () => DATASET_MANAGER.deleteModifier());
|
87
87
|
document.getElementById('ds-convert-modif-btn').addEventListener(
|
88
88
|
'click', () => DATASET_MANAGER.promptToConvertModifiers());
|
89
|
-
// Modifier table
|
89
|
+
// Modifier table.
|
90
90
|
this.modifier_table = document.getElementById('dataset-modif-table');
|
91
|
-
// Modal dialogs
|
91
|
+
// Modal dialogs.
|
92
92
|
this.new_modal = new ModalDialog('new-dataset');
|
93
93
|
this.new_modal.ok.addEventListener(
|
94
94
|
'click', () => DATASET_MANAGER.newDataset());
|
@@ -170,7 +170,8 @@ class GUIDatasetManager extends DatasetManager {
|
|
170
170
|
}
|
171
171
|
|
172
172
|
enterKey() {
|
173
|
-
// Open "edit" dialog for the selected dataset or modifier expression
|
173
|
+
// Open "edit" dialog for the selected dataset or modifier expression.
|
174
|
+
if(!this.focal_table) this.focal_table = this.dataset_table;
|
174
175
|
const srl = this.focal_table.getElementsByClassName('sel-set');
|
175
176
|
if(srl.length > 0) {
|
176
177
|
const r = this.focal_table.rows[srl[0].rowIndex];
|
@@ -190,7 +191,8 @@ class GUIDatasetManager extends DatasetManager {
|
|
190
191
|
}
|
191
192
|
|
192
193
|
upDownKey(dir) {
|
193
|
-
// Select row above or below the selected one (if possible)
|
194
|
+
// Select row above or below the selected one (if possible).
|
195
|
+
if(!this.focal_table) this.focal_table = this.dataset_table;
|
194
196
|
const srl = this.focal_table.getElementsByClassName('sel-set');
|
195
197
|
if(srl.length > 0) {
|
196
198
|
let r = this.focal_table.rows[srl[0].rowIndex + dir];
|
@@ -206,12 +208,24 @@ class GUIDatasetManager extends DatasetManager {
|
|
206
208
|
}
|
207
209
|
}
|
208
210
|
|
211
|
+
expandToShow(name) {
|
212
|
+
// Expand all prefix rows for dataset having `name` so as to make it visible.
|
213
|
+
const pn = UI.prefixesAndName(name);
|
214
|
+
if(pn.length > 1) {
|
215
|
+
pn.pop();
|
216
|
+
while(pn.length) {
|
217
|
+
addDistinct(pn.join(UI.PREFIXER).toLowerCase(), this.expanded_rows);
|
218
|
+
pn.pop();
|
219
|
+
}
|
220
|
+
this.updateDialog();
|
221
|
+
}
|
222
|
+
}
|
223
|
+
|
209
224
|
hideCollapsedRows() {
|
210
|
-
//
|
211
|
-
for(
|
225
|
+
// Hide all rows except top level and immediate children of expanded.
|
226
|
+
for(const row of this.dataset_table.rows) {
|
212
227
|
const
|
213
|
-
|
214
|
-
// Get the first DIV in the first TD of this row
|
228
|
+
// Get the first DIV in the first TD of this row.
|
215
229
|
first_div = row.firstChild.firstElementChild,
|
216
230
|
btn = first_div.dataset.prefix === 'x';
|
217
231
|
let p = row.dataset.prefix,
|
@@ -219,18 +233,18 @@ class GUIDatasetManager extends DatasetManager {
|
|
219
233
|
show = !p || x;
|
220
234
|
if(btn) {
|
221
235
|
const btn_div = row.getElementsByClassName('tree-btn')[0];
|
222
|
-
// Special expand/collapse row
|
236
|
+
// Special expand/collapse row.
|
223
237
|
if(show) {
|
224
|
-
// Set triangle to point down
|
238
|
+
// Set triangle to point down.
|
225
239
|
btn_div.innerText = '\u25BC';
|
226
240
|
} else {
|
227
|
-
// Set triangle to point right
|
241
|
+
// Set triangle to point right.
|
228
242
|
btn_div.innerText = '\u25BA';
|
229
|
-
// See whether "parent prefix" is expanded
|
243
|
+
// See whether "parent prefix" is expanded.
|
230
244
|
p = p.split(UI.PREFIXER);
|
231
245
|
p.pop();
|
232
246
|
p = p.join(UI.PREFIXER);
|
233
|
-
// If so, then also show the row
|
247
|
+
// If so, then also show the row.
|
234
248
|
show = (!p || this.expanded_rows.indexOf(p) >= 0);
|
235
249
|
}
|
236
250
|
}
|
@@ -239,7 +253,7 @@ class GUIDatasetManager extends DatasetManager {
|
|
239
253
|
}
|
240
254
|
|
241
255
|
togglePrefixRow(e) {
|
242
|
-
//
|
256
|
+
// Show list items of the next prefix level.
|
243
257
|
let r = e.target;
|
244
258
|
while(r.tagName !== 'TR') r = r.parentNode;
|
245
259
|
const
|
@@ -247,7 +261,7 @@ class GUIDatasetManager extends DatasetManager {
|
|
247
261
|
i = this.expanded_rows.indexOf(p);
|
248
262
|
if(i >= 0) {
|
249
263
|
this.expanded_rows.splice(i, 1);
|
250
|
-
// Also remove all prefixes that have `p` as prefix
|
264
|
+
// Also remove all prefixes that have `p` as prefix.
|
251
265
|
for(let j = this.expanded_rows.length - 1; j >= 0; j--) {
|
252
266
|
if(this.expanded_rows[j].startsWith(p + UI.PREFIXER)) {
|
253
267
|
this.expanded_rows.splice(j, 1);
|
@@ -260,7 +274,7 @@ class GUIDatasetManager extends DatasetManager {
|
|
260
274
|
}
|
261
275
|
|
262
276
|
rowByPrefix(prefix) {
|
263
|
-
//
|
277
|
+
// Return the first table row with the specified prefix.
|
264
278
|
if(!prefix) return null;
|
265
279
|
let lcp = prefix.toLowerCase(),
|
266
280
|
pl = lcp.split(': ');
|
@@ -274,20 +288,17 @@ class GUIDatasetManager extends DatasetManager {
|
|
274
288
|
pl.pop();
|
275
289
|
}
|
276
290
|
this.hideCollapsedRows();
|
277
|
-
for(
|
278
|
-
const r = this.dataset_table.rows[i];
|
279
|
-
if(r.dataset.prefix === lcp) return r;
|
280
|
-
}
|
291
|
+
for(const r of this.dataset_table.rows) if(r.dataset.prefix === lcp) return r;
|
281
292
|
return null;
|
282
293
|
}
|
283
294
|
|
284
295
|
selectPrefixRow(e) {
|
285
|
-
//
|
296
|
+
// Select expand/collapse prefix row.
|
286
297
|
this.focal_table = this.dataset_table;
|
287
|
-
// NOTE: `e` can also be a string specifying the prefix to select
|
298
|
+
// NOTE: `e` can also be a string specifying the prefix to select.
|
288
299
|
let r = e.target || this.rowByPrefix(e);
|
289
300
|
if(!r) return;
|
290
|
-
// Modeler may have clicked on the expand/collapse triangle
|
301
|
+
// Modeler may have clicked on the expand/collapse triangle.
|
291
302
|
const toggle = r.classList.contains('tree-btn');
|
292
303
|
while(r.tagName !== 'TR') r = r.parentNode;
|
293
304
|
this.selected_prefix_row = r;
|
@@ -328,8 +339,8 @@ class GUIDatasetManager extends DatasetManager {
|
|
328
339
|
names = [],
|
329
340
|
pref_names = {},
|
330
341
|
xids = [];
|
331
|
-
for(
|
332
|
-
const pref = UI.prefixesAndName(MODEL.datasets[
|
342
|
+
for(const dn of dnl) {
|
343
|
+
const pref = UI.prefixesAndName(MODEL.datasets[dn].name);
|
333
344
|
// NOTE: only the name part (so no prefixes at all) will be shown
|
334
345
|
names.push(pref.pop());
|
335
346
|
indent.push(pref.length);
|
@@ -851,8 +862,7 @@ class GUIDatasetManager extends DatasetManager {
|
|
851
862
|
// Update all chartvariables referencing this dataset + old selector.
|
852
863
|
const vl = MODEL.datasetVariables;
|
853
864
|
let cv_cnt = 0;
|
854
|
-
for(
|
855
|
-
const v = vl[i];
|
865
|
+
for(const v of vl) {
|
856
866
|
if(v.object === this.selected_dataset && v.attribute === oldm.selector) {
|
857
867
|
v.attribute = m.selector;
|
858
868
|
cv_cnt++;
|
@@ -949,16 +959,10 @@ class GUIDatasetManager extends DatasetManager {
|
|
949
959
|
return;
|
950
960
|
}
|
951
961
|
prefix += UI.PREFIXER;
|
952
|
-
const
|
953
|
-
|
954
|
-
pml = ds.inferPrefixableModifiers,
|
955
|
-
xl = MODEL.allExpressions,
|
956
|
-
vl = MODEL.datasetVariables,
|
957
|
-
nl = MODEL.notesWithTags;
|
958
|
-
for(let i = 0; i < pml.length; i++) {
|
962
|
+
const dsn = ds.displayName;
|
963
|
+
for(const m of ds.inferPrefixableModifiers) {
|
959
964
|
// Create prefixed dataset with correct default value
|
960
965
|
const
|
961
|
-
m = pml[i],
|
962
966
|
sel = m.selector,
|
963
967
|
newds = MODEL.addDataset(prefix + sel);
|
964
968
|
if(newds) {
|
@@ -976,17 +980,16 @@ class GUIDatasetManager extends DatasetManager {
|
|
976
980
|
const
|
977
981
|
from = dsn + UI.OA_SEPARATOR + sel,
|
978
982
|
to = newds.displayName;
|
979
|
-
for(
|
980
|
-
|
981
|
-
// NOTE: variable should match original dataset + selector
|
983
|
+
for(const v of MODEL.datasetVariables) {
|
984
|
+
// NOTE: variable should match original dataset + selector.
|
982
985
|
if(v.displayName === from) {
|
983
|
-
// Change to new dataset WITHOUT selector
|
986
|
+
// Change to new dataset WITHOUT selector.
|
984
987
|
v.object = newds;
|
985
988
|
v.attribute = '';
|
986
989
|
vcount++;
|
987
990
|
}
|
988
991
|
}
|
989
|
-
// Rename variable in the Sensitivity Analysis
|
992
|
+
// Rename variable in the Sensitivity Analysis.
|
990
993
|
for(let j = 0; j < MODEL.sensitivity_parameters.length; j++) {
|
991
994
|
if(MODEL.sensitivity_parameters[j] === from) {
|
992
995
|
MODEL.sensitivity_parameters[j] = to;
|
@@ -1008,10 +1011,8 @@ class GUIDatasetManager extends DatasetManager {
|
|
1008
1011
|
// Pattern ends at any character that is invalid for a
|
1009
1012
|
// dataset modifier selector (unlike equation names)
|
1010
1013
|
'\\s*[^a-zA-Z0-9\\+\\-\\%\\_]', 'gi');
|
1011
|
-
for(
|
1012
|
-
const
|
1013
|
-
x = xl[j],
|
1014
|
-
matches = x.text.match(re);
|
1014
|
+
for(const x of MODEL.allExpressions) {
|
1015
|
+
const matches = x.text.match(re);
|
1015
1016
|
if(matches) {
|
1016
1017
|
for(let k = 0; k < matches.length; k++) {
|
1017
1018
|
// NOTE: each match will start with the opening bracket,
|
@@ -1025,10 +1026,8 @@ class GUIDatasetManager extends DatasetManager {
|
|
1025
1026
|
x.code = null;
|
1026
1027
|
}
|
1027
1028
|
}
|
1028
|
-
for(
|
1029
|
-
const
|
1030
|
-
n = nl[j],
|
1031
|
-
matches = n.contents.match(re);
|
1029
|
+
for(const n of MODEL.notesWithTags) {
|
1030
|
+
const matches = n.contents.match(re);
|
1032
1031
|
if(matches) {
|
1033
1032
|
for(let k = 0; k < matches.length; k++) {
|
1034
1033
|
// See NOTE above for the use of `slice` here
|
@@ -1063,7 +1062,7 @@ class GUIDatasetManager extends DatasetManager {
|
|
1063
1062
|
}
|
1064
1063
|
|
1065
1064
|
editData() {
|
1066
|
-
// Show the Edit time series dialog
|
1065
|
+
// Show the Edit time series dialog.
|
1067
1066
|
const
|
1068
1067
|
ds = this.selected_dataset,
|
1069
1068
|
md = this.series_modal,
|
@@ -1073,7 +1072,7 @@ class GUIDatasetManager extends DatasetManager {
|
|
1073
1072
|
md.element('unit').value = ds.scale_unit;
|
1074
1073
|
cover.style.display = (ds.array ? 'block' : 'none');
|
1075
1074
|
md.element('time-scale').value = VM.sig4Dig(ds.time_scale);
|
1076
|
-
// Add options for time unit selector
|
1075
|
+
// Add options for time unit selector.
|
1077
1076
|
const ol = [];
|
1078
1077
|
for(let u in VM.time_unit_shorthand) {
|
1079
1078
|
if(VM.time_unit_shorthand.hasOwnProperty(u)) {
|
@@ -1083,7 +1082,7 @@ class GUIDatasetManager extends DatasetManager {
|
|
1083
1082
|
}
|
1084
1083
|
}
|
1085
1084
|
md.element('time-unit').innerHTML = ol.join('');
|
1086
|
-
// Add options for(dis)aggregation method selector
|
1085
|
+
// Add options for(dis)aggregation method selector.
|
1087
1086
|
ol.length = 0;
|
1088
1087
|
for(let i = 0; i < this.methods.length; i++) {
|
1089
1088
|
ol.push(['<option value="', this.methods[i],
|
@@ -1091,12 +1090,12 @@ class GUIDatasetManager extends DatasetManager {
|
|
1091
1090
|
'">', this.method_names[i], '</option>'].join(''));
|
1092
1091
|
}
|
1093
1092
|
md.element('method').innerHTML = ol.join('');
|
1094
|
-
// Update the "periodic" box
|
1093
|
+
// Update the "periodic" box.
|
1095
1094
|
UI.setBox('series-periodic', ds.periodic);
|
1096
|
-
// Update the "array" box
|
1095
|
+
// Update the "array" box.
|
1097
1096
|
UI.setBox('series-array', ds.array);
|
1098
1097
|
md.element('url').value = ds.url;
|
1099
|
-
// Show data as decimal numbers (JS default notation) on separate lines
|
1098
|
+
// Show data as decimal numbers (JS default notation) on separate lines.
|
1100
1099
|
this.series_data.value = ds.data.join('\n');
|
1101
1100
|
md.show('default');
|
1102
1101
|
}
|
@@ -1181,9 +1180,8 @@ class GUIDatasetManager extends DatasetManager {
|
|
1181
1180
|
parts = lines[0].split(sep),
|
1182
1181
|
dsn = [];
|
1183
1182
|
let quoted = false;
|
1184
|
-
for(
|
1183
|
+
for(const p of parts) {
|
1185
1184
|
const
|
1186
|
-
p = parts[i],
|
1187
1185
|
swq = /^\"(\"\")*($|[^\"])/.test(p),
|
1188
1186
|
ewq = p.endsWith('"');
|
1189
1187
|
if(!quoted && swq && !ewq) {
|
@@ -1220,7 +1218,8 @@ class GUIDatasetManager extends DatasetManager {
|
|
1220
1218
|
UI.warn(`Invalid default value "${v}" in column ${i}`);
|
1221
1219
|
return false;
|
1222
1220
|
} else {
|
1223
|
-
|
1221
|
+
// Push empty list, as this will become the actual dataset without default value.
|
1222
|
+
dsa.push([]);
|
1224
1223
|
}
|
1225
1224
|
}
|
1226
1225
|
for(let i = 2; i < n; i++) {
|
@@ -1229,16 +1228,15 @@ class GUIDatasetManager extends DatasetManager {
|
|
1229
1228
|
UI.warn(`Number of values (${dsv.length}) on line ${i} does not match number of dataset names (${ncol})`);
|
1230
1229
|
return false;
|
1231
1230
|
}
|
1232
|
-
for(let j = 0; j <
|
1231
|
+
for(let j = 0; j < ncol; j++) {
|
1233
1232
|
const
|
1234
1233
|
v = dsv[j].trim(),
|
1235
1234
|
sf = safeStrToFloat(v, '');
|
1236
1235
|
if(sf === '' && v !== '') {
|
1237
1236
|
UI.warn(`Invalid numerical value "${v}" for <strong>${dsn[j]}</strong> on line ${i}`);
|
1238
1237
|
return false;
|
1239
|
-
} else {
|
1240
|
-
dsa[j].push(sf);
|
1241
1238
|
}
|
1239
|
+
dsa[j].push(sf);
|
1242
1240
|
}
|
1243
1241
|
}
|
1244
1242
|
// Add or update datasets.
|
@@ -280,10 +280,10 @@ class DocumentationManager {
|
|
280
280
|
|
281
281
|
clearEntity(list) {
|
282
282
|
// To be called when entities are deleted
|
283
|
-
if(list.indexOf(this.entity) >= 0) {
|
283
|
+
if(list === true || list.indexOf(this.entity) >= 0) {
|
284
284
|
this.stopEditing();
|
285
285
|
this.entity = null;
|
286
|
-
this.title.innerHTML = '
|
286
|
+
this.title.innerHTML = 'About Linny-R';
|
287
287
|
this.viewer.innerHTML = this.about_linny_r;
|
288
288
|
}
|
289
289
|
}
|
@@ -540,18 +540,17 @@ class DocumentationManager {
|
|
540
540
|
|
541
541
|
showArrowLinks(arrow) {
|
542
542
|
// Show list of links represented by a composite arrow.
|
543
|
-
const
|
544
|
-
n = arrow.links.length,
|
545
|
-
msg = 'Arrow represents ' + pluralS(n, 'link');
|
543
|
+
const msg = 'Arrow represents ' + pluralS(arrow.links.length, 'link');
|
546
544
|
UI.setMessage(msg);
|
547
545
|
if(this.visible && !this.editing) {
|
548
546
|
// Set the dialog title.
|
549
547
|
this.title.innerHTML = msg;
|
550
548
|
// Show list.
|
551
549
|
const lis = [];
|
552
|
-
let
|
553
|
-
|
554
|
-
|
550
|
+
let dn,
|
551
|
+
c,
|
552
|
+
af;
|
553
|
+
for(const l of arrow.links) {
|
555
554
|
dn = l.displayName;
|
556
555
|
if(l.from_node instanceof Process) {
|
557
556
|
c = UI.color.produced;
|
@@ -600,9 +599,7 @@ class DocumentationManager {
|
|
600
599
|
this.title.innerHTML = msg;
|
601
600
|
// Show list.
|
602
601
|
const lis = [];
|
603
|
-
for(
|
604
|
-
lis.push(`<li>${iol[i].displayName}</li>`);
|
605
|
-
}
|
602
|
+
for(const io of iol) lis.push(`<li>${io.displayName}</li>`);
|
606
603
|
lis.sort(ciCompare);
|
607
604
|
this.viewer.innerHTML = `<ul>${lis.join('')}</ul>`;
|
608
605
|
}
|
@@ -714,9 +711,8 @@ class DocumentationManager {
|
|
714
711
|
this.differenceAsTable(d.settings));
|
715
712
|
if('units' in d) html.push('<h2>Units</h2>',
|
716
713
|
this.differenceAsTable(d.units));
|
717
|
-
for(
|
718
|
-
|
719
|
-
if(e in d) html.push('<h2>' + this.propertyName(e) + '</h2>',
|
714
|
+
for(const e of UI.MC.ENTITY_PROPS) if(e in d) {
|
715
|
+
html.push('<h2>' + this.propertyName(e) + '</h2>',
|
720
716
|
this.differenceAsTable(d[e]));
|
721
717
|
}
|
722
718
|
if('charts' in d) html.push('<h2><em>Charts</em></h2>',
|
@@ -759,9 +755,7 @@ class DocumentationManager {
|
|
759
755
|
const
|
760
756
|
html = ['<table>'],
|
761
757
|
keys = Object.keys(d).sort();
|
762
|
-
for(
|
763
|
-
html.push(this.differenceAsTableRow(d, keys[i]));
|
764
|
-
}
|
758
|
+
for(const k of keys) html.push(this.differenceAsTableRow(d, k));
|
765
759
|
html.push('</table>');
|
766
760
|
return html.join('\n');
|
767
761
|
}
|
@@ -148,7 +148,7 @@ class EquationManager {
|
|
148
148
|
}
|
149
149
|
|
150
150
|
updateDialog() {
|
151
|
-
// Updates equation list, highlighting selected equation (if any)
|
151
|
+
// Updates equation list, highlighting selected equation (if any).
|
152
152
|
const
|
153
153
|
ed = MODEL.equations_dataset,
|
154
154
|
ml = [],
|
@@ -160,34 +160,36 @@ class EquationManager {
|
|
160
160
|
this.outcome_btn.classList.add('not-selected');
|
161
161
|
}
|
162
162
|
let smid = 'eqmtr';
|
163
|
+
// NOTE> Selector list `msl` contains names, not IDs.
|
163
164
|
for(let i = 0; i < msl.length; i++) {
|
164
165
|
const
|
165
|
-
|
166
|
-
|
167
|
-
|
166
|
+
id = UI.nameToID(msl[i]),
|
167
|
+
m = ed.modifiers[id],
|
168
|
+
sel = safeDoubleQuotes(m.selector),
|
169
|
+
expr = m.expression,
|
170
|
+
wild = (sel.indexOf('??') >= 0),
|
171
|
+
method = sel.startsWith(':'),
|
168
172
|
multi = (this.multi_line ? '-multi' : ''),
|
169
|
-
issue = (
|
170
|
-
(
|
171
|
-
clk =
|
172
|
-
|
173
|
-
|
174
|
-
m.identifier + '\', event.shiftKey);"' : '');
|
173
|
+
issue = (expr.compile_issue ? ' compile-issue' :
|
174
|
+
(expr.compute_issue ? ' compute-issue' : '')),
|
175
|
+
clk = `" onclick="EQUATION_MANAGER.selectModifier(event, '${id}'`,
|
176
|
+
mover = (!method ? '' :
|
177
|
+
`onmouseover="EQUATION_MANAGER.showInfo('${id}', event.shiftKey);"`);
|
175
178
|
if(m === sm) smid += i;
|
176
179
|
ml.push(['<tr id="eqmtr', i, '" class="dataset-modif',
|
177
180
|
(m === sm ? ' sel-set' : ''),
|
178
181
|
'"><td class="equation-selector',
|
179
182
|
(method ? ' method' : ''),
|
180
183
|
// Display in gray when method cannot be applied.
|
181
|
-
(
|
182
|
-
(
|
184
|
+
(expr.noMethodObject ? ' no-object' : ''),
|
185
|
+
(expr.isStatic ? '' : ' it'), issue,
|
183
186
|
(wild ? ' wildcard' : ''), clk, ', false);"', mover, '>',
|
184
187
|
(m.outcome_equation ? '<span class="outcome"></span>' : ''),
|
185
|
-
(wild ? wildcardFormat(
|
188
|
+
(wild ? wildcardFormat(sel) : sel),
|
186
189
|
'</td><td class="equation-expression', multi, issue,
|
187
190
|
(issue ? '"title="' +
|
188
|
-
safeDoubleQuotes(
|
189
|
-
|
190
|
-
clk, ');">', m.expression.text, '</td></tr>'].join(''));
|
191
|
+
safeDoubleQuotes(expr.compile_issue || expr.compute_issue) : ''),
|
192
|
+
clk, ');">', expr.text, '</td></tr>'].join(''));
|
191
193
|
}
|
192
194
|
this.table.innerHTML = ml.join('');
|
193
195
|
this.scroll_area.style.display = 'block';
|
@@ -208,10 +210,10 @@ class EquationManager {
|
|
208
210
|
|
209
211
|
selectModifier(event, id, x=true) {
|
210
212
|
// Select modifier, or when Alt- or double-clicked, edit its expression
|
211
|
-
// or the equation name (= name of the modifier)
|
213
|
+
// or the equation name (= name of the modifier).
|
212
214
|
if(MODEL.equations_dataset) {
|
213
215
|
const
|
214
|
-
m = MODEL.equations_dataset.modifiers[
|
216
|
+
m = MODEL.equations_dataset.modifiers[id] || null,
|
215
217
|
edit = event.altKey || this.doubleClicked(m);
|
216
218
|
this.selected_modifier = m;
|
217
219
|
if(m && edit) {
|
@@ -327,10 +329,8 @@ class EquationManager {
|
|
327
329
|
}
|
328
330
|
// Update all chartvariables referencing this dataset + old selector
|
329
331
|
let cv_cnt = 0;
|
330
|
-
for(
|
331
|
-
const
|
332
|
-
for(let j = 0; j < c.variables.length; j++) {
|
333
|
-
const v = c.variables[j];
|
332
|
+
for(const c of MODEL.charts) {
|
333
|
+
for(const v of c.variables) {
|
334
334
|
if(v.object === MODEL.equations_dataset && v.attribute === olds) {
|
335
335
|
v.attribute = m.selector;
|
336
336
|
cv_cnt++;
|