linny-r 2.0.12 → 2.1.0
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/package.json +1 -1
- package/static/images/update.png +0 -0
- package/static/index.html +34 -3
- package/static/linny-r.css +42 -3
- package/static/scripts/linny-r-ctrl.js +6 -0
- package/static/scripts/linny-r-gui-actor-manager.js +6 -6
- package/static/scripts/linny-r-gui-chart-manager.js +1 -7
- package/static/scripts/linny-r-gui-controller.js +7 -8
- package/static/scripts/linny-r-gui-dataset-manager.js +7 -16
- package/static/scripts/linny-r-gui-paper.js +10 -1
- package/static/scripts/linny-r-gui-repository-browser.js +304 -17
- package/static/scripts/linny-r-gui-undo-redo.js +41 -52
- package/static/scripts/linny-r-model.js +341 -265
- package/static/scripts/linny-r-utils.js +9 -11
- package/static/scripts/linny-r-vm.js +95 -14
package/package.json
CHANGED
Binary file
|
package/static/index.html
CHANGED
@@ -914,6 +914,8 @@ NOTE: Products directly linked to such processes should have a proportional unit
|
|
914
914
|
title="Include module in current model">
|
915
915
|
<img id="repo-load-btn" class="btn disab" src="images/open.png"
|
916
916
|
title="Load model from repository">
|
917
|
+
<img id="repo-update-btn" class="btn disab" src="images/update.png"
|
918
|
+
title="Update clusters that have been included in the model">
|
917
919
|
<img id="repo-store-btn" class="btn enab" src="images/store.png"
|
918
920
|
title="Store model as module in repository">
|
919
921
|
<img id="repo-black-box-btn" class="btn enab" src="images/black-box.png"
|
@@ -1061,9 +1063,6 @@ NOTE: Products directly linked to such processes should have a proportional unit
|
|
1061
1063
|
<img class="cancel-btn" src="images/cancel.png">
|
1062
1064
|
<img class="ok-btn" src="images/ok.png">
|
1063
1065
|
</div>
|
1064
|
-
<div style="margin:2px; background-color: Yellow">
|
1065
|
-
<strong>NOTE:</strong> <em>This action cannot be undone!</em>
|
1066
|
-
</div>
|
1067
1066
|
<div style="margin:2px">
|
1068
1067
|
<label>Cluster:</label>
|
1069
1068
|
<input id="include-prefix" type="text"
|
@@ -1082,6 +1081,38 @@ NOTE: Products directly linked to such processes should have a proportional unit
|
|
1082
1081
|
</div>
|
1083
1082
|
</div>
|
1084
1083
|
|
1084
|
+
<!-- the UPDATE dialog prompts for a module name -->
|
1085
|
+
<div id="update-modal" class="modal">
|
1086
|
+
<div id="update-dlg" class="inp-dlg" style="min-width: 320px">
|
1087
|
+
<div class="dlg-title">
|
1088
|
+
Update clusters previously included from a module
|
1089
|
+
<img class="cancel-btn" src="images/cancel.png">
|
1090
|
+
<img class="ok-btn" src="images/ok.png">
|
1091
|
+
</div>
|
1092
|
+
<div style="padding: 2px">
|
1093
|
+
<div>
|
1094
|
+
Update using module: <span id="update-name"></span>
|
1095
|
+
</div>
|
1096
|
+
<div>
|
1097
|
+
<label>Clusters based on:</label>
|
1098
|
+
<select id="update-module">
|
1099
|
+
</select>
|
1100
|
+
<div id="update-count" class="update-cc"></div>
|
1101
|
+
</div>
|
1102
|
+
<div id="update-issues">
|
1103
|
+
<div id="update-issues-header"></div>
|
1104
|
+
<div id="update-issues-area"></div>
|
1105
|
+
</div>
|
1106
|
+
<div id="update-remove">
|
1107
|
+
<div id="update-remove-header">
|
1108
|
+
<span id="update-remove-count"></span> will be removed
|
1109
|
+
</div>
|
1110
|
+
<div id="update-remove-area"></div>
|
1111
|
+
</div>
|
1112
|
+
</div>
|
1113
|
+
</div>
|
1114
|
+
</div>
|
1115
|
+
|
1085
1116
|
<!-- the CONFIRM LOAD modal asks to confirm to load model from repository -->
|
1086
1117
|
<div id="confirm-load-from-repo-modal" class="modal">
|
1087
1118
|
<div id="confirm-load-from-repo-dlg" class="inp-dlg">
|
package/static/linny-r.css
CHANGED
@@ -607,6 +607,11 @@ span.node-details {
|
|
607
607
|
margin-left: 15px;
|
608
608
|
}
|
609
609
|
|
610
|
+
span.mod-name {
|
611
|
+
font-style: normal;
|
612
|
+
color: #403080;
|
613
|
+
}
|
614
|
+
|
610
615
|
#issue-panel {
|
611
616
|
display: none;
|
612
617
|
background-color: Yellow;
|
@@ -707,6 +712,11 @@ img.inline-cancel-btn:hover {
|
|
707
712
|
filter: brightness(120%);
|
708
713
|
}
|
709
714
|
|
715
|
+
img.ok-btn.disab {
|
716
|
+
filter: saturate(0%) brightness(150%) contrast(70%);
|
717
|
+
pointer-events: none;
|
718
|
+
}
|
719
|
+
|
710
720
|
#actor-group,
|
711
721
|
#constraint-group,
|
712
722
|
#cluster-group,
|
@@ -4152,8 +4162,9 @@ td.sa-not-run {
|
|
4152
4162
|
|
4153
4163
|
#xp-ignore-count {
|
4154
4164
|
position: absolute;
|
4155
|
-
right:
|
4165
|
+
right: 2px;
|
4156
4166
|
bottom: 3px;
|
4167
|
+
width: 18px;
|
4157
4168
|
height: 16px;
|
4158
4169
|
font-size: 9px;
|
4159
4170
|
color: #806070;
|
@@ -4882,6 +4893,7 @@ span.sd-clear {
|
|
4882
4893
|
}
|
4883
4894
|
|
4884
4895
|
#paste-dlg,
|
4896
|
+
#update-dlg,
|
4885
4897
|
#include-dlg {
|
4886
4898
|
width: 320px;
|
4887
4899
|
height: min-content;
|
@@ -4907,9 +4919,29 @@ span.sd-clear {
|
|
4907
4919
|
margin-left: 9px;
|
4908
4920
|
}
|
4909
4921
|
|
4922
|
+
#update-name {
|
4923
|
+
font-family: monospace;
|
4924
|
+
font-size: 14px;
|
4925
|
+
}
|
4926
|
+
|
4927
|
+
div.update-cc {
|
4928
|
+
display: inline-block;
|
4929
|
+
margin-left: 6px;
|
4930
|
+
font-style: italic;
|
4931
|
+
color: Gray;
|
4932
|
+
}
|
4933
|
+
|
4934
|
+
#update-remove-header,
|
4935
|
+
#update-issues-header {
|
4936
|
+
font-weight: bold;
|
4937
|
+
margin: 4px 2px 2px 2px;
|
4938
|
+
}
|
4939
|
+
|
4910
4940
|
#paste-scroll-area,
|
4911
|
-
#include-scroll-area
|
4912
|
-
|
4941
|
+
#include-scroll-area,
|
4942
|
+
#update-remove-area,
|
4943
|
+
#update-issues-area {
|
4944
|
+
margin: 0 2px;
|
4913
4945
|
height: min-content;
|
4914
4946
|
max-height: 350px !important;
|
4915
4947
|
width: calc(100% - 4px);
|
@@ -4918,6 +4950,13 @@ span.sd-clear {
|
|
4918
4950
|
overflow-y: auto;
|
4919
4951
|
}
|
4920
4952
|
|
4953
|
+
#update-remove-area,
|
4954
|
+
#update-issues-area {
|
4955
|
+
max-height: 150px !important;
|
4956
|
+
background-color: white;
|
4957
|
+
border: 1px solid Silver;
|
4958
|
+
}
|
4959
|
+
|
4921
4960
|
div.paste-tactic {
|
4922
4961
|
display: inline-block;
|
4923
4962
|
vertical-align: top;
|
@@ -311,6 +311,12 @@ class Controller {
|
|
311
311
|
(name.startsWith(this.BLACK_BOX) || name[0].match(/[\w]/));
|
312
312
|
}
|
313
313
|
|
314
|
+
realActorName(name) {
|
315
|
+
// Return `name` unless it is '(no actor)'; then return empty string.
|
316
|
+
if(name === this.NO_ACTOR) return '';
|
317
|
+
return name;
|
318
|
+
}
|
319
|
+
|
314
320
|
prefixesAndName(name, key=false) {
|
315
321
|
// Returns name split exclusively at '[non-space]: [non-space]'
|
316
322
|
let sep = this.PREFIXER,
|
@@ -224,10 +224,10 @@ class ActorManager {
|
|
224
224
|
}
|
225
225
|
|
226
226
|
showEditActorDialog(name, expr) {
|
227
|
-
// Display modal for editing properties of one actor
|
227
|
+
// Display modal for editing properties of one actor.
|
228
228
|
this.actor_span.innerHTML = name;
|
229
229
|
this.actor_name.value = name;
|
230
|
-
// Do not allow modification of the name '(no actor)'
|
230
|
+
// Do not allow modification of the name '(no actor)'.
|
231
231
|
if(name === UI.NO_ACTOR) {
|
232
232
|
this.actor_name.disabled = true;
|
233
233
|
this.actor_io.style.display = 'none';
|
@@ -242,22 +242,22 @@ class ActorManager {
|
|
242
242
|
|
243
243
|
modifyActorEntry() {
|
244
244
|
// This method is called when the modeler submits the "actor properties"
|
245
|
-
// dialog
|
245
|
+
// dialog.
|
246
246
|
let n = this.actor_span.innerHTML,
|
247
247
|
nn = UI.NO_ACTOR,
|
248
248
|
x = this.actor_weight.value.trim(),
|
249
249
|
xp = new ExpressionParser(x);
|
250
250
|
if(n !== UI.NO_ACTOR) {
|
251
251
|
nn = this.actor_name.value.trim();
|
252
|
-
// NOTE:
|
253
|
-
// prefixed entities
|
252
|
+
// NOTE: Prohibit colons in actor names to avoid confusion with
|
253
|
+
// prefixed entities.
|
254
254
|
if(!UI.validName(nn) || nn.indexOf(':') >= 0) {
|
255
255
|
UI.warn(UI.WARNING.INVALID_ACTOR_NAME);
|
256
256
|
return false;
|
257
257
|
}
|
258
258
|
}
|
259
259
|
if(xp.error) {
|
260
|
-
// NOTE:
|
260
|
+
// NOTE: Do not pass the actor, as its name is being edited as well.
|
261
261
|
UI.warningInvalidWeightExpression(null, xp.error);
|
262
262
|
return false;
|
263
263
|
}
|
@@ -637,13 +637,7 @@ class GUIChartManager extends ChartManager {
|
|
637
637
|
deleteChart() {
|
638
638
|
// Delete the shown chart (if any).
|
639
639
|
if(this.chart_index >= 0) {
|
640
|
-
|
641
|
-
if(MODEL.charts[this.chart_index].title === this.new_chart_title) {
|
642
|
-
MODEL.charts[this.chart_index].reset();
|
643
|
-
} else {
|
644
|
-
MODEL.charts.splice(this.chart_index, 1);
|
645
|
-
this.chart_index = -1;
|
646
|
-
}
|
640
|
+
MODEL.deleteChart(this.chart_index);
|
647
641
|
// Also update the experiment viewer, because this chart may be
|
648
642
|
// one of the output charts of the selected experiment.
|
649
643
|
UI.updateControllerDialogs('CFX');
|
@@ -2979,7 +2979,7 @@ class GUIController extends Controller {
|
|
2979
2979
|
//
|
2980
2980
|
|
2981
2981
|
validNames(nn, an='') {
|
2982
|
-
// Check whether names meet conventions
|
2982
|
+
// Check whether names meet conventions. If not, warn user.
|
2983
2983
|
if(!UI.validName(nn) || nn.indexOf(UI.BLACK_BOX) >= 0) {
|
2984
2984
|
this.warningInvalidName(nn);
|
2985
2985
|
return false;
|
@@ -3148,10 +3148,10 @@ class GUIController extends Controller {
|
|
3148
3148
|
UI.info_line.classList.remove(...UI.info_line.classList);
|
3149
3149
|
}
|
3150
3150
|
|
3151
|
-
setMessage(msg, type=null) {
|
3151
|
+
setMessage(msg, type=null, cause=null) {
|
3152
3152
|
// Display `msg` on infoline unless no type (= plain text) and some
|
3153
3153
|
// info, warning or error message is already displayed.
|
3154
|
-
super.setMessage(msg, type);
|
3154
|
+
super.setMessage(msg, type, cause);
|
3155
3155
|
const types = ['notification', 'warning', 'error'];
|
3156
3156
|
let d = new Date(),
|
3157
3157
|
t = d.getTime(),
|
@@ -3666,7 +3666,7 @@ class GUIController extends Controller {
|
|
3666
3666
|
// proceed to paste.
|
3667
3667
|
const
|
3668
3668
|
md = this.paste_modal,
|
3669
|
-
mapping = Object.assign(md.mapping
|
3669
|
+
mapping = Object.assign({}, md.mapping),
|
3670
3670
|
tc = (mapping.top_clusters ?
|
3671
3671
|
Object.keys(mapping.top_clusters).sort(ciCompare) : []),
|
3672
3672
|
ft = (mapping.from_to ?
|
@@ -3908,8 +3908,8 @@ class GUIController extends Controller {
|
|
3908
3908
|
mapping.shared_prefix = sp;
|
3909
3909
|
mapping.from_prefix = (fpn ? sp + fpn + UI.PREFIXER : sp);
|
3910
3910
|
mapping.to_prefix = (tpn ? sp + tpn + UI.PREFIXER : sp);
|
3911
|
-
mapping.from_actor =
|
3912
|
-
mapping.to_actor =
|
3911
|
+
mapping.from_actor = UI.realActorName(ca);
|
3912
|
+
mapping.to_actor = UI.realActorName(fca);
|
3913
3913
|
// Prompt for mapping when pasting to the same model and cluster.
|
3914
3914
|
if(parseInt(mts) === MODEL.time_created.getTime() &&
|
3915
3915
|
ca === fca && mapping.from_prefix === mapping.to_prefix &&
|
@@ -4546,8 +4546,7 @@ console.log('HERE name conflicts', name_conflicts, mapping);
|
|
4546
4546
|
md.group = group;
|
4547
4547
|
md.element('action').innerText = 'Edit';
|
4548
4548
|
md.element('name').value = c.name;
|
4549
|
-
md.element('actor').value = (c.actor.name
|
4550
|
-
'' : c.actor.name);
|
4549
|
+
md.element('actor').value = UI.realActorName(c.actor.name);
|
4551
4550
|
md.element('options').style.display = 'block';
|
4552
4551
|
this.setBox('cluster-collapsed', c.collapsed);
|
4553
4552
|
this.setBox('cluster-ignore', c.ignore);
|
@@ -289,17 +289,6 @@ class GUIDatasetManager extends DatasetManager {
|
|
289
289
|
return null;
|
290
290
|
}
|
291
291
|
|
292
|
-
datasetsByPrefix(prefix) {
|
293
|
-
// Return the list of datasets having the specified prefix.
|
294
|
-
const
|
295
|
-
pid = UI.nameToID(prefix + UI.PREFIXER),
|
296
|
-
dsl = [];
|
297
|
-
for(const k of Object.keys(MODEL.datasets)) {
|
298
|
-
if(k.startsWith(pid)) dsl.push(k);
|
299
|
-
}
|
300
|
-
return dsl;
|
301
|
-
}
|
302
|
-
|
303
292
|
selectPrefixRow(e) {
|
304
293
|
// Select expand/collapse prefix row.
|
305
294
|
this.focal_table = this.dataset_table;
|
@@ -310,7 +299,7 @@ class GUIDatasetManager extends DatasetManager {
|
|
310
299
|
const toggle = r.classList.contains('tree-btn');
|
311
300
|
while(r.tagName !== 'TR') r = r.parentNode;
|
312
301
|
this.selected_prefix_row = r;
|
313
|
-
this.prefixed_datasets =
|
302
|
+
this.prefixed_datasets = MODEL.datasetKeysByPrefix(r.dataset.prefix);
|
314
303
|
const sel = this.dataset_table.getElementsByClassName('sel-set');
|
315
304
|
this.selected_dataset = null;
|
316
305
|
if(sel.length > 0) {
|
@@ -746,7 +735,7 @@ class GUIDatasetManager extends DatasetManager {
|
|
746
735
|
}
|
747
736
|
|
748
737
|
get selectedAsList() {
|
749
|
-
|
738
|
+
// Return list of datasets selected directly or by prefix.
|
750
739
|
const dsl = [];
|
751
740
|
// Prevent including the equations dataset (just in case).
|
752
741
|
if(this.selected_dataset && this.selected_dataset !== MODEL.equations_dataset) {
|
@@ -1243,12 +1232,14 @@ class GUIDatasetManager extends DatasetManager {
|
|
1243
1232
|
for(let j = 0; j < ncol; j++) {
|
1244
1233
|
const
|
1245
1234
|
v = dsv[j].trim(),
|
1246
|
-
sf = safeStrToFloat(v,
|
1247
|
-
|
1235
|
+
sf = safeStrToFloat(v, NaN);
|
1236
|
+
// NOTE: Ignore empty strings, but this may "shift up" numerical values on later rows.
|
1237
|
+
if(isNaN(sf) && v !== '') {
|
1248
1238
|
UI.warn(`Invalid numerical value "${v}" for <strong>${dsn[j]}</strong> on line ${i}`);
|
1249
1239
|
return false;
|
1240
|
+
} else if(!isNaN(sf)) {
|
1241
|
+
dsa[j].push(sf);
|
1250
1242
|
}
|
1251
|
-
dsa[j].push(sf);
|
1252
1243
|
}
|
1253
1244
|
}
|
1254
1245
|
// Add or update datasets.
|
@@ -1955,7 +1955,7 @@ class Paper {
|
|
1955
1955
|
let l = (MODEL.solved ? proc.actualLevel(MODEL.t) : VM.NOT_COMPUTED),
|
1956
1956
|
lb = proc.lower_bound.result(MODEL.t),
|
1957
1957
|
ub = (proc.equal_bounds ? lb : proc.upper_bound.result(MODEL.t));
|
1958
|
-
// NOTE:
|
1958
|
+
// NOTE: By default, lower bound = 0 (but do show exceptional values).
|
1959
1959
|
if(lb === VM.UNDEFINED && !proc.lower_bound.defined) lb = 0;
|
1960
1960
|
let hw,
|
1961
1961
|
hh,
|
@@ -2766,6 +2766,15 @@ class Paper {
|
|
2766
2766
|
'h-', w - shadow_width, 'v-', shadow_width,
|
2767
2767
|
'h', w - 2*shadow_width, 'z'],
|
2768
2768
|
{fill:stroke_color, stroke:stroke_color, 'stroke-width':stroke_width});
|
2769
|
+
if(clstr.module) {
|
2770
|
+
// Add three white dots at middle of bottom shade.
|
2771
|
+
const
|
2772
|
+
ely = y + hh - shadow_width / 2,
|
2773
|
+
elfill = {fill: 'white'};
|
2774
|
+
clstr.shape.addEllipse(x - 4, ely, 1, 1, elfill);
|
2775
|
+
clstr.shape.addEllipse(x, ely, 1, 1, elfill);
|
2776
|
+
clstr.shape.addEllipse(x + 4, ely, 1, 1, elfill);
|
2777
|
+
}
|
2769
2778
|
// Set fill color if slack used by some product contained by this cluster
|
2770
2779
|
if(MODEL.t in clstr.slack_info) {
|
2771
2780
|
const s = clstr.slack_info[MODEL.t];
|