linny-r 1.1.13 → 1.1.14
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
CHANGED
package/static/linny-r.css
CHANGED
@@ -2168,8 +2168,8 @@ td.equation-expression {
|
|
2168
2168
|
|
2169
2169
|
/* SERIES modal dialog */
|
2170
2170
|
#series-dlg {
|
2171
|
-
width:
|
2172
|
-
height:
|
2171
|
+
width: 165px;
|
2172
|
+
height: 320px;
|
2173
2173
|
}
|
2174
2174
|
|
2175
2175
|
#series-default-lbl {
|
@@ -2182,7 +2182,7 @@ td.equation-expression {
|
|
2182
2182
|
position: absolute;
|
2183
2183
|
top: 24px;
|
2184
2184
|
left: 79px;
|
2185
|
-
width:
|
2185
|
+
width: calc(100% - 83px);
|
2186
2186
|
font-size: 12px;
|
2187
2187
|
margin-bottom: 2px;
|
2188
2188
|
}
|
@@ -2202,13 +2202,13 @@ td.equation-expression {
|
|
2202
2202
|
#series-array {
|
2203
2203
|
position: absolute;
|
2204
2204
|
top: 45px;
|
2205
|
-
left:
|
2205
|
+
left: 77px;
|
2206
2206
|
}
|
2207
2207
|
|
2208
2208
|
#series-array-lbl {
|
2209
2209
|
position: absolute;
|
2210
2210
|
top: 47px;
|
2211
|
-
left:
|
2211
|
+
left: 99px;
|
2212
2212
|
}
|
2213
2213
|
|
2214
2214
|
#series-no-time-msg {
|
@@ -2216,7 +2216,7 @@ td.equation-expression {
|
|
2216
2216
|
top: 66px;
|
2217
2217
|
left: 1px;
|
2218
2218
|
width: calc(100% - 2px);
|
2219
|
-
height:
|
2219
|
+
height: 30px;
|
2220
2220
|
z-index: 1;
|
2221
2221
|
background-color: inherit;
|
2222
2222
|
font-style: italic;
|
@@ -2235,7 +2235,7 @@ td.equation-expression {
|
|
2235
2235
|
position: absolute;
|
2236
2236
|
top: 67px;
|
2237
2237
|
left: 60px;
|
2238
|
-
width:
|
2238
|
+
width: 50px;
|
2239
2239
|
font-size: 12px;
|
2240
2240
|
margin-bottom: 2px;
|
2241
2241
|
}
|
@@ -2243,7 +2243,7 @@ td.equation-expression {
|
|
2243
2243
|
#series-time-unit {
|
2244
2244
|
position: absolute;
|
2245
2245
|
top: 67px;
|
2246
|
-
left:
|
2246
|
+
left: 116px;
|
2247
2247
|
height: 19px;
|
2248
2248
|
width: 45px;
|
2249
2249
|
font-size: 12px;
|
@@ -2251,40 +2251,43 @@ td.equation-expression {
|
|
2251
2251
|
|
2252
2252
|
#series-method-lbl {
|
2253
2253
|
position: absolute;
|
2254
|
-
top:
|
2254
|
+
top: 91px;
|
2255
2255
|
left: 2px;
|
2256
2256
|
}
|
2257
2257
|
|
2258
2258
|
#series-method {
|
2259
2259
|
position: absolute;
|
2260
|
-
top:
|
2261
|
-
left:
|
2262
|
-
height:
|
2263
|
-
width:
|
2264
|
-
font-size:
|
2260
|
+
top: 89px;
|
2261
|
+
left: 50px;
|
2262
|
+
height: 21px;
|
2263
|
+
width: 111px;
|
2264
|
+
font-size: 12px;
|
2265
2265
|
}
|
2266
2266
|
|
2267
2267
|
#series-remote {
|
2268
2268
|
position: absolute;
|
2269
|
-
top:
|
2270
|
-
left:
|
2269
|
+
top: 113px;
|
2270
|
+
left: 3px;
|
2271
|
+
width: calc(100% - 8px);
|
2271
2272
|
}
|
2272
2273
|
|
2273
2274
|
#series-url {
|
2274
|
-
width:
|
2275
|
+
width: 100%;
|
2275
2276
|
font-size: 12px;
|
2276
2277
|
}
|
2277
2278
|
|
2278
2279
|
#series-data-lbl {
|
2279
2280
|
position: absolute;
|
2280
|
-
top:
|
2281
|
+
top: 133px;
|
2281
2282
|
left: 2px;
|
2282
2283
|
}
|
2283
2284
|
|
2284
2285
|
#series-data {
|
2285
|
-
|
2286
|
-
|
2287
|
-
|
2286
|
+
position: absolute;
|
2287
|
+
bottom: 13px;
|
2288
|
+
width: calc(100% - 6px);
|
2289
|
+
height: calc(100% - 165px);
|
2290
|
+
margin: 3px;
|
2288
2291
|
}
|
2289
2292
|
|
2290
2293
|
#series-line {
|
@@ -140,7 +140,7 @@ class Controller {
|
|
140
140
|
'scale_unit', 'equal_bounds', 'price', 'is_source', 'is_sink', 'is_buffer',
|
141
141
|
'is_data', 'integer_level', 'no_slack'],
|
142
142
|
DATASET_PROPS: ['comments', 'default_value', 'time_scale', 'time_unit',
|
143
|
-
'method', 'periodic', 'array', 'url'],
|
143
|
+
'method', 'periodic', 'array', 'url', 'default_selector'],
|
144
144
|
LINK_PROPS: ['comments', 'multiplier', 'relative_rate', 'share_of_cost',
|
145
145
|
'flow_delay'],
|
146
146
|
CONSTRAINT_PROPS: ['comments', 'no_slack', 'share_of_cost'],
|
@@ -8833,7 +8833,7 @@ class GUIDatasetManager extends DatasetManager {
|
|
8833
8833
|
if(d === sd) sdid += i;
|
8834
8834
|
dl.push(['<tr id="dstr', i, '" class="dataset',
|
8835
8835
|
(d === sd ? ' sel-set' : ''),
|
8836
|
-
'" onclick="DATASET_MANAGER.selectDataset(\'',
|
8836
|
+
'" onclick="DATASET_MANAGER.selectDataset(event, \'',
|
8837
8837
|
dnl[i], '\');" onmouseover="DATASET_MANAGER.showInfo(\'', dnl[i],
|
8838
8838
|
'\', event.shiftKey);"><td', cls, '>', d.displayName,
|
8839
8839
|
'</td></tr>'].join(''));
|
@@ -8907,14 +8907,19 @@ class GUIDatasetManager extends DatasetManager {
|
|
8907
8907
|
for(let i = 0; i < msl.length; i++) {
|
8908
8908
|
const
|
8909
8909
|
m = sd.modifiers[UI.nameToID(msl[i])],
|
8910
|
-
|
8910
|
+
defsel = (m.selector === sd.default_selector),
|
8911
|
+
clk = '" onclick="DATASET_MANAGER.selectModifier(event, \'' +
|
8911
8912
|
m.selector + '\'';
|
8912
8913
|
if(m === sm) smid += i;
|
8913
8914
|
ml.push(['<tr id="dsmtr', i, '" class="dataset-modif',
|
8914
8915
|
(m === sm ? ' sel-set' : ''),
|
8915
8916
|
'"><td class="dataset-selector',
|
8916
8917
|
(m.hasWildcards ? ' wildcard' : ''),
|
8918
|
+
'" title="Shift-click to ', (defsel ? 'clear' : 'set as'),
|
8919
|
+
' default modifier',
|
8917
8920
|
clk, ', false);">',
|
8921
|
+
(defsel ? '<img src="images/solve.png" style="height: 14px;' +
|
8922
|
+
' width: 14px; margin: 0 1px -3px -1px;">' : ''),
|
8918
8923
|
m.selector, '</td><td class="dataset-expression',
|
8919
8924
|
clk, ');">', m.expression.text, '</td></tr>'].join(''));
|
8920
8925
|
}
|
@@ -8961,46 +8966,54 @@ class GUIDatasetManager extends DatasetManager {
|
|
8961
8966
|
this.updateDialog();
|
8962
8967
|
}
|
8963
8968
|
|
8964
|
-
selectDataset(id) {
|
8965
|
-
// Select dataset, or edit it when double-clicked
|
8969
|
+
selectDataset(event, id) {
|
8970
|
+
// Select dataset, or edit it when Alt- or double-clicked
|
8966
8971
|
const
|
8967
|
-
d = MODEL.datasets[id],
|
8972
|
+
d = MODEL.datasets[id] || null,
|
8968
8973
|
now = Date.now(),
|
8969
|
-
dt = now - this.last_time_selected
|
8974
|
+
dt = now - this.last_time_selected,
|
8975
|
+
// Consider click to be "double" if it occurred less than 300 ms ago
|
8976
|
+
edit = event.altKey || (d === this.selected_dataset && dt < 300);
|
8977
|
+
this.selected_dataset = d;
|
8970
8978
|
this.last_time_selected = now;
|
8971
|
-
if(d
|
8972
|
-
|
8973
|
-
|
8974
|
-
|
8975
|
-
this.editData();
|
8976
|
-
return;
|
8977
|
-
}
|
8979
|
+
if(d && edit) {
|
8980
|
+
this.last_time_selected = 0;
|
8981
|
+
this.editData();
|
8982
|
+
return;
|
8978
8983
|
}
|
8979
|
-
this.selected_dataset = MODEL.datasets[id];
|
8980
8984
|
this.updateDialog();
|
8981
8985
|
}
|
8982
8986
|
|
8983
|
-
selectModifier(id, x=true) {
|
8987
|
+
selectModifier(event, id, x=true) {
|
8984
8988
|
// Select modifier, or when double-clicked, edit its expression or the
|
8985
8989
|
// name of the modifier
|
8986
8990
|
if(this.selected_dataset) {
|
8987
8991
|
const m = this.selected_dataset.modifiers[UI.nameToID(id)],
|
8988
8992
|
now = Date.now(),
|
8989
|
-
dt = now - this.last_time_selected
|
8993
|
+
dt = now - this.last_time_selected,
|
8994
|
+
// NOTE: Alt-click and double-click indicate: edit
|
8995
|
+
// Consider click to be "double" if the same modifier was clicked
|
8996
|
+
// less than 300 ms ago
|
8997
|
+
edit = event.altKey || (m === this.selected_modifier && dt < 300);
|
8990
8998
|
this.last_time_selected = now;
|
8991
|
-
if(
|
8992
|
-
//
|
8993
|
-
if(
|
8994
|
-
this.
|
8995
|
-
|
8996
|
-
|
8997
|
-
} else {
|
8998
|
-
this.promptForSelector('rename');
|
8999
|
-
}
|
9000
|
-
return;
|
8999
|
+
if(event.shiftKey) {
|
9000
|
+
// Toggle dataset default selector
|
9001
|
+
if(m.selector === this.selected_dataset.default_selector) {
|
9002
|
+
this.selected_dataset.default_selector = '';
|
9003
|
+
} else {
|
9004
|
+
this.selected_dataset.default_selector = m.selector;
|
9001
9005
|
}
|
9002
9006
|
}
|
9003
9007
|
this.selected_modifier = m;
|
9008
|
+
if(edit) {
|
9009
|
+
this.last_time_selected = 0;
|
9010
|
+
if(x) {
|
9011
|
+
this.editExpression();
|
9012
|
+
} else {
|
9013
|
+
this.promptForSelector('rename');
|
9014
|
+
}
|
9015
|
+
return;
|
9016
|
+
}
|
9004
9017
|
} else {
|
9005
9018
|
this.selected_modifier = null;
|
9006
9019
|
}
|
@@ -9167,6 +9180,10 @@ class GUIDatasetManager extends DatasetManager {
|
|
9167
9180
|
m = this.selected_dataset.addModifier(sel);
|
9168
9181
|
// NULL can result when new name is invalid
|
9169
9182
|
if(!m) return;
|
9183
|
+
// If selected modifier was the dataset default selector, update it
|
9184
|
+
if(oldm.selector === this.selected_dataset.default_selector) {
|
9185
|
+
this.selected_dataset.default_selector = m.selector;
|
9186
|
+
}
|
9170
9187
|
// If only case has changed, just update the selector
|
9171
9188
|
// NOTE: normal dataset selector, so remove all invalid characters
|
9172
9189
|
if(m === oldm) {
|
@@ -9248,8 +9265,14 @@ class GUIDatasetManager extends DatasetManager {
|
|
9248
9265
|
}
|
9249
9266
|
|
9250
9267
|
deleteModifier() {
|
9268
|
+
// Delete modifier from selected dataset
|
9251
9269
|
const m = this.selected_modifier;
|
9252
9270
|
if(m) {
|
9271
|
+
// If it was the dataset default modifier, clear the default
|
9272
|
+
if(m.selector === this.selected_dataset.default_selector) {
|
9273
|
+
this.selected_dataset.default_selector = '';
|
9274
|
+
}
|
9275
|
+
// Then simply remove the object
|
9253
9276
|
delete this.selected_dataset.modifiers[UI.nameToID(m.selector)];
|
9254
9277
|
this.selected_modifier = null;
|
9255
9278
|
this.updateModifiers();
|
@@ -9418,7 +9441,7 @@ class EquationManager {
|
|
9418
9441
|
const
|
9419
9442
|
m = ed.modifiers[UI.nameToID(msl[i])],
|
9420
9443
|
mp = (m.parameters ? '\\' + m.parameters.join('\\') : ''),
|
9421
|
-
clk = '" onclick="EQUATION_MANAGER.selectModifier(\'' +
|
9444
|
+
clk = '" onclick="EQUATION_MANAGER.selectModifier(event, \'' +
|
9422
9445
|
m.selector + '\'';
|
9423
9446
|
if(m === sm) smid += i;
|
9424
9447
|
ml.push(['<tr id="eqmtr', i, '" class="dataset-modif',
|
@@ -9444,28 +9467,27 @@ class EquationManager {
|
|
9444
9467
|
// @@TO DO: Display documentation for the equation => extra comments field?
|
9445
9468
|
}
|
9446
9469
|
|
9447
|
-
selectModifier(id, x=true) {
|
9448
|
-
// Select modifier, or when double-clicked, edit its expression
|
9449
|
-
// name of the modifier
|
9470
|
+
selectModifier(event, id, x=true) {
|
9471
|
+
// Select modifier, or when Alt- or double-clicked, edit its expression
|
9472
|
+
// or the equation name (= name of the modifier)
|
9450
9473
|
if(MODEL.equations_dataset) {
|
9451
9474
|
const
|
9452
|
-
m = MODEL.equations_dataset.modifiers[UI.nameToID(id)],
|
9475
|
+
m = MODEL.equations_dataset.modifiers[UI.nameToID(id)] || null,
|
9453
9476
|
now = Date.now(),
|
9454
|
-
dt = now - this.last_time_selected
|
9477
|
+
dt = now - this.last_time_selected,
|
9478
|
+
// Consider click to be "double" if it occurred less than 300 ms ago
|
9479
|
+
edit = event.altKey || (m === this.selected_modifier && dt < 300);
|
9455
9480
|
this.last_time_selected = now;
|
9456
|
-
|
9457
|
-
|
9458
|
-
|
9459
|
-
|
9460
|
-
|
9461
|
-
|
9462
|
-
|
9463
|
-
this.promptForName();
|
9464
|
-
}
|
9465
|
-
return;
|
9481
|
+
this.selected_modifier = m;
|
9482
|
+
if(m && edit) {
|
9483
|
+
this.last_time_selected = 0;
|
9484
|
+
if(x) {
|
9485
|
+
this.editEquation();
|
9486
|
+
} else {
|
9487
|
+
this.promptForName();
|
9466
9488
|
}
|
9489
|
+
return;
|
9467
9490
|
}
|
9468
|
-
this.selected_modifier = m;
|
9469
9491
|
} else {
|
9470
9492
|
this.selected_modifier = null;
|
9471
9493
|
}
|
@@ -6301,6 +6301,37 @@ class Node extends NodeBox {
|
|
6301
6301
|
}
|
6302
6302
|
return cac;
|
6303
6303
|
}
|
6304
|
+
|
6305
|
+
convertLegacyBoundData(lb_data, ub_data) {
|
6306
|
+
// Convert time series data for LB and UB in legacy models to datasets,
|
6307
|
+
// and replace attribute expressions by references to these datasets
|
6308
|
+
if(!lb_data && !ub_data) return;
|
6309
|
+
const same = lb_data === ub_data;
|
6310
|
+
if(lb_data) {
|
6311
|
+
const
|
6312
|
+
dsn = this.displayName + (same ? '' : ' LOWER') + ' BOUND DATA',
|
6313
|
+
ds = MODEL.addDataset(dsn);
|
6314
|
+
// Use the LB attribute as default value for the dataset
|
6315
|
+
ds.default_value = parseFloat(this.lower_bound.text);
|
6316
|
+
ds.data = stringToFloatArray(lb_data);
|
6317
|
+
ds.computeVector();
|
6318
|
+
ds.computeStatistics();
|
6319
|
+
this.lower_bound.text = `[${dsn}]`;
|
6320
|
+
if(same) this.equal_bounds = true;
|
6321
|
+
MODEL.legacy_datasets = true;
|
6322
|
+
}
|
6323
|
+
if(ub_data && !same) {
|
6324
|
+
const
|
6325
|
+
dsn = this.displayName + ' UPPER BOUND DATA',
|
6326
|
+
ds = MODEL.addDataset(dsn);
|
6327
|
+
ds.default_value = parseFloat(this.upper_bound.text);
|
6328
|
+
ds.data = stringToFloatArray(ub_data);
|
6329
|
+
ds.computeVector();
|
6330
|
+
ds.computeStatistics();
|
6331
|
+
this.upper_bound.text = `[${dsn}]`;
|
6332
|
+
MODEL.legacy_datasets = true;
|
6333
|
+
}
|
6334
|
+
}
|
6304
6335
|
|
6305
6336
|
actualLevel(t) {
|
6306
6337
|
// Returns the production level c.q. stock level for this node in time step t
|
@@ -6459,6 +6490,13 @@ class Process extends Node {
|
|
6459
6490
|
this.comments = xmlDecoded(nodeContentByTag(node, 'notes'));
|
6460
6491
|
this.lower_bound.text = xmlDecoded(nodeContentByTag(node, 'lower-bound'));
|
6461
6492
|
this.upper_bound.text = xmlDecoded(nodeContentByTag(node, 'upper-bound'));
|
6493
|
+
// legacy models can have LB and UB hexadecimal data strings
|
6494
|
+
this.convertLegacyBoundData(nodeContentByTag(node, 'lower-bound-data'),
|
6495
|
+
nodeContentByTag(node, 'upper-bound-data'));
|
6496
|
+
if(nodeParameterValue(node, 'reversible') === '1') {
|
6497
|
+
// For legacy "reversible" processes, the LB is set to -UB
|
6498
|
+
this.lower_bound.text = '-' + this.upper_bound.text;
|
6499
|
+
}
|
6462
6500
|
// NOTE: legacy models have no initial level field => default to 0
|
6463
6501
|
const ilt = xmlDecoded(nodeContentByTag(node, 'initial-level'));
|
6464
6502
|
this.initial_level.text = ilt || '0';
|
@@ -6871,47 +6909,25 @@ class Product extends Node {
|
|
6871
6909
|
this.equal_bounds = nodeParameterValue(node, 'equal-bounds') === '1';
|
6872
6910
|
this.integer_level = nodeParameterValue(node, 'integer-level') === '1';
|
6873
6911
|
this.no_slack = nodeParameterValue(node, 'no-slack') === '1';
|
6874
|
-
//
|
6912
|
+
// Legacy models have tag "hidden" instead of "no-links"
|
6875
6913
|
this.no_links = (nodeParameterValue(node, 'no-links') ||
|
6876
6914
|
nodeParameterValue(node, 'hidden')) === '1';
|
6877
6915
|
this.scale_unit = MODEL.addScaleUnit(
|
6878
6916
|
xmlDecoded(nodeContentByTag(node, 'unit')));
|
6879
|
-
//
|
6917
|
+
// Legacy models have tag "profit" instead of "price"
|
6880
6918
|
let pp = nodeContentByTag(node, 'price');
|
6881
6919
|
if(!pp) pp = nodeContentByTag(node, 'profit');
|
6882
6920
|
this.price.text = xmlDecoded(pp);
|
6921
|
+
// Legacy models can have price time series data as hexadecimal string
|
6922
|
+
this.convertLegacyPriceData(nodeContentByTag(node, 'profit-data'));
|
6883
6923
|
this.lower_bound.text = xmlDecoded(nodeContentByTag(node, 'lower-bound'));
|
6884
6924
|
this.upper_bound.text = xmlDecoded(nodeContentByTag(node, 'upper-bound'));
|
6885
6925
|
// legacy models can have LB and UB hexadecimal data strings
|
6886
|
-
|
6887
|
-
|
6888
|
-
|
6889
|
-
|
6890
|
-
|
6891
|
-
const
|
6892
|
-
dsn = this.displayName + (same ? '' : ' LOWER') + ' BOUND DATA',
|
6893
|
-
ds = MODEL.addDataset(dsn);
|
6894
|
-
ds.default_value = parseFloat(this.lower_bound.text);
|
6895
|
-
ds.data = stringToFloatArray(lb_data);
|
6896
|
-
ds.computeVector();
|
6897
|
-
ds.computeStatistics();
|
6898
|
-
this.lower_bound.text = `[${dsn}]`;
|
6899
|
-
if(same) this.equal_bounds = true;
|
6900
|
-
MODEL.legacy_datasets = true;
|
6901
|
-
}
|
6902
|
-
if(ub_data && !same) {
|
6903
|
-
const
|
6904
|
-
dsn = this.displayName + ' UPPER BOUND DATA',
|
6905
|
-
ds = MODEL.addDataset(dsn);
|
6906
|
-
ds.default_value = parseFloat(this.upper_bound.text);
|
6907
|
-
ds.data = stringToFloatArray(ub_data);
|
6908
|
-
ds.computeVector();
|
6909
|
-
ds.computeStatistics();
|
6910
|
-
this.upper_bound.text = `[${dsn}]`;
|
6911
|
-
MODEL.legacy_datasets = true;
|
6912
|
-
}
|
6913
|
-
this.initial_level.text = xmlDecoded(
|
6914
|
-
nodeContentByTag(node, 'initial-level'));
|
6926
|
+
this.convertLegacyBoundData(nodeContentByTag(node, 'lower-bound-data'),
|
6927
|
+
nodeContentByTag(node, 'upper-bound-data'));
|
6928
|
+
// NOTE: legacy models have no initial level field => default to 0
|
6929
|
+
const ilt = xmlDecoded(nodeContentByTag(node, 'initial-level'));
|
6930
|
+
this.initial_level.text = ilt || '0';
|
6915
6931
|
this.comments = xmlDecoded(nodeContentByTag(node, 'notes'));
|
6916
6932
|
this.x = safeStrToInt(nodeContentByTag(node, 'x-coord'));
|
6917
6933
|
this.y = safeStrToInt(nodeContentByTag(node, 'y-coord'));
|
@@ -6927,6 +6943,23 @@ class Product extends Node {
|
|
6927
6943
|
this.resize();
|
6928
6944
|
}
|
6929
6945
|
|
6946
|
+
convertLegacyPriceData(data) {
|
6947
|
+
// Convert time series data for prices in legacy models to a dataset,
|
6948
|
+
// and replace the price expression by a reference to this dataset
|
6949
|
+
if(data) {
|
6950
|
+
const
|
6951
|
+
dsn = this.displayName + ' PRICE DATA',
|
6952
|
+
ds = MODEL.addDataset(dsn);
|
6953
|
+
// Use the price attribute as default value for the dataset
|
6954
|
+
ds.default_value = parseFloat(this.price.text);
|
6955
|
+
ds.data = stringToFloatArray(data);
|
6956
|
+
ds.computeVector();
|
6957
|
+
ds.computeStatistics();
|
6958
|
+
this.price.text = `[${dsn}]`;
|
6959
|
+
MODEL.legacy_datasets = true;
|
6960
|
+
}
|
6961
|
+
}
|
6962
|
+
|
6930
6963
|
get defaultAttribute() {
|
6931
6964
|
return 'L';
|
6932
6965
|
}
|
@@ -7396,6 +7429,8 @@ class Dataset {
|
|
7396
7429
|
// *model* time step t = 0
|
7397
7430
|
this.vector = [];
|
7398
7431
|
this.modifiers = {};
|
7432
|
+
// Selector to be used when model is run normally, i.e., no experiment
|
7433
|
+
this.default_selector = '';
|
7399
7434
|
}
|
7400
7435
|
|
7401
7436
|
get type() {
|
@@ -7781,7 +7816,8 @@ class Dataset {
|
|
7781
7816
|
'</method><url>', xmlEncoded(this.url),
|
7782
7817
|
'</url><data>', xmlEncoded(this.dataString),
|
7783
7818
|
'</data><modifiers>', ml.join(''),
|
7784
|
-
'</modifiers
|
7819
|
+
'</modifiers><default-selector>', xmlEncoded(this.default_selector),
|
7820
|
+
'</default-selector></dataset>'].join('');
|
7785
7821
|
return xml;
|
7786
7822
|
}
|
7787
7823
|
|
@@ -7812,6 +7848,12 @@ class Dataset {
|
|
7812
7848
|
}
|
7813
7849
|
}
|
7814
7850
|
}
|
7851
|
+
const ds = xmlDecoded(nodeContentByTag(node, 'default-selector'));
|
7852
|
+
if(ds && !this.modifiers[ds]) {
|
7853
|
+
UI.warn(`Dataset <tt>${this.name}</tt> has no selector <tt>${ds}</tt>`);
|
7854
|
+
} else {
|
7855
|
+
this.default_selector = ds;
|
7856
|
+
}
|
7815
7857
|
}
|
7816
7858
|
|
7817
7859
|
rename(name) {
|
@@ -564,7 +564,9 @@ function hexToFloat(s) {
|
|
564
564
|
const
|
565
565
|
sign = (n >> 31 ? -1 : 1),
|
566
566
|
exp = Math.pow(2, ((n >> 23) & 0xFF) - 127);
|
567
|
-
|
567
|
+
f = sign * (n & 0x7fffff | 0x800000) * 1.0 / Math.pow(2, 23) * exp;
|
568
|
+
// NOTE: must consider precision of 32-bit floating point numbers!
|
569
|
+
return parseFloat(f.toPrecision(7));
|
568
570
|
}
|
569
571
|
|
570
572
|
function stringToFloatArray(s) {
|
@@ -714,8 +714,11 @@ class ExpressionParser {
|
|
714
714
|
return [x, anchor1, offset1, anchor2, offset2];
|
715
715
|
}
|
716
716
|
}
|
717
|
+
|
718
|
+
//
|
717
719
|
// NOTE: for experiment results, the method will ALWAYS have returned
|
718
720
|
// a result, so what follows does not apply to experiment results
|
721
|
+
//
|
719
722
|
|
720
723
|
// Attribute name (optional) follows object-attribute separator
|
721
724
|
s = name.split(UI.OA_SEPARATOR);
|
@@ -725,7 +728,7 @@ class ExpressionParser {
|
|
725
728
|
// ... so restore name if itself contains other vertical bars
|
726
729
|
name = s.join(UI.OA_SEPARATOR).trim();
|
727
730
|
if(!attr) {
|
728
|
-
// Explicit empty attribute, e.g., [name|]
|
731
|
+
// Explicit *empty* attribute, e.g., [name|]
|
729
732
|
// NOTE: this matters for datasets having specifiers: the vertical
|
730
733
|
// bar indicates "do not infer a modifier from a running experiment,
|
731
734
|
// but use the data"
|
@@ -817,8 +820,12 @@ class ExpressionParser {
|
|
817
820
|
(attr ? ' and have attribute ' + attr : '');
|
818
821
|
return false;
|
819
822
|
}
|
823
|
+
|
824
|
+
//
|
820
825
|
// NOTE: for statistics, the method will ALWAYS have returned a result,
|
821
826
|
// so what follows does not apply to statistics results
|
827
|
+
//
|
828
|
+
|
822
829
|
let by_reference = false;
|
823
830
|
if(name === '.') {
|
824
831
|
// NOTE: when name is a single dot, it refers to the data of a dataset
|
@@ -5227,15 +5234,26 @@ function VMI_push_dataset_modifier(x, args) {
|
|
5227
5234
|
MODEL.end_period - MODEL.start_period + MODEL.look_ahead + 1, t));
|
5228
5235
|
}
|
5229
5236
|
if(ms) {
|
5230
|
-
// If modifier selector is specified, use the associated expression
|
5237
|
+
// If modifier selector is specified, use the associated expression
|
5231
5238
|
obj = mx;
|
5232
|
-
|
5233
|
-
|
5234
|
-
|
5235
|
-
|
5236
|
-
|
5237
|
-
|
5238
|
-
|
5239
|
+
} else if(!ud) {
|
5240
|
+
if(MODEL.running_experiment) {
|
5241
|
+
// If an experiment is running, check if dataset modifiers match the
|
5242
|
+
// combination of selectors for the active run
|
5243
|
+
const mm = ds.matchingModifiers(MODEL.running_experiment.activeCombination);
|
5244
|
+
// If so, use the first match
|
5245
|
+
if(mm.length > 0) obj = mm[0].expression;
|
5246
|
+
} else if(ds.default_selector) {
|
5247
|
+
// If no expriment (so "normal" run), use default selector if specified
|
5248
|
+
const dm = ds.modifiers[ds.default_selector];
|
5249
|
+
if(dm) {
|
5250
|
+
obj = dm.expression;
|
5251
|
+
} else {
|
5252
|
+
// Exception should never occur, but check anyway and log it
|
5253
|
+
console.log('WARNING: Dataset "' + ds.name +
|
5254
|
+
`" has no default selector "${ds.default_selector}"`);
|
5255
|
+
}
|
5256
|
+
}
|
5239
5257
|
}
|
5240
5258
|
// By default, use the dataset default value
|
5241
5259
|
let v = ds.defaultValue,
|