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
package/README.md CHANGED
@@ -36,8 +36,8 @@ Linny-R is developed as a JavaScript package, and requires that **Node.js**
36
36
  is installed on your computer. This software can be downloaded from
37
37
  <a href="https://nodejs.org" target="_blank">https://nodejs.org</a>.
38
38
  Make sure that you choose the correct installer for your computer.
39
- Linny-R is developed using the _current_ release. Presently (December 2024)
40
- this is 23.3.0.
39
+ Linny-R is developed using the _current_ release. Presently (May 2025)
40
+ this is 24.1.0.
41
41
 
42
42
  Run the installer and accept the default settings.
43
43
  There is <u>**no**</u> need to install the optional _Tools for Native Modules_.
@@ -48,7 +48,7 @@ Verify the installation by typing:
48
48
 
49
49
  ``node --version``
50
50
 
51
- The response should be the version number of Node.js, for example: v23.3.0.
51
+ The response should be the version number of Node.js, for example: v24.1.0.
52
52
 
53
53
  ## Installing Linny-R
54
54
  It is advisable to install Linny-R in a directory on your computer, **not**
@@ -116,7 +116,6 @@ The `linny-r` package directory should contain this file `README.md`, the files
116
116
  `static`. This `static` directory should contain three HTML files:
117
117
 
118
118
  * `index.html` (the browser-based GUI)
119
- * `show-png.html` (to render SVG diagrams as PNG images)
120
119
  * `show-diff.html` (to display differences between two Linny-R models)
121
120
 
122
121
  It should also contain the style sheet `linny-r.css` required by the GUI.
@@ -379,7 +378,6 @@ You can customize Linny-R by adding more arguments to the `node` command
379
378
  in the launch script:
380
379
 
381
380
  <pre>
382
- dpi=[number] to overrule the default resolution (300 dpi) for Inkscape
383
381
  launch to automatically launch Linny-R in your default browser
384
382
  port=[number] to overrule the default port number (5050)
385
383
  solver=[name] to overrule the default sequence (Gurobi, MOSEK, CPLEX, SCIP, LP_solve)
@@ -402,8 +400,6 @@ The sub-directories of this directory `user` are used by Linny-R to store files.
402
400
  * `channel` and `callback` will be used to interact with Linny-R via its _Receiver_
403
401
  * `data` will be used by the _Dataset Manager_ to locate datasets for which
404
402
  a path has been specified
405
- * `diagrams` will be used to render Scalable Vector Graphics (SVG) files as
406
- Portable Network Graphics (PNG) using Inkscape (if installed)
407
403
  * `models` will contain models that you saved by Shift-clicking on the
408
404
  _Save_ button, or using the keyboard shortcut Ctrl-Shift-S
409
405
  * `modules` will contain models stored in the `local host` _repository_
@@ -419,39 +415,6 @@ The sub-directories of this directory `user` are used by Linny-R to store files.
419
415
  > option. This will create a new, empty workspace in the specified path.
420
416
  > It will **not** affect or duplicate information from existing workspaces.
421
417
 
422
- ## Installing Inkscape
423
-
424
- Linny-R creates its diagrams and charts as SVG images.
425
- When you download a diagram, it will be saved as a .svg file.
426
- These files can be viewed and edited using Inkscape, an open source
427
- vector graphics editor.
428
-
429
- As it may be tedious to first save a diagram as SVG and then render it
430
- manually as a bitmap image, Linny-R features a *Render diagram as bitmap*
431
- button on the top toolbar, and on the bottom toolbar of the _Chart manager_.
432
- When you click it, Linny-R will send the image as SVG to the server.
433
- The server script will save the SVG in the `user/diagrams` sub-directory,
434
- and then try to execute an Inkscape command that will convert this SVG to
435
- a PNG image file in the same directory.
436
- The file name will be `diagram-(date and time).png`.
437
- Meanwhile, the browser will have opened a new tab that will be "waiting"
438
- for this PNG image to become available.
439
- If rendering was successful, the image will appear in this browser tab;
440
- if rendering failed, the original SVG image will be shown.
441
-
442
- To install Inkscape, please look here:
443
- <a href="https://inkscape.org/release"
444
- target="_blank">https://inkscape.org/release</a>
445
-
446
- Linny-R will automatically detect whether Inkscape is installed by searching
447
- for it in the environment variable PATH on your computer. On a macOS computer,
448
- Linny-R will look for Inkscape in `/Applications/Inkscape.app/Contents/MacOS`.
449
-
450
- > [!NOTE]
451
- > The installation wizard for Inkscape (version 1.3) may **not**
452
- > add the application to the PATH variable. Please check whether you need to
453
- > do this yourself.
454
-
455
418
  ## Using Linny-R console
456
419
 
457
420
  The console-only version of Linny-R allows you to run a Linny-R model without
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "linny-r",
3
- "version": "2.0.7",
3
+ "version": "2.0.9",
4
4
  "description": "Executable graphical language with WYSIWYG editor for MILP models",
5
5
  "main": "server.js",
6
6
  "scripts": {
package/server.js CHANGED
@@ -1265,29 +1265,27 @@ function rcvrCallBack(res, rpath, rfile, script) {
1265
1265
  const STATIC_FILES = {
1266
1266
  // MIME types of files that can be served
1267
1267
  extensions: {
1268
- js: 'application/javascript',
1269
- xml: 'application/xml',
1270
- wav: 'audio/x-wav',
1271
- ttc: 'font/collection',
1272
- otf: 'font/otf',
1273
- ttf: 'font/ttf',
1274
- icns: 'image/icns',
1275
- png: 'image/png',
1276
- svg: 'image/svg+xml',
1277
- ico: 'image/x-icon',
1278
- css: 'text/css',
1279
- html: 'text/html',
1280
- txt: 'text/plain'
1281
- },
1268
+ js: 'application/javascript',
1269
+ xml: 'application/xml',
1270
+ wav: 'audio/x-wav',
1271
+ ttc: 'font/collection',
1272
+ otf: 'font/otf',
1273
+ ttf: 'font/ttf',
1274
+ icns: 'image/icns',
1275
+ png: 'image/png',
1276
+ svg: 'image/svg+xml',
1277
+ ico: 'image/x-icon',
1278
+ css: 'text/css',
1279
+ html: 'text/html',
1280
+ txt: 'text/plain'
1281
+ },
1282
1282
  // Subdirectories of (main)/static/ directory from which files with
1283
1283
  // accepted MIME types can be served
1284
1284
  directories: {
1285
1285
  '/scripts': ['js'],
1286
1286
  '/images': ['icns', 'ico', 'png', 'svg'],
1287
1287
  '/fonts': ['otf', 'ttc', 'ttf'],
1288
- '/sounds': ['wav'],
1289
- // NOTE: diagrams will actually be served from (main)/user/diagrams/
1290
- '/diagrams': ['png', 'svg']
1288
+ '/sounds': ['wav']
1291
1289
  },
1292
1290
  // Files that can be served from the (main)/static/ directory itself
1293
1291
  files: [
@@ -1417,17 +1415,11 @@ function permittedFile(path) {
1417
1415
  }
1418
1416
 
1419
1417
  function serveStaticFile(res, path) {
1420
- // Serve the specified path (if permitted: only diagrams and static files)
1418
+ // Serve the specified path (if permitted: only static files)
1421
1419
  if(path === '/' || path === '') path = '/index.html';
1422
- if(path.startsWith('/diagrams/')) {
1423
- // Serve diagrams from the (main)/user/diagrams/ sub-directory
1424
- logAction('Diagram: ' + path);
1425
- path = WORKING_DIRECTORY + '/user' + path;
1426
- } else {
1427
- // Other files from the (main)/static/ subdirectory
1428
- logAction('Static file: ' + path);
1429
- path = MODULE_DIRECTORY + '/static' + path;
1430
- }
1420
+ // Serve files from the (main)/static/ subdirectory
1421
+ logAction('Static file: ' + path);
1422
+ path = MODULE_DIRECTORY + '/static' + path;
1431
1423
  fs.readFile(path, (err, data) => {
1432
1424
  if(err) {
1433
1425
  console.log(err);
@@ -1442,75 +1434,6 @@ function serveStaticFile(res, path) {
1442
1434
  });
1443
1435
  }
1444
1436
 
1445
- function convertSVGtoPNG(req, res, sp) {
1446
- // Convert SVG data from browser to PNG image using Inkscape
1447
- // NOTE: images can be huge, so send only the file name as response;
1448
- // Linny-R will open a new browser window, load the file, and display it
1449
- const
1450
- svg = decodeURI(atob(sp.get('data'))),
1451
- // Use current time as file name
1452
- fn = 'diagram-' +
1453
- (new Date()).toISOString().slice(0, 19).replace(/[\-\:]/g, ''),
1454
- fp = path.join(WORKSPACE.diagrams, fn);
1455
- // NOTE: use binary encoding for SVG file
1456
- logAction('Saving SVG file: ' + fp);
1457
- try {
1458
- fs.writeFileSync(fp + '.svg', svg);
1459
- } catch(error) {
1460
- console.log('WARNING: Failed to save SVG --', error);
1461
- }
1462
- // Use Inkscape to convert SVG to the requested format
1463
- if(SETTINGS.inkscape) {
1464
- logAction('Rendering image');
1465
- let
1466
- cmd = SETTINGS.inkscape,
1467
- svg = fp + '.svg';
1468
- // Enclose paths in double quotes if they contain spaces
1469
- if(cmd.indexOf(' ') >= 0) cmd = `"${cmd}"`;
1470
- if(svg.indexOf(' ') >= 0) svg = `"${svg}"`;
1471
- cmd += ` --export-type=png --export-dpi=${SETTINGS.dpi} ${svg}`;
1472
- console.log(cmd);
1473
- child_process.exec(cmd,
1474
- (error, stdout, stderr) => {
1475
- let ext = '.svg';
1476
- console.log(stdout);
1477
- if(error) {
1478
- console.log('WARNING: Failed to run Inkscape --', error);
1479
- console.log(stderr);
1480
- } else {
1481
- // Look for the PNG file.
1482
- const mode = fs.constants.R_OK | fs.constants.W_O;
1483
- try {
1484
- fs.accessSync(fp + '.png', mode);
1485
- ext = '.png';
1486
- } catch(err) {
1487
- // NOTE: Inkscape 1.3 adds ".png" to the original file name,
1488
- // so this may end on ".svg.png"
1489
- try {
1490
- fs.accessSync(fp + '.svg.png', mode);
1491
- // If found, rename the PNG file.
1492
- fs.renameSync(fp + '.svg.png', fp + '.png');
1493
- ext = '.png';
1494
- } catch(err) {
1495
- console.log('WARNING: Inkscape did not output a PNG file');
1496
- }
1497
- }
1498
- // Delete the SVG file.
1499
- try {
1500
- fs.unlinkSync(fp + '.svg');
1501
- } catch(error) {
1502
- console.log(`NOTICE: Failed to delete SVG file "${fp}.svg"`);
1503
- }
1504
- }
1505
- // Return the image file name (PNG if successful, otherwise SVG)
1506
- servePlainText(res, 'diagrams/' + fn + ext);
1507
- }
1508
- );
1509
- } else {
1510
- servePlainText(res, 'diagrams/' + fn + '.svg');
1511
- }
1512
- }
1513
-
1514
1437
  // Convenience functions to fetch data from external URL
1515
1438
  // NOTE: the parameters `on_ok` and `on_error` must be functions with two
1516
1439
  // parameters:
@@ -1591,8 +1514,6 @@ function commandLineSettings() {
1591
1514
  // Sets default settings, and then checks the command line arguments.
1592
1515
  const settings = {
1593
1516
  cli_name: (PLATFORM.startsWith('win') ? 'Command Prompt' : 'Terminal'),
1594
- inkscape: '',
1595
- dpi: 300,
1596
1517
  launch: false,
1597
1518
  port: 5050,
1598
1519
  preferred_solver: '',
@@ -1604,7 +1525,6 @@ function commandLineSettings() {
1604
1525
  usage = `Usage: ${app} server [options]
1605
1526
 
1606
1527
  Possible options are:
1607
- dpi=[number] will make InkScape render SVGs in the specified resolution
1608
1528
  help will display these command line options
1609
1529
  launch will open the Linny-R GUI in a browser window
1610
1530
  port=[number] will listen at the specified port number
@@ -1635,14 +1555,6 @@ Possible options are:
1635
1555
  } else {
1636
1556
  settings.preferred_solver = av[1];
1637
1557
  }
1638
- } else if(av[0] === 'dpi') {
1639
- // Accept any number greater than or equal to 1024.
1640
- const n = parseInt(av[1]);
1641
- if(isNaN(n) || n > 1200) {
1642
- console.log(`WARNING: Invalid resolution ${av[1]} (max. 1200 dpi)`);
1643
- } else {
1644
- settings.dpi = n;
1645
- }
1646
1558
  } else if(av[0] === 'workspace') {
1647
1559
  // User directory must be READ/WRITE-accessible.
1648
1560
  try {
@@ -1665,55 +1577,6 @@ Possible options are:
1665
1577
  }
1666
1578
  }
1667
1579
  }
1668
- // Check whether Inkscape has been installed.
1669
- const path_list = process.env.PATH.split(path.delimiter);
1670
- for(let i = 0; i < path_list.length; i++) {
1671
- // Check whether it is an Inkscape path.
1672
- match = path_list[i].match(/inkscape/i);
1673
- // If so, use it (so the *last* matching path will be used).
1674
- if(match) settings.inkscape = path_list[i];
1675
- }
1676
- // NOTE: On macOS, Inkscape is not added to the PATH environment variable.
1677
- if(!settings.inkscape && PLATFORM === 'darwin') {
1678
- console.log('Looking for Inkscape in Applications...');
1679
- try {
1680
- // Look in the default directory.
1681
- const ip = '/Applications/Inkscape.app/Contents/MacOS';
1682
- fs.accessSync(ip);
1683
- settings.inkscape = ip;
1684
- } catch(err) {
1685
- // No real error, so no action needed.
1686
- }
1687
- }
1688
- // Verify that Inkscape is installed.
1689
- if(settings.inkscape) {
1690
- // NOTE: On Windows, the command line version is a .com file.
1691
- let ip = path.join(settings.inkscape,
1692
- 'inkscape' + (PLATFORM.startsWith('win') ? '.com' : ''));
1693
- try {
1694
- fs.accessSync(ip, fs.constants.X_OK);
1695
- } catch(err) {
1696
- // NOTE: As of Inkscape version 1.3, the command line executable
1697
- // is called inkscapecom(.com).
1698
- ip = path.join(settings.inkscape,
1699
- 'inkscapecom' + (PLATFORM.startsWith('win') ? '.com' : ''));
1700
- }
1701
- try {
1702
- fs.accessSync(ip, fs.constants.X_OK);
1703
- console.log('Path to Inkscape:', settings.inkscape);
1704
- settings.inkscape = ip;
1705
- console.log(
1706
- `SVG will be rendered with ${settings.dpi} dpi resolution`);
1707
- } catch(err) {
1708
- settings.inkscape = '';
1709
- console.log(err.message);
1710
- console.log(
1711
- 'WARNING: Failed to access the Inkscape command line application');
1712
- }
1713
- } else {
1714
- console.log(
1715
- 'Inkscape not installed, so images will not be rendered as PNG');
1716
- }
1717
1580
  return settings;
1718
1581
  }
1719
1582
 
@@ -1741,7 +1604,6 @@ function createWorkspace() {
1741
1604
  channel: path.join(SETTINGS.user_dir, 'channel'),
1742
1605
  callback: path.join(SETTINGS.user_dir, 'callback'),
1743
1606
  data: path.join(SETTINGS.user_dir, 'data'),
1744
- diagrams: path.join(SETTINGS.user_dir, 'diagrams'),
1745
1607
  models: path.join(SETTINGS.user_dir, 'models'),
1746
1608
  modules: path.join(SETTINGS.user_dir, 'modules'),
1747
1609
  reports: path.join(SETTINGS.user_dir, 'reports'),
package/static/index.html CHANGED
@@ -277,13 +277,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
277
277
  title="Sensitivity analysis (Ctrl-J)">
278
278
  <img id="experiment-btn" class="btn enab" src="images/experiment.png"
279
279
  title="Manage experiments (Ctrl-X)">
280
- <a href="show-png.html" target="_blank"
281
- style="text-decoration: none; outline: none">
282
- <img id="diagram-btn" class="btn enab" src="images/diagram.png"
283
- title="Render diagram as bitmap at 300 dpi (Ctrl-P)">
284
- </a>
285
280
  <img id="savediagram-btn" class="btn enab" src="images/save-diagram.png"
286
- title="Download diagram as Scalable Vector Graphics (Ctrl-G)">
281
+ title="Download diagram as Portable Network Graphics bitmap image (Ctrl-P)
282
+ &#x270E; Shift-click to download as Scalable Vector Graphics (Ctrl-Shift-P)">
287
283
  <img id="finder-btn" class="btn enab" src="images/find.png"
288
284
  title="Find model elements by their name (Ctrl-F)">
289
285
  <img id="monitor-btn" class="btn enab" src="images/monitor.png"
@@ -771,6 +767,24 @@ NOTE: Unit symbols are case-sensitive, so BTU &ne; Btu">
771
767
  <table id="power-grids-table">
772
768
  </table>
773
769
  </div>
770
+ <table>
771
+ <tr title="Checked aspects will be ignored for all power grids">
772
+ <td style="padding-bottom: 3px; font-style: italic">Disregard:</td>
773
+ <td style="padding: 0px; width: 20px">
774
+ <div id="power-grids-capacity" class="box clear"></div>
775
+ </td>
776
+ <td style="padding-bottom: 3px; width: 70px">Congestion</td>
777
+ <td style="padding: 0px; width: 20px">
778
+ <div id="power-grids-KVL" class="box clear"></div>
779
+ </td>
780
+ <td style="padding-bottom: 3px; width: 35px"
781
+ title="Kirchhoff's Voltage Law">KVL</td>
782
+ <td style="padding: 0px; width: 20px">
783
+ <div id="power-grids-losses" class="box clear"></div>
784
+ </td>
785
+ <td style="padding-bottom: 3px">Losses</td>
786
+ </tr>
787
+ </table>
774
788
  </div>
775
789
  </div>
776
790
 
@@ -1843,6 +1857,8 @@ Each line should represent the points of a different bound line.">
1843
1857
  title="Rename selected dataset">
1844
1858
  <img id="ds-clone-btn" class="btn disab" src="images/clone.png"
1845
1859
  title="Clone selected dataset">
1860
+ <img id="ds-load-btn" class="btn enab" src="images/open.png"
1861
+ title="Load dataset(s) from CSV file">
1846
1862
  <img id="ds-delete-btn" class="btn disab" src="images/delete.png"
1847
1863
  title="Delete selected dataset"
1848
1864
  style="position: absolute; right: 2px">
@@ -1940,6 +1956,19 @@ Start with = to find exact match, with ~ to match first characters">
1940
1956
  <input id="rename-dataset-name" type="text" autocomplete="off">
1941
1957
  </div>
1942
1958
  </div>
1959
+
1960
+ <!-- the LOAD CSV dialog prompts the user for a CSV file on disk
1961
+ to be loaded -->
1962
+ <div id="load-csv-modal" class="modal">
1963
+ <div id="load-csv-dlg" class="inp-dlg">
1964
+ <div class="dlg-title">
1965
+ Load datasets from CSV file
1966
+ <img class="cancel-btn" src="images/cancel.png">
1967
+ <img class="ok-btn" src="images/ok.png">
1968
+ </div>
1969
+ <input id="load-csv-file" type="file">
1970
+ </div>
1971
+ </div>
1943
1972
 
1944
1973
  <!-- the NEW SELECTOR dialog prompts for a new selector for a dataset -->
1945
1974
  <div id="new-selector-modal" class="modal">
@@ -2219,13 +2248,8 @@ NOTE: * and ? will be interpreted as wildcards"
2219
2248
  </div>
2220
2249
  <div id="chart-only-buttons">
2221
2250
  <img id="chart-save-btn" class="btn enab" src="images/save-chart.png"
2222
- title="Save chart diagram">
2223
- <a id="chart-render-btn" href="show-png.html" target="_blank"
2224
- style="margin-left: 4px; text-decoration: none; outline: none">
2225
- <img class="btn enab" src="images/chart.png"
2226
- title="Render chart diagram as PNG"
2227
- style="height: 20px; width: 20px">
2228
- </a>
2251
+ title="Download chart as bitmap image (PNG)
2252
+ &#x270E; Shift-click to download chart as Scalable Vector Graphics (SVG)">
2229
2253
  <img id="chart-widen-btn" class="btn enab" src="images/stretch.png"
2230
2254
  title="Widen chart" style="margin-left: 24px">
2231
2255
  <img id="chart-narrow-btn" class="btn enab" src="images/compress.png"
@@ -2495,7 +2519,7 @@ NOTE: * and ? will be interpreted as wildcards"
2495
2519
  <div id="experiment-separator"></div>
2496
2520
  <div id="experiment-default-message">
2497
2521
  To define a meaningful experiment, a model must feature at least
2498
- one outcome dataset, or a graph having one or more variables.
2522
+ one outcome dataset, or a chart having one or more variables.
2499
2523
  </div>
2500
2524
  <div id="experiment-params-header">(no experiment selected)</div>
2501
2525
  <div id="experiment-params-div">
@@ -2843,7 +2867,9 @@ NOTE: * and ? will be interpreted as wildcards"
2843
2867
  <label>Settings:</label>
2844
2868
  <input id="xp-settings-selector-string" type="text" autocomplete="off"
2845
2869
  title="Can specifiy time step, simulation period, block length and look-ahead.
2846
- Example: s=0.5h t=1-480 b=24 l=24 (in this order: s t b l)">
2870
+ Example: s=0.5h t=1-480 b=24 l=24
2871
+ When modeling power grids, -c indicates &ldquo;disregard grid capacity&rdquo;,
2872
+ -k &ldquo;disregard Kirchhoff's Voltage Law&rdquo;, and -l &ldquo;disregard power losses&rdquo;">
2847
2873
  </div>
2848
2874
  </div>
2849
2875
  </div>
@@ -3033,12 +3059,17 @@ where X can be one or several of these letters: ABCDELPQ">
3033
3059
  <div id="finder-status">
3034
3060
  <span id="finder-count"></span> found
3035
3061
  </div>
3036
- <img id="finder-edit-btn" class="btn enab"
3037
- src="images/edit.png"
3038
- title="Edit attributes">
3039
- <img id="finder-copy-btn" class="btn enab"
3040
- src="images/table-to-clpbrd.png"
3041
- title="Copy entity attributes to clipboard">
3062
+ <div id="finder-status-buttons">
3063
+ <img id="finder-edit-btn" class="btn enab"
3064
+ src="images/edit.png"
3065
+ title="Edit attributes">
3066
+ <img id="finder-chart-btn" class="btn enab"
3067
+ src="images/chart.png"
3068
+ title="Add attribute to chart">
3069
+ <img id="finder-copy-btn" class="btn enab"
3070
+ src="images/table-to-clpbrd.png"
3071
+ title="Copy entity attributes to clipboard">
3072
+ </div>
3042
3073
  <div id="finder-separator"></div>
3043
3074
  <div id="finder-item-header">(no item selected)</div>
3044
3075
  <div id="finder-item-scroll-area">
@@ -3053,6 +3084,28 @@ where X can be one or several of these letters: ABCDELPQ">
3053
3084
  <div id="finder-resize" class="resizer"></div>
3054
3085
  </div>
3055
3086
 
3087
+ <!-- the CONFIRM ADD CHART VARIABLES modal asks to confirm to add a
3088
+ specific attribute of the filtered entities to the current chart -->
3089
+ <div id="confirm-add-chart-variables-modal" class="modal">
3090
+ <div id="confirm-add-chart-variables-dlg" class="inp-dlg">
3091
+ <div class="dlg-title">
3092
+ Add to chart
3093
+ <img class="cancel-btn" src="images/cancel.png">
3094
+ <img class="ok-btn" src="images/ok.png">
3095
+ </div>
3096
+ <div style="margin: 2px; padding-right: 2px; white-space: nowrap">
3097
+ <select id="confirm-add-chart-variables-attribute"></select>
3098
+ of <span id="confirm-add-chart-variables-count"></span>
3099
+ </div>
3100
+ <div>
3101
+ <div id="confirm-add-chart-variables-stacked" class="box clear"></div>
3102
+ <div style="display:inline-block; vertical-align:top; margin-top: 3px">
3103
+ Display as stacked areas
3104
+ </div>
3105
+ </div>
3106
+ </div>
3107
+ </div>
3108
+
3056
3109
  <!-- the MONITOR dialog shows solver progress and messages, and model equations -->
3057
3110
  <div id="monitor-dlg" class="inp-dlg">
3058
3111
  <div id="monitor-hdr" class="dragger dlg-title">Solver monitor
@@ -794,6 +794,7 @@ div.checked.not-same-not-changed {
794
794
 
795
795
  /* LOAD modal dialog */
796
796
  #load-dlg,
797
+ #load-csv-dlg,
797
798
  #comparison-dlg {
798
799
  width: 430px;
799
800
  height: min-content;
@@ -801,6 +802,7 @@ div.checked.not-same-not-changed {
801
802
  }
802
803
 
803
804
  #load-xml-file,
805
+ #load-csv-file,
804
806
  #comparison-xml-file {
805
807
  padding: 2px;
806
808
  max-width: 375px;
@@ -1063,7 +1065,7 @@ div.grid-watts {
1063
1065
  margin-right: 3px;
1064
1066
  }
1065
1067
 
1066
- div.grid-kcl-symbol {
1068
+ div.grid-kvl-symbol {
1067
1069
  display: inline-block;
1068
1070
  height: 17px;
1069
1071
  font-size: 19px;
@@ -5032,24 +5034,20 @@ img.finder {
5032
5034
  width: 50%;
5033
5035
  }
5034
5036
 
5035
- /* NOTE: Finder buttons are initially invisible */
5036
-
5037
- #finder-edit-btn {
5037
+ #finder-status-buttons {
5038
5038
  position: absolute;
5039
- height: 16px;
5040
- width: 16px;
5041
- bottom: 1px;
5042
- right: calc(50% + 24px);
5043
- display: none;
5039
+ bottom: 0px;
5040
+ right: calc(50% + 1px);
5044
5041
  }
5045
5042
 
5043
+ /* NOTE: Finder buttons are initially invisible */
5044
+
5045
+ #finder-edit-btn,
5046
+ #finder-chart-btn,
5046
5047
  #finder-copy-btn {
5047
- position: absolute;
5048
- height: 16px;
5049
- width: 16px;
5050
- bottom: 1px;
5051
- right: calc(50% + 1px);
5052
- display: none; /* NOTE: button is initially invisible */
5048
+ height: 15px;
5049
+ width: 15px;
5050
+ display: none;
5053
5051
  }
5054
5052
 
5055
5053
  #finder-separator {
@@ -5097,6 +5095,14 @@ img.finder {
5097
5095
  border-top: 1px solid Silver;
5098
5096
  }
5099
5097
 
5098
+ #confirm-add-chart-variables-dlg {
5099
+ width: min-content;
5100
+ height: min-content;
5101
+ }
5102
+
5103
+ #confirm-add-chart-variables-attribute {
5104
+ font-size: 12px;
5105
+ }
5100
5106
 
5101
5107
  /* the MONITOR DIALOG displays solver progress and messages */
5102
5108
  #monitor-dlg {
@@ -5302,7 +5308,7 @@ div.sel-tab {
5302
5308
  z-index: 150; /* should be highest of all dialogs (even modal ones) */
5303
5309
  margin: 0;
5304
5310
  width: 395px;
5305
- height: 195px;
5311
+ height: 200px;
5306
5312
  min-width: 340px;
5307
5313
  min-height: 150px;
5308
5314
  max-height: 99vh;