linny-r 2.0.7 → 2.0.9

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 (31) hide show
  1. package/README.md +3 -40
  2. package/package.json +1 -1
  3. package/server.js +19 -157
  4. package/static/index.html +74 -21
  5. package/static/linny-r.css +22 -16
  6. package/static/scripts/iro.min.js +7 -7
  7. package/static/scripts/linny-r-ctrl.js +51 -72
  8. package/static/scripts/linny-r-gui-actor-manager.js +23 -33
  9. package/static/scripts/linny-r-gui-chart-manager.js +50 -45
  10. package/static/scripts/linny-r-gui-constraint-editor.js +6 -10
  11. package/static/scripts/linny-r-gui-controller.js +254 -230
  12. package/static/scripts/linny-r-gui-dataset-manager.js +143 -32
  13. package/static/scripts/linny-r-gui-documentation-manager.js +11 -17
  14. package/static/scripts/linny-r-gui-equation-manager.js +22 -22
  15. package/static/scripts/linny-r-gui-experiment-manager.js +102 -129
  16. package/static/scripts/linny-r-gui-file-manager.js +53 -46
  17. package/static/scripts/linny-r-gui-finder.js +105 -51
  18. package/static/scripts/linny-r-gui-model-autosaver.js +2 -4
  19. package/static/scripts/linny-r-gui-monitor.js +35 -41
  20. package/static/scripts/linny-r-gui-paper.js +42 -70
  21. package/static/scripts/linny-r-gui-power-grid-manager.js +31 -34
  22. package/static/scripts/linny-r-gui-receiver.js +1 -2
  23. package/static/scripts/linny-r-gui-repository-browser.js +44 -46
  24. package/static/scripts/linny-r-gui-scale-unit-manager.js +32 -32
  25. package/static/scripts/linny-r-gui-sensitivity-analysis.js +61 -67
  26. package/static/scripts/linny-r-gui-undo-redo.js +94 -95
  27. package/static/scripts/linny-r-milp.js +20 -24
  28. package/static/scripts/linny-r-model.js +1832 -2248
  29. package/static/scripts/linny-r-utils.js +35 -27
  30. package/static/scripts/linny-r-vm.js +807 -905
  31. 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(let i = 0; i < MODEL.experiments.length; i++) {
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(let i = 0; i < xtl.length; i++) {
306
+ for(const xt of xtl) {
309
307
  const
310
- xi = MODEL.indexOfExperiment(xtl[i]),
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(xtl[i]),
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(let i = 0; i < x.actual_dimensions.length; i++) {
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');
@@ -532,9 +528,9 @@ class GUIExperimentManager extends ExperimentManager {
532
528
  ol = [],
533
529
  ov = MODEL.outcomeNames,
534
530
  vl = [...ov];
535
- for(let i = 0; i < x.variables.length; i++) {
531
+ for(const v of x.variables) {
536
532
  const
537
- vn = x.variables[i].displayName,
533
+ vn = v.displayName,
538
534
  oi = ov.indexOf(vn);
539
535
  // If an outcome dataset or equation is plotted in an experiment
540
536
  // chart, remove its name from the outcome variable list.
@@ -546,8 +542,7 @@ class GUIExperimentManager extends ExperimentManager {
546
542
  // name will not be in the list (and its old name cannot be inferred)
547
543
  // so then clear it.
548
544
  if(vl.indexOf(x.selected_variable) < 0) x.selected_variable = '';
549
- for(let i = 0; i < vl.length; i++) {
550
- const vn = vl[i];
545
+ for(const vn of vl) {
551
546
  // NOTE: FireFox selector dropdown areas have a pale gray
552
547
  // background that darkens when color is set, so always set it
553
548
  // to white (like Chrome). Then set color of outcome variables
@@ -565,12 +560,12 @@ class GUIExperimentManager extends ExperimentManager {
565
560
  }
566
561
 
567
562
  drawTable() {
568
- // Draw experimental design as table
563
+ // Draw experimental design as table.
569
564
  const x = this.selected_experiment;
570
565
  if(x) {
571
566
  this.clean_columns = [];
572
567
  this.clean_rows = [];
573
- // Calculate the actual number of columns and rows of the table
568
+ // Calculate the actual number of columns and rows of the table.
574
569
  const
575
570
  coldims = x.configuration_dims + x.column_scenario_dims,
576
571
  rowdims = x.actual_dimensions.length - coldims,
@@ -795,32 +790,28 @@ class GUIExperimentManager extends ExperimentManager {
795
790
  const combi = x.combinations[n];
796
791
  info.title = `Combination: <tt>${tupelString(combi)}</tt>`;
797
792
  const html = [], list = [];
798
- for(let i = 0; i < combi.length; i++) {
799
- const sel = combi[i];
793
+ for(const sel of combi) {
800
794
  html.push('<h3>Selector <tt>', sel, '</tt></h3>');
801
- // List associated model settings (if any)
795
+ // List associated model settings (if any).
802
796
  list.length = 0;
803
- for(let j = 0; j < x.settings_selectors.length; j++) {
804
- const ss = x.settings_selectors[j].split('|');
805
- if(sel === ss[0]) list.push(ss[1]);
797
+ for(const ss of x.settings_selectors) {
798
+ const tuple = ss.split('|');
799
+ if(sel === tuple[0]) list.push(tuple[1]);
806
800
  }
807
801
  if(list.length > 0) {
808
802
  html.push('<p><em>Model settings:</em> <tt>', list.join(';'),
809
803
  '</tt></p>');
810
804
  }
811
- // List associated actor settings (if any)
805
+ // List associated actor settings (if any).
812
806
  list.length = 0;
813
- for(let j = 0; j < x.actor_selectors.length; j++) {
814
- const as = x.actor_selectors[j];
815
- if(sel === as.selector) {
816
- list.push(as.round_sequence);
817
- }
807
+ for(const as of x.actor_selectors) {
808
+ if(sel === as.selector) list.push(as.round_sequence);
818
809
  }
819
810
  if(list.length > 0) {
820
811
  html.push('<p><em>Actor settings:</em> <tt>', list.join(';'),
821
812
  '</tt></p>');
822
813
  }
823
- // List associated datasets (if any)
814
+ // List associated datasets (if any).
824
815
  list.length = 0;
825
816
  for(let id in MODEL.datasets) if(MODEL.datasets.hasOwnProperty(id)) {
826
817
  const ds = MODEL.datasets[id];
@@ -888,10 +879,8 @@ class GUIExperimentManager extends ExperimentManager {
888
879
  // Set reference column indices so that values for the reference|
889
880
  // configuration can be displayed in orange.
890
881
  const ref_conf_indices = [];
891
- for(let i = 0; i < x.runs.length; i++) {
892
- const
893
- r = x.runs[i],
894
- rr = r.results[rri];
882
+ for(const r of x.runs) {
883
+ const rr = r.results[rri];
895
884
  if(!rr) {
896
885
  data.push(VM.UNDEFINED);
897
886
  } else if(x.selected_scale === 'sec') {
@@ -918,12 +907,12 @@ class GUIExperimentManager extends ExperimentManager {
918
907
  }
919
908
  // Scale data as selected.
920
909
  const scaled = data.slice();
921
- // NOTE: scale only after the experiment has been completed AND
922
- // configurations have been defined (otherwise comparison is pointless)
910
+ // NOTE: Scale only after the experiment has been completed AND
911
+ // configurations have been defined (otherwise comparison is pointless).
923
912
  if(x.completed && this.nr_of_configurations > 0) {
924
913
  const n = scaled.length / this.nr_of_configurations;
925
914
  if(x.selected_scale === 'dif') {
926
- // Compute difference: current configuration - reference configuration
915
+ // Compute difference: current configuration - reference configuration.
927
916
  const rc = x.reference_configuration;
928
917
  for(let i = 0; i < this.nr_of_configurations; i++) {
929
918
  if(i != rc) {
@@ -932,47 +921,36 @@ class GUIExperimentManager extends ExperimentManager {
932
921
  }
933
922
  }
934
923
  }
935
- // Set difference for reference configuration itself to 0
924
+ // Set difference for reference configuration itself to 0.
936
925
  for(let i = 0; i < n; i++) {
937
926
  const index = rc * n + i;
938
927
  scaled[index] = 0;
939
928
  ref_conf_indices.push(index);
940
929
  }
941
930
  } else if(x.selected_scale === 'reg') {
942
- // Compute regret: current config - high value config in same scenario
931
+ // Compute regret: current config - high value config in same scenario.
943
932
  for(let i = 0; i < n; i++) {
944
- // Get high value
933
+ // Get high value.
945
934
  let high = VM.MINUS_INFINITY;
946
935
  for(let j = 0; j < this.nr_of_configurations; j++) {
947
936
  high = Math.max(high, scaled[j * n + i]);
948
937
  }
949
- // Scale (so high config always has value 0)
938
+ // Scale (so high config always has value 0).
950
939
  for(let j = 0; j < this.nr_of_configurations; j++) {
951
940
  scaled[j * n + i] -= high;
952
941
  }
953
942
  }
954
943
  }
955
944
  }
956
- // For color scales, compute normalized scores
957
- let normalized = scaled.slice(),
958
- high = VM.MINUS_INFINITY,
959
- low = VM.PLUS_INFINITY;
960
- for(let i = 0; i < normalized.length; i++) {
961
- high = Math.max(high, normalized[i]);
962
- low = Math.min(low, normalized[i]);
963
- }
964
- // Avoid too small value ranges
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
- }
945
+ // For color scales, compute normalized scores.
946
+ const
947
+ high = Math.max(...scaled),
948
+ low = Math.min(...scaled),
949
+ // Avoid too small value ranges.
950
+ range = (high - low < VM.NEAR_ZERO ? 0 : high - low),
951
+ normalized = scaled.map((v) => range ? (v - low) / range : v),
952
+ formatted = scaled.map((v) => VM.sig4Dig(v));
953
+ // Format data such that they all have same number of decimals.
976
954
  uniformDecimals(formatted);
977
955
  // Display formatted data in cells.
978
956
  for(let i = 0; i < x.combinations.length; i++) {
@@ -1072,11 +1050,11 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
1072
1050
  const
1073
1051
  spl = this.viewer.getElementsByClassName('spin-plus'),
1074
1052
  rem = (x.configuration_dims + x.column_scenario_dims < xdims);
1075
- for(let i = 0; i < spl.length; i++) {
1053
+ for(const sp of spl) {
1076
1054
  if(rem) {
1077
- spl.item(i).classList.remove('no-spin');
1055
+ sp.classList.remove('no-spin');
1078
1056
  } else {
1079
- spl.item(i).classList.add('no-spin');
1057
+ sp.classList.add('no-spin');
1080
1058
  }
1081
1059
  }
1082
1060
  if(dir != 0 ) this.drawTable();
@@ -1103,9 +1081,7 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
1103
1081
  x.selected_color_scale = cs;
1104
1082
  this.color_scale.set(cs);
1105
1083
  const csl = this.viewer.getElementsByClassName('color-scale');
1106
- for(let i = 0; i < csl.length; i++) {
1107
- csl.item(i).classList.remove('sel-cs');
1108
- }
1084
+ for(const cs of csl) cs.classList.remove('sel-cs');
1109
1085
  document.getElementById(`xv-${cs}-scale`).classList.add('sel-cs');
1110
1086
  }
1111
1087
  this.updateData();
@@ -1189,14 +1165,14 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
1189
1165
  }
1190
1166
 
1191
1167
  editIteratorRanges() {
1192
- // Open dialog for editing iterator ranges
1168
+ // Open dialog for editing iterator ranges.
1193
1169
  const
1194
1170
  x = this.selected_experiment,
1195
1171
  md = this.iterator_modal,
1196
1172
  il = ['i', 'j', 'k'];
1197
1173
  if(x) {
1198
1174
  // NOTE: there are always 3 iterators (i, j k) so these have fixed
1199
- // FROM and TO input fields in the dialog
1175
+ // FROM and TO input fields in the dialog.
1200
1176
  for(let i = 0; i < 3; i++) {
1201
1177
  const k = il[i];
1202
1178
  md.element(k + '-from').value = x.iterator_ranges[i][0];
@@ -1211,8 +1187,8 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
1211
1187
  x = this.selected_experiment,
1212
1188
  md = this.iterator_modal;
1213
1189
  if(x) {
1214
- // First validate all input fields (must be integer values)
1215
- // NOTE: test using a copy so as not to overwrite values until OK
1190
+ // First validate all input fields (must be integer values).
1191
+ // NOTE: Test using a copy so as not to overwrite values until OK.
1216
1192
  const
1217
1193
  il = ['i', 'j', 'k'],
1218
1194
  ir = [[0, 0], [0, 0], [0, 0]],
@@ -1227,7 +1203,7 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
1227
1203
  t = el.value.trim() || '0';
1228
1204
  if(t === '' || re.test(t)) el = null;
1229
1205
  }
1230
- // NULL value signals that field inputs are valid
1206
+ // NULL value signals that field inputs are valid.
1231
1207
  if(el === null) {
1232
1208
  ir[i] = [f, t];
1233
1209
  } else {
@@ -1236,7 +1212,7 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
1236
1212
  return;
1237
1213
  }
1238
1214
  }
1239
- // Input validated, so modify the iterator dimensions
1215
+ // Input validated, so modify the iterator dimensions.
1240
1216
  x.iterator_ranges = ir;
1241
1217
  this.updateDialog();
1242
1218
  }
@@ -1244,17 +1220,17 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
1244
1220
  }
1245
1221
 
1246
1222
  editSettingsDimensions() {
1247
- // Open dialog for editing model settings dimensions
1223
+ // Open dialog for editing model settings dimensions.
1248
1224
  const x = this.selected_experiment, rows = [];
1249
1225
  if(x) {
1250
- // Initialize selector list
1226
+ // Initialize selector list.
1251
1227
  for(let i = 0; i < x.settings_selectors.length; i++) {
1252
1228
  const sel = x.settings_selectors[i].split('|');
1253
1229
  rows.push('<tr onclick="EXPERIMENT_MANAGER.editSettingsSelector(', i,
1254
1230
  ');"><td width="25%">', sel[0], '</td><td>', sel[1], '</td></tr>');
1255
1231
  }
1256
1232
  this.settings_modal.element('s-table').innerHTML = rows.join('');
1257
- // Initialize combination list
1233
+ // Initialize combination list.
1258
1234
  rows.length = 0;
1259
1235
  for(let i = 0; i < x.settings_dimensions.length; i++) {
1260
1236
  const dim = x.settings_dimensions[i];
@@ -1263,14 +1239,14 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
1263
1239
  }
1264
1240
  this.settings_modal.element('d-table').innerHTML = rows.join('');
1265
1241
  this.settings_modal.show();
1266
- // NOTE: clear infoline because dialog can generate warnings that would
1267
- // otherwise remain visible while no longer relevant
1242
+ // NOTE: Clear infoline because dialog can generate warnings that would
1243
+ // otherwise remain visible while no longer relevant.
1268
1244
  UI.setMessage('');
1269
1245
  }
1270
1246
  }
1271
1247
 
1272
1248
  closeSettingsDimensions() {
1273
- // Hide editor, and then update the experiment manager to reflect changes
1249
+ // Hide editor, and then update the experiment manager to reflect changes.
1274
1250
  this.settings_modal.hide();
1275
1251
  this.updateDialog();
1276
1252
  }
@@ -1303,8 +1279,10 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
1303
1279
  md = this.settings_selector_modal,
1304
1280
  sc = md.element('code'),
1305
1281
  ss = md.element('string'),
1306
- code = sc.value.replace(/[^\w\+\-\%]/g, ''),
1307
- value = ss.value.trim().replace(',', '.'),
1282
+ // NOTE: Simply remove invalid characters from selector, but accept
1283
+ // '=' here to permit associating settings with iterator selectors.
1284
+ code = sc.value.replace(/[^\w\+\-\%\=]/g, ''),
1285
+ value = ss.value.trim().toLowerCase().replace(',', '.'),
1308
1286
  add = this.edited_selector_index < 0;
1309
1287
  // Remove selector if either field has been cleared
1310
1288
  if(code.length === 0 || value.length === 0) {
@@ -1312,7 +1290,7 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
1312
1290
  x.settings_selectors.splice(this.edited_selector_index, 1);
1313
1291
  }
1314
1292
  } else {
1315
- // Check for uniqueness of code
1293
+ // Check for uniqueness of code.
1316
1294
  for(let i = 0; i < x.settings_selectors.length; i++) {
1317
1295
  // NOTE: ignore selector being edited, as this selector can be renamed
1318
1296
  if(i != this.edited_selector_index &&
@@ -1323,7 +1301,7 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
1323
1301
  }
1324
1302
  }
1325
1303
  // 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;
1304
+ 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
1305
  if(!re.test(value + ' ')) {
1328
1306
  UI.warn(`Invalid settings "${value}"`);
1329
1307
  ss.focus();
@@ -1560,40 +1538,37 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
1560
1538
  if(x) {
1561
1539
  const
1562
1540
  add = this.edited_combi_dimension_index < 0,
1563
- // Trim whitespace and reduce inner spacing to a single space
1541
+ // Trim whitespace and reduce inner spacing to a single space.
1564
1542
  dimstr = this.combination_dimension_modal.element('string').value.trim();
1565
- // Remove dimension if field has been cleared
1543
+ // Remove dimension if field has been cleared.
1566
1544
  if(dimstr.length === 0) {
1567
1545
  if(!add) {
1568
1546
  x.combination_dimensions.splice(this.edited_combi_dimension_index, 1);
1569
1547
  }
1570
1548
  } else {
1571
- // Check for valid selector list
1549
+ // Check for valid selector list.
1572
1550
  const
1573
1551
  dim = dimstr.split(/\s+/g),
1574
1552
  ssl = [];
1575
- // Get this experiment's combination selector list
1576
- for(let i = 0; i < x.combination_selectors.length; i++) {
1577
- ssl.push(x.combination_selectors[i].split('|')[0]);
1578
- }
1579
- // All selectors in string should have been defined
1553
+ // Get this experiment's combination selector list.
1554
+ for(const sel of x.combination_selectors) ssl.push(sel.split('|')[0]);
1555
+ // All selectors in string should have been defined.
1580
1556
  let c = complement(dim, ssl);
1581
1557
  if(c.length > 0) {
1582
1558
  UI.warn('Combination dimension contains ' +
1583
1559
  pluralS(c.length, 'unknown selector') + ': ' + c.join(' '));
1584
1560
  return;
1585
1561
  }
1586
- // All selectors should expand to non-overlapping selector sets
1562
+ // All selectors should expand to non-overlapping selector sets.
1587
1563
  if(!x.orthogonalCombinationDimensions(dim)) return;
1588
- // Do not add when a (setwise) identical combination dimension exists
1589
- for(let i = 0; i < x.combination_dimensions.length; i++) {
1590
- const cd = x.combination_dimensions[i];
1564
+ // Do not add when a (setwise) identical combination dimension exists.
1565
+ for(const cd of x.combination_dimensions) {
1591
1566
  if(intersection(dim, cd).length === dim.length) {
1592
1567
  UI.notify('Combination already defined: ' + setString(cd));
1593
1568
  return;
1594
1569
  }
1595
1570
  }
1596
- // OK? Then add or modify
1571
+ // OK? Then add or modify.
1597
1572
  if(add) {
1598
1573
  x.combination_dimensions.push(dim);
1599
1574
  } else {
@@ -1602,15 +1577,15 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
1602
1577
  }
1603
1578
  }
1604
1579
  this.combination_dimension_modal.hide();
1605
- // Update combination dimensions dialog
1580
+ // Update combination dimensions dialog.
1606
1581
  this.editCombinationDimensions();
1607
1582
  }
1608
1583
 
1609
1584
  editActorDimension() {
1610
- // Open dialog for editing the actor dimension
1585
+ // Open dialog for editing the actor dimension.
1611
1586
  const x = this.selected_experiment, rows = [];
1612
1587
  if(x) {
1613
- // Initialize selector list
1588
+ // Initialize selector list.
1614
1589
  for(let i = 0; i < x.actor_selectors.length; i++) {
1615
1590
  rows.push('<tr onclick="EXPERIMENT_MANAGER.editActorSelector(', i,
1616
1591
  ');"><td>', x.actor_selectors[i].selector,
@@ -1619,8 +1594,8 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
1619
1594
  }
1620
1595
  this.actor_dimension_modal.element('table').innerHTML = rows.join('');
1621
1596
  this.actor_dimension_modal.show();
1622
- // NOTE: clear infoline because dialog can generate warnings that would
1623
- // otherwise remain visible while no longer relevant
1597
+ // NOTE: Clear infoline because dialog can generate warnings that would
1598
+ // otherwise remain visible while no longer relevant.
1624
1599
  UI.setMessage('');
1625
1600
  }
1626
1601
  }
@@ -1707,17 +1682,16 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
1707
1682
  clist = [],
1708
1683
  csel = md.element('select'),
1709
1684
  sinp = md.element('selectors');
1710
- // NOTE: copy experiment property to modal dialog property, so that changes
1685
+ // NOTE: Copy experiment property to modal dialog property, so that changes
1711
1686
  // are made only when OK is clicked
1712
1687
  md.clusters = [];
1713
- for(let i = 0; i < x.clusters_to_ignore.length; i++) {
1714
- const cs = x.clusters_to_ignore[i];
1715
- md.clusters.push({cluster: cs.cluster, selectors: cs. selectors});
1688
+ for(const cs of x.clusters_to_ignore) {
1689
+ md.clusters.push({cluster: cs.cluster, selectors: cs.selectors});
1716
1690
  }
1717
1691
  md.cluster_index = -1;
1718
1692
  for(let k in MODEL.clusters) if(MODEL.clusters.hasOwnProperty(k)) {
1719
1693
  const c = MODEL.clusters[k];
1720
- // Do not add top cluster, nor clusters already on the list
1694
+ // Do not add top cluster, nor clusters already on the list.
1721
1695
  if(c !== MODEL.top_cluster && !c.ignore && !x.mayBeIgnored(c)) {
1722
1696
  clist.push(`<option value="${k}">${c.displayName}</option>`);
1723
1697
  }
@@ -1738,7 +1712,7 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
1738
1712
  sdiv = md.element('selectors-div'),
1739
1713
  cl = md.clusters.length;
1740
1714
  if(cl > 0) {
1741
- // Show cluster+selectors list
1715
+ // Show cluster+selectors list.
1742
1716
  const ol = [];
1743
1717
  for(let i = 0; i < cl; i++) {
1744
1718
  const cti = md.clusters[i];
@@ -1751,7 +1725,7 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
1751
1725
  clst.style.display = 'block';
1752
1726
  nlst.style.display = 'none';
1753
1727
  } else {
1754
- // Hide list and show "no clusters set to be ignored"
1728
+ // Hide list and show "no clusters set to be ignored".
1755
1729
  clst.style.display = 'none';
1756
1730
  nlst.style.display = 'block';
1757
1731
  }
@@ -1767,7 +1741,7 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
1767
1741
  }
1768
1742
 
1769
1743
  selectCluster(n) {
1770
- // Set selected cluster index to `n`
1744
+ // Set selected cluster index to `n`.
1771
1745
  this.clusters_modal.cluster_index = n;
1772
1746
  this.updateClusterList();
1773
1747
  }
@@ -1780,7 +1754,7 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
1780
1754
  if(c) {
1781
1755
  md.clusters.push({cluster: c, selectors: ''});
1782
1756
  md.cluster_index = md.clusters.length - 1;
1783
- // Remove cluster from select so it cannot be added again
1757
+ // Remove cluster from select so it cannot be added again.
1784
1758
  sel.remove(sel.selectedIndex);
1785
1759
  this.updateClusterList();
1786
1760
  }
@@ -1803,7 +1777,7 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
1803
1777
  }
1804
1778
 
1805
1779
  deleteClusterFromIgnoreList() {
1806
- // Delete selected cluster+selectors from list
1780
+ // Delete selected cluster+selectors from list.
1807
1781
  const md = this.clusters_modal;
1808
1782
  if(md.cluster_index >= 0) {
1809
1783
  md.clusters.splice(md.cluster_index, 1);
@@ -1813,7 +1787,7 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
1813
1787
  }
1814
1788
 
1815
1789
  modifyClustersToIgnore() {
1816
- // Replace current list by cluster+selectors list of modal dialog
1790
+ // Replace current list by cluster+selectors list of modal dialog.
1817
1791
  const
1818
1792
  md = this.clusters_modal,
1819
1793
  x = this.selected_experiment;
@@ -1823,7 +1797,7 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
1823
1797
  }
1824
1798
 
1825
1799
  promptForParameter(type) {
1826
- // Open dialog for adding new dimension or chart
1800
+ // Open dialog for adding new dimension or chart.
1827
1801
  const x = this.selected_experiment;
1828
1802
  if(x) {
1829
1803
  const ol = [];
@@ -1835,9 +1809,8 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
1835
1809
  ol.push(`<option value="${i}">${ds}</option>`);
1836
1810
  }
1837
1811
  } else {
1838
- for(let i = 0; i < this.suitable_charts.length; i++) {
1839
- const c = this.suitable_charts[i];
1840
- // NOTE: exclude charts already in the selected experiment
1812
+ for(const c of this.suitable_charts) {
1813
+ // NOTE: Exclude charts already in the selected experiment.
1841
1814
  if (x.charts.indexOf(c) < 0) {
1842
1815
  ol.push(`<option value="${c.title}">${c.title}</option>`);
1843
1816
  }
@@ -1849,7 +1822,7 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
1849
1822
  }
1850
1823
 
1851
1824
  addParameter() {
1852
- // Add parameter (dimension or chart) to experiment
1825
+ // Add parameter (dimension or chart) to experiment.
1853
1826
  const
1854
1827
  x = this.selected_experiment,
1855
1828
  name = this.parameter_modal.element('select').value;
@@ -1871,7 +1844,7 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
1871
1844
  }
1872
1845
 
1873
1846
  deleteParameter() {
1874
- // Remove selected dimension or chart from selected experiment
1847
+ // Remove selected dimension or chart from selected experiment.
1875
1848
  const
1876
1849
  x = this.selected_experiment,
1877
1850
  sp = this.selected_parameter;
@@ -1888,12 +1861,12 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
1888
1861
  }
1889
1862
 
1890
1863
  editExclusions() {
1891
- // Give visual feedback by setting background color to white
1864
+ // Give visual feedback by setting background color to white.
1892
1865
  this.exclude.style.backgroundColor = 'white';
1893
1866
  }
1894
1867
 
1895
1868
  setExclusions() {
1896
- // Sanitize string before accepting it as space-separated selector list
1869
+ // Sanitize string before accepting it as space-separated selector list.
1897
1870
  const
1898
1871
  x = this.selected_experiment;
1899
1872
  if(x) {
@@ -1907,24 +1880,24 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
1907
1880
  }
1908
1881
 
1909
1882
  readyButtons() {
1910
- // Set experiment run control buttons in "ready" state
1883
+ // Set experiment run control buttons in "ready" state.
1911
1884
  this.pause_btn.classList.add('off');
1912
1885
  this.stop_btn.classList.add('off');
1913
1886
  this.start_btn.classList.remove('off', 'blink');
1914
1887
  }
1915
1888
 
1916
1889
  pausedButtons(aci) {
1917
- // Set experiment run control buttons in "paused" state
1890
+ // Set experiment run control buttons in "paused" state.
1918
1891
  this.pause_btn.classList.remove('blink');
1919
1892
  this.pause_btn.classList.add('off');
1920
1893
  this.start_btn.classList.remove('off');
1921
- // Blinking start button indicates: paused -- click to resume
1894
+ // Blinking start button indicates: paused -- click to resume.
1922
1895
  this.start_btn.classList.add('blink');
1923
1896
  this.viewer_progress.innerHTML = `Run ${aci} PAUSED`;
1924
1897
  }
1925
1898
 
1926
1899
  resumeButtons() {
1927
- // Changes buttons to "running" state, and return TRUE if state was "paused"
1900
+ // Changes buttons to "running" state, and return TRUE if state was "paused".
1928
1901
  const paused = this.start_btn.classList.contains('blink');
1929
1902
  this.start_btn.classList.remove('blink');
1930
1903
  this.start_btn.classList.add('off');
@@ -1934,7 +1907,7 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
1934
1907
  }
1935
1908
 
1936
1909
  pauseExperiment() {
1937
- // Interrupt solver but retain data on server and allow resume
1910
+ // Interrupt solver but retain data on server and allow resume.
1938
1911
  UI.notify('Run sequence will be suspended after the current run');
1939
1912
  this.pause_btn.classList.add('blink');
1940
1913
  this.stop_btn.classList.remove('off');
@@ -1942,7 +1915,7 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
1942
1915
  }
1943
1916
 
1944
1917
  stopExperiment() {
1945
- // Interrupt solver but retain data on server (and no resume)
1918
+ // Interrupt solver but retain data on server (and no resume).
1946
1919
  VM.halt();
1947
1920
  MODEL.running_experiment = null;
1948
1921
  UI.notify('Experiment has been stopped');
@@ -1951,7 +1924,7 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
1951
1924
  }
1952
1925
 
1953
1926
  showProgress(ci, p, n) {
1954
- // Show progress in the viewer
1927
+ // Show progress in the viewer.
1955
1928
  this.viewer_progress.innerHTML = `Run ${ci} (${p}% of ${n})`;
1956
1929
  }
1957
1930
 
@@ -1962,7 +1935,7 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
1962
1935
  }
1963
1936
 
1964
1937
  promptForDownload() {
1965
- // Show the download modal
1938
+ // Show the download modal.
1966
1939
  const x = this.selected_experiment;
1967
1940
  if(!x) return;
1968
1941
  const
@@ -1975,13 +1948,13 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
1975
1948
  return;
1976
1949
  }
1977
1950
  md.element(ds.variables + '-v').checked = true;
1978
- // Disable "selected runs" button when no runs have been selected
1951
+ // Disable "selected runs" button when no runs have been selected.
1979
1952
  if(sruns) {
1980
1953
  md.element('selected-r').disabled = false;
1981
1954
  md.element(ds.runs + '-r').checked = true;
1982
1955
  } else {
1983
1956
  md.element('selected-r').disabled = true;
1984
- // Check "all runs" but do not change download setting
1957
+ // Check "all runs" but do not change download setting.
1985
1958
  md.element('all-r').checked = true;
1986
1959
  }
1987
1960
  this.download_modal.show();
@@ -1997,7 +1970,7 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
1997
1970
  }
1998
1971
 
1999
1972
  downloadDataAsCSV() {
2000
- // Push results to browser
1973
+ // Push results to browser.
2001
1974
  if(this.selected_experiment) {
2002
1975
  const md = this.download_modal;
2003
1976
  this.selected_experiment.download_settings = {