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
@@ -301,18 +301,16 @@ class GUIExperimentManager extends ExperimentManager {
|
|
301
301
|
xl = [],
|
302
302
|
xtl = [],
|
303
303
|
sx = this.selected_experiment;
|
304
|
-
for(
|
305
|
-
xtl.push(MODEL.experiments[i].title);
|
306
|
-
}
|
304
|
+
for(const x of MODEL.experiments) xtl.push(x.title);
|
307
305
|
xtl.sort(ciCompare);
|
308
|
-
for(
|
306
|
+
for(const xt of xtl) {
|
309
307
|
const
|
310
|
-
xi = MODEL.indexOfExperiment(
|
308
|
+
xi = MODEL.indexOfExperiment(xt),
|
311
309
|
x = (xi < 0 ? null : MODEL.experiments[xi]);
|
312
310
|
xl.push(['<tr class="experiment',
|
313
311
|
(x == sx ? ' sel-set' : ''),
|
314
312
|
'" onclick="EXPERIMENT_MANAGER.selectExperiment(\'',
|
315
|
-
escapedSingleQuotes(
|
313
|
+
escapedSingleQuotes(xt),
|
316
314
|
'\');" onmouseover="EXPERIMENT_MANAGER.showInfo(', xi,
|
317
315
|
', event.shiftKey);"><td>', x.title, '</td></tr>'].join(''));
|
318
316
|
}
|
@@ -369,9 +367,7 @@ class GUIExperimentManager extends ExperimentManager {
|
|
369
367
|
dim_count.innerHTML = pluralS(x.available_dimensions.length,
|
370
368
|
'more dimension');
|
371
369
|
x.inferActualDimensions();
|
372
|
-
for(
|
373
|
-
x.actual_dimensions[i].sort(compareSelectors);
|
374
|
-
}
|
370
|
+
for(const ad of x.actual_dimensions) ad.sort(compareSelectors);
|
375
371
|
x.inferCombinations();
|
376
372
|
//x.combinations.sort(compareCombinations);
|
377
373
|
combi_count.innerHTML = pluralS(x.combinations.length, 'combination');
|
@@ -445,19 +441,25 @@ class GUIExperimentManager extends ExperimentManager {
|
|
445
441
|
}
|
446
442
|
|
447
443
|
newExperiment() {
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
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;
|
454
458
|
}
|
455
459
|
}
|
456
460
|
|
457
461
|
promptForName() {
|
458
462
|
if(this.selected_experiment) {
|
459
|
-
this.rename_modal.element('former-name').innerHTML =
|
460
|
-
this.selected_experiment.title;
|
461
463
|
this.rename_modal.element('name').value = '';
|
462
464
|
this.rename_modal.show('name');
|
463
465
|
}
|
@@ -468,12 +470,16 @@ class GUIExperimentManager extends ExperimentManager {
|
|
468
470
|
const
|
469
471
|
nel = this.rename_modal.element('name'),
|
470
472
|
n = UI.cleanName(nel.value);
|
471
|
-
// Show modeler the "cleaned" new name
|
473
|
+
// Show modeler the "cleaned" new name.
|
472
474
|
nel.value = n;
|
473
|
-
// 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();
|
474
478
|
if(n) {
|
475
|
-
// Warn modeler if name already in use for some experiment
|
476
|
-
|
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()) {
|
477
483
|
UI.warn(`An experiment with title "${n}" already exists`);
|
478
484
|
} else {
|
479
485
|
this.selected_experiment.title = n;
|
@@ -532,9 +538,9 @@ class GUIExperimentManager extends ExperimentManager {
|
|
532
538
|
ol = [],
|
533
539
|
ov = MODEL.outcomeNames,
|
534
540
|
vl = [...ov];
|
535
|
-
for(
|
541
|
+
for(const v of x.variables) {
|
536
542
|
const
|
537
|
-
vn =
|
543
|
+
vn = v.displayName,
|
538
544
|
oi = ov.indexOf(vn);
|
539
545
|
// If an outcome dataset or equation is plotted in an experiment
|
540
546
|
// chart, remove its name from the outcome variable list.
|
@@ -546,8 +552,7 @@ class GUIExperimentManager extends ExperimentManager {
|
|
546
552
|
// name will not be in the list (and its old name cannot be inferred)
|
547
553
|
// so then clear it.
|
548
554
|
if(vl.indexOf(x.selected_variable) < 0) x.selected_variable = '';
|
549
|
-
for(
|
550
|
-
const vn = vl[i];
|
555
|
+
for(const vn of vl) {
|
551
556
|
// NOTE: FireFox selector dropdown areas have a pale gray
|
552
557
|
// background that darkens when color is set, so always set it
|
553
558
|
// to white (like Chrome). Then set color of outcome variables
|
@@ -565,12 +570,12 @@ class GUIExperimentManager extends ExperimentManager {
|
|
565
570
|
}
|
566
571
|
|
567
572
|
drawTable() {
|
568
|
-
// Draw experimental design as table
|
573
|
+
// Draw experimental design as table.
|
569
574
|
const x = this.selected_experiment;
|
570
575
|
if(x) {
|
571
576
|
this.clean_columns = [];
|
572
577
|
this.clean_rows = [];
|
573
|
-
// Calculate the actual number of columns and rows of the table
|
578
|
+
// Calculate the actual number of columns and rows of the table.
|
574
579
|
const
|
575
580
|
coldims = x.configuration_dims + x.column_scenario_dims,
|
576
581
|
rowdims = x.actual_dimensions.length - coldims,
|
@@ -795,32 +800,28 @@ class GUIExperimentManager extends ExperimentManager {
|
|
795
800
|
const combi = x.combinations[n];
|
796
801
|
info.title = `Combination: <tt>${tupelString(combi)}</tt>`;
|
797
802
|
const html = [], list = [];
|
798
|
-
for(
|
799
|
-
const sel = combi[i];
|
803
|
+
for(const sel of combi) {
|
800
804
|
html.push('<h3>Selector <tt>', sel, '</tt></h3>');
|
801
|
-
// List associated model settings (if any)
|
805
|
+
// List associated model settings (if any).
|
802
806
|
list.length = 0;
|
803
|
-
for(
|
804
|
-
const
|
805
|
-
if(sel ===
|
807
|
+
for(const ss of x.settings_selectors) {
|
808
|
+
const tuple = ss.split('|');
|
809
|
+
if(sel === tuple[0]) list.push(tuple[1]);
|
806
810
|
}
|
807
811
|
if(list.length > 0) {
|
808
812
|
html.push('<p><em>Model settings:</em> <tt>', list.join(';'),
|
809
813
|
'</tt></p>');
|
810
814
|
}
|
811
|
-
// List associated actor settings (if any)
|
815
|
+
// List associated actor settings (if any).
|
812
816
|
list.length = 0;
|
813
|
-
for(
|
814
|
-
|
815
|
-
if(sel === as.selector) {
|
816
|
-
list.push(as.round_sequence);
|
817
|
-
}
|
817
|
+
for(const as of x.actor_selectors) {
|
818
|
+
if(sel === as.selector) list.push(as.round_sequence);
|
818
819
|
}
|
819
820
|
if(list.length > 0) {
|
820
821
|
html.push('<p><em>Actor settings:</em> <tt>', list.join(';'),
|
821
822
|
'</tt></p>');
|
822
823
|
}
|
823
|
-
// List associated datasets (if any)
|
824
|
+
// List associated datasets (if any).
|
824
825
|
list.length = 0;
|
825
826
|
for(let id in MODEL.datasets) if(MODEL.datasets.hasOwnProperty(id)) {
|
826
827
|
const ds = MODEL.datasets[id];
|
@@ -888,10 +889,8 @@ class GUIExperimentManager extends ExperimentManager {
|
|
888
889
|
// Set reference column indices so that values for the reference|
|
889
890
|
// configuration can be displayed in orange.
|
890
891
|
const ref_conf_indices = [];
|
891
|
-
for(
|
892
|
-
const
|
893
|
-
r = x.runs[i],
|
894
|
-
rr = r.results[rri];
|
892
|
+
for(const r of x.runs) {
|
893
|
+
const rr = r.results[rri];
|
895
894
|
if(!rr) {
|
896
895
|
data.push(VM.UNDEFINED);
|
897
896
|
} else if(x.selected_scale === 'sec') {
|
@@ -918,12 +917,12 @@ class GUIExperimentManager extends ExperimentManager {
|
|
918
917
|
}
|
919
918
|
// Scale data as selected.
|
920
919
|
const scaled = data.slice();
|
921
|
-
// NOTE:
|
922
|
-
// configurations have been defined (otherwise comparison is pointless)
|
920
|
+
// NOTE: Scale only after the experiment has been completed AND
|
921
|
+
// configurations have been defined (otherwise comparison is pointless).
|
923
922
|
if(x.completed && this.nr_of_configurations > 0) {
|
924
923
|
const n = scaled.length / this.nr_of_configurations;
|
925
924
|
if(x.selected_scale === 'dif') {
|
926
|
-
// Compute difference: current configuration - reference configuration
|
925
|
+
// Compute difference: current configuration - reference configuration.
|
927
926
|
const rc = x.reference_configuration;
|
928
927
|
for(let i = 0; i < this.nr_of_configurations; i++) {
|
929
928
|
if(i != rc) {
|
@@ -932,47 +931,36 @@ class GUIExperimentManager extends ExperimentManager {
|
|
932
931
|
}
|
933
932
|
}
|
934
933
|
}
|
935
|
-
// Set difference for reference configuration itself to 0
|
934
|
+
// Set difference for reference configuration itself to 0.
|
936
935
|
for(let i = 0; i < n; i++) {
|
937
936
|
const index = rc * n + i;
|
938
937
|
scaled[index] = 0;
|
939
938
|
ref_conf_indices.push(index);
|
940
939
|
}
|
941
940
|
} else if(x.selected_scale === 'reg') {
|
942
|
-
// Compute regret: current config - high value config in same scenario
|
941
|
+
// Compute regret: current config - high value config in same scenario.
|
943
942
|
for(let i = 0; i < n; i++) {
|
944
|
-
// Get high value
|
943
|
+
// Get high value.
|
945
944
|
let high = VM.MINUS_INFINITY;
|
946
945
|
for(let j = 0; j < this.nr_of_configurations; j++) {
|
947
946
|
high = Math.max(high, scaled[j * n + i]);
|
948
947
|
}
|
949
|
-
// Scale (so high config always has value 0)
|
948
|
+
// Scale (so high config always has value 0).
|
950
949
|
for(let j = 0; j < this.nr_of_configurations; j++) {
|
951
950
|
scaled[j * n + i] -= high;
|
952
951
|
}
|
953
952
|
}
|
954
953
|
}
|
955
954
|
}
|
956
|
-
// For color scales, compute normalized scores
|
957
|
-
|
958
|
-
high =
|
959
|
-
low =
|
960
|
-
|
961
|
-
|
962
|
-
|
963
|
-
|
964
|
-
//
|
965
|
-
const range = (high - low < VM.NEAR_ZERO ? 0 : high - low);
|
966
|
-
if(range > 0) {
|
967
|
-
for(let i = 0; i < normalized.length; i++) {
|
968
|
-
normalized[i] = (normalized[i] - low) / range;
|
969
|
-
}
|
970
|
-
}
|
971
|
-
// Format data such that they all have same number of decimals
|
972
|
-
let formatted = [];
|
973
|
-
for(let i = 0; i < scaled.length; i++) {
|
974
|
-
formatted.push(VM.sig4Dig(scaled[i]));
|
975
|
-
}
|
955
|
+
// For color scales, compute normalized scores.
|
956
|
+
const
|
957
|
+
high = Math.max(...scaled),
|
958
|
+
low = Math.min(...scaled),
|
959
|
+
// Avoid too small value ranges.
|
960
|
+
range = (high - low < VM.NEAR_ZERO ? 0 : high - low),
|
961
|
+
normalized = scaled.map((v) => range ? (v - low) / range : v),
|
962
|
+
formatted = scaled.map((v) => VM.sig4Dig(v));
|
963
|
+
// Format data such that they all have same number of decimals.
|
976
964
|
uniformDecimals(formatted);
|
977
965
|
// Display formatted data in cells.
|
978
966
|
for(let i = 0; i < x.combinations.length; i++) {
|
@@ -1072,11 +1060,11 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
|
|
1072
1060
|
const
|
1073
1061
|
spl = this.viewer.getElementsByClassName('spin-plus'),
|
1074
1062
|
rem = (x.configuration_dims + x.column_scenario_dims < xdims);
|
1075
|
-
for(
|
1063
|
+
for(const sp of spl) {
|
1076
1064
|
if(rem) {
|
1077
|
-
|
1065
|
+
sp.classList.remove('no-spin');
|
1078
1066
|
} else {
|
1079
|
-
|
1067
|
+
sp.classList.add('no-spin');
|
1080
1068
|
}
|
1081
1069
|
}
|
1082
1070
|
if(dir != 0 ) this.drawTable();
|
@@ -1103,9 +1091,7 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
|
|
1103
1091
|
x.selected_color_scale = cs;
|
1104
1092
|
this.color_scale.set(cs);
|
1105
1093
|
const csl = this.viewer.getElementsByClassName('color-scale');
|
1106
|
-
for(
|
1107
|
-
csl.item(i).classList.remove('sel-cs');
|
1108
|
-
}
|
1094
|
+
for(const cs of csl) cs.classList.remove('sel-cs');
|
1109
1095
|
document.getElementById(`xv-${cs}-scale`).classList.add('sel-cs');
|
1110
1096
|
}
|
1111
1097
|
this.updateData();
|
@@ -1189,14 +1175,14 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
|
|
1189
1175
|
}
|
1190
1176
|
|
1191
1177
|
editIteratorRanges() {
|
1192
|
-
// Open dialog for editing iterator ranges
|
1178
|
+
// Open dialog for editing iterator ranges.
|
1193
1179
|
const
|
1194
1180
|
x = this.selected_experiment,
|
1195
1181
|
md = this.iterator_modal,
|
1196
1182
|
il = ['i', 'j', 'k'];
|
1197
1183
|
if(x) {
|
1198
1184
|
// NOTE: there are always 3 iterators (i, j k) so these have fixed
|
1199
|
-
// FROM and TO input fields in the dialog
|
1185
|
+
// FROM and TO input fields in the dialog.
|
1200
1186
|
for(let i = 0; i < 3; i++) {
|
1201
1187
|
const k = il[i];
|
1202
1188
|
md.element(k + '-from').value = x.iterator_ranges[i][0];
|
@@ -1211,8 +1197,8 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
|
|
1211
1197
|
x = this.selected_experiment,
|
1212
1198
|
md = this.iterator_modal;
|
1213
1199
|
if(x) {
|
1214
|
-
// First validate all input fields (must be integer values)
|
1215
|
-
// NOTE:
|
1200
|
+
// First validate all input fields (must be integer values).
|
1201
|
+
// NOTE: Test using a copy so as not to overwrite values until OK.
|
1216
1202
|
const
|
1217
1203
|
il = ['i', 'j', 'k'],
|
1218
1204
|
ir = [[0, 0], [0, 0], [0, 0]],
|
@@ -1227,7 +1213,7 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
|
|
1227
1213
|
t = el.value.trim() || '0';
|
1228
1214
|
if(t === '' || re.test(t)) el = null;
|
1229
1215
|
}
|
1230
|
-
// NULL value signals that field inputs are valid
|
1216
|
+
// NULL value signals that field inputs are valid.
|
1231
1217
|
if(el === null) {
|
1232
1218
|
ir[i] = [f, t];
|
1233
1219
|
} else {
|
@@ -1236,7 +1222,7 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
|
|
1236
1222
|
return;
|
1237
1223
|
}
|
1238
1224
|
}
|
1239
|
-
// Input validated, so modify the iterator dimensions
|
1225
|
+
// Input validated, so modify the iterator dimensions.
|
1240
1226
|
x.iterator_ranges = ir;
|
1241
1227
|
this.updateDialog();
|
1242
1228
|
}
|
@@ -1244,17 +1230,17 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
|
|
1244
1230
|
}
|
1245
1231
|
|
1246
1232
|
editSettingsDimensions() {
|
1247
|
-
// Open dialog for editing model settings dimensions
|
1233
|
+
// Open dialog for editing model settings dimensions.
|
1248
1234
|
const x = this.selected_experiment, rows = [];
|
1249
1235
|
if(x) {
|
1250
|
-
// Initialize selector list
|
1236
|
+
// Initialize selector list.
|
1251
1237
|
for(let i = 0; i < x.settings_selectors.length; i++) {
|
1252
1238
|
const sel = x.settings_selectors[i].split('|');
|
1253
1239
|
rows.push('<tr onclick="EXPERIMENT_MANAGER.editSettingsSelector(', i,
|
1254
1240
|
');"><td width="25%">', sel[0], '</td><td>', sel[1], '</td></tr>');
|
1255
1241
|
}
|
1256
1242
|
this.settings_modal.element('s-table').innerHTML = rows.join('');
|
1257
|
-
// Initialize combination list
|
1243
|
+
// Initialize combination list.
|
1258
1244
|
rows.length = 0;
|
1259
1245
|
for(let i = 0; i < x.settings_dimensions.length; i++) {
|
1260
1246
|
const dim = x.settings_dimensions[i];
|
@@ -1263,14 +1249,14 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
|
|
1263
1249
|
}
|
1264
1250
|
this.settings_modal.element('d-table').innerHTML = rows.join('');
|
1265
1251
|
this.settings_modal.show();
|
1266
|
-
// NOTE:
|
1267
|
-
// otherwise remain visible while no longer relevant
|
1252
|
+
// NOTE: Clear infoline because dialog can generate warnings that would
|
1253
|
+
// otherwise remain visible while no longer relevant.
|
1268
1254
|
UI.setMessage('');
|
1269
1255
|
}
|
1270
1256
|
}
|
1271
1257
|
|
1272
1258
|
closeSettingsDimensions() {
|
1273
|
-
// Hide editor, and then update the experiment manager to reflect changes
|
1259
|
+
// Hide editor, and then update the experiment manager to reflect changes.
|
1274
1260
|
this.settings_modal.hide();
|
1275
1261
|
this.updateDialog();
|
1276
1262
|
}
|
@@ -1303,8 +1289,10 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
|
|
1303
1289
|
md = this.settings_selector_modal,
|
1304
1290
|
sc = md.element('code'),
|
1305
1291
|
ss = md.element('string'),
|
1306
|
-
|
1307
|
-
|
1292
|
+
// NOTE: Simply remove invalid characters from selector, but accept
|
1293
|
+
// '=' here to permit associating settings with iterator selectors.
|
1294
|
+
code = sc.value.replace(/[^\w\+\-\%\=]/g, ''),
|
1295
|
+
value = ss.value.trim().toLowerCase().replace(',', '.'),
|
1308
1296
|
add = this.edited_selector_index < 0;
|
1309
1297
|
// Remove selector if either field has been cleared
|
1310
1298
|
if(code.length === 0 || value.length === 0) {
|
@@ -1312,7 +1300,7 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
|
|
1312
1300
|
x.settings_selectors.splice(this.edited_selector_index, 1);
|
1313
1301
|
}
|
1314
1302
|
} else {
|
1315
|
-
// Check for uniqueness of code
|
1303
|
+
// Check for uniqueness of code.
|
1316
1304
|
for(let i = 0; i < x.settings_selectors.length; i++) {
|
1317
1305
|
// NOTE: ignore selector being edited, as this selector can be renamed
|
1318
1306
|
if(i != this.edited_selector_index &&
|
@@ -1323,7 +1311,7 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
|
|
1323
1311
|
}
|
1324
1312
|
}
|
1325
1313
|
// Check for valid syntax -- canonical example: s=0.25h t=1-100 b=12 l=6
|
1326
|
-
const re = /^(s\=\d+(\.?\d+)?(yr?|wk?|d|h|m|min|s)\s+)?(t\=\d+(\-\d+)?\s+)?(b\=\d+\s+)?(l=\d+\s+)?$/i;
|
1314
|
+
const re = /^(s\=\d+(\.?\d+)?(yr?|wk?|d|h|m|min|s)\s+)?(t\=\d+(\-\d+)?\s+)?(b\=\d+\s+)?(l=\d+\s+)?(\-[ckl]+\s+)?$/i;
|
1327
1315
|
if(!re.test(value + ' ')) {
|
1328
1316
|
UI.warn(`Invalid settings "${value}"`);
|
1329
1317
|
ss.focus();
|
@@ -1560,40 +1548,37 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
|
|
1560
1548
|
if(x) {
|
1561
1549
|
const
|
1562
1550
|
add = this.edited_combi_dimension_index < 0,
|
1563
|
-
// Trim whitespace and reduce inner spacing to a single space
|
1551
|
+
// Trim whitespace and reduce inner spacing to a single space.
|
1564
1552
|
dimstr = this.combination_dimension_modal.element('string').value.trim();
|
1565
|
-
// Remove dimension if field has been cleared
|
1553
|
+
// Remove dimension if field has been cleared.
|
1566
1554
|
if(dimstr.length === 0) {
|
1567
1555
|
if(!add) {
|
1568
1556
|
x.combination_dimensions.splice(this.edited_combi_dimension_index, 1);
|
1569
1557
|
}
|
1570
1558
|
} else {
|
1571
|
-
// Check for valid selector list
|
1559
|
+
// Check for valid selector list.
|
1572
1560
|
const
|
1573
1561
|
dim = dimstr.split(/\s+/g),
|
1574
1562
|
ssl = [];
|
1575
|
-
// Get this experiment's combination selector list
|
1576
|
-
for(
|
1577
|
-
|
1578
|
-
}
|
1579
|
-
// All selectors in string should have been defined
|
1563
|
+
// Get this experiment's combination selector list.
|
1564
|
+
for(const sel of x.combination_selectors) ssl.push(sel.split('|')[0]);
|
1565
|
+
// All selectors in string should have been defined.
|
1580
1566
|
let c = complement(dim, ssl);
|
1581
1567
|
if(c.length > 0) {
|
1582
1568
|
UI.warn('Combination dimension contains ' +
|
1583
1569
|
pluralS(c.length, 'unknown selector') + ': ' + c.join(' '));
|
1584
1570
|
return;
|
1585
1571
|
}
|
1586
|
-
// All selectors should expand to non-overlapping selector sets
|
1572
|
+
// All selectors should expand to non-overlapping selector sets.
|
1587
1573
|
if(!x.orthogonalCombinationDimensions(dim)) return;
|
1588
|
-
// Do not add when a (setwise) identical combination dimension exists
|
1589
|
-
for(
|
1590
|
-
const cd = x.combination_dimensions[i];
|
1574
|
+
// Do not add when a (setwise) identical combination dimension exists.
|
1575
|
+
for(const cd of x.combination_dimensions) {
|
1591
1576
|
if(intersection(dim, cd).length === dim.length) {
|
1592
1577
|
UI.notify('Combination already defined: ' + setString(cd));
|
1593
1578
|
return;
|
1594
1579
|
}
|
1595
1580
|
}
|
1596
|
-
// OK? Then add or modify
|
1581
|
+
// OK? Then add or modify.
|
1597
1582
|
if(add) {
|
1598
1583
|
x.combination_dimensions.push(dim);
|
1599
1584
|
} else {
|
@@ -1602,15 +1587,15 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
|
|
1602
1587
|
}
|
1603
1588
|
}
|
1604
1589
|
this.combination_dimension_modal.hide();
|
1605
|
-
// Update combination dimensions dialog
|
1590
|
+
// Update combination dimensions dialog.
|
1606
1591
|
this.editCombinationDimensions();
|
1607
1592
|
}
|
1608
1593
|
|
1609
1594
|
editActorDimension() {
|
1610
|
-
// Open dialog for editing the actor dimension
|
1595
|
+
// Open dialog for editing the actor dimension.
|
1611
1596
|
const x = this.selected_experiment, rows = [];
|
1612
1597
|
if(x) {
|
1613
|
-
// Initialize selector list
|
1598
|
+
// Initialize selector list.
|
1614
1599
|
for(let i = 0; i < x.actor_selectors.length; i++) {
|
1615
1600
|
rows.push('<tr onclick="EXPERIMENT_MANAGER.editActorSelector(', i,
|
1616
1601
|
');"><td>', x.actor_selectors[i].selector,
|
@@ -1619,8 +1604,8 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
|
|
1619
1604
|
}
|
1620
1605
|
this.actor_dimension_modal.element('table').innerHTML = rows.join('');
|
1621
1606
|
this.actor_dimension_modal.show();
|
1622
|
-
// NOTE:
|
1623
|
-
// otherwise remain visible while no longer relevant
|
1607
|
+
// NOTE: Clear infoline because dialog can generate warnings that would
|
1608
|
+
// otherwise remain visible while no longer relevant.
|
1624
1609
|
UI.setMessage('');
|
1625
1610
|
}
|
1626
1611
|
}
|
@@ -1707,17 +1692,16 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
|
|
1707
1692
|
clist = [],
|
1708
1693
|
csel = md.element('select'),
|
1709
1694
|
sinp = md.element('selectors');
|
1710
|
-
// NOTE:
|
1695
|
+
// NOTE: Copy experiment property to modal dialog property, so that changes
|
1711
1696
|
// are made only when OK is clicked
|
1712
1697
|
md.clusters = [];
|
1713
|
-
for(
|
1714
|
-
|
1715
|
-
md.clusters.push({cluster: cs.cluster, selectors: cs. selectors});
|
1698
|
+
for(const cs of x.clusters_to_ignore) {
|
1699
|
+
md.clusters.push({cluster: cs.cluster, selectors: cs.selectors});
|
1716
1700
|
}
|
1717
1701
|
md.cluster_index = -1;
|
1718
1702
|
for(let k in MODEL.clusters) if(MODEL.clusters.hasOwnProperty(k)) {
|
1719
1703
|
const c = MODEL.clusters[k];
|
1720
|
-
// Do not add top cluster, nor clusters already on the list
|
1704
|
+
// Do not add top cluster, nor clusters already on the list.
|
1721
1705
|
if(c !== MODEL.top_cluster && !c.ignore && !x.mayBeIgnored(c)) {
|
1722
1706
|
clist.push(`<option value="${k}">${c.displayName}</option>`);
|
1723
1707
|
}
|
@@ -1738,7 +1722,7 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
|
|
1738
1722
|
sdiv = md.element('selectors-div'),
|
1739
1723
|
cl = md.clusters.length;
|
1740
1724
|
if(cl > 0) {
|
1741
|
-
// Show cluster+selectors list
|
1725
|
+
// Show cluster+selectors list.
|
1742
1726
|
const ol = [];
|
1743
1727
|
for(let i = 0; i < cl; i++) {
|
1744
1728
|
const cti = md.clusters[i];
|
@@ -1751,7 +1735,7 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
|
|
1751
1735
|
clst.style.display = 'block';
|
1752
1736
|
nlst.style.display = 'none';
|
1753
1737
|
} else {
|
1754
|
-
// Hide list and show "no clusters set to be ignored"
|
1738
|
+
// Hide list and show "no clusters set to be ignored".
|
1755
1739
|
clst.style.display = 'none';
|
1756
1740
|
nlst.style.display = 'block';
|
1757
1741
|
}
|
@@ -1767,7 +1751,7 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
|
|
1767
1751
|
}
|
1768
1752
|
|
1769
1753
|
selectCluster(n) {
|
1770
|
-
// Set selected cluster index to `n
|
1754
|
+
// Set selected cluster index to `n`.
|
1771
1755
|
this.clusters_modal.cluster_index = n;
|
1772
1756
|
this.updateClusterList();
|
1773
1757
|
}
|
@@ -1780,7 +1764,7 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
|
|
1780
1764
|
if(c) {
|
1781
1765
|
md.clusters.push({cluster: c, selectors: ''});
|
1782
1766
|
md.cluster_index = md.clusters.length - 1;
|
1783
|
-
// Remove cluster from select so it cannot be added again
|
1767
|
+
// Remove cluster from select so it cannot be added again.
|
1784
1768
|
sel.remove(sel.selectedIndex);
|
1785
1769
|
this.updateClusterList();
|
1786
1770
|
}
|
@@ -1803,7 +1787,7 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
|
|
1803
1787
|
}
|
1804
1788
|
|
1805
1789
|
deleteClusterFromIgnoreList() {
|
1806
|
-
// Delete selected cluster+selectors from list
|
1790
|
+
// Delete selected cluster+selectors from list.
|
1807
1791
|
const md = this.clusters_modal;
|
1808
1792
|
if(md.cluster_index >= 0) {
|
1809
1793
|
md.clusters.splice(md.cluster_index, 1);
|
@@ -1813,7 +1797,7 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
|
|
1813
1797
|
}
|
1814
1798
|
|
1815
1799
|
modifyClustersToIgnore() {
|
1816
|
-
// Replace current list by cluster+selectors list of modal dialog
|
1800
|
+
// Replace current list by cluster+selectors list of modal dialog.
|
1817
1801
|
const
|
1818
1802
|
md = this.clusters_modal,
|
1819
1803
|
x = this.selected_experiment;
|
@@ -1823,7 +1807,7 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
|
|
1823
1807
|
}
|
1824
1808
|
|
1825
1809
|
promptForParameter(type) {
|
1826
|
-
// Open dialog for adding new dimension or chart
|
1810
|
+
// Open dialog for adding new dimension or chart.
|
1827
1811
|
const x = this.selected_experiment;
|
1828
1812
|
if(x) {
|
1829
1813
|
const ol = [];
|
@@ -1835,9 +1819,8 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
|
|
1835
1819
|
ol.push(`<option value="${i}">${ds}</option>`);
|
1836
1820
|
}
|
1837
1821
|
} else {
|
1838
|
-
for(
|
1839
|
-
|
1840
|
-
// NOTE: exclude charts already in the selected experiment
|
1822
|
+
for(const c of this.suitable_charts) {
|
1823
|
+
// NOTE: Exclude charts already in the selected experiment.
|
1841
1824
|
if (x.charts.indexOf(c) < 0) {
|
1842
1825
|
ol.push(`<option value="${c.title}">${c.title}</option>`);
|
1843
1826
|
}
|
@@ -1849,7 +1832,7 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
|
|
1849
1832
|
}
|
1850
1833
|
|
1851
1834
|
addParameter() {
|
1852
|
-
// Add parameter (dimension or chart) to experiment
|
1835
|
+
// Add parameter (dimension or chart) to experiment.
|
1853
1836
|
const
|
1854
1837
|
x = this.selected_experiment,
|
1855
1838
|
name = this.parameter_modal.element('select').value;
|
@@ -1871,7 +1854,7 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
|
|
1871
1854
|
}
|
1872
1855
|
|
1873
1856
|
deleteParameter() {
|
1874
|
-
// Remove selected dimension or chart from selected experiment
|
1857
|
+
// Remove selected dimension or chart from selected experiment.
|
1875
1858
|
const
|
1876
1859
|
x = this.selected_experiment,
|
1877
1860
|
sp = this.selected_parameter;
|
@@ -1888,12 +1871,12 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
|
|
1888
1871
|
}
|
1889
1872
|
|
1890
1873
|
editExclusions() {
|
1891
|
-
// Give visual feedback by setting background color to white
|
1874
|
+
// Give visual feedback by setting background color to white.
|
1892
1875
|
this.exclude.style.backgroundColor = 'white';
|
1893
1876
|
}
|
1894
1877
|
|
1895
1878
|
setExclusions() {
|
1896
|
-
// Sanitize string before accepting it as space-separated selector list
|
1879
|
+
// Sanitize string before accepting it as space-separated selector list.
|
1897
1880
|
const
|
1898
1881
|
x = this.selected_experiment;
|
1899
1882
|
if(x) {
|
@@ -1907,24 +1890,24 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
|
|
1907
1890
|
}
|
1908
1891
|
|
1909
1892
|
readyButtons() {
|
1910
|
-
// Set experiment run control buttons in "ready" state
|
1893
|
+
// Set experiment run control buttons in "ready" state.
|
1911
1894
|
this.pause_btn.classList.add('off');
|
1912
1895
|
this.stop_btn.classList.add('off');
|
1913
1896
|
this.start_btn.classList.remove('off', 'blink');
|
1914
1897
|
}
|
1915
1898
|
|
1916
1899
|
pausedButtons(aci) {
|
1917
|
-
// Set experiment run control buttons in "paused" state
|
1900
|
+
// Set experiment run control buttons in "paused" state.
|
1918
1901
|
this.pause_btn.classList.remove('blink');
|
1919
1902
|
this.pause_btn.classList.add('off');
|
1920
1903
|
this.start_btn.classList.remove('off');
|
1921
|
-
// Blinking start button indicates: paused -- click to resume
|
1904
|
+
// Blinking start button indicates: paused -- click to resume.
|
1922
1905
|
this.start_btn.classList.add('blink');
|
1923
1906
|
this.viewer_progress.innerHTML = `Run ${aci} PAUSED`;
|
1924
1907
|
}
|
1925
1908
|
|
1926
1909
|
resumeButtons() {
|
1927
|
-
// Changes buttons to "running" state, and return TRUE if state was "paused"
|
1910
|
+
// Changes buttons to "running" state, and return TRUE if state was "paused".
|
1928
1911
|
const paused = this.start_btn.classList.contains('blink');
|
1929
1912
|
this.start_btn.classList.remove('blink');
|
1930
1913
|
this.start_btn.classList.add('off');
|
@@ -1934,7 +1917,7 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
|
|
1934
1917
|
}
|
1935
1918
|
|
1936
1919
|
pauseExperiment() {
|
1937
|
-
// Interrupt solver but retain data on server and allow resume
|
1920
|
+
// Interrupt solver but retain data on server and allow resume.
|
1938
1921
|
UI.notify('Run sequence will be suspended after the current run');
|
1939
1922
|
this.pause_btn.classList.add('blink');
|
1940
1923
|
this.stop_btn.classList.remove('off');
|
@@ -1942,7 +1925,7 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
|
|
1942
1925
|
}
|
1943
1926
|
|
1944
1927
|
stopExperiment() {
|
1945
|
-
// Interrupt solver but retain data on server (and no resume)
|
1928
|
+
// Interrupt solver but retain data on server (and no resume).
|
1946
1929
|
VM.halt();
|
1947
1930
|
MODEL.running_experiment = null;
|
1948
1931
|
UI.notify('Experiment has been stopped');
|
@@ -1951,7 +1934,7 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
|
|
1951
1934
|
}
|
1952
1935
|
|
1953
1936
|
showProgress(ci, p, n) {
|
1954
|
-
// Show progress in the viewer
|
1937
|
+
// Show progress in the viewer.
|
1955
1938
|
this.viewer_progress.innerHTML = `Run ${ci} (${p}% of ${n})`;
|
1956
1939
|
}
|
1957
1940
|
|
@@ -1962,7 +1945,7 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
|
|
1962
1945
|
}
|
1963
1946
|
|
1964
1947
|
promptForDownload() {
|
1965
|
-
// Show the download modal
|
1948
|
+
// Show the download modal.
|
1966
1949
|
const x = this.selected_experiment;
|
1967
1950
|
if(!x) return;
|
1968
1951
|
const
|
@@ -1975,13 +1958,13 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
|
|
1975
1958
|
return;
|
1976
1959
|
}
|
1977
1960
|
md.element(ds.variables + '-v').checked = true;
|
1978
|
-
// Disable "selected runs" button when no runs have been selected
|
1961
|
+
// Disable "selected runs" button when no runs have been selected.
|
1979
1962
|
if(sruns) {
|
1980
1963
|
md.element('selected-r').disabled = false;
|
1981
1964
|
md.element(ds.runs + '-r').checked = true;
|
1982
1965
|
} else {
|
1983
1966
|
md.element('selected-r').disabled = true;
|
1984
|
-
// Check "all runs" but do not change download setting
|
1967
|
+
// Check "all runs" but do not change download setting.
|
1985
1968
|
md.element('all-r').checked = true;
|
1986
1969
|
}
|
1987
1970
|
this.download_modal.show();
|
@@ -1997,7 +1980,7 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
|
|
1997
1980
|
}
|
1998
1981
|
|
1999
1982
|
downloadDataAsCSV() {
|
2000
|
-
// Push results to browser
|
1983
|
+
// Push results to browser.
|
2001
1984
|
if(this.selected_experiment) {
|
2002
1985
|
const md = this.download_modal;
|
2003
1986
|
this.selected_experiment.download_settings = {
|