linny-r 2.0.9 → 2.0.11
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/package.json +6 -2
- 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 +81 -11
- package/static/linny-r.css +250 -7
- package/static/scripts/linny-r-ctrl.js +76 -13
- package/static/scripts/linny-r-gui-chart-manager.js +13 -12
- package/static/scripts/linny-r-gui-constraint-editor.js +4 -4
- package/static/scripts/linny-r-gui-controller.js +416 -43
- package/static/scripts/linny-r-gui-dataset-manager.js +122 -93
- package/static/scripts/linny-r-gui-experiment-manager.js +22 -12
- package/static/scripts/linny-r-gui-expression-editor.js +26 -12
- package/static/scripts/linny-r-gui-finder.js +190 -5
- package/static/scripts/linny-r-gui-repository-browser.js +18 -0
- package/static/scripts/linny-r-model.js +192 -84
- package/static/scripts/linny-r-utils.js +13 -2
- package/static/scripts/linny-r-vm.js +137 -95
@@ -11,7 +11,7 @@ for the Linny-R Dataset Manager dialog.
|
|
11
11
|
*/
|
12
12
|
|
13
13
|
/*
|
14
|
-
Copyright (c) 2017-
|
14
|
+
Copyright (c) 2017-2025 Delft University of Technology
|
15
15
|
|
16
16
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
17
17
|
of this software and associated documentation files (the "Software"), to deal
|
@@ -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());
|
@@ -56,16 +56,12 @@ class GUIDatasetManager extends DatasetManager {
|
|
56
56
|
'click', () => DATASET_MANAGER.load_csv_modal.show());
|
57
57
|
document.getElementById('ds-delete-btn').addEventListener(
|
58
58
|
'click', () => DATASET_MANAGER.deleteDataset());
|
59
|
-
document.getElementById('ds-filter-btn').addEventListener(
|
60
|
-
'click', () => DATASET_MANAGER.toggleFilter());
|
61
|
-
// Update when filter input text changes
|
62
|
-
this.filter_text = document.getElementById('ds-filter-text');
|
63
|
-
this.filter_text.addEventListener(
|
64
|
-
'input', () => DATASET_MANAGER.changeFilter());
|
65
59
|
this.dataset_table = document.getElementById('dataset-table');
|
66
|
-
// Data properties pane
|
60
|
+
// Data properties pane below the dataset scroll area.
|
67
61
|
this.properties = document.getElementById('dataset-properties');
|
68
|
-
//
|
62
|
+
// Number of prefixed datasets is displayed at bottom of left pane.
|
63
|
+
this.prefixed_count = document.getElementById('dataset-prefixed-count');
|
64
|
+
// Toggle buttons at bottom of dialog.
|
69
65
|
this.blackbox = document.getElementById('dataset-blackbox');
|
70
66
|
this.blackbox.addEventListener(
|
71
67
|
'click', () => DATASET_MANAGER.toggleBlackBox());
|
@@ -75,7 +71,7 @@ class GUIDatasetManager extends DatasetManager {
|
|
75
71
|
this.io_box = document.getElementById('dataset-io');
|
76
72
|
this.io_box.addEventListener(
|
77
73
|
'click', () => DATASET_MANAGER.toggleImportExport());
|
78
|
-
// Modifier pane buttons
|
74
|
+
// Modifier pane buttons.
|
79
75
|
document.getElementById('ds-add-modif-btn').addEventListener(
|
80
76
|
'click', () => DATASET_MANAGER.promptForSelector('new'));
|
81
77
|
document.getElementById('ds-rename-modif-btn').addEventListener(
|
@@ -86,9 +82,9 @@ class GUIDatasetManager extends DatasetManager {
|
|
86
82
|
'click', () => DATASET_MANAGER.deleteModifier());
|
87
83
|
document.getElementById('ds-convert-modif-btn').addEventListener(
|
88
84
|
'click', () => DATASET_MANAGER.promptToConvertModifiers());
|
89
|
-
// Modifier table
|
85
|
+
// Modifier table.
|
90
86
|
this.modifier_table = document.getElementById('dataset-modif-table');
|
91
|
-
// Modal dialogs
|
87
|
+
// Modal dialogs.
|
92
88
|
this.new_modal = new ModalDialog('new-dataset');
|
93
89
|
this.new_modal.ok.addEventListener(
|
94
90
|
'click', () => DATASET_MANAGER.newDataset());
|
@@ -144,12 +140,13 @@ class GUIDatasetManager extends DatasetManager {
|
|
144
140
|
reset() {
|
145
141
|
super.reset();
|
146
142
|
this.selected_prefix_row = null;
|
143
|
+
this.selected_dataset = null;
|
147
144
|
this.selected_modifier = null;
|
148
145
|
this.edited_expression = null;
|
149
|
-
this.filter_pattern = null;
|
150
146
|
this.clicked_object = null;
|
151
147
|
this.last_time_clicked = 0;
|
152
148
|
this.focal_table = null;
|
149
|
+
this.prefixed_datasets = [];
|
153
150
|
this.expanded_rows = [];
|
154
151
|
}
|
155
152
|
|
@@ -170,7 +167,8 @@ class GUIDatasetManager extends DatasetManager {
|
|
170
167
|
}
|
171
168
|
|
172
169
|
enterKey() {
|
173
|
-
// Open "edit" dialog for the selected dataset or modifier expression
|
170
|
+
// Open "edit" dialog for the selected dataset or modifier expression.
|
171
|
+
if(!this.focal_table) this.focal_table = this.dataset_table;
|
174
172
|
const srl = this.focal_table.getElementsByClassName('sel-set');
|
175
173
|
if(srl.length > 0) {
|
176
174
|
const r = this.focal_table.rows[srl[0].rowIndex];
|
@@ -190,7 +188,8 @@ class GUIDatasetManager extends DatasetManager {
|
|
190
188
|
}
|
191
189
|
|
192
190
|
upDownKey(dir) {
|
193
|
-
// Select row above or below the selected one (if possible)
|
191
|
+
// Select row above or below the selected one (if possible).
|
192
|
+
if(!this.focal_table) this.focal_table = this.dataset_table;
|
194
193
|
const srl = this.focal_table.getElementsByClassName('sel-set');
|
195
194
|
if(srl.length > 0) {
|
196
195
|
let r = this.focal_table.rows[srl[0].rowIndex + dir];
|
@@ -206,11 +205,24 @@ class GUIDatasetManager extends DatasetManager {
|
|
206
205
|
}
|
207
206
|
}
|
208
207
|
|
208
|
+
expandToShow(name) {
|
209
|
+
// Expand all prefix rows for dataset having `name` so as to make it visible.
|
210
|
+
const pn = UI.prefixesAndName(name);
|
211
|
+
if(pn.length > 1) {
|
212
|
+
pn.pop();
|
213
|
+
while(pn.length) {
|
214
|
+
addDistinct(pn.join(UI.PREFIXER).toLowerCase(), this.expanded_rows);
|
215
|
+
pn.pop();
|
216
|
+
}
|
217
|
+
this.updateDialog();
|
218
|
+
}
|
219
|
+
}
|
220
|
+
|
209
221
|
hideCollapsedRows() {
|
210
|
-
//
|
222
|
+
// Hide all rows except top level and immediate children of expanded.
|
211
223
|
for(const row of this.dataset_table.rows) {
|
212
224
|
const
|
213
|
-
// Get the first DIV in the first TD of this row
|
225
|
+
// Get the first DIV in the first TD of this row.
|
214
226
|
first_div = row.firstChild.firstElementChild,
|
215
227
|
btn = first_div.dataset.prefix === 'x';
|
216
228
|
let p = row.dataset.prefix,
|
@@ -218,18 +230,18 @@ class GUIDatasetManager extends DatasetManager {
|
|
218
230
|
show = !p || x;
|
219
231
|
if(btn) {
|
220
232
|
const btn_div = row.getElementsByClassName('tree-btn')[0];
|
221
|
-
// Special expand/collapse row
|
233
|
+
// Special expand/collapse row.
|
222
234
|
if(show) {
|
223
|
-
// Set triangle to point down
|
235
|
+
// Set triangle to point down.
|
224
236
|
btn_div.innerText = '\u25BC';
|
225
237
|
} else {
|
226
|
-
// Set triangle to point right
|
238
|
+
// Set triangle to point right.
|
227
239
|
btn_div.innerText = '\u25BA';
|
228
|
-
// See whether "parent prefix" is expanded
|
240
|
+
// See whether "parent prefix" is expanded.
|
229
241
|
p = p.split(UI.PREFIXER);
|
230
242
|
p.pop();
|
231
243
|
p = p.join(UI.PREFIXER);
|
232
|
-
// If so, then also show the row
|
244
|
+
// If so, then also show the row.
|
233
245
|
show = (!p || this.expanded_rows.indexOf(p) >= 0);
|
234
246
|
}
|
235
247
|
}
|
@@ -238,7 +250,7 @@ class GUIDatasetManager extends DatasetManager {
|
|
238
250
|
}
|
239
251
|
|
240
252
|
togglePrefixRow(e) {
|
241
|
-
//
|
253
|
+
// Show list items of the next prefix level.
|
242
254
|
let r = e.target;
|
243
255
|
while(r.tagName !== 'TR') r = r.parentNode;
|
244
256
|
const
|
@@ -246,7 +258,7 @@ class GUIDatasetManager extends DatasetManager {
|
|
246
258
|
i = this.expanded_rows.indexOf(p);
|
247
259
|
if(i >= 0) {
|
248
260
|
this.expanded_rows.splice(i, 1);
|
249
|
-
// Also remove all prefixes that have `p` as prefix
|
261
|
+
// Also remove all prefixes that have `p` as prefix.
|
250
262
|
for(let j = this.expanded_rows.length - 1; j >= 0; j--) {
|
251
263
|
if(this.expanded_rows[j].startsWith(p + UI.PREFIXER)) {
|
252
264
|
this.expanded_rows.splice(j, 1);
|
@@ -259,7 +271,7 @@ class GUIDatasetManager extends DatasetManager {
|
|
259
271
|
}
|
260
272
|
|
261
273
|
rowByPrefix(prefix) {
|
262
|
-
//
|
274
|
+
// Return the first table row with the specified prefix.
|
263
275
|
if(!prefix) return null;
|
264
276
|
let lcp = prefix.toLowerCase(),
|
265
277
|
pl = lcp.split(': ');
|
@@ -276,17 +288,29 @@ class GUIDatasetManager extends DatasetManager {
|
|
276
288
|
for(const r of this.dataset_table.rows) if(r.dataset.prefix === lcp) return r;
|
277
289
|
return null;
|
278
290
|
}
|
291
|
+
|
292
|
+
datasetsByPrefix(prefix) {
|
293
|
+
// Return the list of datasets having the specified prefix.
|
294
|
+
const
|
295
|
+
pid = UI.nameToID(prefix + UI.PREFIXER),
|
296
|
+
dsl = [];
|
297
|
+
for(const k of Object.keys(MODEL.datasets)) {
|
298
|
+
if(k.startsWith(pid)) dsl.push(k);
|
299
|
+
}
|
300
|
+
return dsl;
|
301
|
+
}
|
279
302
|
|
280
303
|
selectPrefixRow(e) {
|
281
|
-
//
|
304
|
+
// Select expand/collapse prefix row.
|
282
305
|
this.focal_table = this.dataset_table;
|
283
|
-
// NOTE: `e` can also be a string specifying the prefix to select
|
306
|
+
// NOTE: `e` can also be a string specifying the prefix to select.
|
284
307
|
let r = e.target || this.rowByPrefix(e);
|
285
308
|
if(!r) return;
|
286
|
-
// Modeler may have clicked on the expand/collapse triangle
|
309
|
+
// Modeler may have clicked on the expand/collapse triangle.
|
287
310
|
const toggle = r.classList.contains('tree-btn');
|
288
311
|
while(r.tagName !== 'TR') r = r.parentNode;
|
289
312
|
this.selected_prefix_row = r;
|
313
|
+
this.prefixed_datasets = this.datasetsByPrefix(r.dataset.prefix);
|
290
314
|
const sel = this.dataset_table.getElementsByClassName('sel-set');
|
291
315
|
this.selected_dataset = null;
|
292
316
|
if(sel.length > 0) {
|
@@ -296,7 +320,7 @@ class GUIDatasetManager extends DatasetManager {
|
|
296
320
|
r.classList.add('sel-set');
|
297
321
|
if(!e.target) r.scrollIntoView({block: 'center'});
|
298
322
|
if(toggle || e.altKey || this.doubleClicked(r)) this.togglePrefixRow(e);
|
299
|
-
|
323
|
+
this.updatePanes();
|
300
324
|
}
|
301
325
|
|
302
326
|
updateDialog() {
|
@@ -306,18 +330,13 @@ class GUIDatasetManager extends DatasetManager {
|
|
306
330
|
dnl = [],
|
307
331
|
sd = this.selected_dataset,
|
308
332
|
ioclass = ['', 'import', 'export'];
|
309
|
-
for(let d in MODEL.datasets) if(MODEL.datasets.hasOwnProperty(d)
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
MODEL.datasets[d] !== MODEL.equations_dataset) {
|
314
|
-
if(!this.filter_pattern || this.filter_pattern.length === 0 ||
|
315
|
-
patternMatch(MODEL.datasets[d].displayName, this.filter_pattern)) {
|
316
|
-
dnl.push(d);
|
317
|
-
}
|
333
|
+
for(let d in MODEL.datasets) if(MODEL.datasets.hasOwnProperty(d)) {
|
334
|
+
// NOTE: Do not list "black-boxed" entities or the equations dataset.
|
335
|
+
if(!d.startsWith(UI.BLACK_BOX) &&
|
336
|
+
MODEL.datasets[d] !== MODEL.equations_dataset) dnl.push(d);
|
318
337
|
}
|
319
338
|
dnl.sort((a, b) => UI.compareFullNames(a, b, true));
|
320
|
-
// First determine indentation levels, prefixes and names
|
339
|
+
// First determine indentation levels, prefixes and names.
|
321
340
|
const
|
322
341
|
indent = [],
|
323
342
|
pref_ids = [],
|
@@ -326,11 +345,11 @@ class GUIDatasetManager extends DatasetManager {
|
|
326
345
|
xids = [];
|
327
346
|
for(const dn of dnl) {
|
328
347
|
const pref = UI.prefixesAndName(MODEL.datasets[dn].name);
|
329
|
-
// NOTE:
|
348
|
+
// NOTE: Only the name part (so no prefixes at all) will be shown.
|
330
349
|
names.push(pref.pop());
|
331
350
|
indent.push(pref.length);
|
332
|
-
// NOTE:
|
333
|
-
// can contain any character; only the prefixer is "reserved"
|
351
|
+
// NOTE: Ignore case but join again with ": " because prefixes
|
352
|
+
// can contain any character; only the prefixer is "reserved".
|
334
353
|
const pref_id = pref.join(UI.PREFIXER).toLowerCase();
|
335
354
|
pref_ids.push(pref_id);
|
336
355
|
pref_names[pref_id] = pref;
|
@@ -348,11 +367,11 @@ class GUIDatasetManager extends DatasetManager {
|
|
348
367
|
} else {
|
349
368
|
ind_div = '';
|
350
369
|
}
|
351
|
-
// NOTE:
|
370
|
+
// NOTE: Empty string should not add a collapse/expand row.
|
352
371
|
if(pid && pid != prev_id && xids.indexOf(pid) < 0) {
|
353
372
|
// NOTE: XX: aa may be followed by XX: YY: ZZ: bb, which requires
|
354
373
|
// *two* collapsable lines: XX: YY and XX: YY: ZZ: before adding
|
355
|
-
// XX: YY: ZZ: bb
|
374
|
+
// XX: YY: ZZ: bb.
|
356
375
|
const
|
357
376
|
ps = pid.split(UI.PREFIXER),
|
358
377
|
pps = prev_id.split(UI.PREFIXER),
|
@@ -360,20 +379,20 @@ class GUIDatasetManager extends DatasetManager {
|
|
360
379
|
pns = pn.join(UI.PREFIXER),
|
361
380
|
lpl = [];
|
362
381
|
let lindent = 0;
|
363
|
-
// Ignore identical leading prefixes
|
382
|
+
// Ignore identical leading prefixes.
|
364
383
|
while(ps.length > 0 && pps.length > 0 && ps[0] === pps[0]) {
|
365
384
|
lpl.push(ps.shift());
|
366
385
|
pps.shift();
|
367
386
|
pn.shift();
|
368
387
|
lindent++;
|
369
388
|
}
|
370
|
-
// Add a "collapse" row for each new prefix
|
389
|
+
// Add a "collapse" row for each new prefix.
|
371
390
|
while(ps.length > 0) {
|
372
391
|
lpl.push(ps.shift());
|
373
392
|
lindent++;
|
374
393
|
const lpid = lpl.join(UI.PREFIXER);
|
375
394
|
dl.push(['<tr data-prefix="', lpid,
|
376
|
-
'" data-prefix-name="', pns, '" class="dataset"',
|
395
|
+
'" data-prefix-name="', pns.slice(0, lpid.length), '" class="dataset"',
|
377
396
|
'onclick="DATASET_MANAGER.selectPrefixRow(event);"><td>',
|
378
397
|
// NOTE: data-prefix="x" signals that this is an extra row
|
379
398
|
(lindent > 0 ?
|
@@ -383,7 +402,7 @@ class GUIDatasetManager extends DatasetManager {
|
|
383
402
|
'<div data-prefix="x" class="tree-btn">',
|
384
403
|
(this.expanded_rows.indexOf(lpid) >= 0 ? '\u25BC' : '\u25BA'),
|
385
404
|
'</div>', pn.shift(), '</td></tr>'].join(''));
|
386
|
-
// Add to the list to prevent multiple c/x-rows for the same prefix
|
405
|
+
// Add to the list to prevent multiple c/x-rows for the same prefix.
|
387
406
|
xids.push(lpid);
|
388
407
|
}
|
389
408
|
}
|
@@ -422,6 +441,7 @@ class GUIDatasetManager extends DatasetManager {
|
|
422
441
|
sd = this.selected_dataset,
|
423
442
|
btns = 'ds-data ds-clone ds-delete ds-rename';
|
424
443
|
if(sd) {
|
444
|
+
this.prefixed_count.style.display = 'none';
|
425
445
|
this.properties.style.display = 'block';
|
426
446
|
document.getElementById('dataset-default').innerHTML =
|
427
447
|
VM.sig4Dig(sd.default_value) +
|
@@ -454,8 +474,22 @@ class GUIDatasetManager extends DatasetManager {
|
|
454
474
|
UI.enableButtons(btns);
|
455
475
|
} else {
|
456
476
|
this.properties.style.display = 'none';
|
477
|
+
const
|
478
|
+
pdsl = this.prefixed_datasets.length,
|
479
|
+
npds = pluralS(pdsl, 'dataset');
|
480
|
+
this.prefixed_count.innerText = npds;
|
481
|
+
this.prefixed_count.style.display = (pdsl ? 'block' : 'none');
|
457
482
|
UI.disableButtons(btns);
|
458
|
-
if(this.selected_prefix_row)
|
483
|
+
if(this.selected_prefix_row) {
|
484
|
+
UI.enableButtons('ds-rename ds-delete', true);
|
485
|
+
document.getElementById('ds-rename-btn')
|
486
|
+
.title = `Rename ${npds} by changing prefix "${this.selectedPrefix}"`;
|
487
|
+
document.getElementById('ds-delete-btn')
|
488
|
+
.title = `Delete ${npds} having prefix "${this.selectedPrefix}"`;
|
489
|
+
} else {
|
490
|
+
document.getElementById('ds-rename-btn').title = 'Rename selected dataset';
|
491
|
+
document.getElementById('ds-delete-btn').title = 'Delete selected dataset';
|
492
|
+
}
|
459
493
|
}
|
460
494
|
this.updateModifiers();
|
461
495
|
}
|
@@ -540,37 +574,14 @@ class GUIDatasetManager extends DatasetManager {
|
|
540
574
|
if(d) DOCUMENTATION_MANAGER.update(d, shift);
|
541
575
|
}
|
542
576
|
|
543
|
-
toggleFilter() {
|
544
|
-
const
|
545
|
-
btn = document.getElementById('ds-filter-btn'),
|
546
|
-
bar = document.getElementById('ds-filter-bar'),
|
547
|
-
dsa = document.getElementById('dataset-scroll-area');
|
548
|
-
if(btn.classList.toggle('stay-activ')) {
|
549
|
-
bar.style.display = 'block';
|
550
|
-
dsa.style.top = '81px';
|
551
|
-
dsa.style.height = 'calc(100% - 141px)';
|
552
|
-
this.changeFilter();
|
553
|
-
} else {
|
554
|
-
bar.style.display = 'none';
|
555
|
-
dsa.style.top = '62px';
|
556
|
-
dsa.style.height = 'calc(100% - 122px)';
|
557
|
-
this.filter_pattern = null;
|
558
|
-
this.updateDialog();
|
559
|
-
}
|
560
|
-
}
|
561
|
-
|
562
|
-
changeFilter() {
|
563
|
-
this.filter_pattern = patternList(this.filter_text.value);
|
564
|
-
this.updateDialog();
|
565
|
-
}
|
566
|
-
|
567
577
|
selectDataset(event, id) {
|
568
|
-
// Select dataset, or edit it when Alt- or double-clicked
|
578
|
+
// Select dataset, or edit it when Alt- or double-clicked.
|
569
579
|
this.focal_table = this.dataset_table;
|
570
580
|
const
|
571
581
|
d = MODEL.datasets[id] || null,
|
572
582
|
edit = event.altKey || this.doubleClicked(d);
|
573
583
|
this.selected_dataset = d;
|
584
|
+
this.prefixed_datasets.length = 0;
|
574
585
|
if(d && edit) {
|
575
586
|
this.last_time_clicked = 0;
|
576
587
|
this.editData();
|
@@ -733,18 +744,34 @@ class GUIDatasetManager extends DatasetManager {
|
|
733
744
|
this.updateDialog();
|
734
745
|
}
|
735
746
|
}
|
747
|
+
|
748
|
+
get selectedAsList() {
|
749
|
+
// Return list of datasets selected directly or by prefix.
|
750
|
+
const dsl = [];
|
751
|
+
// Prevent including the equations dataset (just in case).
|
752
|
+
if(this.selected_dataset && this.selected_dataset !== MODEL.equations_dataset) {
|
753
|
+
dsl.push(this.selected_dataset);
|
754
|
+
} else {
|
755
|
+
// NOTE: List of prefixed datasets contains keys, not objects.
|
756
|
+
for(const k of this.prefixed_datasets) {
|
757
|
+
const ds = MODEL.datasets[k];
|
758
|
+
if(ds !== MODEL.equations_dataset) dsl.push();
|
759
|
+
}
|
760
|
+
}
|
761
|
+
return dsl;
|
762
|
+
}
|
736
763
|
|
737
764
|
deleteDataset() {
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
MODEL.
|
742
|
-
MODEL.
|
743
|
-
delete MODEL.datasets[d.identifier];
|
744
|
-
this.selected_dataset = null;
|
745
|
-
this.updateDialog();
|
746
|
-
MODEL.updateDimensions();
|
765
|
+
// Delete selected dataset(s).
|
766
|
+
for(const ds of this.selectedAsList) {
|
767
|
+
MODEL.removeImport(ds);
|
768
|
+
MODEL.removeExport(ds);
|
769
|
+
delete MODEL.datasets[ds.identifier];
|
747
770
|
}
|
771
|
+
this.selected_dataset = null;
|
772
|
+
this.prefixed_datasets.length = 0;
|
773
|
+
this.updateDialog();
|
774
|
+
MODEL.updateDimensions();
|
748
775
|
}
|
749
776
|
|
750
777
|
toggleBlackBox() {
|
@@ -1047,7 +1074,7 @@ class GUIDatasetManager extends DatasetManager {
|
|
1047
1074
|
}
|
1048
1075
|
|
1049
1076
|
editData() {
|
1050
|
-
// Show the Edit time series dialog
|
1077
|
+
// Show the Edit time series dialog.
|
1051
1078
|
const
|
1052
1079
|
ds = this.selected_dataset,
|
1053
1080
|
md = this.series_modal,
|
@@ -1057,7 +1084,7 @@ class GUIDatasetManager extends DatasetManager {
|
|
1057
1084
|
md.element('unit').value = ds.scale_unit;
|
1058
1085
|
cover.style.display = (ds.array ? 'block' : 'none');
|
1059
1086
|
md.element('time-scale').value = VM.sig4Dig(ds.time_scale);
|
1060
|
-
// Add options for time unit selector
|
1087
|
+
// Add options for time unit selector.
|
1061
1088
|
const ol = [];
|
1062
1089
|
for(let u in VM.time_unit_shorthand) {
|
1063
1090
|
if(VM.time_unit_shorthand.hasOwnProperty(u)) {
|
@@ -1067,7 +1094,7 @@ class GUIDatasetManager extends DatasetManager {
|
|
1067
1094
|
}
|
1068
1095
|
}
|
1069
1096
|
md.element('time-unit').innerHTML = ol.join('');
|
1070
|
-
// Add options for(dis)aggregation method selector
|
1097
|
+
// Add options for(dis)aggregation method selector.
|
1071
1098
|
ol.length = 0;
|
1072
1099
|
for(let i = 0; i < this.methods.length; i++) {
|
1073
1100
|
ol.push(['<option value="', this.methods[i],
|
@@ -1075,12 +1102,12 @@ class GUIDatasetManager extends DatasetManager {
|
|
1075
1102
|
'">', this.method_names[i], '</option>'].join(''));
|
1076
1103
|
}
|
1077
1104
|
md.element('method').innerHTML = ol.join('');
|
1078
|
-
// Update the "periodic" box
|
1105
|
+
// Update the "periodic" box.
|
1079
1106
|
UI.setBox('series-periodic', ds.periodic);
|
1080
|
-
// Update the "array" box
|
1107
|
+
// Update the "array" box.
|
1081
1108
|
UI.setBox('series-array', ds.array);
|
1082
1109
|
md.element('url').value = ds.url;
|
1083
|
-
// Show data as decimal numbers (JS default notation) on separate lines
|
1110
|
+
// Show data as decimal numbers (JS default notation) on separate lines.
|
1084
1111
|
this.series_data.value = ds.data.join('\n');
|
1085
1112
|
md.show('default');
|
1086
1113
|
}
|
@@ -1203,7 +1230,8 @@ class GUIDatasetManager extends DatasetManager {
|
|
1203
1230
|
UI.warn(`Invalid default value "${v}" in column ${i}`);
|
1204
1231
|
return false;
|
1205
1232
|
} else {
|
1206
|
-
|
1233
|
+
// Push empty list, as this will become the actual dataset without default value.
|
1234
|
+
dsa.push([]);
|
1207
1235
|
}
|
1208
1236
|
}
|
1209
1237
|
for(let i = 2; i < n; i++) {
|
@@ -1212,7 +1240,7 @@ class GUIDatasetManager extends DatasetManager {
|
|
1212
1240
|
UI.warn(`Number of values (${dsv.length}) on line ${i} does not match number of dataset names (${ncol})`);
|
1213
1241
|
return false;
|
1214
1242
|
}
|
1215
|
-
for(let j = 0; j <
|
1243
|
+
for(let j = 0; j < ncol; j++) {
|
1216
1244
|
const
|
1217
1245
|
v = dsv[j].trim(),
|
1218
1246
|
sf = safeStrToFloat(v, '');
|
@@ -1220,6 +1248,7 @@ class GUIDatasetManager extends DatasetManager {
|
|
1220
1248
|
UI.warn(`Invalid numerical value "${v}" for <strong>${dsn[j]}</strong> on line ${i}`);
|
1221
1249
|
return false;
|
1222
1250
|
}
|
1251
|
+
dsa[j].push(sf);
|
1223
1252
|
}
|
1224
1253
|
}
|
1225
1254
|
// Add or update datasets.
|
@@ -441,19 +441,25 @@ class GUIExperimentManager extends ExperimentManager {
|
|
441
441
|
}
|
442
442
|
|
443
443
|
newExperiment() {
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
444
|
+
// NOTE: Title must be a "clean" name: no \ or | and spacing reduced to
|
445
|
+
// a single space to permit using it unambiguously in experiment result
|
446
|
+
// specifiers of variable names.
|
447
|
+
const n = UI.cleanName(this.new_modal.element('name').value);
|
448
|
+
if(n) {
|
449
|
+
const x = MODEL.addExperiment(n);
|
450
|
+
if(x) {
|
451
|
+
this.new_modal.hide();
|
452
|
+
this.selected_experiment = x;
|
453
|
+
this.updateDialog();
|
454
|
+
}
|
455
|
+
} else {
|
456
|
+
this.new_modal.element('name').focus();
|
457
|
+
return;
|
450
458
|
}
|
451
459
|
}
|
452
460
|
|
453
461
|
promptForName() {
|
454
462
|
if(this.selected_experiment) {
|
455
|
-
this.rename_modal.element('former-name').innerHTML =
|
456
|
-
this.selected_experiment.title;
|
457
463
|
this.rename_modal.element('name').value = '';
|
458
464
|
this.rename_modal.show('name');
|
459
465
|
}
|
@@ -464,12 +470,16 @@ class GUIExperimentManager extends ExperimentManager {
|
|
464
470
|
const
|
465
471
|
nel = this.rename_modal.element('name'),
|
466
472
|
n = UI.cleanName(nel.value);
|
467
|
-
// Show modeler the "cleaned" new name
|
473
|
+
// Show modeler the "cleaned" new name.
|
468
474
|
nel.value = n;
|
469
|
-
// Keep prompt open if title is empty string
|
475
|
+
// Keep prompt open if cleaned title is empty string, or identifies
|
476
|
+
// an existing experiment.
|
477
|
+
nel.focus();
|
470
478
|
if(n) {
|
471
|
-
// Warn modeler if name already in use for some experiment
|
472
|
-
|
479
|
+
// Warn modeler if name already in use for some experiment, other than
|
480
|
+
// the selected experiment (as upper/lower case changes must be possible).
|
481
|
+
if(MODEL.indexOfExperiment(n) >= 0 &&
|
482
|
+
n.toLowerCase() !== this.selected_experiment.title.toLowerCase()) {
|
473
483
|
UI.warn(`An experiment with title "${n}" already exists`);
|
474
484
|
} else {
|
475
485
|
this.selected_experiment.title = n;
|
@@ -197,9 +197,9 @@ NOTE: Grouping groups results in a single group, e.g., (1;2);(3;4;5) evaluates a
|
|
197
197
|
let n = '',
|
198
198
|
a = '';
|
199
199
|
if(ids[0] === 'link') {
|
200
|
-
n = document.getElementById('link-from-name').
|
200
|
+
n = document.getElementById('link-from-name').innerText +
|
201
201
|
UI.LINK_ARROW +
|
202
|
-
document.getElementById('link-to-name').
|
202
|
+
document.getElementById('link-to-name').innerText;
|
203
203
|
} else {
|
204
204
|
n = document.getElementById(ids[0] + '-name').value;
|
205
205
|
if(ids[0] === 'process') {
|
@@ -218,6 +218,7 @@ NOTE: Grouping groups results in a single group, e.g., (1;2);(3;4;5) evaluates a
|
|
218
218
|
this.obj.value = 0;
|
219
219
|
this.updateVariableBar();
|
220
220
|
this.clearStatusBar();
|
221
|
+
this.showPrefix(UI.entityPrefix(prop));
|
221
222
|
md.show('text');
|
222
223
|
}
|
223
224
|
|
@@ -257,15 +258,20 @@ NOTE: Grouping groups results in a single group, e.g., (1;2);(3;4;5) evaluates a
|
|
257
258
|
// the dataset and the selector as extra parameters for the parser.
|
258
259
|
let own = null,
|
259
260
|
sel = '';
|
260
|
-
if(!this.edited_input_id
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
261
|
+
if(!this.edited_input_id) {
|
262
|
+
if(DATASET_MANAGER.edited_expression) {
|
263
|
+
own = DATASET_MANAGER.selected_dataset;
|
264
|
+
sel = DATASET_MANAGER.selected_modifier.selector;
|
265
|
+
} else if(EQUATION_MANAGER.edited_expression) {
|
266
|
+
own = MODEL.equations_dataset;
|
267
|
+
sel = EQUATION_MANAGER.selected_modifier.selector;
|
268
|
+
} else if(CONSTRAINT_EDITOR.edited_expression) {
|
269
|
+
own = CONSTRAINT_EDITOR.selected;
|
270
|
+
sel = CONSTRAINT_EDITOR.selected_selector;
|
271
|
+
} else if(UI.modals.datasetgroup.showing) {
|
272
|
+
own = UI.modals.datasetgroup.selected_ds;
|
273
|
+
sel = UI.modals.datasetgroup.selected_selector;
|
274
|
+
}
|
269
275
|
} else {
|
270
276
|
own = UI.edited_object;
|
271
277
|
sel = this.edited_input_id.split('-').pop();
|
@@ -298,6 +304,8 @@ NOTE: Grouping groups results in a single group, e.g., (1;2);(3;4;5) evaluates a
|
|
298
304
|
} else if(CONSTRAINT_EDITOR.edited_expression) {
|
299
305
|
// NOTE: Boundline selector expressions may result in a grouping.
|
300
306
|
CONSTRAINT_EDITOR.modifyExpression(xp.expr, xp.concatenating);
|
307
|
+
} else if(UI.modals.datasetgroup.showing) {
|
308
|
+
UI.modals.datasetgroup.modifyExpression(xp.expr);
|
301
309
|
}
|
302
310
|
UI.modals.expression.hide();
|
303
311
|
return true;
|
@@ -308,7 +316,13 @@ NOTE: Grouping groups results in a single group, e.g., (1;2);(3;4;5) evaluates a
|
|
308
316
|
this.status.style.backgroundColor = UI.color.dialog_background;
|
309
317
|
this.status.innerHTML = ' ';
|
310
318
|
}
|
311
|
-
|
319
|
+
|
320
|
+
showPrefix(prefix) {
|
321
|
+
// When editing an expression for a prefixed entity, show the prefix
|
322
|
+
// on the status line.
|
323
|
+
if(prefix) this.status.innerHTML = '<em>Prefix:</em> ' + prefix;
|
324
|
+
}
|
325
|
+
|
312
326
|
namesByType(type) {
|
313
327
|
// Returns a list of entity names of the specified types
|
314
328
|
// (used only to generate the options of SELECT elements)
|