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
@@ -225,6 +225,13 @@ function ellipsedText(text, n=50, m=10) {
225
225
  return text.slice(0, n) + ' \u2026 ' + text.slice(text.length - m);
226
226
  }
227
227
 
228
+ function unquoteCSV(s) {
229
+ // Returns a double-quoted string `s` without its quotes, and with
230
+ // quote pairs "" replaced by single " quotes.
231
+ if(!s.startsWith('"') || !s.endsWith('"')) return s;
232
+ return s.slice(1, -1).replaceAll('""', '"');
233
+ }
234
+
228
235
  //
229
236
  // Functions used when comparing two Linny-R models
230
237
  //
@@ -535,8 +542,8 @@ function matchingNumber(m, s) {
535
542
  function matchingNumberInList(ml, s) {
536
543
  // Traverses list `ml` and returns the first matching number, or FALSE
537
544
  // if no match is found.
538
- for(let i = 0; i < ml.length; i++) {
539
- const n = matchingNumber(ml[i], s);
545
+ for(const m of ml) {
546
+ const n = matchingNumber(m, s);
540
547
  if(n !== false) return n;
541
548
  }
542
549
  return false;
@@ -673,19 +680,17 @@ function compareCombinations(c1, c2) {
673
680
  //
674
681
 
675
682
  function addDistinct(e, list) {
676
- // Adds element `e` to `list` only if it does not already occur in `list`
683
+ // Add element `e` to `list` only if it does not already occur in `list`.
677
684
  if(list.indexOf(e) < 0) list.push(e);
678
685
  }
679
686
 
680
687
  function mergeDistinct(list, into) {
681
- // Adds elements of `list` to `into` if not already in `into`
682
- for(let i = 0; i < list.length; i++) {
683
- addDistinct(list[i], into);
684
- }
688
+ // Add elements of `list` to `into` if not already in `into`.
689
+ for(const e of list) addDistinct(e, into);
685
690
  }
686
691
 
687
692
  function iteratorSet(list) {
688
- // Returns TRUE iff list is something like ['i=1', 'i=2', 'i=3'].
693
+ // Return TRUE iff list is something like ['i=1', 'i=2', 'i=3'].
689
694
  if(list.length === 0) return false;
690
695
  // Analyze the first element: must start with i=, j= or k=.
691
696
  const
@@ -715,9 +720,7 @@ function iteratorSet(list) {
715
720
  function integerSet(list) {
716
721
  // Returns TRUE iff all elements in list evaluate as integer numbers.
717
722
  if(list.length === 0) return false;
718
- for(let i = 0; i < list.length; i++) {
719
- if(list[i] - 0 != list[i]) return false;
720
- }
723
+ for(const n of list) if(n - 0 != n) return false;
721
724
  return true;
722
725
  }
723
726
 
@@ -750,20 +753,18 @@ function tupelString(sl) {
750
753
  }
751
754
 
752
755
  function tupelSetString(ssl) {
753
- // Returns string of stringlists `sll` as set of tuples
756
+ // Return string of stringlists `sll` as set of tuples.
754
757
  const tl = [];
755
- for(let i = 0; i < ssl.length; i++) {
756
- tl.push(tupelString(ssl[i]));
757
- }
758
+ for(const ss of ssl) tl.push(tupelString(ss));
758
759
  return setString(tl);
759
760
  }
760
761
 
761
762
  function tupelIndex(sl, ssl) {
762
- // Returns index of stringlist `sl` if it exists in `ssl`, otherwise -1
763
+ // Return index of stringlist `sl` if it exists in `ssl`, otherwise -1.
763
764
  for(let i = 0; i < ssl.length; i++) {
764
765
  let n = 0;
765
- for(let j = 0; j < sl.length; j++) {
766
- if(ssl[i].indexOf(sl[j]) < 0) break;
766
+ for(const s of sl) {
767
+ if(ssl[i].indexOf(s) < 0) break;
767
768
  n++;
768
769
  }
769
770
  if(n == sl.length) return i;
@@ -772,20 +773,16 @@ function tupelIndex(sl, ssl) {
772
773
  }
773
774
 
774
775
  function intersection(sl1, sl2) {
775
- // Returns the list of common elements of stringlists `l1` and `l2`
776
+ // Return the list of common elements of stringlists `l1` and `l2`.
776
777
  const shared = [];
777
- for(let i = 0; i < sl1.length; i++) {
778
- if(sl2.indexOf(sl1[i]) >= 0) shared.push(sl1[i]);
779
- }
778
+ for(const s of sl1) if(sl2.indexOf(s) >= 0) shared.push(s);
780
779
  return shared;
781
780
  }
782
781
 
783
782
  function complement(sl1, sl2) {
784
783
  // Returns the list of elements of stringlist `l1` that are NOT in `l2`
785
784
  const cmplmnt = [];
786
- for(let i = 0; i < sl1.length; i++) {
787
- if(sl2.indexOf(sl1[i]) < 0) cmplmnt.push(sl1[i]);
788
- }
785
+ for(const s of sl1) if(sl2.indexOf(s) < 0) cmplmnt.push(s);
789
786
  return cmplmnt;
790
787
  }
791
788
 
@@ -793,6 +790,15 @@ function complement(sl1, sl2) {
793
790
  // Functions that support loading and saving data and models
794
791
  //
795
792
 
793
+ function asFileName(s) {
794
+ // Return string `s` with whitespace converted to a single dash, and
795
+ // special characters converted to underscores.
796
+ return s.normalize('NFKD').trim()
797
+ .replace(/[\s\-]+/g, '-')
798
+ .replace(/[^A-Za-z0-9_\-]/g, '_')
799
+ .replace(/^[\-\_]+|[\-\_]+$/g, '');
800
+ }
801
+
796
802
  function xmlEncoded(str) {
797
803
  // Replaces &, <, >, ' and " by their HTML entity code
798
804
  return str.replace(/\&/g, '&amp;').replace(/</g, '&lt;'
@@ -836,8 +842,8 @@ function customizeXML(str) {
836
842
  }
837
843
 
838
844
  function cleanXML(node) {
839
- // Removes all unnamed text nodes and comment nodes from the XML
840
- // subtree under node
845
+ // Remove all unnamed text nodes and comment nodes from the XML
846
+ // subtree under node.
841
847
  const cn = node.childNodes;
842
848
  if(cn) {
843
849
  for(let i = cn.length - 1; i >= 0; i--) {
@@ -1132,6 +1138,7 @@ if(NODE) module.exports = {
1132
1138
  uniformDecimals: uniformDecimals,
1133
1139
  capitalized: capitalized,
1134
1140
  ellipsedText: ellipsedText,
1141
+ unquoteCSV: unquoteCSV,
1135
1142
  earlierVersion: earlierVersion,
1136
1143
  differences: differences,
1137
1144
  markFirstDifference: markFirstDifference,
@@ -1161,6 +1168,7 @@ if(NODE) module.exports = {
1161
1168
  tupelIndex: tupelIndex,
1162
1169
  intersection: intersection,
1163
1170
  complement: complement,
1171
+ asFileName: asFileName,
1164
1172
  xmlEncoded: xmlEncoded,
1165
1173
  xmlDecoded: xmlDecoded,
1166
1174
  customizeXML: customizeXML,