linny-r 1.5.8 → 1.6.1
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 +7 -3
- package/static/linny-r.css +19 -0
- package/static/scripts/linny-r-gui-actor-manager.js +3 -3
- package/static/scripts/linny-r-gui-chart-manager.js +10 -9
- package/static/scripts/linny-r-gui-controller.js +49 -36
- package/static/scripts/linny-r-gui-dataset-manager.js +17 -49
- package/static/scripts/linny-r-gui-documentation-manager.js +71 -47
- package/static/scripts/linny-r-gui-equation-manager.js +26 -10
- package/static/scripts/linny-r-gui-experiment-manager.js +41 -23
- package/static/scripts/linny-r-gui-expression-editor.js +26 -21
- package/static/scripts/linny-r-gui-finder.js +1 -0
- package/static/scripts/linny-r-gui-monitor.js +4 -4
- package/static/scripts/linny-r-milp.js +18 -15
- package/static/scripts/linny-r-model.js +354 -137
- package/static/scripts/linny-r-utils.js +68 -18
- package/static/scripts/linny-r-vm.js +593 -300
package/package.json
CHANGED
package/static/index.html
CHANGED
@@ -304,8 +304,12 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
304
304
|
</svg>
|
305
305
|
</div>
|
306
306
|
</main>
|
307
|
-
<!--
|
308
|
-
in
|
307
|
+
<!-- The footer displays the status bar with X/Y coordinates, zoom
|
308
|
+
in/out buttons, buttons for moving forward/backward in time, and
|
309
|
+
the status line that displays info, notifications, warnings, and
|
310
|
+
error messages. The set-up progress bar shows a growing colored
|
311
|
+
needle indicating progress of making Simplex tableau and input
|
312
|
+
file for the solver.
|
309
313
|
-->
|
310
314
|
<footer>
|
311
315
|
<div id="set-up-progress">
|
@@ -2238,7 +2242,7 @@ NOTE: * and ? will be interpreted as wildcards"
|
|
2238
2242
|
</div>
|
2239
2243
|
<div id="viewer-selectors">
|
2240
2244
|
<label>Variable:</label>
|
2241
|
-
<select id="viewer-variable"
|
2245
|
+
<select id="viewer-variable">
|
2242
2246
|
</select>
|
2243
2247
|
<label>Statistic:</label>
|
2244
2248
|
<select id="viewer-statistic">
|
package/static/linny-r.css
CHANGED
@@ -2366,6 +2366,24 @@ td.equation-selector {
|
|
2366
2366
|
text-overflow: ellipsis;
|
2367
2367
|
}
|
2368
2368
|
|
2369
|
+
td.method {
|
2370
|
+
color: #1080e0;
|
2371
|
+
font-weight: bold;
|
2372
|
+
}
|
2373
|
+
|
2374
|
+
td.no-object {
|
2375
|
+
color: #80a0c0;
|
2376
|
+
font-weight: normal;
|
2377
|
+
}
|
2378
|
+
|
2379
|
+
td.compile-issue {
|
2380
|
+
color: #e00000;
|
2381
|
+
}
|
2382
|
+
|
2383
|
+
td.compute-issue {
|
2384
|
+
color: #f08000;
|
2385
|
+
}
|
2386
|
+
|
2369
2387
|
td.wildcard {
|
2370
2388
|
color: #400080;
|
2371
2389
|
}
|
@@ -3558,6 +3576,7 @@ td.sa-not-run {
|
|
3558
3576
|
#viewer-variable {
|
3559
3577
|
font-size: 10px;
|
3560
3578
|
max-width: calc(100% - 160px);
|
3579
|
+
margin-right: 10px;
|
3561
3580
|
}
|
3562
3581
|
|
3563
3582
|
#sensitivity-statistic,
|
@@ -304,15 +304,15 @@ class ActorManager {
|
|
304
304
|
if(a.displayName != ali[1]) a.rename(ali[1]);
|
305
305
|
// Set its round flags
|
306
306
|
a.round_flags = ali[2];
|
307
|
-
// Double-check: parse expression if weight has been changed
|
307
|
+
// Double-check: parse expression if weight has been changed.
|
308
308
|
if(a.weight.text != ali[3]) {
|
309
|
-
xp.expr = ali[3];
|
309
|
+
xp.expr = monoSpacedVariables(ali[3]);
|
310
310
|
xp.compile();
|
311
311
|
if(xp.error) {
|
312
312
|
UI.warningInvalidWeightExpression(a, xp.error);
|
313
313
|
ok = false;
|
314
314
|
} else {
|
315
|
-
a.weight.update(
|
315
|
+
a.weight.update(ali[3]);
|
316
316
|
}
|
317
317
|
}
|
318
318
|
// Update import/export status
|
@@ -574,7 +574,7 @@ class GUIChartManager extends ChartManager {
|
|
574
574
|
}
|
575
575
|
|
576
576
|
cloneChart() {
|
577
|
-
//
|
577
|
+
// Create a new chart that is identical to the current one.
|
578
578
|
if(this.chart_index >= 0) {
|
579
579
|
let c = MODEL.charts[this.chart_index],
|
580
580
|
nt = c.title + '-copy';
|
@@ -582,7 +582,7 @@ class GUIChartManager extends ChartManager {
|
|
582
582
|
nt += '-copy';
|
583
583
|
}
|
584
584
|
const nc = MODEL.addChart(nt);
|
585
|
-
// Copy properties of c to nc
|
585
|
+
// Copy properties of `c` to `nc`;
|
586
586
|
nc.histogram = c.histogram;
|
587
587
|
nc.bins = c.bins;
|
588
588
|
nc.show_title = c.show_title;
|
@@ -609,7 +609,7 @@ class GUIChartManager extends ChartManager {
|
|
609
609
|
}
|
610
610
|
|
611
611
|
toggleRunStat() {
|
612
|
-
//
|
612
|
+
// Toggle the Boolean property that signals charts that they must
|
613
613
|
// plot the selected statistic for the selected runs if they are
|
614
614
|
// part of the selected experiment chart set.
|
615
615
|
this.setRunsStat(!this.runs_stat);
|
@@ -618,16 +618,17 @@ class GUIChartManager extends ChartManager {
|
|
618
618
|
}
|
619
619
|
|
620
620
|
deleteChart() {
|
621
|
-
//
|
621
|
+
// Delete the shown chart (if any).
|
622
622
|
if(this.chart_index >= 0) {
|
623
|
-
// NOTE:
|
623
|
+
// NOTE: Do not delete the default chart, but clear it instead.
|
624
624
|
if(MODEL.charts[this.chart_index].title === this.new_chart_title) {
|
625
625
|
MODEL.charts[this.chart_index].reset();
|
626
626
|
} else {
|
627
627
|
MODEL.charts.splice(this.chart_index, 1);
|
628
628
|
this.chart_index = -1;
|
629
629
|
}
|
630
|
-
// Also update the experiment viewer
|
630
|
+
// Also update the experiment viewer, because this chart may be
|
631
|
+
// one of the output charts of the selected experiment.
|
631
632
|
UI.updateControllerDialogs('CFX');
|
632
633
|
}
|
633
634
|
}
|
@@ -689,8 +690,8 @@ class GUIChartManager extends ChartManager {
|
|
689
690
|
}
|
690
691
|
|
691
692
|
addVariable(eq='') {
|
692
|
-
//
|
693
|
-
// NOTE:
|
693
|
+
// Add the variable specified by the add-variable-dialog to the chart.
|
694
|
+
// NOTE: When defined, `eq` is the selector of the equation to be added.
|
694
695
|
if(this.chart_index >= 0) {
|
695
696
|
let o = '',
|
696
697
|
a = eq;
|
@@ -698,7 +699,7 @@ class GUIChartManager extends ChartManager {
|
|
698
699
|
o = this.add_variable_modal.selectedOption('name').text;
|
699
700
|
a = this.add_variable_modal.selectedOption('attr').text;
|
700
701
|
}
|
701
|
-
// NOTE:
|
702
|
+
// NOTE: When equation is added, object specifier is empty string.
|
702
703
|
if(!o && a) o = UI.EQUATIONS_DATASET_NAME;
|
703
704
|
this.variable_index = MODEL.charts[this.chart_index].addVariable(o, a);
|
704
705
|
if(this.variable_index >= 0) {
|
@@ -2608,10 +2608,11 @@ class GUIController extends Controller {
|
|
2608
2608
|
document.body.className = '';
|
2609
2609
|
}
|
2610
2610
|
|
2611
|
-
setProgressNeedle(fraction) {
|
2611
|
+
setProgressNeedle(fraction, color='#500080') {
|
2612
2612
|
// Shows a thin purple line just above the status line to indicate progress
|
2613
2613
|
const el = document.getElementById('set-up-progress-bar');
|
2614
2614
|
el.style.width = Math.round(Math.max(0, Math.min(1, fraction)) * 100) + '%';
|
2615
|
+
el.style.backgroundColor = color;
|
2615
2616
|
}
|
2616
2617
|
|
2617
2618
|
hideStayOnTopDialogs() {
|
@@ -2934,8 +2935,8 @@ class GUIController extends Controller {
|
|
2934
2935
|
}
|
2935
2936
|
const err = MODEL.cloneSelection(prefix, actor_name, renumber);
|
2936
2937
|
if(err) {
|
2937
|
-
// Something went wrong, so do not hide the modal, but focus on
|
2938
|
-
// DOM element returned by the model's cloning method
|
2938
|
+
// Something went wrong, so do not hide the modal, but focus on
|
2939
|
+
// the DOM element returned by the model's cloning method.
|
2939
2940
|
const el = md.element(err);
|
2940
2941
|
if(el) {
|
2941
2942
|
el.focus();
|
@@ -2955,7 +2956,7 @@ class GUIController extends Controller {
|
|
2955
2956
|
}
|
2956
2957
|
|
2957
2958
|
copySelection() {
|
2958
|
-
// Save selection as XML in local storage of the browser
|
2959
|
+
// Save selection as XML in local storage of the browser.
|
2959
2960
|
const xml = MODEL.selectionAsXML;
|
2960
2961
|
if(xml) {
|
2961
2962
|
window.localStorage.setItem('Linny-R-selection-XML', xml);
|
@@ -2966,20 +2967,22 @@ class GUIController extends Controller {
|
|
2966
2967
|
}
|
2967
2968
|
|
2968
2969
|
get canPaste() {
|
2970
|
+
// Return TRUE if the browser has a recent selection-as-XML object
|
2971
|
+
// in its local storage.
|
2969
2972
|
const xml = window.localStorage.getItem('Linny-R-selection-XML');
|
2970
2973
|
if(xml) {
|
2971
2974
|
const timestamp = xml.match(/<copy timestamp="(\d+)"/);
|
2972
2975
|
if(timestamp) {
|
2973
2976
|
if(Date.now() - parseInt(timestamp[1]) < 8*3600000) return true;
|
2974
2977
|
}
|
2975
|
-
// Remove XML from local storage if older than 8 hours
|
2978
|
+
// Remove XML from local storage if older than 8 hours.
|
2976
2979
|
window.localStorage.removeItem('Linny-R-selection-XML');
|
2977
2980
|
}
|
2978
2981
|
return false;
|
2979
2982
|
}
|
2980
2983
|
|
2981
2984
|
promptForMapping(mapping) {
|
2982
|
-
// Prompt user to specify name conflict resolution strategy
|
2985
|
+
// Prompt user to specify name conflict resolution strategy.
|
2983
2986
|
const md = this.paste_modal;
|
2984
2987
|
md.mapping = mapping;
|
2985
2988
|
md.element('from-prefix').innerText = mapping.from_prefix || '';
|
@@ -2999,7 +3002,8 @@ class GUIController extends Controller {
|
|
2999
3002
|
if(tc.length) {
|
3000
3003
|
sl.push('<div style="font-weight: bold; margin:4px 2px 2px 2px">',
|
3001
3004
|
'Names for top-level clusters:</div>');
|
3002
|
-
|
3005
|
+
const sll = sl.length;
|
3006
|
+
// Add text inputs for selected cluster nodes.
|
3003
3007
|
for(let i = 0; i < tc.length; i++) {
|
3004
3008
|
const
|
3005
3009
|
ti = mapping.top_clusters[tc[i]],
|
@@ -3011,11 +3015,14 @@ class GUIController extends Controller {
|
|
3011
3015
|
'" type="text" style="', state, 'font-size: 12px" value="',
|
3012
3016
|
ti, '"></div></div>');
|
3013
3017
|
}
|
3018
|
+
// Remove header when no items were added.
|
3019
|
+
if(sl.length === sll) sl.pop();
|
3014
3020
|
}
|
3015
3021
|
if(ft.length) {
|
3016
3022
|
sl.push('<div style="font-weight: bold; margin:4px 2px 2px 2px">',
|
3017
3023
|
'Mapping of nodes to link from/to:</div>');
|
3018
|
-
|
3024
|
+
const sll = sl.length;
|
3025
|
+
// Add selectors for unresolved FROM/TO nodes.
|
3019
3026
|
for(let i = 0; i < ft.length; i++) {
|
3020
3027
|
const ti = mapping.from_to[ft[i]];
|
3021
3028
|
if(ft[i] === ti) {
|
@@ -3035,15 +3042,17 @@ class GUIController extends Controller {
|
|
3035
3042
|
sl.push('</div>');
|
3036
3043
|
}
|
3037
3044
|
}
|
3045
|
+
// Remove header when no items were added.
|
3046
|
+
if(sl.length === sll) sl.pop();
|
3038
3047
|
}
|
3039
3048
|
md.element('scroll-area').innerHTML = sl.join('');
|
3040
|
-
// Open dialog, which will call pasteSelection(...) on OK
|
3049
|
+
// Open dialog, which will call pasteSelection(...) on OK.
|
3041
3050
|
this.paste_modal.show();
|
3042
3051
|
}
|
3043
3052
|
|
3044
3053
|
setPasteMapping() {
|
3045
|
-
//
|
3046
|
-
//
|
3054
|
+
// Update the paste mapping as specified by the modeler and then
|
3055
|
+
// proceed to paste.
|
3047
3056
|
const
|
3048
3057
|
md = this.paste_modal,
|
3049
3058
|
mapping = Object.assign(md.mapping, {}),
|
@@ -3070,7 +3079,7 @@ class GUIController extends Controller {
|
|
3070
3079
|
pasteSelection(mapping={}) {
|
3071
3080
|
// If selection has been saved as XML in local storage, test to
|
3072
3081
|
// see whether PASTE would result in name conflicts, and if so,
|
3073
|
-
// open the name conflict resolution window
|
3082
|
+
// open the name conflict resolution window.
|
3074
3083
|
let xml = window.localStorage.getItem('Linny-R-selection-XML');
|
3075
3084
|
try {
|
3076
3085
|
xml = parseXML(xml);
|
@@ -3094,7 +3103,7 @@ class GUIController extends Controller {
|
|
3094
3103
|
// AUXILIARY FUNCTIONS
|
3095
3104
|
|
3096
3105
|
function fullName(node) {
|
3097
|
-
//
|
3106
|
+
// Return full entity name inferred from XML node data.
|
3098
3107
|
if(node.nodeName === 'from-to' || node.nodeName === 'selc') {
|
3099
3108
|
const
|
3100
3109
|
n = xmlDecoded(nodeParameterValue(node, 'name')),
|
@@ -3133,12 +3142,12 @@ class GUIController extends Controller {
|
|
3133
3142
|
}
|
3134
3143
|
|
3135
3144
|
function nameAndActor(name) {
|
3136
|
-
//
|
3137
|
-
//
|
3145
|
+
// Return tuple [entity name, actor name] if `name` ends with a
|
3146
|
+
// parenthesized string that identifies an actor in the selection.
|
3138
3147
|
const ai = name.lastIndexOf(' (');
|
3139
3148
|
if(ai < 0) return [name, ''];
|
3140
3149
|
let actor = name.slice(ai + 2, -1);
|
3141
|
-
// Test whether parenthesized string denotes an actor
|
3150
|
+
// Test whether parenthesized string denotes an actor.
|
3142
3151
|
if(actor_names.indexOf(actor) >= 0 || actor === mapping.actor ||
|
3143
3152
|
actor === mapping.from_actor || actor === mapping.to_actor) {
|
3144
3153
|
name = name.substring(0, ai);
|
@@ -3149,8 +3158,8 @@ class GUIController extends Controller {
|
|
3149
3158
|
}
|
3150
3159
|
|
3151
3160
|
function mappedName(n) {
|
3152
|
-
// Returns full name `n` modified according to the mapping
|
3153
|
-
// NOTE:
|
3161
|
+
// Returns full name `n` modified according to the mapping.
|
3162
|
+
// NOTE: Links and constraints require two mappings (recursion!).
|
3154
3163
|
if(n.indexOf(UI.LINK_ARROW) > 0) {
|
3155
3164
|
const ft = n.split(UI.LINK_ARROW);
|
3156
3165
|
return mappedName(ft[0]) + UI.LINK_ARROW + mappedName(ft[1]);
|
@@ -3173,7 +3182,7 @@ class GUIController extends Controller {
|
|
3173
3182
|
const ai = n.lastIndexOf(mapping.from_actor);
|
3174
3183
|
if(ai > 0) return n.substring(0, ai) + mapping.to_actor;
|
3175
3184
|
}
|
3176
|
-
// NOTE: specified actor cannot override existing actor
|
3185
|
+
// NOTE: specified actor cannot override existing actor.
|
3177
3186
|
if(mapping.actor && !nameAndActor(n)[1]) {
|
3178
3187
|
return `${n} (${mapping.actor})`;
|
3179
3188
|
}
|
@@ -3190,23 +3199,27 @@ class GUIController extends Controller {
|
|
3190
3199
|
if(mapping.from_to && mapping.from_to[n]) {
|
3191
3200
|
return mapping.from_to[n];
|
3192
3201
|
}
|
3193
|
-
// No mapping => return original name
|
3202
|
+
// No mapping => return original name.
|
3194
3203
|
return n;
|
3195
3204
|
}
|
3196
3205
|
|
3197
3206
|
function nameConflicts(node) {
|
3198
3207
|
// Maps names of entities defined by the child nodes of `node`
|
3199
|
-
// while detecting name conflicts
|
3208
|
+
// while detecting name conflicts.
|
3200
3209
|
for(let i = 0; i < node.childNodes.length; i++) {
|
3201
3210
|
const c = node.childNodes[i];
|
3202
3211
|
if(c.nodeName !== 'link' && c.nodeName !== 'constraint') {
|
3203
3212
|
const
|
3204
3213
|
fn = fullName(c),
|
3205
|
-
mn = mappedName(fn)
|
3214
|
+
mn = mappedName(fn),
|
3215
|
+
obj = MODEL.objectByName(mn),
|
3216
|
+
// Assume that existing products can be added as product
|
3217
|
+
// positions if they are not prefixed.
|
3218
|
+
add_pp = (obj instanceof Product && mn.indexOf(UI.PREFIXER) < 0);
|
3206
3219
|
// Name conflict occurs when the mapped name is already in use
|
3207
3220
|
// in the target model, or when the original name is mapped onto
|
3208
|
-
// different names (this might occur due to modeler input)
|
3209
|
-
if(
|
3221
|
+
// different names (this might occur due to modeler input).
|
3222
|
+
if((obj && !add_pp) || (name_map[fn] && name_map[fn] !== mn)) {
|
3210
3223
|
addDistinct(fn, name_conflicts);
|
3211
3224
|
} else {
|
3212
3225
|
name_map[fn] = mn;
|
@@ -3216,10 +3229,10 @@ class GUIController extends Controller {
|
|
3216
3229
|
}
|
3217
3230
|
|
3218
3231
|
function addEntityFromNode(node) {
|
3219
|
-
// Adds entity to model based on XML node data and mapping
|
3220
|
-
// NOTE:
|
3232
|
+
// Adds entity to model based on XML node data and mapping.
|
3233
|
+
// NOTE: Do not add if an entity having this type and mapped name
|
3221
3234
|
// already exists; name conflicts accross entity types may occur
|
3222
|
-
// and result in error messages
|
3235
|
+
// and result in error messages.
|
3223
3236
|
const
|
3224
3237
|
et = node.nodeName,
|
3225
3238
|
fn = fullName(node),
|
@@ -3281,17 +3294,17 @@ class GUIController extends Controller {
|
|
3281
3294
|
sp = this.sharedPrefix(cn, fcn),
|
3282
3295
|
fpn = (cn === UI.TOP_CLUSTER_NAME ? '' : cn.replace(sp, '')),
|
3283
3296
|
tpn = (fcn === UI.TOP_CLUSTER_NAME ? '' : fcn.replace(sp, ''));
|
3284
|
-
// Infer mapping from XML data and focal cluster name & actor name
|
3297
|
+
// Infer mapping from XML data and focal cluster name & actor name.
|
3285
3298
|
mapping.shared_prefix = sp;
|
3286
3299
|
mapping.from_prefix = (fpn ? sp + fpn + UI.PREFIXER : sp);
|
3287
3300
|
mapping.to_prefix = (tpn ? sp + tpn + UI.PREFIXER : sp);
|
3288
3301
|
mapping.from_actor = (ca === UI.NO_ACTOR ? '' : ca);
|
3289
3302
|
mapping.to_actor = (fca === UI.NO_ACTOR ? '' : fca);
|
3290
|
-
// Prompt for mapping when pasting to the same model and cluster
|
3303
|
+
// Prompt for mapping when pasting to the same model and cluster.
|
3291
3304
|
if(parseInt(mts) === MODEL.time_created.getTime() &&
|
3292
3305
|
ca === fca && mapping.from_prefix === mapping.to_prefix &&
|
3293
3306
|
!(mapping.prefix || mapping.actor || mapping.increment)) {
|
3294
|
-
// Prompt for names of selected cluster nodes
|
3307
|
+
// Prompt for names of selected cluster nodes.
|
3295
3308
|
if(selc_node.childNodes.length && !mapping.prefix) {
|
3296
3309
|
mapping.top_clusters = {};
|
3297
3310
|
for(let i = 0; i < selc_node.childNodes.length; i++) {
|
@@ -3306,7 +3319,7 @@ class GUIController extends Controller {
|
|
3306
3319
|
return;
|
3307
3320
|
}
|
3308
3321
|
// Also prompt if FROM and/or TO nodes are not selected, and map to
|
3309
|
-
// existing entities
|
3322
|
+
// existing entities.
|
3310
3323
|
if(from_tos_node.childNodes.length && !mapping.from_to) {
|
3311
3324
|
const
|
3312
3325
|
ft_map = {},
|
@@ -3322,7 +3335,7 @@ class GUIController extends Controller {
|
|
3322
3335
|
'Data' : nodeParameterValue(c, 'type'));
|
3323
3336
|
}
|
3324
3337
|
}
|
3325
|
-
// Prompt only for FROM/TO nodes that map to existing nodes
|
3338
|
+
// Prompt only for FROM/TO nodes that map to existing nodes.
|
3326
3339
|
if(Object.keys(ft_map).length) {
|
3327
3340
|
mapping.from_to = ft_map;
|
3328
3341
|
mapping.from_to_type = ft_type;
|
@@ -3333,7 +3346,7 @@ class GUIController extends Controller {
|
|
3333
3346
|
|
3334
3347
|
// Only check for selected entities; from-to's and extra's should be
|
3335
3348
|
// used if they exist, or should be created when copying to a different
|
3336
|
-
// model
|
3349
|
+
// model.
|
3337
3350
|
name_map.length = 0;
|
3338
3351
|
nameConflicts(entities_node);
|
3339
3352
|
if(name_conflicts.length) {
|
@@ -3352,20 +3365,20 @@ console.log('HERE name conflicts', name_conflicts, mapping);
|
|
3352
3365
|
for(let i = 0; i < entities_node.childNodes.length; i++) {
|
3353
3366
|
addEntityFromNode(entities_node.childNodes[i]);
|
3354
3367
|
}
|
3355
|
-
// Update diagram, showing newly added nodes as selection
|
3368
|
+
// Update diagram, showing newly added nodes as selection.
|
3356
3369
|
MODEL.clearSelection();
|
3357
3370
|
for(let i = 0; i < selection_node.childNodes.length; i++) {
|
3358
3371
|
const
|
3359
3372
|
n = xmlDecoded(nodeContent(selection_node.childNodes[i])),
|
3360
3373
|
obj = MODEL.objectByName(mappedName(n));
|
3361
3374
|
if(obj) {
|
3362
|
-
// NOTE:
|
3375
|
+
// NOTE: Selected products must be positioned.
|
3363
3376
|
if(obj instanceof Product) MODEL.focal_cluster.addProductPosition(obj);
|
3364
3377
|
MODEL.select(obj);
|
3365
3378
|
}
|
3366
3379
|
}
|
3367
3380
|
// Force redrawing the selection to ensure that links to positioned
|
3368
|
-
// products are displayed as arrows instead of block arrows
|
3381
|
+
// products are displayed as arrows instead of block arrows.
|
3369
3382
|
fc.clearAllProcesses();
|
3370
3383
|
UI.drawDiagram(MODEL);
|
3371
3384
|
this.paste_modal.hide();
|
@@ -486,12 +486,14 @@ class GUIDatasetManager extends DatasetManager {
|
|
486
486
|
m = sd.modifiers[UI.nameToID(msl[i])],
|
487
487
|
wild = m.hasWildcards,
|
488
488
|
defsel = (m.selector === sd.default_selector),
|
489
|
+
issue = (m.expression.compile_issue ? ' compile-issue' :
|
490
|
+
(m.expression.compute_issue ? ' compute-issue' : '')),
|
489
491
|
clk = '" onclick="DATASET_MANAGER.selectModifier(event, \'' +
|
490
492
|
m.selector + '\'';
|
491
493
|
if(m === sm) smid += i;
|
492
494
|
ml.push(['<tr id="dsmtr', i, '" class="dataset-modif',
|
493
495
|
(m === sm ? ' sel-set' : ''),
|
494
|
-
'"><td class="dataset-selector',
|
496
|
+
'"><td class="dataset-selector', issue,
|
495
497
|
(wild ? ' wildcard' : ''),
|
496
498
|
'" title="Shift-click to ', (defsel ? 'clear' : 'set as'),
|
497
499
|
' default modifier',
|
@@ -499,7 +501,10 @@ class GUIDatasetManager extends DatasetManager {
|
|
499
501
|
(defsel ? '<img src="images/solve.png" style="height: 14px;' +
|
500
502
|
' width: 14px; margin: 0 1px -3px -1px;">' : ''),
|
501
503
|
(wild ? wildcardFormat(m.selector, true) : m.selector),
|
502
|
-
'</td><td class="dataset-expression',
|
504
|
+
'</td><td class="dataset-expression', issue,
|
505
|
+
(issue ? '"title="' +
|
506
|
+
safeDoubleQuotes(m.expression.compile_issue ||
|
507
|
+
m.expression.compute_issue) : ''),
|
503
508
|
clk, ');">', m.expression.text, '</td></tr>'].join(''));
|
504
509
|
}
|
505
510
|
this.modifier_table.innerHTML = ml.join('');
|
@@ -654,14 +659,14 @@ class GUIDatasetManager extends DatasetManager {
|
|
654
659
|
}
|
655
660
|
|
656
661
|
renameDataset() {
|
657
|
-
//
|
662
|
+
// Change the name of the selected dataset.
|
658
663
|
if(this.selected_dataset) {
|
659
664
|
const
|
660
665
|
inp = this.rename_modal.element('name'),
|
661
666
|
n = UI.cleanName(inp.value);
|
662
|
-
// Show modeler the "cleaned" new name
|
667
|
+
// Show modeler the "cleaned" new name.
|
663
668
|
inp.value = n;
|
664
|
-
// Then try to rename -- this may generate a warning
|
669
|
+
// Then try to rename -- this may generate a warning.
|
665
670
|
if(this.selected_dataset.rename(n)) {
|
666
671
|
this.rename_modal.hide();
|
667
672
|
if(EXPERIMENT_MANAGER.selected_experiment) {
|
@@ -670,59 +675,22 @@ class GUIDatasetManager extends DatasetManager {
|
|
670
675
|
UI.updateControllerDialogs('CDEFJX');
|
671
676
|
}
|
672
677
|
} else if(this.selected_prefix_row) {
|
673
|
-
// Create a list of datasets to be renamed
|
678
|
+
// Create a list of datasets to be renamed.
|
674
679
|
let e = this.rename_modal.element('name'),
|
675
680
|
prefix = e.value.trim();
|
676
681
|
e.focus();
|
677
|
-
// Trim trailing colon if user
|
682
|
+
// Trim trailing colon if user added it.
|
678
683
|
while(prefix.endsWith(':')) prefix = prefix.slice(0, -1);
|
679
|
-
// NOTE:
|
684
|
+
// NOTE: Prefix may be empty string, but otherwise should be a
|
685
|
+
// valid name.
|
680
686
|
if(prefix && !UI.validName(prefix)) {
|
681
687
|
UI.warn('Invalid prefix');
|
682
688
|
return;
|
683
689
|
}
|
684
|
-
// Now add the colon-plus-space prefix separator
|
690
|
+
// Now add the colon-plus-space prefix separator.
|
685
691
|
prefix += UI.PREFIXER;
|
686
|
-
|
687
|
-
|
688
|
-
key = oldpref.toLowerCase().split(UI.PREFIXER).join(':_'),
|
689
|
-
newkey = prefix.toLowerCase().split(UI.PREFIXER).join(':_'),
|
690
|
-
dsl = [];
|
691
|
-
// No change if new prefix is identical to old prefix
|
692
|
-
if(oldpref !== prefix) {
|
693
|
-
for(let k in MODEL.datasets) if(MODEL.datasets.hasOwnProperty(k)) {
|
694
|
-
if(k.startsWith(key)) dsl.push(k);
|
695
|
-
}
|
696
|
-
// NOTE: no check needed for mere upper/lower case changes
|
697
|
-
if(newkey !== key) {
|
698
|
-
let nc = 0;
|
699
|
-
for(let i = 0; i < dsl.length; i++) {
|
700
|
-
let nk = newkey + dsl[i].substring(key.length);
|
701
|
-
if(MODEL.datasets[nk]) nc++;
|
702
|
-
}
|
703
|
-
if(nc) {
|
704
|
-
UI.warn('Renaming ' + pluralS(dsl.length, 'dataset').toLowerCase() +
|
705
|
-
' would cause ' + pluralS(nc, 'name conflict'));
|
706
|
-
return;
|
707
|
-
}
|
708
|
-
}
|
709
|
-
// Reset counts of effects of a rename operation
|
710
|
-
this.entity_count = 0;
|
711
|
-
this.expression_count = 0;
|
712
|
-
// Rename datasets one by one, suppressing notifications
|
713
|
-
for(let i = 0; i < dsl.length; i++) {
|
714
|
-
const d = MODEL.datasets[dsl[i]];
|
715
|
-
d.rename(d.displayName.replace(oldpref, prefix), false);
|
716
|
-
}
|
717
|
-
let msg = 'Renamed ' + pluralS(dsl.length, 'dataset').toLowerCase();
|
718
|
-
if(MODEL.variable_count) msg += ', and updated ' +
|
719
|
-
pluralS(MODEL.variable_count, 'variable') + ' in ' +
|
720
|
-
pluralS(MODEL.expression_count, 'expression');
|
721
|
-
UI.notify(msg);
|
722
|
-
if(EXPERIMENT_MANAGER.selected_experiment) {
|
723
|
-
EXPERIMENT_MANAGER.selected_experiment.inferVariables();
|
724
|
-
}
|
725
|
-
UI.updateControllerDialogs('CDEFJX');
|
692
|
+
// Perform the renaming operation.
|
693
|
+
if(MODEL.renamePrefixedDatasets(this.selectedPrefix, prefix)) {
|
726
694
|
this.selectPrefixRow(prefix);
|
727
695
|
}
|
728
696
|
}
|