linny-r 1.5.8 → 1.6.1

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.
@@ -307,6 +307,26 @@ function indexOfMatchingBracket(str, offset) {
307
307
  return -1;
308
308
  }
309
309
 
310
+ function monoSpaced(vbl) {
311
+ // Removes all non-essential spaces from variable reference `vbl`.
312
+ // First reduce all whitespace to a single space.
313
+ return vbl.replace(/\s+/g, ' ')
314
+ // Then remove spaces after the opening bracket.
315
+ .replace(/\[\s+/g, '[')
316
+ // Also remove spaces after a leading colon (if any).
317
+ .replace(/\[:\s+/g, '[:')
318
+ // Also remove spaces before closing bracket.
319
+ .replace(/\s+\]/, ']');
320
+ }
321
+
322
+ function monoSpacedVariables(xt) {
323
+ // Return expression text `xt` with all non-functional whitespace
324
+ // *inside* its variable references (i.e., substrings like
325
+ // "[entity name|attribute]") removed.
326
+ // NOTE: All spacing *outside* variable references is preserved.
327
+ return xt.replace(/\[[^\]]*\]/g, monoSpaced);
328
+ }
329
+
310
330
  function patternList(str) {
311
331
  // Returns the &|^-pattern defined by `str`
312
332
  // Pattern operators: & (and), ^ (not) and | (or) in sequence, e.g.,
@@ -510,13 +530,19 @@ function compareWithTailNumbers(s1, s2) {
510
530
 
511
531
  function compareSelectors(s1, s2) {
512
532
  // Dataset selectors comparison is case-insensitive, and puts wildcards
513
- // last, where * comes later than ?
514
- // NOTE: without wildcards, strings that are identical except for the
515
- // digits they *end* on are sorted on this "end number" (so abc12 > abc2)
516
- // NOTE: this also applies to percentages ("end number"+ %)
533
+ // last, where * comes later than ?, and leading colons come AFTER
534
+ // regular selector names.
535
+ // NOTE: Without wildcards, strings that are identical except for the
536
+ // digits they *end* on are sorted on this "end number" (so abc12 > abc2).
537
+ // NOTE: This also applies to percentages ("end number"+ %).
517
538
  if(s1 === s2) return 0;
518
539
  if(s1 === '*') return 1;
519
540
  if(s2 === '*') return -1;
541
+ // Replace leading colon by | because | has a higher ASCII value than
542
+ // all other characters. This will move selectors with leading colons
543
+ // to the end of the list.
544
+ if(s1.startsWith(':')) s1 = '|' + s1.substring(1);
545
+ if(s2.startsWith(':')) s2 = '|' + s2.substring(1);
520
546
  const
521
547
  star1 = s1.indexOf('*'),
522
548
  star2 = s2.indexOf('*');
@@ -525,10 +551,10 @@ function compareSelectors(s1, s2) {
525
551
  return ciCompare(s1, s2);
526
552
  }
527
553
  if(star2 >= 0) return -1;
528
- // Replace ? by | because | has a higher ASCII value than all other chars
554
+ // Now replace ? by | to make it sort further down than other characters.
529
555
  let s_1 = s1.replace('?', '|').toLowerCase(),
530
556
  s_2 = s2.replace('?', '|').toLowerCase(),
531
- // NOTE: treat selectors ending on a number or percentage as special case
557
+ // NOTE: Selectors ending on a number or percentage are special.
532
558
  n_1 = endsWithDigits(s_1),
533
559
  p_1 = (s1.endsWith('%') ? endsWithDigits(s1.slice(0, -1)) : '');
534
560
  if(n_1) {
@@ -546,10 +572,11 @@ function compareSelectors(s1, s2) {
546
572
  return parseInt(p_1) - parseInt(p_2);
547
573
  }
548
574
  }
549
- // Also sort selectors ending on minuses lower than those ending on plusses,
550
- // and such that X-- comes before X-, like X+ automatically comes before X++
551
- // ASCII(+) = 43, ASCII(-) = 45, so replace trailing minuses by as many spaces
552
- // (ASCII 32) and add a '!' (ASCII 33) -- this then "sorts things out"
575
+ // Also sort selectors ending on minuses lower than those ending on
576
+ // plusses, and so that X-- comes before X-, like X+ automatically comes
577
+ // before X++. ASCII(+) = 43, ASCII(-) = 45, so replace trailing minuses
578
+ // by as many spaces (ASCII 32) and add a '!' (ASCII 33). This will then
579
+ // "sorts things out".
553
580
  let n = s_1.length,
554
581
  i = n - 1;
555
582
  while(i >= 0 && s_1[i] === '-') i--;
@@ -716,17 +743,28 @@ function xmlDecoded(str) {
716
743
  }
717
744
 
718
745
  function customizeXML(str) {
719
- // NOTE: this function can be customized to pre-process a model file,
746
+ // NOTE: This function can be customized to pre-process a model file,
720
747
  // for example to rename entities in one go -- USE WITH CARE!
721
- // First modify `str` -- by default, do nothing
722
-
748
+ // To prevent unintended customization, check whether the model name
749
+ // ends with "!!CUSTOMIZE". This check ensures that the modeler must
750
+ // first save the model with this text as the (end of the) model name
751
+ // and then load it again for the customization to be performed.
752
+ if(str.indexOf('!!CUSTOMIZE</name><author>') >= 0) {
753
+ // Modify `str` -- by default, do nothing, but typical modifications
754
+ // will replace RexEx patterns by other strings.
755
+
723
756
  /*
724
- if(str.indexOf('<version>1.4.') >= 0) {
725
- str = str.replace(/<url>NL\/(\w+)\.csv<\/url>/g, '<url></url>');
726
- }
757
+ const
758
+ re = /xyz/gi,
759
+ r = 'abc';
727
760
  */
728
761
 
729
- // Finally, return the modified string
762
+ // Trace the changes to the console.
763
+ console.log('Customizing:', re, r);
764
+ console.log('Matches:', str.match(re));
765
+ str = str.replace(re, r);
766
+ }
767
+ // Finally, return the modified string.
730
768
  return str;
731
769
  }
732
770
 
@@ -830,10 +868,22 @@ function randomID() {
830
868
  }
831
869
 
832
870
  function escapedSingleQuotes(s) {
833
- // Returns string `s` with "escaped" single quotes
871
+ // Return string `s` with "escaped" single quotes.
834
872
  return s.replace('\'', '\\\'');
835
873
  }
836
874
 
875
+ function safeDoubleQuotes(s) {
876
+ // Return string `s` with ASCII quotes " replaced by curly quotes to
877
+ // ensure that it does not break HTML attribute strings.
878
+ const q = ['\u201C', '\u201D'];
879
+ let index = 0;
880
+ while(s.indexOf('"') >= 0) {
881
+ s = s.replace('"', q[index]);
882
+ index = 1 - index;
883
+ }
884
+ return s;
885
+ }
886
+
837
887
  function nameToLines(name, actor_name = '') {
838
888
  // Return the name of a Linny-R entity as a string-with-line-breaks
839
889
  // that fits nicely in an oblong box. For efficiency reasons, a fixed