linny-r 1.3.4 → 1.4.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/index.html +6 -12
- package/static/linny-r.css +28 -2
- package/static/scripts/linny-r-ctrl.js +40 -8
- package/static/scripts/linny-r-gui.js +124 -65
- package/static/scripts/linny-r-model.js +474 -143
- package/static/scripts/linny-r-utils.js +134 -12
- package/static/scripts/linny-r-vm.js +806 -390
@@ -916,6 +916,8 @@ class Paper {
|
|
916
916
|
for(let i = 0; i < fc.sub_clusters.length; i++) {
|
917
917
|
fc.sub_clusters[i].clearHiddenIO();
|
918
918
|
}
|
919
|
+
// NOTE: also ensure that notes will update their fields
|
920
|
+
fc.resetNoteFields();
|
919
921
|
// Draw link arrows and constraints first, as all other entities are
|
920
922
|
// slightly transparent so they cannot completely hide these lines
|
921
923
|
for(let i = 0; i < fc.arrows.length; i++) {
|
@@ -953,6 +955,7 @@ class Paper {
|
|
953
955
|
// Links and constraints are drawn separately, so do not draw those
|
954
956
|
// contained in the selection
|
955
957
|
if(!(obj instanceof Link || obj instanceof Constraint)) {
|
958
|
+
if(obj instanceof Note) obj.parsed = false;
|
956
959
|
UI.drawObject(obj, dx, dy);
|
957
960
|
}
|
958
961
|
}
|
@@ -1519,6 +1522,7 @@ class Paper {
|
|
1519
1522
|
} else if(mf[0] > 1) {
|
1520
1523
|
// Multi-flow arrow with flow data computed
|
1521
1524
|
let clr = this.palette.active_process;
|
1525
|
+
// Highlight if related process(es) are at upper bound
|
1522
1526
|
if(mf[3]) ffill.fill = this.palette.at_process_ub_bar;
|
1523
1527
|
s = VM.sig4Dig(mf[1]);
|
1524
1528
|
bb = this.numberSize(s, 10, 700);
|
@@ -2828,6 +2832,21 @@ class GUIController extends Controller {
|
|
2828
2832
|
constructor() {
|
2829
2833
|
super();
|
2830
2834
|
this.console = false;
|
2835
|
+
const
|
2836
|
+
ua = window.navigator.userAgent.toLowerCase(),
|
2837
|
+
browsers = [
|
2838
|
+
['edg', 'Edge'],
|
2839
|
+
['opr', 'Opera'],
|
2840
|
+
['chrome', 'Chrome'],
|
2841
|
+
['firefox', 'Firefox'],
|
2842
|
+
['safari', 'Safari']];
|
2843
|
+
for(let i = 0; i < browsers.length; i++) {
|
2844
|
+
const b = browsers[i];
|
2845
|
+
if(ua.indexOf(b[0]) >= 0) {
|
2846
|
+
this.browser_name = b[1];
|
2847
|
+
break;
|
2848
|
+
}
|
2849
|
+
}
|
2831
2850
|
// Display version number as clickable link (just below the Linny-R logo)
|
2832
2851
|
this.version_number = LINNY_R_VERSION;
|
2833
2852
|
this.version_div = document.getElementById('linny-r-version-number');
|
@@ -3484,8 +3503,7 @@ class GUIController extends Controller {
|
|
3484
3503
|
if(VM.issue_index === -1) {
|
3485
3504
|
VM.issue_index = 0;
|
3486
3505
|
} else if(change) {
|
3487
|
-
VM.issue_index
|
3488
|
-
setTimeout(() => UI.jumpToIssue(), 10);
|
3506
|
+
VM.issue_index = Math.min(VM.issue_index + change, count - 1);
|
3489
3507
|
}
|
3490
3508
|
nr.innerText = VM.issue_index + 1;
|
3491
3509
|
if(VM.issue_index <= 0) {
|
@@ -3493,12 +3511,13 @@ class GUIController extends Controller {
|
|
3493
3511
|
} else {
|
3494
3512
|
prev.classList.remove('disab');
|
3495
3513
|
}
|
3496
|
-
if(
|
3514
|
+
if(VM.issue_index >= count - 1) {
|
3497
3515
|
next.classList.add('disab');
|
3498
3516
|
} else {
|
3499
3517
|
next.classList.remove('disab');
|
3500
3518
|
}
|
3501
3519
|
panel.style.display = 'table-cell';
|
3520
|
+
if(change) UI.jumpToIssue();
|
3502
3521
|
} else {
|
3503
3522
|
panel.style.display = 'none';
|
3504
3523
|
VM.issue_index = -1;
|
@@ -4770,7 +4789,7 @@ class GUIController extends Controller {
|
|
4770
4789
|
|
4771
4790
|
updateExpressionInput(id, name, x) {
|
4772
4791
|
// Updates expression object `x` if input field identified by `id`
|
4773
|
-
// contains a well-formed expression
|
4792
|
+
// contains a well-formed expression. If error, focuses on the field
|
4774
4793
|
// and shows the error while specifying the name of the field.
|
4775
4794
|
const
|
4776
4795
|
inp = document.getElementById(id),
|
@@ -5006,14 +5025,19 @@ class GUIController extends Controller {
|
|
5006
5025
|
an,
|
5007
5026
|
md;
|
5008
5027
|
if(type === 'note') {
|
5028
|
+
|
5009
5029
|
md = this.modals.note;
|
5010
|
-
|
5030
|
+
n = this.dbl_clicked_node;
|
5031
|
+
const
|
5032
|
+
editing = md.element('action').innerHTML === 'Edit',
|
5033
|
+
cx = new Expression(editing ? n : null, '', 'C');
|
5011
5034
|
if(this.updateExpressionInput('note-C', 'note color', cx)) {
|
5012
|
-
if(
|
5035
|
+
if(editing) {
|
5013
5036
|
n = this.dbl_clicked_node;
|
5014
5037
|
this.dbl_clicked_node = null;
|
5015
5038
|
UNDO_STACK.push('modify', n);
|
5016
5039
|
n.contents = md.element('text').value;
|
5040
|
+
n.color.owner = n;
|
5017
5041
|
n.color.text = md.element('C').value;
|
5018
5042
|
n.color.compile();
|
5019
5043
|
n.parsed = false;
|
@@ -5024,10 +5048,10 @@ class GUIController extends Controller {
|
|
5024
5048
|
n.y = this.add_y;
|
5025
5049
|
n.contents = md.element('text').value;
|
5026
5050
|
n.color.text = md.element('C').value;
|
5027
|
-
n.color.compile();
|
5028
5051
|
n.parsed = false;
|
5029
5052
|
n.resize();
|
5030
|
-
|
5053
|
+
n.color.compile();
|
5054
|
+
UNDO_STACK.push('add', n);
|
5031
5055
|
}
|
5032
5056
|
}
|
5033
5057
|
} else if(type === 'cluster') {
|
@@ -5242,7 +5266,8 @@ class GUIController extends Controller {
|
|
5242
5266
|
if(xml) {
|
5243
5267
|
window.localStorage.setItem('Linny-R-selection-XML', xml);
|
5244
5268
|
this.updateButtons();
|
5245
|
-
|
5269
|
+
const bn = (this.browser_name ? ` of ${this.browser_name}` : '');
|
5270
|
+
this.notify('Selection copied to local storage' + bn);
|
5246
5271
|
}
|
5247
5272
|
}
|
5248
5273
|
|
@@ -5261,7 +5286,6 @@ class GUIController extends Controller {
|
|
5261
5286
|
|
5262
5287
|
promptForMapping(mapping) {
|
5263
5288
|
// Prompt user to specify name conflict resolution strategy
|
5264
|
-
console.log('HERE prompt for mapping', mapping);
|
5265
5289
|
const md = this.paste_modal;
|
5266
5290
|
md.mapping = mapping;
|
5267
5291
|
md.element('from-prefix').innerText = mapping.from_prefix || '';
|
@@ -5301,10 +5325,20 @@ class GUIController extends Controller {
|
|
5301
5325
|
for(let i = 0; i < ft.length; i++) {
|
5302
5326
|
const ti = mapping.from_to[ft[i]];
|
5303
5327
|
if(ft[i] === ti) {
|
5304
|
-
|
5305
|
-
|
5306
|
-
|
5307
|
-
|
5328
|
+
const elig = MODEL.eligibleFromToNodes(mapping.from_to_type[ti]);
|
5329
|
+
sl.push('<div class="paste-option"><span>', ft[i], '</span> ');
|
5330
|
+
if(elig.length) {
|
5331
|
+
sl.push('<div class="paste-select"><select id="paste-ft-', i,
|
5332
|
+
'" style="font-size: 12px">');
|
5333
|
+
for(let j = 0; j < elig.length; j++) {
|
5334
|
+
const dn = elig[j].displayName;
|
5335
|
+
sl.push('<option value="', dn, '">', dn, '</option>');
|
5336
|
+
}
|
5337
|
+
sl.push('</select></div>');
|
5338
|
+
} else {
|
5339
|
+
sl.push('<span><em>(no eligible node)</em></span');
|
5340
|
+
}
|
5341
|
+
sl.push('</div>');
|
5308
5342
|
}
|
5309
5343
|
}
|
5310
5344
|
}
|
@@ -5330,6 +5364,12 @@ class GUIController extends Controller {
|
|
5330
5364
|
const cn = md.element('selc-' + i).value.trim();
|
5331
5365
|
if(this.validName(cn)) mapping.top_clusters[tc[i]] = cn;
|
5332
5366
|
}
|
5367
|
+
for(let i = 0; i < ft.length; i++) if(mapping.from_to[ft[i]] === ft[i]) {
|
5368
|
+
const
|
5369
|
+
ftn = md.element('ft-' + i).value,
|
5370
|
+
fto = MODEL.objectByName(ftn);
|
5371
|
+
if(fto) mapping.from_to[ft[i]] = ftn;
|
5372
|
+
}
|
5333
5373
|
this.pasteSelection(mapping);
|
5334
5374
|
}
|
5335
5375
|
|
@@ -5338,7 +5378,6 @@ class GUIController extends Controller {
|
|
5338
5378
|
// see whether PASTE would result in name conflicts, and if so,
|
5339
5379
|
// open the name conflict resolution window
|
5340
5380
|
let xml = window.localStorage.getItem('Linny-R-selection-XML');
|
5341
|
-
console.log('HERE xml', xml);
|
5342
5381
|
try {
|
5343
5382
|
xml = parseXML(xml);
|
5344
5383
|
} catch(e) {
|
@@ -5347,9 +5386,6 @@ console.log('HERE xml', xml);
|
|
5347
5386
|
return;
|
5348
5387
|
}
|
5349
5388
|
|
5350
|
-
// For now, while still implementing:
|
5351
|
-
this.notify('Paste not fully implemented yet -- WORK IN PROGRESS!');
|
5352
|
-
|
5353
5389
|
const
|
5354
5390
|
entities_node = childNodeByTag(xml, 'entities'),
|
5355
5391
|
from_tos_node = childNodeByTag(xml, 'from-tos'),
|
@@ -5454,6 +5490,12 @@ console.log('HERE xml', xml);
|
|
5454
5490
|
if(mapping.increment && nr) {
|
5455
5491
|
return n.replace(new RegExp(nr + '$'), parseInt(nr) + 1);
|
5456
5492
|
}
|
5493
|
+
if(mapping.top_clusters && mapping.top_clusters[n]) {
|
5494
|
+
return mapping.top_clusters[n];
|
5495
|
+
}
|
5496
|
+
if(mapping.from_to && mapping.from_to[n]) {
|
5497
|
+
return mapping.from_to[n];
|
5498
|
+
}
|
5457
5499
|
// No mapping => return original name
|
5458
5500
|
return n;
|
5459
5501
|
}
|
@@ -5572,36 +5614,41 @@ console.log('HERE xml', xml);
|
|
5572
5614
|
// Also prompt if FROM and/or TO nodes are not selected, and map to
|
5573
5615
|
// existing entities
|
5574
5616
|
if(from_tos_node.childNodes.length && !mapping.from_to) {
|
5575
|
-
const
|
5617
|
+
const
|
5618
|
+
ft_map = {},
|
5619
|
+
ft_type = {};
|
5576
5620
|
for(let i = 0; i < from_tos_node.childNodes.length; i++) {
|
5577
5621
|
const
|
5578
5622
|
c = from_tos_node.childNodes[i],
|
5579
5623
|
fn = fullName(c),
|
5580
5624
|
mn = mappedName(fn);
|
5581
|
-
if(MODEL.objectByName(mn))
|
5625
|
+
if(MODEL.objectByName(mn)) {
|
5626
|
+
ft_map[fn] = mn;
|
5627
|
+
ft_type[fn] = (nodeParameterValue(c, 'is-data') === '1' ?
|
5628
|
+
'Data' : nodeParameterValue(c, 'type'));
|
5629
|
+
}
|
5582
5630
|
}
|
5583
5631
|
// Prompt only for FROM/TO nodes that map to existing nodes
|
5584
5632
|
if(Object.keys(ft_map).length) {
|
5585
5633
|
mapping.from_to = ft_map;
|
5634
|
+
mapping.from_to_type = ft_type;
|
5586
5635
|
this.promptForMapping(mapping);
|
5587
5636
|
return;
|
5588
5637
|
}
|
5589
5638
|
}
|
5590
5639
|
|
5591
|
-
// Only check for selected entities
|
5640
|
+
// Only check for selected entities; from-to's and extra's should be
|
5592
5641
|
// used if they exist, or should be created when copying to a different
|
5593
5642
|
// model
|
5594
5643
|
name_map.length = 0;
|
5595
5644
|
nameConflicts(entities_node);
|
5596
|
-
nameConflicts(from_tos_node);
|
5597
5645
|
if(name_conflicts.length) {
|
5598
|
-
UI.
|
5599
|
-
console.log('HERE name conflicts', name_conflicts);
|
5646
|
+
UI.warn(pluralS(name_conflicts.length, 'name conflict'));
|
5647
|
+
console.log('HERE name conflicts', name_conflicts, mapping);
|
5600
5648
|
return;
|
5601
5649
|
}
|
5602
5650
|
|
5603
5651
|
// No conflicts => add all
|
5604
|
-
console.log('HERE name map', name_map);
|
5605
5652
|
for(let i = 0; i < extras_node.childNodes.length; i++) {
|
5606
5653
|
addEntityFromNode(extras_node.childNodes[i]);
|
5607
5654
|
}
|
@@ -5627,6 +5674,7 @@ console.log('HERE name map', name_map);
|
|
5627
5674
|
// products are displayed as arrows instead of block arrows
|
5628
5675
|
fc.clearAllProcesses();
|
5629
5676
|
UI.drawDiagram(MODEL);
|
5677
|
+
this.paste_modal.hide();
|
5630
5678
|
}
|
5631
5679
|
|
5632
5680
|
//
|
@@ -5766,6 +5814,8 @@ console.log('HERE name map', name_map);
|
|
5766
5814
|
const md = this.modals.note;
|
5767
5815
|
if(n) {
|
5768
5816
|
md.element('action').innerHTML = 'Edit';
|
5817
|
+
const nr = n.number;
|
5818
|
+
md.element('number').innerHTML = (nr ? '#' + nr : '');
|
5769
5819
|
md.element('text').value = n.contents;
|
5770
5820
|
md.element('C').value = n.color.text;
|
5771
5821
|
} else {
|
@@ -5787,6 +5837,10 @@ console.log('HERE name map', name_map);
|
|
5787
5837
|
} else {
|
5788
5838
|
md.element('actor').value = '';
|
5789
5839
|
}
|
5840
|
+
const sim = p.similarNumberedEntities;
|
5841
|
+
if(sim.length) {
|
5842
|
+
console.log('HERE!', sim);
|
5843
|
+
}
|
5790
5844
|
md.element('LB').value = p.lower_bound.text;
|
5791
5845
|
md.element('UB').value = p.upper_bound.text;
|
5792
5846
|
this.setEqualBounds('process', p.equal_bounds);
|
@@ -6017,7 +6071,7 @@ console.log('HERE name map', name_map);
|
|
6017
6071
|
const
|
6018
6072
|
from_process = l.from_node instanceof Process,
|
6019
6073
|
to_process = l.to_node instanceof Process,
|
6020
|
-
md = this.modals.link;
|
6074
|
+
md = this.modals.link;
|
6021
6075
|
md.show();
|
6022
6076
|
md.element('from-name').innerHTML = l.from_node.displayName;
|
6023
6077
|
md.element('to-name').innerHTML = l.to_node.displayName;
|
@@ -6064,6 +6118,7 @@ console.log('HERE name map', name_map);
|
|
6064
6118
|
md.element('output-soc').style.display = 'none';
|
6065
6119
|
}
|
6066
6120
|
}
|
6121
|
+
this.edited_object = l;
|
6067
6122
|
if(alt) md.element(attr + '-x').dispatchEvent(new Event('click'));
|
6068
6123
|
}
|
6069
6124
|
|
@@ -6108,7 +6163,7 @@ console.log('HERE name map', name_map);
|
|
6108
6163
|
// @@TO DO: prepare for undo
|
6109
6164
|
const
|
6110
6165
|
md = this.modals.link,
|
6111
|
-
l = this.
|
6166
|
+
l = this.edited_object;
|
6112
6167
|
// Check whether all input fields are valid
|
6113
6168
|
if(!this.updateExpressionInput('link-R', 'rate', l.relative_rate)) {
|
6114
6169
|
return false;
|
@@ -6415,7 +6470,8 @@ class GUIMonitor {
|
|
6415
6470
|
if(this.call_stack_shown) return;
|
6416
6471
|
const
|
6417
6472
|
csl = VM.call_stack.length,
|
6418
|
-
|
6473
|
+
top = VM.call_stack[csl - 1],
|
6474
|
+
err = top.vector[t],
|
6419
6475
|
// Make separate lists of variable names and their expressions
|
6420
6476
|
vlist = [],
|
6421
6477
|
xlist = [];
|
@@ -6480,6 +6536,9 @@ class GUIMonitor {
|
|
6480
6536
|
tbl.push('<div style="color: gray; margin-top: 8px; font-size: 10px">',
|
6481
6537
|
VM.out_of_bounds_msg.replace(VM.out_of_bounds_array, anc), '</div>');
|
6482
6538
|
}
|
6539
|
+
// Dump the code for the last expression to the console
|
6540
|
+
console.log('Code for', top.text, top.code);
|
6541
|
+
// Show the call stack dialog
|
6483
6542
|
document.getElementById('call-stack-table').innerHTML = tbl.join('');
|
6484
6543
|
document.getElementById('call-stack-modal').style.display = 'block';
|
6485
6544
|
this.call_stack_shown = true;
|
@@ -7244,10 +7303,12 @@ NOTE: Grouping groups results in a single group, e.g., (1;2);(3;4;5) evaluates a
|
|
7244
7303
|
if(this.edited_input_id) {
|
7245
7304
|
document.getElementById(this.edited_input_id).value = xp.expr;
|
7246
7305
|
// NOTE: entity properties must be exogenous parameters
|
7247
|
-
|
7248
|
-
|
7249
|
-
|
7250
|
-
|
7306
|
+
const eo = UI.edited_object;
|
7307
|
+
if(eo && xp.is_level_based &&
|
7308
|
+
!(eo instanceof Dataset || eo instanceof Note)) {
|
7309
|
+
UI.warn(['Expression for', this.property.innerHTML,
|
7310
|
+
'of<strong>', eo.displayName,
|
7311
|
+
'</strong>contains a solution-dependent variable'].join(' '));
|
7251
7312
|
}
|
7252
7313
|
this.edited_input_id = '';
|
7253
7314
|
} else if(DATASET_MANAGER.edited_expression) {
|
@@ -8743,6 +8804,7 @@ class ConstraintEditor {
|
|
8743
8804
|
// NOTE: this could be expanded to apply to the selected BL only
|
8744
8805
|
UI.setBox('ce-no-slack', this.constraint.no_slack);
|
8745
8806
|
// NOTE: share of cost can only be transferred between two processes
|
8807
|
+
// @@TO DO: CHECK WHETHER THIS LIMITATION IS VALID -- for now, allow both
|
8746
8808
|
if(true||this.from_node instanceof Process && this.from_node instanceof Process) {
|
8747
8809
|
this.soc_direct.value = this.constraint.soc_direction;
|
8748
8810
|
// NOTE: share of cost is input as a percentage
|
@@ -9894,6 +9956,7 @@ class GUIDatasetManager extends DatasetManager {
|
|
9894
9956
|
ps = pid.split(UI.PREFIXER),
|
9895
9957
|
pps = prev_id.split(UI.PREFIXER),
|
9896
9958
|
pn = pref_names[pid],
|
9959
|
+
pns = pn.join(UI.PREFIXER),
|
9897
9960
|
lpl = [];
|
9898
9961
|
let lindent = 0;
|
9899
9962
|
// Ignore identical leading prefixes
|
@@ -9908,8 +9971,9 @@ class GUIDatasetManager extends DatasetManager {
|
|
9908
9971
|
lpl.push(ps.shift());
|
9909
9972
|
lindent++;
|
9910
9973
|
const lpid = lpl.join(UI.PREFIXER);
|
9911
|
-
dl.push(['<tr data-prefix="', lpid,
|
9912
|
-
'"
|
9974
|
+
dl.push(['<tr data-prefix="', lpid,
|
9975
|
+
'" data-prefix-name="', pns, '" class="dataset"',
|
9976
|
+
'onclick="DATASET_MANAGER.selectPrefixRow(event);"><td>',
|
9913
9977
|
// NOTE: data-prefix="x" signals that this is an extra row
|
9914
9978
|
(lindent > 0 ?
|
9915
9979
|
'<div data-prefix="x" style="width: ' + lindent * indent_px +
|
@@ -10022,6 +10086,7 @@ class GUIDatasetManager extends DatasetManager {
|
|
10022
10086
|
for(let i = 0; i < msl.length; i++) {
|
10023
10087
|
const
|
10024
10088
|
m = sd.modifiers[UI.nameToID(msl[i])],
|
10089
|
+
wild = m.hasWildcards,
|
10025
10090
|
defsel = (m.selector === sd.default_selector),
|
10026
10091
|
clk = '" onclick="DATASET_MANAGER.selectModifier(event, \'' +
|
10027
10092
|
m.selector + '\'';
|
@@ -10029,13 +10094,14 @@ class GUIDatasetManager extends DatasetManager {
|
|
10029
10094
|
ml.push(['<tr id="dsmtr', i, '" class="dataset-modif',
|
10030
10095
|
(m === sm ? ' sel-set' : ''),
|
10031
10096
|
'"><td class="dataset-selector',
|
10032
|
-
(
|
10097
|
+
(wild ? ' wildcard' : ''),
|
10033
10098
|
'" title="Shift-click to ', (defsel ? 'clear' : 'set as'),
|
10034
10099
|
' default modifier',
|
10035
10100
|
clk, ', false);">',
|
10036
10101
|
(defsel ? '<img src="images/solve.png" style="height: 14px;' +
|
10037
10102
|
' width: 14px; margin: 0 1px -3px -1px;">' : ''),
|
10038
|
-
m.selector,
|
10103
|
+
(wild ? wildcardFormat(m.selector, true) : m.selector),
|
10104
|
+
'</td><td class="dataset-expression',
|
10039
10105
|
clk, ');">', m.expression.text, '</td></tr>'].join(''));
|
10040
10106
|
}
|
10041
10107
|
this.modifier_table.innerHTML = ml.join('');
|
@@ -10138,24 +10204,15 @@ class GUIDatasetManager extends DatasetManager {
|
|
10138
10204
|
}
|
10139
10205
|
} else {
|
10140
10206
|
this.selected_modifier = null;
|
10141
|
-
}
|
10207
|
+
}
|
10142
10208
|
this.updateModifiers();
|
10143
10209
|
}
|
10144
10210
|
|
10145
10211
|
get selectedPrefix() {
|
10146
10212
|
// Returns the selected prefix (with its trailing colon-space)
|
10147
|
-
|
10148
|
-
|
10149
|
-
|
10150
|
-
const td = tr.firstElementChild;
|
10151
|
-
if(td && td.firstElementChild.dataset.prefix === 'x') {
|
10152
|
-
prefix = td.lastChild.textContent + UI.PREFIXER + prefix;
|
10153
|
-
tr = tr.previousSibling;
|
10154
|
-
} else {
|
10155
|
-
tr = null;
|
10156
|
-
}
|
10157
|
-
}
|
10158
|
-
return prefix;
|
10213
|
+
const tr = this.selected_prefix_row;
|
10214
|
+
if(tr && tr.dataset.prefixName) return tr.dataset.prefixName + UI.PREFIXER;
|
10215
|
+
return '';
|
10159
10216
|
}
|
10160
10217
|
|
10161
10218
|
promptForDataset(shift=false) {
|
@@ -10164,9 +10221,7 @@ class GUIDatasetManager extends DatasetManager {
|
|
10164
10221
|
let prefix = '';
|
10165
10222
|
if(shift) {
|
10166
10223
|
if(this.selected_dataset) {
|
10167
|
-
|
10168
|
-
p[p.length - 1] = '';
|
10169
|
-
prefix = p.join(UI.PREFIXER);
|
10224
|
+
prefix = UI.completePrefix(this.selected_dataset.name);
|
10170
10225
|
} else if(this.selected_prefix) {
|
10171
10226
|
prefix = this.selectedPrefix;
|
10172
10227
|
}
|
@@ -10181,6 +10236,7 @@ class GUIDatasetManager extends DatasetManager {
|
|
10181
10236
|
if(d) {
|
10182
10237
|
this.new_modal.hide();
|
10183
10238
|
this.selected_dataset = d;
|
10239
|
+
this.focal_table = this.dataset_table;
|
10184
10240
|
this.updateDialog();
|
10185
10241
|
}
|
10186
10242
|
}
|
@@ -10220,12 +10276,14 @@ class GUIDatasetManager extends DatasetManager {
|
|
10220
10276
|
let e = this.rename_modal.element('name'),
|
10221
10277
|
prefix = e.value.trim();
|
10222
10278
|
e.focus();
|
10279
|
+
// Trim trailing colon if user entered it
|
10223
10280
|
while(prefix.endsWith(':')) prefix = prefix.slice(0, -1);
|
10224
10281
|
// NOTE: prefix may be empty string, but otherwise should be a valid name
|
10225
10282
|
if(prefix && !UI.validName(prefix)) {
|
10226
10283
|
UI.warn('Invalid prefix');
|
10227
10284
|
return;
|
10228
10285
|
}
|
10286
|
+
// Now add the colon-plus-space prefix separator
|
10229
10287
|
prefix += UI.PREFIXER;
|
10230
10288
|
const
|
10231
10289
|
oldpref = this.selectedPrefix,
|
@@ -10245,7 +10303,7 @@ class GUIDatasetManager extends DatasetManager {
|
|
10245
10303
|
if(MODEL.datasets[nk]) nc++;
|
10246
10304
|
}
|
10247
10305
|
if(nc) {
|
10248
|
-
UI.warn('Renaming ' + pluralS(dsl.length, 'dataset') +
|
10306
|
+
UI.warn('Renaming ' + pluralS(dsl.length, 'dataset').toLowerCase() +
|
10249
10307
|
' would cause ' + pluralS(nc, 'name conflict'));
|
10250
10308
|
return;
|
10251
10309
|
}
|
@@ -10258,7 +10316,7 @@ class GUIDatasetManager extends DatasetManager {
|
|
10258
10316
|
const d = MODEL.datasets[dsl[i]];
|
10259
10317
|
d.rename(d.displayName.replace(oldpref, prefix), false);
|
10260
10318
|
}
|
10261
|
-
let msg = 'Renamed ' + pluralS(dsl.length, 'dataset');
|
10319
|
+
let msg = 'Renamed ' + pluralS(dsl.length, 'dataset').toLowerCase();
|
10262
10320
|
if(MODEL.variable_count) msg += ', and updated ' +
|
10263
10321
|
pluralS(MODEL.variable_count, 'variable') + ' in ' +
|
10264
10322
|
pluralS(MODEL.expression_count, 'expression');
|
@@ -10379,14 +10437,14 @@ class GUIDatasetManager extends DatasetManager {
|
|
10379
10437
|
renameModifier() {
|
10380
10438
|
if(!this.selected_modifier) return;
|
10381
10439
|
const
|
10382
|
-
|
10440
|
+
wild = this.selected_modifier.hasWildcards,
|
10383
10441
|
sel = this.rename_selector_modal.element('name').value,
|
10384
10442
|
// NOTE: normal dataset selector, so remove all invalid characters
|
10385
|
-
clean_sel = sel.replace(/[^a-zA-z0-9
|
10443
|
+
clean_sel = sel.replace(/[^a-zA-z0-9\%\+\-\?\*]/g, ''),
|
10386
10444
|
// Keep track of old name
|
10387
10445
|
oldm = this.selected_modifier,
|
10388
10446
|
// NOTE: addModifier returns existing one if selector not changed
|
10389
|
-
m = this.selected_dataset.addModifier(
|
10447
|
+
m = this.selected_dataset.addModifier(clean_sel);
|
10390
10448
|
// NULL can result when new name is invalid
|
10391
10449
|
if(!m) return;
|
10392
10450
|
// If selected modifier was the dataset default selector, update it
|
@@ -10398,11 +10456,12 @@ class GUIDatasetManager extends DatasetManager {
|
|
10398
10456
|
if(m === oldm) {
|
10399
10457
|
m.selector = clean_sel;
|
10400
10458
|
this.updateDialog();
|
10459
|
+
this.rename_selector_modal.hide();
|
10401
10460
|
return;
|
10402
10461
|
}
|
10403
10462
|
// Rest is needed only when a new modifier has been added
|
10404
10463
|
m.expression = oldm.expression;
|
10405
|
-
if(
|
10464
|
+
if(wild) {
|
10406
10465
|
// Wildcard selector means: recompile the modifier expression
|
10407
10466
|
m.expression.attribute = m.selector;
|
10408
10467
|
m.expression.compile();
|
@@ -10410,11 +10469,10 @@ class GUIDatasetManager extends DatasetManager {
|
|
10410
10469
|
this.deleteModifier();
|
10411
10470
|
this.selected_modifier = m;
|
10412
10471
|
// Update all chartvariables referencing this dataset + old selector
|
10413
|
-
const vl = MODEL.
|
10472
|
+
const vl = MODEL.datasetVariables;
|
10414
10473
|
let cv_cnt = 0;
|
10415
10474
|
for(let i = 0; i < vl.length; i++) {
|
10416
|
-
if(v.object === this.selected_dataset &&
|
10417
|
-
v.attribute === oldm.selector) {
|
10475
|
+
if(v.object === this.selected_dataset && v.attribute === oldm.selector) {
|
10418
10476
|
v.attribute = m.selector;
|
10419
10477
|
cv_cnt++;
|
10420
10478
|
}
|
@@ -10818,6 +10876,7 @@ class EquationManager {
|
|
10818
10876
|
for(let i = 0; i < msl.length; i++) {
|
10819
10877
|
const
|
10820
10878
|
m = ed.modifiers[UI.nameToID(msl[i])],
|
10879
|
+
wild = (m.selector.indexOf('??') >= 0),
|
10821
10880
|
clk = '" onclick="EQUATION_MANAGER.selectModifier(event, \'' +
|
10822
10881
|
m.selector + '\'';
|
10823
10882
|
if(m === sm) smid += i;
|
@@ -10825,8 +10884,9 @@ class EquationManager {
|
|
10825
10884
|
(m === sm ? ' sel-set' : ''),
|
10826
10885
|
'"><td class="equation-selector',
|
10827
10886
|
(m.expression.isStatic ? '' : ' it'),
|
10828
|
-
clk, ', false);">',
|
10829
|
-
m.selector
|
10887
|
+
(wild ? ' wildcard' : ''), clk, ', false);">',
|
10888
|
+
(wild ? wildcardFormat(m.selector) : m.selector),
|
10889
|
+
'</td><td class="equation-expression',
|
10830
10890
|
clk, ');">', m.expression.text, '</td></tr>'].join(''));
|
10831
10891
|
}
|
10832
10892
|
this.table.innerHTML = ml.join('');
|
@@ -10956,8 +11016,7 @@ class EquationManager {
|
|
10956
11016
|
const c = MODEL.charts[i];
|
10957
11017
|
for(let j = 0; j < c.variables.length; j++) {
|
10958
11018
|
const v = c.variables[j];
|
10959
|
-
if(v.object === MODEL.equations_dataset &&
|
10960
|
-
v.attribute === olds) {
|
11019
|
+
if(v.object === MODEL.equations_dataset && v.attribute === olds) {
|
10961
11020
|
v.attribute = m.selector;
|
10962
11021
|
cv_cnt++;
|
10963
11022
|
}
|