linny-r 1.4.2 → 1.4.4
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 +162 -74
- package/package.json +1 -1
- package/server.js +145 -49
- package/static/images/check-off-not-same-changed.png +0 -0
- package/static/images/check-off-not-same-not-changed.png +0 -0
- package/static/images/check-off-same-changed.png +0 -0
- package/static/images/check-off-same-not-changed.png +0 -0
- package/static/images/check-on-not-same-changed.png +0 -0
- package/static/images/check-on-not-same-not-changed.png +0 -0
- package/static/images/check-on-same-changed.png +0 -0
- package/static/images/check-on-same-not-changed.png +0 -0
- package/static/images/eq-not-same-changed.png +0 -0
- package/static/images/eq-not-same-not-changed.png +0 -0
- package/static/images/eq-same-changed.png +0 -0
- package/static/images/eq-same-not-changed.png +0 -0
- package/static/images/ne-not-same-changed.png +0 -0
- package/static/images/ne-not-same-not-changed.png +0 -0
- package/static/images/ne-same-changed.png +0 -0
- package/static/images/ne-same-not-changed.png +0 -0
- package/static/images/octaeder.svg +993 -0
- package/static/images/sort-asc-lead.png +0 -0
- package/static/images/sort-asc.png +0 -0
- package/static/images/sort-desc-lead.png +0 -0
- package/static/images/sort-desc.png +0 -0
- package/static/images/sort-not.png +0 -0
- package/static/index.html +72 -647
- package/static/linny-r.css +199 -417
- package/static/scripts/linny-r-gui-actor-manager.js +340 -0
- package/static/scripts/linny-r-gui-chart-manager.js +944 -0
- package/static/scripts/linny-r-gui-constraint-editor.js +681 -0
- package/static/scripts/linny-r-gui-controller.js +4005 -0
- package/static/scripts/linny-r-gui-dataset-manager.js +1176 -0
- package/static/scripts/linny-r-gui-documentation-manager.js +739 -0
- package/static/scripts/linny-r-gui-equation-manager.js +307 -0
- package/static/scripts/linny-r-gui-experiment-manager.js +1944 -0
- package/static/scripts/linny-r-gui-expression-editor.js +449 -0
- package/static/scripts/linny-r-gui-file-manager.js +392 -0
- package/static/scripts/linny-r-gui-finder.js +727 -0
- package/static/scripts/linny-r-gui-model-autosaver.js +230 -0
- package/static/scripts/linny-r-gui-monitor.js +448 -0
- package/static/scripts/linny-r-gui-paper.js +2789 -0
- package/static/scripts/linny-r-gui-receiver.js +323 -0
- package/static/scripts/linny-r-gui-repository-browser.js +819 -0
- package/static/scripts/linny-r-gui-scale-unit-manager.js +244 -0
- package/static/scripts/linny-r-gui-sensitivity-analysis.js +778 -0
- package/static/scripts/linny-r-gui-undo-redo.js +560 -0
- package/static/scripts/linny-r-model.js +27 -11
- package/static/scripts/linny-r-utils.js +17 -2
- package/static/scripts/linny-r-vm.js +31 -12
- package/static/scripts/linny-r-gui.js +0 -16761
package/server.js
CHANGED
@@ -87,9 +87,10 @@ function getVersionInfo() {
|
|
87
87
|
info.current_time = new Date(Date.parse(obj.time[info.current]));
|
88
88
|
info.up_to_date = info.current === info.latest;
|
89
89
|
} catch(err) {
|
90
|
-
// `latest` = 0 indicates that version check failed
|
90
|
+
// `latest` = 0 indicates that version check failed.
|
91
91
|
info.latest = 0;
|
92
92
|
}
|
93
|
+
clearNewerVersion();
|
93
94
|
if(!info.latest) {
|
94
95
|
console.log(connectionErrorText('Could not connect to https://registry.npmjs.org/'));
|
95
96
|
} else if(!info.up_to_date) {
|
@@ -145,12 +146,13 @@ const SOLVER = new MILPSolver(SETTINGS, WORKSPACE);
|
|
145
146
|
// Create launch script
|
146
147
|
createLaunchScript();
|
147
148
|
|
148
|
-
// Create the HTTP server
|
149
|
+
// Create the HTTP server.
|
149
150
|
const SERVER = http.createServer((req, res) => {
|
150
151
|
const u = new URL(req.url, 'http://127.0.0.1:' + SETTINGS.port);
|
151
|
-
// When POST, first get all the full body
|
152
|
+
// When POST, first get all the full body.
|
152
153
|
if(req.method === 'POST') {
|
153
154
|
let body = '';
|
155
|
+
// @@TO DO: For big data requests, string may become too long.
|
154
156
|
req.on('data', (data) => body += data);
|
155
157
|
req.on('end', () => processRequest(req, res, u.pathname, body));
|
156
158
|
} else if(req.method === 'GET') {
|
@@ -198,7 +200,7 @@ function logAction(msg) {
|
|
198
200
|
|
199
201
|
function autoCheck(res) {
|
200
202
|
// Serves a string with the current version number plus info on a
|
201
|
-
// newer release if this is available
|
203
|
+
// newer release if this is available.
|
202
204
|
let check = VERSION_INFO.current + '|';
|
203
205
|
if(VERSION_INFO.up_to_date) {
|
204
206
|
check += 'up-to-date';
|
@@ -208,22 +210,46 @@ function autoCheck(res) {
|
|
208
210
|
servePlainText(res, check);
|
209
211
|
}
|
210
212
|
|
211
|
-
|
213
|
+
function setNewerVersion() {
|
214
|
+
// Creates the file "newer_version" in the working directory, so that
|
215
|
+
// when the server is run from the standard batch script it will detect
|
216
|
+
// that an update is required.
|
217
|
+
const nvf = path.join(WORKING_DIRECTORY, 'newer_version');
|
218
|
+
try {
|
219
|
+
fs.writeFileSync(nvf, VERSION_INFO.latest);
|
220
|
+
} catch(err) {
|
221
|
+
console.log('WARNING: Failed to create file:', nvf);
|
222
|
+
console.log(err);
|
223
|
+
}
|
224
|
+
}
|
225
|
+
|
226
|
+
function clearNewerVersion() {
|
227
|
+
// Forestalls auto-update by deleting the file "newer_version" that may
|
228
|
+
// have been created at start-up from the working directory.
|
229
|
+
try {
|
230
|
+
fs.unlink(path.join(WORKING_DIRECTORY, 'newer_version'));
|
231
|
+
} catch(err) {
|
232
|
+
// No action, as error is nogt fatal.
|
233
|
+
}
|
234
|
+
}
|
235
|
+
|
236
|
+
// HTML page to show when the server is shut down by the user.
|
212
237
|
// NOTE: on a macOS machine, this is slightly more work
|
213
|
-
const
|
214
|
-
|
238
|
+
const OS_TEXT = {close: '', reopen: ''};
|
239
|
+
if(PLATFORM === 'darwin') {
|
240
|
+
OS_TEXT.close =
|
215
241
|
`<p>You can close the <em>Terminal</em> window that shows
|
216
242
|
<tt>[Process Terminated]</tt> at the bottom.
|
217
|
-
</p
|
243
|
+
</p>`;
|
244
|
+
OS_TEXT.reopen =
|
218
245
|
`open <em>Terminal</em> again, change to your Linny-R directory by typing:
|
219
246
|
</p>
|
220
247
|
<p><code>cd ${WORKING_DIRECTORY}</code></p>
|
221
|
-
<p
|
222
|
-
|
223
|
-
''
|
224
|
-
|
225
|
-
|
226
|
-
SHUTDOWN_MESSAGE = `<!DOCTYPE html>
|
248
|
+
<p>`;
|
249
|
+
} else {
|
250
|
+
OS_TEXT.reopen = 'switch to your <em>Command Prompt</em> window ';
|
251
|
+
}
|
252
|
+
const SHUTDOWN_MESSAGE = `<!DOCTYPE html>
|
227
253
|
<html lang="en-US">
|
228
254
|
<head>
|
229
255
|
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
|
@@ -243,13 +269,8 @@ const
|
|
243
269
|
</style>
|
244
270
|
</head>
|
245
271
|
<body>
|
246
|
-
<h3>Linny-R server (127.0.0.1) is shutting down</h3
|
247
|
-
<p>To restart Linny-R,
|
248
|
-
(VERSION_INFO.up_to_date ? '' : `
|
249
|
-
first type:</p>
|
250
|
-
<p><code>npm update linny-r</code><p>
|
251
|
-
to upgrade to Linny-R version ${VERSION_INFO.latest}, and then`) +
|
252
|
-
` type:</p>
|
272
|
+
<h3>Linny-R server (127.0.0.1) is shutting down</h3>${OS_TEXT.close}
|
273
|
+
<p>To restart Linny-R, ${OS_TEXT.reopen} and then at the prompt type:</p>
|
253
274
|
<p><code>node node_modules${path.sep}linny-r${path.sep}server</code></p>
|
254
275
|
<p>
|
255
276
|
Then switch back to this window, and click this
|
@@ -1064,7 +1085,7 @@ function rcvrReport(res, rpath, rfile, run, data, stats, log) {
|
|
1064
1085
|
}
|
1065
1086
|
}
|
1066
1087
|
}
|
1067
|
-
if(n) console.log(n + 'report file' + (n > 1 ? 's' : '') + 'purged');
|
1088
|
+
if(n) console.log(n + ' report file' + (n > 1 ? 's' : '') + ' purged');
|
1068
1089
|
} catch(err) {
|
1069
1090
|
// Log error, but do not abort.
|
1070
1091
|
console.log(err);
|
@@ -1207,16 +1228,20 @@ function processRequest(req, res, cmd, data) {
|
|
1207
1228
|
// NOTE: `data` is a string of form field1=value1&field2=value2& ... etc.
|
1208
1229
|
// regardless of the request method (GET or POST)
|
1209
1230
|
if(permittedFile(cmd)) {
|
1210
|
-
// Path contains valid MIME file type extension => serve if allowed
|
1231
|
+
// Path contains valid MIME file type extension => serve if allowed.
|
1211
1232
|
serveStaticFile(res, cmd);
|
1212
|
-
|
1233
|
+
return;
|
1234
|
+
}
|
1235
|
+
// Be permissive w.r.t. leading and trailing slashes.
|
1236
|
+
cmd = cmd.replace(/^\/+/, '').replace(/\/+$/, '');
|
1237
|
+
if(cmd === 'solver') {
|
1213
1238
|
const
|
1214
1239
|
sp = new URLSearchParams(data),
|
1215
1240
|
action = sp.get('action');
|
1216
|
-
// NOTE:
|
1241
|
+
// NOTE: On remote servers, solver actions require authentication.
|
1217
1242
|
if(action === 'logon') {
|
1218
|
-
// No authentication -- simply return the passed token, "local host"
|
1219
|
-
// server name, and the identifier of the solver
|
1243
|
+
// No authentication -- simply return the passed token, "local host"
|
1244
|
+
// as server name, and the identifier of the solver.
|
1220
1245
|
serveJSON(res,
|
1221
1246
|
{token: 'local host', server: 'local host', solver: SOLVER.id});
|
1222
1247
|
} else if(action === 'png') {
|
@@ -1229,19 +1254,40 @@ function processRequest(req, res, cmd, data) {
|
|
1229
1254
|
console.log(msg);
|
1230
1255
|
serveJSON(res, {error: msg});
|
1231
1256
|
}
|
1232
|
-
} else if(cmd === '
|
1233
|
-
// Shut down this server
|
1257
|
+
} else if(cmd === 'shutdown') {
|
1258
|
+
// Shut down this server WITHOUT updating, and show page with
|
1259
|
+
// "shut down" message and restart button.
|
1260
|
+
clearNewerVersion();
|
1234
1261
|
serveHTML(res, SHUTDOWN_MESSAGE);
|
1235
1262
|
SERVER.close();
|
1236
|
-
} else if(cmd === '
|
1263
|
+
} else if(cmd === 'version') {
|
1264
|
+
logAction('HERE version = ' + VERSION_INFO.current);
|
1265
|
+
servePlainText(res, 'Current version is ' + VERSION_INFO.current);
|
1266
|
+
} else if(cmd === 'update') {
|
1267
|
+
// Shut down this server silently. When the server was started from
|
1268
|
+
// a batch script, this will update via npm, and then restart.
|
1269
|
+
// NOTE: Self-protect against overwriting development scripts.
|
1270
|
+
if(WORKING_DIRECTORY.indexOf('LTR3') >= 0) {
|
1271
|
+
servePlainText(res, 'No version update in development environment');
|
1272
|
+
} else {
|
1273
|
+
setNewerVersion();
|
1274
|
+
servePlainText(res, 'Installing Linny-R version ' + VERSION_INFO.latest);
|
1275
|
+
SERVER.close();
|
1276
|
+
}
|
1277
|
+
} else if(cmd === 'no-update') {
|
1278
|
+
// Remove file "newer_version" so no update will take place when
|
1279
|
+
// server is shut down.
|
1280
|
+
clearNewerVersion();
|
1281
|
+
servePlainText(res, 'No update to version ' + VERSION_INFO.latest);
|
1282
|
+
} else if(cmd === 'auto-check') {
|
1237
1283
|
autoCheck(res);
|
1238
|
-
} else if(cmd === '
|
1284
|
+
} else if(cmd === 'autosave') {
|
1239
1285
|
autoSave(res, new URLSearchParams(data));
|
1240
|
-
} else if(cmd === '
|
1286
|
+
} else if(cmd === 'repo') {
|
1241
1287
|
repo(res, new URLSearchParams(data));
|
1242
|
-
} else if(cmd === '
|
1288
|
+
} else if(cmd === 'load-data') {
|
1243
1289
|
loadData(res, (new URLSearchParams(data)).get('url'));
|
1244
|
-
} else if(cmd === '
|
1290
|
+
} else if(cmd === 'receiver') {
|
1245
1291
|
receiver(res, new URLSearchParams(data));
|
1246
1292
|
} else {
|
1247
1293
|
serveJSON(res, {error: `Unknown Linny-R request: "${cmd}"`});
|
@@ -1249,7 +1295,7 @@ function processRequest(req, res, cmd, data) {
|
|
1249
1295
|
}
|
1250
1296
|
|
1251
1297
|
function servePlainText(res, msg) {
|
1252
|
-
// Serve string `msg` as plain text
|
1298
|
+
// Serve string `msg` as plain text.
|
1253
1299
|
res.setHeader('Content-Type', 'text/plain');
|
1254
1300
|
res.writeHead(200);
|
1255
1301
|
res.end(msg);
|
@@ -1334,8 +1380,9 @@ function convertSVGtoPNG(req, res, sp) {
|
|
1334
1380
|
// Enclose paths in double quotes if they contain spaces
|
1335
1381
|
if(cmd.indexOf(' ') >= 0) cmd = `"${cmd}"`;
|
1336
1382
|
if(svg.indexOf(' ') >= 0) svg = `"${svg}"`;
|
1337
|
-
|
1338
|
-
|
1383
|
+
cmd += ` --export-type=png --export-dpi=${SETTINGS.dpi} ${svg}`;
|
1384
|
+
console.log(cmd);
|
1385
|
+
child_process.exec(cmd,
|
1339
1386
|
(error, stdout, stderr) => {
|
1340
1387
|
let ext = '.svg';
|
1341
1388
|
console.log(stdout);
|
@@ -1343,8 +1390,24 @@ function convertSVGtoPNG(req, res, sp) {
|
|
1343
1390
|
console.log('WARNING: Failed to run Inkscape --', error);
|
1344
1391
|
console.log(stderr);
|
1345
1392
|
} else {
|
1346
|
-
|
1347
|
-
|
1393
|
+
// Look for the PNG file.
|
1394
|
+
const mode = fs.constants.R_OK | fs.constants.W_O;
|
1395
|
+
try {
|
1396
|
+
fs.accessSync(fp + '.png', mode);
|
1397
|
+
ext = '.png';
|
1398
|
+
} catch(err) {
|
1399
|
+
// NOTE: Inkscape 1.3 adds ".png" to the original file name,
|
1400
|
+
// so this may end on ".svg.png"
|
1401
|
+
try {
|
1402
|
+
fs.accessSync(fp + '.svg.png', mode);
|
1403
|
+
// If found, rename the PNG file.
|
1404
|
+
fs.renameSync(fp + '.svg.png', fp + '.png');
|
1405
|
+
ext = '.png';
|
1406
|
+
} catch(err) {
|
1407
|
+
console.log('WARNING: Inkscape did not output a PNG file');
|
1408
|
+
}
|
1409
|
+
}
|
1410
|
+
// Delete the SVG file.
|
1348
1411
|
try {
|
1349
1412
|
fs.unlinkSync(fp + '.svg');
|
1350
1413
|
} catch(error) {
|
@@ -1623,9 +1686,17 @@ function commandLineSettings() {
|
|
1623
1686
|
}
|
1624
1687
|
// Verify that Inkscape is installed
|
1625
1688
|
if(settings.inkscape) {
|
1626
|
-
// NOTE:
|
1627
|
-
|
1689
|
+
// NOTE: On Windows, the command line version is a .com file
|
1690
|
+
let ip = path.join(settings.inkscape,
|
1628
1691
|
'inkscape' + (PLATFORM.startsWith('win') ? '.com' : ''));
|
1692
|
+
try {
|
1693
|
+
fs.accessSync(ip, fs.constants.X_OK);
|
1694
|
+
} catch(err) {
|
1695
|
+
// NOTE: As of Inkscape version 1.3, the command line executable
|
1696
|
+
// is called inkscapecom(.com)
|
1697
|
+
ip = path.join(settings.inkscape,
|
1698
|
+
'inkscapecom' + (PLATFORM.startsWith('win') ? '.com' : ''));
|
1699
|
+
}
|
1629
1700
|
try {
|
1630
1701
|
fs.accessSync(ip, fs.constants.X_OK);
|
1631
1702
|
console.log('Path to Inkscape:', settings.inkscape);
|
@@ -1698,28 +1769,53 @@ function createLaunchScript() {
|
|
1698
1769
|
// Creates platform-specific script with Linny-R start-up command
|
1699
1770
|
const lines = [
|
1700
1771
|
'# The first line (without the comment symbol #) should be like this:',
|
1701
|
-
'
|
1772
|
+
'',
|
1702
1773
|
'',
|
1703
1774
|
'# Then this command to launch the Linny-R server should work:',
|
1704
|
-
'
|
1775
|
+
'',
|
1776
|
+
'# After shut-down, check whether new version should be installed:'
|
1705
1777
|
];
|
1778
|
+
lines[2] = 'cd ' + WORKING_DIRECTORY;
|
1706
1779
|
let sp;
|
1707
1780
|
if(PLATFORM.startsWith('win')) {
|
1708
1781
|
sp = path.join(WORKING_DIRECTORY, 'linny-r.bat');
|
1709
|
-
lines
|
1782
|
+
lines.push(
|
1783
|
+
':loop',
|
1784
|
+
'if exist newer_version (',
|
1785
|
+
' del newer_version',
|
1786
|
+
' npm update linny-r',
|
1787
|
+
' node node_modules\\linny-r\\server',
|
1788
|
+
' goto loop',
|
1789
|
+
')');
|
1790
|
+
lines[1] = '# cd C:\\path\\to\\main\\Linny-R\\directory';
|
1791
|
+
lines[4] = 'node node_modules\\linny-r\\server launch';
|
1710
1792
|
} else {
|
1711
1793
|
sp = path.join(WORKING_DIRECTORY, 'linny-r.command');
|
1712
|
-
lines
|
1794
|
+
lines.push(
|
1795
|
+
'while test -f newer_version; do',
|
1796
|
+
' unlink newer_version',
|
1797
|
+
' npm update linny-r',
|
1798
|
+
' node node_modules/linny-r/server',
|
1799
|
+
'done');
|
1800
|
+
lines[1] = '# cd /path/to/main/Linny-R/directory';
|
1801
|
+
lines[4] = 'node node_modules/linny-r/server launch';
|
1713
1802
|
}
|
1714
|
-
lines[2] = 'cd ' + WORKING_DIRECTORY;
|
1715
1803
|
try {
|
1804
|
+
let make_script = false,
|
1805
|
+
code = lines.join(os.EOL);
|
1806
|
+
if(PLATFORM.startsWith('win')) code = code.replaceAll('#', '::');
|
1716
1807
|
try {
|
1717
1808
|
fs.accessSync(sp);
|
1809
|
+
// Only write the script content if the file has not been customized
|
1810
|
+
// by the user...
|
1811
|
+
const data = fs.readFileSync(sp, 'utf-8');
|
1812
|
+
make_script = code.indexOf(data) >= 0;
|
1718
1813
|
} catch(err) {
|
1719
|
-
//
|
1814
|
+
// ... or if it does not exist yet.
|
1815
|
+
make_script = true;
|
1816
|
+
}
|
1817
|
+
if(make_script) {
|
1720
1818
|
console.log('Creating launch script:', sp);
|
1721
|
-
let code = lines.join(os.EOL);
|
1722
|
-
if(PLATFORM.startsWith('win')) code = code.replaceAll('#', '::');
|
1723
1819
|
fs.writeFileSync(sp, code, 'utf8');
|
1724
1820
|
}
|
1725
1821
|
} catch(err) {
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|