linny-r 1.9.3 → 2.0.2

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 (39) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +172 -126
  3. package/console.js +2 -1
  4. package/package.json +1 -1
  5. package/post-install.js +93 -37
  6. package/server.js +73 -29
  7. package/static/images/eq-negated.png +0 -0
  8. package/static/images/power.png +0 -0
  9. package/static/images/tex.png +0 -0
  10. package/static/index.html +226 -11
  11. package/static/linny-r.css +458 -8
  12. package/static/scripts/linny-r-ctrl.js +6 -4
  13. package/static/scripts/linny-r-gui-actor-manager.js +1 -1
  14. package/static/scripts/linny-r-gui-chart-manager.js +20 -13
  15. package/static/scripts/linny-r-gui-constraint-editor.js +410 -50
  16. package/static/scripts/linny-r-gui-controller.js +138 -21
  17. package/static/scripts/linny-r-gui-dataset-manager.js +28 -20
  18. package/static/scripts/linny-r-gui-documentation-manager.js +11 -3
  19. package/static/scripts/linny-r-gui-equation-manager.js +1 -1
  20. package/static/scripts/linny-r-gui-experiment-manager.js +1 -1
  21. package/static/scripts/linny-r-gui-expression-editor.js +7 -1
  22. package/static/scripts/linny-r-gui-file-manager.js +63 -19
  23. package/static/scripts/linny-r-gui-finder.js +1 -1
  24. package/static/scripts/linny-r-gui-model-autosaver.js +1 -1
  25. package/static/scripts/linny-r-gui-monitor.js +1 -1
  26. package/static/scripts/linny-r-gui-paper.js +108 -25
  27. package/static/scripts/linny-r-gui-power-grid-manager.js +529 -0
  28. package/static/scripts/linny-r-gui-receiver.js +1 -1
  29. package/static/scripts/linny-r-gui-repository-browser.js +1 -1
  30. package/static/scripts/linny-r-gui-scale-unit-manager.js +1 -1
  31. package/static/scripts/linny-r-gui-sensitivity-analysis.js +1 -1
  32. package/static/scripts/linny-r-gui-tex-manager.js +110 -0
  33. package/static/scripts/linny-r-gui-undo-redo.js +1 -1
  34. package/static/scripts/linny-r-milp.js +1 -1
  35. package/static/scripts/linny-r-model.js +982 -123
  36. package/static/scripts/linny-r-utils.js +3 -3
  37. package/static/scripts/linny-r-vm.js +731 -252
  38. package/static/show-diff.html +1 -1
  39. package/static/show-png.html +1 -1
package/post-install.js CHANGED
@@ -5,28 +5,30 @@ The Linny-R language and tool have been developed by Pieter Bots at Delft
5
5
  University of Technology, starting in 2009. The project to develop a browser-
6
6
  based version started in 2017. See https://linny-r.org for more information.
7
7
 
8
- This NodeJS script (post-install.js) creates a launch script for Linny-R
9
- that facilitates start-up: the user can then type `linny-r` at the command
10
- line prompt, and also create a clickable icon as desktop shortcut.
8
+ This NodeJS script (post-install.js) checks whether the Linny-R directory
9
+ contains a launch script for Linny-R. For macOS, the script file is
10
+ `linny-r.command`, for Windows `linny-r.bat`.
11
+ If such a file already exists, this script will try to rename it, adding
12
+ the prefix `OLD-` to the name. Doing this will make that the the Linny-R
13
+ server script will create the newest version of the launch script the
14
+ first time it is run.
11
15
 
12
- For macOS, the script file is `linny-r.command`, for Windows `linny-r.bat`.
13
- The script comprises two commands:
16
+ The launch script has two intended functions:
17
+ (1) to facilitate start-up: the user can type `linny-r` at the command
18
+ line prompt,and (more importantly) create a clickable icon as desktop
19
+ shortcut.
20
+ (2) to facilitate automatic software updates: when (after lauch in a browser)
21
+ Linny-R detects a newer version, it will prompt the user whether this
22
+ update should be installed. When the user confirms, the server script
23
+ is terminated, and then launch script executes the commands to update
24
+ and then restart the Linny-R server.
14
25
 
15
- cd path/to/linny-r/directory
16
- node node_modules/linny-r/server launch
17
-
18
- since Windows also supports the slash as path separator. The "launch"
19
- command tells the script `server.js` to start Linny-R in the default
20
- web browser.
21
-
22
- Comments are added to the script file to facilitate customization of the
23
- scripts by the user. The README.md file explains how the script file can be
24
- used for single-click launch of Linny-R, and how the "workspace" parameter
25
- can be used in a multi-user network environment to provide individual
26
- workspaces for users.
26
+ The README.md file explains how the script file can be used for single-click
27
+ launch of Linny-R, and how the "workspace" parameter can be used in a
28
+ multi-user network environment to provide individual workspaces for users.
27
29
  */
28
30
  /*
29
- Copyright (c) 2020-2022 Delft University of Technology
31
+ Copyright (c) 2020-2024 Delft University of Technology
30
32
 
31
33
  Permission is hereby granted, free of charge, to any person obtaining a copy
32
34
  of this software and associated documentation files (the "Software"), to deal
@@ -56,32 +58,86 @@ const
56
58
  // NOTE: working directory for this script is the *module* directory,
57
59
  // which is two levels deeper than the actual working directory
58
60
  WORKING_DIRECTORY = process.cwd().replace(path.sep + mod_dir, ''),
59
- lines = [
61
+ ext = (PLATFORM.startsWith('win') ? 'bat' : 'command'),
62
+ sp = path.join(WORKING_DIRECTORY, 'linny-r.' + ext);
63
+
64
+ // NOTE: Function `createLaunchScript` is a copy of this function as
65
+ // defined in script file `server.js`.
66
+
67
+ function createLaunchScript() {
68
+ // Creates platform-specific script with Linny-R start-up command
69
+ const
70
+ lines = [
60
71
  '# The first line (without the comment symbol #) should be like this:',
61
- '# cd ',
62
72
  '',
73
+ 'cd ' + WORKING_DIRECTORY,
63
74
  '# Then this command to launch the Linny-R server should work:',
64
- `node ${mod_dir}${path.sep}server launch`
65
- ];
66
- let sp;
67
- if(PLATFORM.startsWith('win')) {
68
- sp = path.join(WORKING_DIRECTORY, 'linny-r.bat');
69
- lines[1] += 'C:\\path\\to\\\\your\\Linny-R\\directory';
70
- } else {
71
- sp = path.join(WORKING_DIRECTORY, 'linny-r.command');
72
- lines[1] += '/path/to/your/Linny-R/directory';
75
+ '',
76
+ '# After shut-down, check whether new version should be installed:'
77
+ ],
78
+ windows = PLATFORM.startsWith('win'),
79
+ sp = path.join(WORKING_DIRECTORY, 'linny-r.' + (windows ? 'bat' : 'command'));
80
+ if(windows) {
81
+ lines.push(
82
+ ':loop',
83
+ 'if exist newer_version (',
84
+ ' del newer_version',
85
+ ' npm update linny-r',
86
+ ' node node_modules\\linny-r\\server',
87
+ ' goto loop',
88
+ ')');
89
+ lines[1] = '# cd C:\\path\\to\\main\\Linny-R\\directory';
90
+ lines[4] = 'node node_modules\\linny-r\\server launch';
91
+ } else {
92
+ lines.push(
93
+ 'while test -f newer_version; do',
94
+ ' unlink newer_version',
95
+ ' npm update linny-r',
96
+ ' node node_modules/linny-r/server',
97
+ 'done');
98
+ lines[1] = '# cd /path/to/main/Linny-R/directory';
99
+ lines[4] = 'node node_modules/linny-r/server launch';
100
+ }
101
+ try {
102
+ let code = lines.join(os.EOL);
103
+ if(windows) code = code.replaceAll('#', '::');
104
+ try {
105
+ fs.accessSync(sp);
106
+ // Do not overwrite existing script, as it may have been customized
107
+ // by the user. When istalling/updating Linny-R, the post-install
108
+ // script should have renamed it, so typically it is created the
109
+ // first time Linny-R is run after install/update.
110
+ } catch(err) {
111
+ console.log('Creating launch script:', sp);
112
+ fs.writeFileSync(sp, code, 'utf8');
113
+ // On macOS machines, try to make the script executable.
114
+ if(!windows) try {
115
+ fs.chmodSync(sp, '+x');
116
+ } catch(err) {
117
+ console.log('WARNING: Failed to make script executable -- please check');
118
+ }
119
+ }
120
+ } catch(err) {
121
+ console.log('WARNING: Failed to create launch script');
122
+ }
73
123
  }
74
- lines[2] = 'cd ' + WORKING_DIRECTORY;
124
+
125
+ // First rename the existing script (if any) as this may have been changed
126
+ // by the user, and the idea of the changes would be lost by overwriting it.
75
127
  try {
128
+ fs.accessSync(sp);
76
129
  try {
77
- fs.accessSync(sp);
130
+ // Only rename the script content if the file it does not yet exist
131
+ console.log('Renaming existing launch script:', sp);
132
+ fs.renameSync(sp, path.join(WORKING_DIRECTORY, 'OLD-linny-r.' + ext));
78
133
  } catch(err) {
79
- // Only write the script content if the file it does not yet exist
80
- console.log('Creating launch script:', sp);
81
- let code = lines.join(os.EOL);
82
- if(PLATFORM.startsWith('win')) code = code.replaceAll('#', '::');
83
- fs.writeFileSync(sp, code, 'utf8');
134
+ console.log('WARNING: Failed to rename existing launch script');
84
135
  }
85
136
  } catch(err) {
86
- console.log('WARNING: Failed to create launch script');
137
+ // No existing script => action needed.
87
138
  }
139
+
140
+ // Then create new launch script. This way, after update or clean install,
141
+ // the user can
142
+ createLaunchScript();
143
+
package/server.js CHANGED
@@ -12,7 +12,7 @@ that pass the MILP equation model to the solver, and then return the solution
12
12
  to the Linny-R "virtual machine" that is running in the browser.
13
13
  */
14
14
  /*
15
- Copyright (c) 2020-2022 Delft University of Technology
15
+ Copyright (c) 2020-2024 Delft University of Technology
16
16
 
17
17
  Permission is hereby granted, free of charge, to any person obtaining a copy
18
18
  of this software and associated documentation files (the "Software"), to deal
@@ -416,9 +416,21 @@ function autoSaveLoad(res, sp) {
416
416
  function autoSaveStore(res, sp) {
417
417
  // Stores XML data under specified file name in the auto-save directory
418
418
  let data = 'OK';
419
- const fn = sp.get('file');
419
+ const
420
+ fn = sp.get('file'),
421
+ wsd = sp.get('wsd'),
422
+ ws = (wsd ? WORKSPACE.models : WORKSPACE.autosave),
423
+ msg = (wsd === 'models' ? 'save to user workspace' : 'auto-save'),
424
+ exists = (path) => {
425
+ try {
426
+ fs.accessSync(path);
427
+ return true;
428
+ } catch(err) {
429
+ return false;
430
+ }
431
+ };
420
432
  if(!fn) {
421
- data = 'WARNING: No name for file to auto-save';
433
+ data = 'WARNING: No name for file to ' + msg;
422
434
  } else {
423
435
  const xml = sp.get('xml');
424
436
  // Validate XML as a Linny-R model
@@ -429,10 +441,41 @@ function autoSaveStore(res, sp) {
429
441
  root = doc.documentElement;
430
442
  // Linny-R models have a model element as root
431
443
  if(root.nodeName !== 'model') throw 'XML document has no model element';
432
- fs.writeFileSync(path.join(WORKSPACE.autosave, fn + '.lnr'), xml);
444
+ let fp = path.join(ws, fn + '.lnr');
445
+ if(wsd) {
446
+ // Append a version number to the file name if named file exists.
447
+ const re = /\(\d+\).lnr$/;
448
+ if(exists(fp)) {
449
+ const m = fp.match(re);
450
+ let n = 1;
451
+ if(m) {
452
+ // Replace version number (n) by (n+1).
453
+ n = parseInt(m[0].substring(1, m[0].length - 1)) + 1;
454
+ fp = fp.replace(re, `(${n}).lnr`);
455
+ } else {
456
+ // Add (1) as version number.
457
+ fp = fp.substring(0, fp.length - 4) + ' (1).lnr';
458
+ }
459
+ while(exists(fp)) {
460
+ // Iterate to find the first available version number.
461
+ n++;
462
+ fp = fp.replace(re, `(${n}).lnr`);
463
+ }
464
+ }
465
+ }
466
+ try {
467
+ fs.writeFileSync(fp, xml);
468
+ const d = `Model ${ws ? '' : 'auto-'}saved as ${fp}`;
469
+ console.log(d);
470
+ // No message (other than OK) when auto-saving.
471
+ if(ws) data = d;
472
+ } catch(err) {
473
+ console.log(err);
474
+ data = `ERROR: Failed to ${msg} to ${fp}`;
475
+ }
433
476
  } catch(err) {
434
477
  console.log(err);
435
- data = 'ERROR: Not a Linny-R model to auto-save';
478
+ data = 'ERROR: Not a Linny-R model to ' + msg;
436
479
  }
437
480
  }
438
481
  servePlainText(res, data);
@@ -1687,6 +1730,7 @@ function createWorkspace() {
1687
1730
  callback: path.join(SETTINGS.user_dir, 'callback'),
1688
1731
  data: path.join(SETTINGS.user_dir, 'data'),
1689
1732
  diagrams: path.join(SETTINGS.user_dir, 'diagrams'),
1733
+ models: path.join(SETTINGS.user_dir, 'models'),
1690
1734
  modules: path.join(SETTINGS.user_dir, 'modules'),
1691
1735
  reports: path.join(SETTINGS.user_dir, 'reports'),
1692
1736
  solver_output: path.join(SETTINGS.user_dir, 'solver')
@@ -1715,18 +1759,18 @@ function createWorkspace() {
1715
1759
 
1716
1760
  function createLaunchScript() {
1717
1761
  // Creates platform-specific script with Linny-R start-up command
1718
- const lines = [
1719
- '# The first line (without the comment symbol #) should be like this:',
1720
- '',
1721
- '',
1722
- '# Then this command to launch the Linny-R server should work:',
1723
- '',
1724
- '# After shut-down, check whether new version should be installed:'
1725
- ];
1726
- lines[2] = 'cd ' + WORKING_DIRECTORY;
1727
- let sp;
1728
- if(PLATFORM.startsWith('win')) {
1729
- sp = path.join(WORKING_DIRECTORY, 'linny-r.bat');
1762
+ const
1763
+ lines = [
1764
+ '# The first line (without the comment symbol #) should be like this:',
1765
+ '',
1766
+ 'cd ' + WORKING_DIRECTORY,
1767
+ '# Then this command to launch the Linny-R server should work:',
1768
+ '',
1769
+ '# After shut-down, check whether new version should be installed:'
1770
+ ],
1771
+ windows = PLATFORM.startsWith('win'),
1772
+ sp = path.join(WORKING_DIRECTORY, 'linny-r.' + (windows ? 'bat' : 'command'));
1773
+ if(windows) {
1730
1774
  lines.push(
1731
1775
  ':loop',
1732
1776
  'if exist newer_version (',
@@ -1738,7 +1782,6 @@ function createLaunchScript() {
1738
1782
  lines[1] = '# cd C:\\path\\to\\main\\Linny-R\\directory';
1739
1783
  lines[4] = 'node node_modules\\linny-r\\server launch';
1740
1784
  } else {
1741
- sp = path.join(WORKING_DIRECTORY, 'linny-r.command');
1742
1785
  lines.push(
1743
1786
  'while test -f newer_version; do',
1744
1787
  ' unlink newer_version',
@@ -1749,22 +1792,23 @@ function createLaunchScript() {
1749
1792
  lines[4] = 'node node_modules/linny-r/server launch';
1750
1793
  }
1751
1794
  try {
1752
- let make_script = false,
1753
- code = lines.join(os.EOL);
1754
- if(PLATFORM.startsWith('win')) code = code.replaceAll('#', '::');
1795
+ let code = lines.join(os.EOL);
1796
+ if(windows) code = code.replaceAll('#', '::');
1755
1797
  try {
1756
1798
  fs.accessSync(sp);
1757
- // Only write the script content if the file has not been customized
1758
- // by the user...
1759
- const data = fs.readFileSync(sp, 'utf-8');
1760
- make_script = code.indexOf(data) >= 0;
1799
+ // Do not overwrite existing script, as it may have been customized
1800
+ // by the user. When istalling/updating Linny-R, the post-install
1801
+ // script should have renamed it, so typically it is created the
1802
+ // first time Linny-R is run after install/update.
1761
1803
  } catch(err) {
1762
- // ... or if it does not exist yet.
1763
- make_script = true;
1764
- }
1765
- if(make_script) {
1766
1804
  console.log('Creating launch script:', sp);
1767
1805
  fs.writeFileSync(sp, code, 'utf8');
1806
+ // On macOS machines, try to make the script executable.
1807
+ if(!windows) try {
1808
+ fs.chmodSync(sp, '+x');
1809
+ } catch(err) {
1810
+ console.log('WARNING: Failed to make script executable -- please check');
1811
+ }
1768
1812
  }
1769
1813
  } catch(err) {
1770
1814
  console.log('WARNING: Failed to create launch script');
Binary file
Binary file
Binary file