linny-r 1.2.0 → 1.2.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 +20 -0
- package/static/linny-r.css +12 -6
- package/static/scripts/linny-r-ctrl.js +4 -0
- package/static/scripts/linny-r-gui.js +304 -57
- package/static/scripts/linny-r-model.js +20 -10
package/package.json
CHANGED
package/static/index.html
CHANGED
@@ -851,6 +851,26 @@ NOTE: Unit symbols are case-sensitive, so BTU ≠ Btu">
|
|
851
851
|
</div>
|
852
852
|
</div>
|
853
853
|
|
854
|
+
<!-- the CONFIRM LOAD modal asks to confirm to load model from repository -->
|
855
|
+
<div id="confirm-load-from-repo-modal" class="modal">
|
856
|
+
<div id="confirm-load-from-repo-dlg" class="inp-dlg">
|
857
|
+
<div class="dlg-title" style="background-color: #cc88b0">
|
858
|
+
Confirm load model
|
859
|
+
</div>
|
860
|
+
<div id="confirm-load-from-repo-msg">
|
861
|
+
Loading model
|
862
|
+
<span id="confirm-load-from-repo-mod-name"></span>
|
863
|
+
will discard changes you made to the current model. Continue?
|
864
|
+
</div>
|
865
|
+
<div id="confirm-load-from-repo-buttons">
|
866
|
+
<img class="ok-btn big-btn" src="images/ok.png">
|
867
|
+
Yes
|
868
|
+
<img class="cancel-btn big-btn" src="images/cancel.png">
|
869
|
+
No
|
870
|
+
</div>
|
871
|
+
</div>
|
872
|
+
</div>
|
873
|
+
|
854
874
|
<!-- the CONFIRM DELETE modal asks to confirm to delete module from repository -->
|
855
875
|
<div id="confirm-delete-from-repo-modal" class="modal">
|
856
876
|
<div id="confirm-delete-from-repo-dlg" class="inp-dlg">
|
package/static/linny-r.css
CHANGED
@@ -1924,10 +1924,6 @@ td.it {
|
|
1924
1924
|
font-style: italic;
|
1925
1925
|
}
|
1926
1926
|
|
1927
|
-
td.series {
|
1928
|
-
|
1929
|
-
}
|
1930
|
-
|
1931
1927
|
td.io {
|
1932
1928
|
width: 12px;
|
1933
1929
|
}
|
@@ -2116,7 +2112,7 @@ td.array::before {
|
|
2116
2112
|
td.series::before {
|
2117
2113
|
content: ' \28B8';
|
2118
2114
|
color: #b00080;
|
2119
|
-
margin-left: -
|
2115
|
+
margin-left: -4px;
|
2120
2116
|
}
|
2121
2117
|
|
2122
2118
|
td.outcome.modif::before {
|
@@ -2134,7 +2130,7 @@ td.array.modif::before {
|
|
2134
2130
|
td.series.modif::before {
|
2135
2131
|
content: ' \28B8\2045';
|
2136
2132
|
color: #b00080;
|
2137
|
-
margin-left: -
|
2133
|
+
margin-left: -4px;
|
2138
2134
|
}
|
2139
2135
|
|
2140
2136
|
td.blackbox::before {
|
@@ -4036,6 +4032,11 @@ select.i-param {
|
|
4036
4032
|
max-width: calc(100% - 15px);
|
4037
4033
|
}
|
4038
4034
|
|
4035
|
+
#confirm-load-from-repo-dlg {
|
4036
|
+
width: 400px;
|
4037
|
+
height: 85px;
|
4038
|
+
}
|
4039
|
+
|
4039
4040
|
#confirm-delete-from-repo-dlg {
|
4040
4041
|
width: 270px;
|
4041
4042
|
height: 120px;
|
@@ -4047,12 +4048,14 @@ select.i-param {
|
|
4047
4048
|
font-weight: bold;
|
4048
4049
|
}
|
4049
4050
|
|
4051
|
+
#confirm-load-from-repo-mod-name,
|
4050
4052
|
#confirm-delete-from-repo-mod-name {
|
4051
4053
|
word-break: keep-all;
|
4052
4054
|
white-space: nowrap;
|
4053
4055
|
font-family: monospace;
|
4054
4056
|
}
|
4055
4057
|
|
4058
|
+
#confirm-load-from-repo-msg,
|
4056
4059
|
#confirm-delete-from-repo-msg {
|
4057
4060
|
height: calc(100% - 55px);
|
4058
4061
|
}
|
@@ -4577,6 +4580,7 @@ div.call-stack-expr {
|
|
4577
4580
|
height: min-content;
|
4578
4581
|
}
|
4579
4582
|
|
4583
|
+
#confirm-load-from-repo-msg,
|
4580
4584
|
#confirm-delete-from-repo-msg,
|
4581
4585
|
#check-update-msg {
|
4582
4586
|
width: calc(100% - 8px);
|
@@ -4585,6 +4589,7 @@ div.call-stack-expr {
|
|
4585
4589
|
}
|
4586
4590
|
|
4587
4591
|
#confirm-move-buttons,
|
4592
|
+
#confirm-load-from-repo-buttons,
|
4588
4593
|
#confirm-delete-from-repo-buttons,
|
4589
4594
|
#check-update-buttons {
|
4590
4595
|
width: 100%;
|
@@ -4595,6 +4600,7 @@ div.call-stack-expr {
|
|
4595
4600
|
}
|
4596
4601
|
|
4597
4602
|
#confirm-move-buttons > img,
|
4603
|
+
#confirm-load-from-repo-buttons > img,
|
4598
4604
|
#confirm-delete-from-repo-buttons > img,
|
4599
4605
|
#check-update-buttons > img {
|
4600
4606
|
float: none;
|
@@ -974,6 +974,9 @@ class SensitivityAnalysis {
|
|
974
974
|
// Class ExperimentManager controls the collection of experiments of the model
|
975
975
|
class ExperimentManager {
|
976
976
|
constructor() {
|
977
|
+
// NOTE: the properties below are relevant only for the GUI
|
978
|
+
this.experiment_table = null;
|
979
|
+
this.focal_table = null;
|
977
980
|
}
|
978
981
|
|
979
982
|
reset() {
|
@@ -1005,6 +1008,7 @@ class ExperimentManager {
|
|
1005
1008
|
selectExperiment(title) {
|
1006
1009
|
const xi = MODEL.indexOfExperiment(title);
|
1007
1010
|
this.selected_experiment = (xi < 0 ? null : MODEL.experiments[xi]);
|
1011
|
+
this.focal_table = this.experiment_table;
|
1008
1012
|
this.updateDialog();
|
1009
1013
|
}
|
1010
1014
|
|
@@ -4378,6 +4378,13 @@ class GUIController extends Controller {
|
|
4378
4378
|
const btns = topmod.getElementsByClassName('ok-btn');
|
4379
4379
|
if(btns.length > 0) btns[0].dispatchEvent(new Event('click'));
|
4380
4380
|
}
|
4381
|
+
} else if(this.dr_dialog_order.length > 0) {
|
4382
|
+
// Send ENTER key event to the top draggable dialog
|
4383
|
+
const last = this.dr_dialog_order.length - 1;
|
4384
|
+
if(last >= 0) {
|
4385
|
+
const mgr = window[this.dr_dialog_order[last].dataset.manager];
|
4386
|
+
if(mgr && 'enterKey' in mgr) mgr.enterKey();
|
4387
|
+
}
|
4381
4388
|
}
|
4382
4389
|
} else if(e.keyCode === 8 &&
|
4383
4390
|
ttype !== 'text' && ttype !== 'password' && ttype !== 'textarea') {
|
@@ -4392,7 +4399,18 @@ class GUIController extends Controller {
|
|
4392
4399
|
return;
|
4393
4400
|
}
|
4394
4401
|
}
|
4395
|
-
//
|
4402
|
+
// Up and down arrow keys
|
4403
|
+
if([38, 40].indexOf(e.keyCode) >= 0) {
|
4404
|
+
e.preventDefault();
|
4405
|
+
// Send event to the top draggable dialog
|
4406
|
+
const last = this.dr_dialog_order.length - 1;
|
4407
|
+
if(last >= 0) {
|
4408
|
+
const mgr = window[this.dr_dialog_order[last].dataset.manager];
|
4409
|
+
// NOTE: pass key direction as -1 for UP and +1 for DOWN
|
4410
|
+
if(mgr && 'upDownKey' in mgr) mgr.upDownKey(e.keyCode - 39);
|
4411
|
+
}
|
4412
|
+
}
|
4413
|
+
// end, home, Left and right arrow keys
|
4396
4414
|
if([35, 36, 37, 39].indexOf(e.keyCode) >= 0) e.preventDefault();
|
4397
4415
|
if(e.keyCode === 35) {
|
4398
4416
|
MODEL.t = MODEL.end_period - MODEL.start_period + 1;
|
@@ -8478,7 +8496,7 @@ class GUIRepositoryBrowser extends RepositoryBrowser {
|
|
8478
8496
|
document.getElementById('repo-include-btn').addEventListener(
|
8479
8497
|
'click', () => REPOSITORY_BROWSER.includeModule());
|
8480
8498
|
document.getElementById('repo-load-btn').addEventListener(
|
8481
|
-
'click', () => REPOSITORY_BROWSER.
|
8499
|
+
'click', () => REPOSITORY_BROWSER.confirmLoadModuleAsModel());
|
8482
8500
|
document.getElementById('repo-store-btn').addEventListener(
|
8483
8501
|
'click', () => REPOSITORY_BROWSER.promptForStoring());
|
8484
8502
|
document.getElementById('repo-black-box-btn').addEventListener(
|
@@ -8525,6 +8543,12 @@ class GUIRepositoryBrowser extends RepositoryBrowser {
|
|
8525
8543
|
this.include_modal.element('actor').addEventListener(
|
8526
8544
|
'blur', () => REPOSITORY_BROWSER.updateActors());
|
8527
8545
|
|
8546
|
+
this.confirm_load_modal = new ModalDialog('confirm-load-from-repo');
|
8547
|
+
this.confirm_load_modal.ok.addEventListener(
|
8548
|
+
'click', () => REPOSITORY_BROWSER.loadModuleAsModel());
|
8549
|
+
this.confirm_load_modal.cancel.addEventListener(
|
8550
|
+
'click', () => REPOSITORY_BROWSER.confirm_load_modal.hide());
|
8551
|
+
|
8528
8552
|
this.confirm_delete_modal = new ModalDialog('confirm-delete-from-repo');
|
8529
8553
|
this.confirm_delete_modal.ok.addEventListener(
|
8530
8554
|
'click', () => REPOSITORY_BROWSER.deleteFromRepository());
|
@@ -8536,6 +8560,31 @@ class GUIRepositoryBrowser extends RepositoryBrowser {
|
|
8536
8560
|
super.reset();
|
8537
8561
|
this.last_time_selected = 0;
|
8538
8562
|
}
|
8563
|
+
|
8564
|
+
enterKey() {
|
8565
|
+
// Open "edit properties" dialog for the selected entity
|
8566
|
+
const srl = this.modules_table.getElementsByClassName('sel-set');
|
8567
|
+
if(srl.length > 0) {
|
8568
|
+
const r = this.modules_table.rows[srl[0].rowIndex];
|
8569
|
+
if(r) {
|
8570
|
+
// Ensure that click will be interpreted as double-click
|
8571
|
+
this.last_time_selected = Date.now();
|
8572
|
+
r.dispatchEvent(new Event('click'));
|
8573
|
+
}
|
8574
|
+
}
|
8575
|
+
}
|
8576
|
+
|
8577
|
+
upDownKey(dir) {
|
8578
|
+
// Select row above or below the selected one (if possible)
|
8579
|
+
const srl = this.modules_table.getElementsByClassName('sel-set');
|
8580
|
+
if(srl.length > 0) {
|
8581
|
+
const r = this.modules_table.rows[srl[0].rowIndex + dir];
|
8582
|
+
if(r) {
|
8583
|
+
UI.scrollIntoView(r);
|
8584
|
+
r.dispatchEvent(new Event('click'));
|
8585
|
+
}
|
8586
|
+
}
|
8587
|
+
}
|
8539
8588
|
|
8540
8589
|
get isLocalHost() {
|
8541
8590
|
// Returns TRUE if first repository on the list is 'local host'
|
@@ -8718,7 +8767,7 @@ class GUIRepositoryBrowser extends RepositoryBrowser {
|
|
8718
8767
|
// Consider click to be "double" if it occurred less than 300 ms ago
|
8719
8768
|
if(dt < 300) {
|
8720
8769
|
this.last_time_selected = 0;
|
8721
|
-
this.
|
8770
|
+
this.includeModule();
|
8722
8771
|
return;
|
8723
8772
|
}
|
8724
8773
|
}
|
@@ -8967,6 +9016,7 @@ class GUIRepositoryBrowser extends RepositoryBrowser {
|
|
8967
9016
|
|
8968
9017
|
loadModuleAsModel() {
|
8969
9018
|
// Loads selected module as model
|
9019
|
+
this.confirm_load_modal.hide();
|
8970
9020
|
if(this.repository_index >= 0 && this.module_index >= 0) {
|
8971
9021
|
// NOTE: when loading new model, the stay-on-top dialogs must be reset
|
8972
9022
|
UI.hideStayOnTopDialogs();
|
@@ -8983,6 +9033,17 @@ class GUIRepositoryBrowser extends RepositoryBrowser {
|
|
8983
9033
|
r.loadModule(this.module_index, true);
|
8984
9034
|
}
|
8985
9035
|
}
|
9036
|
+
|
9037
|
+
confirmLoadModuleAsModel() {
|
9038
|
+
// Prompts modeler to confirm loading the selected module as model
|
9039
|
+
if(this.repository_index >= 0 && this.module_index >= 0 &&
|
9040
|
+
document.getElementById('repo-load-btn').classList.contains('enab')) {
|
9041
|
+
const r = this.repositories[this.repository_index];
|
9042
|
+
this.confirm_load_modal.element('mod-name').innerText =
|
9043
|
+
r.module_names[this.module_index];
|
9044
|
+
this.confirm_load_modal.show();
|
9045
|
+
}
|
9046
|
+
}
|
8986
9047
|
|
8987
9048
|
confirmDeleteFromRepository() {
|
8988
9049
|
// Prompts modeler to confirm deletion of the selected module
|
@@ -9034,7 +9095,7 @@ class GUIDatasetManager extends DatasetManager {
|
|
9034
9095
|
this.filter_text = document.getElementById('ds-filter-text');
|
9035
9096
|
this.filter_text.addEventListener(
|
9036
9097
|
'input', () => DATASET_MANAGER.changeFilter());
|
9037
|
-
this.
|
9098
|
+
this.dataset_table = document.getElementById('dataset-table');
|
9038
9099
|
// Data properties pane
|
9039
9100
|
this.properties = document.getElementById('dataset-properties');
|
9040
9101
|
// Toggle buttons at bottom of dialog
|
@@ -9056,6 +9117,8 @@ class GUIDatasetManager extends DatasetManager {
|
|
9056
9117
|
'click', () => DATASET_MANAGER.editExpression());
|
9057
9118
|
document.getElementById('ds-delete-modif-btn').addEventListener(
|
9058
9119
|
'click', () => DATASET_MANAGER.deleteModifier());
|
9120
|
+
// Modifier table
|
9121
|
+
this.modifier_table = document.getElementById('dataset-modif-table');
|
9059
9122
|
// Modal dialogs
|
9060
9123
|
this.new_modal = new ModalDialog('new-dataset');
|
9061
9124
|
this.new_modal.ok.addEventListener(
|
@@ -9104,7 +9167,59 @@ class GUIDatasetManager extends DatasetManager {
|
|
9104
9167
|
this.selected_modifier = null;
|
9105
9168
|
this.edited_expression = null;
|
9106
9169
|
this.filter_pattern = null;
|
9107
|
-
this.
|
9170
|
+
this.clicked_object = null;
|
9171
|
+
this.last_time_clicked = 0;
|
9172
|
+
this.focal_table = null;
|
9173
|
+
}
|
9174
|
+
|
9175
|
+
doubleClicked(obj) {
|
9176
|
+
const
|
9177
|
+
now = Date.now(),
|
9178
|
+
dt = now - this.last_time_clicked;
|
9179
|
+
this.last_time_clicked = now;
|
9180
|
+
if(obj === this.clicked_object) {
|
9181
|
+
// Consider click to be "double" if it occurred less than 300 ms ago
|
9182
|
+
if(dt < 300) {
|
9183
|
+
this.last_time_clicked = 0;
|
9184
|
+
return true;
|
9185
|
+
}
|
9186
|
+
}
|
9187
|
+
this.clicked_object = obj;
|
9188
|
+
return false;
|
9189
|
+
}
|
9190
|
+
|
9191
|
+
enterKey() {
|
9192
|
+
// Open "edit" dialog for the selected dataset or modifier expression
|
9193
|
+
const srl = this.focal_table.getElementsByClassName('sel-set');
|
9194
|
+
if(srl.length > 0) {
|
9195
|
+
const r = this.focal_table.rows[srl[0].rowIndex];
|
9196
|
+
if(r) {
|
9197
|
+
const e = new Event('click');
|
9198
|
+
if(this.focal_table === this.dataset_table) {
|
9199
|
+
// Emulate Alt-click in the table to open the time series dialog
|
9200
|
+
e.altKey = true;
|
9201
|
+
r.dispatchEvent(e);
|
9202
|
+
} else if(this.focal_table === this.modifier_table) {
|
9203
|
+
// Emulate a double-click on the second cell to edit the expression
|
9204
|
+
this.last_time_clicked = Date.now();
|
9205
|
+
r.cells[1].dispatchEvent(e);
|
9206
|
+
}
|
9207
|
+
}
|
9208
|
+
}
|
9209
|
+
}
|
9210
|
+
|
9211
|
+
upDownKey(dir) {
|
9212
|
+
// Select row above or below the selected one (if possible)
|
9213
|
+
const srl = this.focal_table.getElementsByClassName('sel-set');
|
9214
|
+
if(srl.length > 0) {
|
9215
|
+
let r = this.focal_table.rows[srl[0].rowIndex + dir];
|
9216
|
+
if(r) {
|
9217
|
+
UI.scrollIntoView(r);
|
9218
|
+
// NOTE: cell, not row, listens for onclick event
|
9219
|
+
if(this.focal_table === this.modifier_table) r = r.cells[1];
|
9220
|
+
r.dispatchEvent(new Event('click'));
|
9221
|
+
}
|
9222
|
+
}
|
9108
9223
|
}
|
9109
9224
|
|
9110
9225
|
updateDialog() {
|
@@ -9148,10 +9263,10 @@ class GUIDatasetManager extends DatasetManager {
|
|
9148
9263
|
'\', event.shiftKey);"><td', cls, '>', d.displayName,
|
9149
9264
|
'</td></tr>'].join(''));
|
9150
9265
|
}
|
9151
|
-
this.
|
9266
|
+
this.dataset_table.innerHTML = dl.join('');
|
9152
9267
|
const btns = 'ds-data ds-rename ds-clone ds-delete';
|
9153
9268
|
if(sd) {
|
9154
|
-
this.
|
9269
|
+
this.dataset_table.innerHTML = dl.join('');
|
9155
9270
|
this.properties.style.display = 'block';
|
9156
9271
|
document.getElementById('dataset-default').innerHTML =
|
9157
9272
|
VM.sig4Dig(sd.default_value) +
|
@@ -9234,7 +9349,7 @@ class GUIDatasetManager extends DatasetManager {
|
|
9234
9349
|
m.selector, '</td><td class="dataset-expression',
|
9235
9350
|
clk, ');">', m.expression.text, '</td></tr>'].join(''));
|
9236
9351
|
}
|
9237
|
-
|
9352
|
+
this.modifier_table.innerHTML = ml.join('');
|
9238
9353
|
ttls.style.display = 'block';
|
9239
9354
|
msa.style.display = 'block';
|
9240
9355
|
mbtns.style.display = 'block';
|
@@ -9279,16 +9394,13 @@ class GUIDatasetManager extends DatasetManager {
|
|
9279
9394
|
|
9280
9395
|
selectDataset(event, id) {
|
9281
9396
|
// Select dataset, or edit it when Alt- or double-clicked
|
9397
|
+
this.focal_table = this.dataset_table;
|
9282
9398
|
const
|
9283
9399
|
d = MODEL.datasets[id] || null,
|
9284
|
-
|
9285
|
-
dt = now - this.last_time_selected,
|
9286
|
-
// Consider click to be "double" if it occurred less than 300 ms ago
|
9287
|
-
edit = event.altKey || (d === this.selected_dataset && dt < 300);
|
9400
|
+
edit = event.altKey || this.doubleClicked(d);
|
9288
9401
|
this.selected_dataset = d;
|
9289
|
-
this.last_time_selected = now;
|
9290
9402
|
if(d && edit) {
|
9291
|
-
this.
|
9403
|
+
this.last_time_clicked = 0;
|
9292
9404
|
this.editData();
|
9293
9405
|
return;
|
9294
9406
|
}
|
@@ -9298,19 +9410,13 @@ class GUIDatasetManager extends DatasetManager {
|
|
9298
9410
|
selectModifier(event, id, x=true) {
|
9299
9411
|
// Select modifier, or when double-clicked, edit its expression or the
|
9300
9412
|
// name of the modifier
|
9413
|
+
this.focal_table = this.modifier_table;
|
9301
9414
|
if(this.selected_dataset) {
|
9302
9415
|
const m = this.selected_dataset.modifiers[UI.nameToID(id)],
|
9303
|
-
|
9304
|
-
dt = now - this.last_time_selected,
|
9305
|
-
// NOTE: Alt-click and double-click indicate: edit
|
9306
|
-
// Consider click to be "double" if the same modifier was clicked
|
9307
|
-
// less than 300 ms ago
|
9308
|
-
edit = event.altKey || (m === this.selected_modifier && dt < 300);
|
9309
|
-
this.last_time_selected = now;
|
9416
|
+
edit = event.altKey || this.doubleClicked(m);
|
9310
9417
|
if(event.shiftKey) {
|
9311
9418
|
// NOTE: prepare to update HTML class of selected dataset
|
9312
|
-
const el =
|
9313
|
-
.getElementsByClassName('sel-set')[0];
|
9419
|
+
const el = this.dataset_table.getElementsByClassName('sel-set')[0];
|
9314
9420
|
// Toggle dataset default selector
|
9315
9421
|
if(m.selector === this.selected_dataset.default_selector) {
|
9316
9422
|
this.selected_dataset.default_selector = '';
|
@@ -9322,7 +9428,7 @@ class GUIDatasetManager extends DatasetManager {
|
|
9322
9428
|
}
|
9323
9429
|
this.selected_modifier = m;
|
9324
9430
|
if(edit) {
|
9325
|
-
this.
|
9431
|
+
this.last_time_clicked = 0;
|
9326
9432
|
if(x) {
|
9327
9433
|
this.editExpression();
|
9328
9434
|
} else {
|
@@ -9743,7 +9849,49 @@ class EquationManager {
|
|
9743
9849
|
this.visible = false;
|
9744
9850
|
this.selected_modifier = null;
|
9745
9851
|
this.edited_expression = null;
|
9746
|
-
this.
|
9852
|
+
this.last_time_clicked = 0;
|
9853
|
+
}
|
9854
|
+
|
9855
|
+
doubleClicked(obj) {
|
9856
|
+
const
|
9857
|
+
now = Date.now(),
|
9858
|
+
dt = now - this.last_time_clicked;
|
9859
|
+
this.last_time_clicked = now;
|
9860
|
+
if(obj === this.clicked_object) {
|
9861
|
+
// Consider click to be "double" if it occurred less than 300 ms ago
|
9862
|
+
if(dt < 300) {
|
9863
|
+
this.last_time_clicked = 0;
|
9864
|
+
return true;
|
9865
|
+
}
|
9866
|
+
}
|
9867
|
+
this.clicked_object = obj;
|
9868
|
+
return false;
|
9869
|
+
}
|
9870
|
+
|
9871
|
+
enterKey() {
|
9872
|
+
// Open the expression editor for the selected equation
|
9873
|
+
const srl = this.table.getElementsByClassName('sel-set');
|
9874
|
+
if(srl.length > 0) {
|
9875
|
+
const r = this.table.rows[srl[0].rowIndex];
|
9876
|
+
if(r) {
|
9877
|
+
// Emulate a double-click on the second cell to edit the expression
|
9878
|
+
this.last_time_clicked = Date.now();
|
9879
|
+
r.cells[1].dispatchEvent(new Event('click'));
|
9880
|
+
}
|
9881
|
+
}
|
9882
|
+
}
|
9883
|
+
|
9884
|
+
upDownKey(dir) {
|
9885
|
+
// Select row above or below the selected one (if possible)
|
9886
|
+
const srl = this.table.getElementsByClassName('sel-set');
|
9887
|
+
if(srl.length > 0) {
|
9888
|
+
const r = this.table.rows[srl[0].rowIndex + dir];
|
9889
|
+
if(r) {
|
9890
|
+
UI.scrollIntoView(r);
|
9891
|
+
// NOTE: not row but cell listens for onclick
|
9892
|
+
r.cells[1].dispatchEvent(new Event('click'));
|
9893
|
+
}
|
9894
|
+
}
|
9747
9895
|
}
|
9748
9896
|
|
9749
9897
|
updateDialog() {
|
@@ -9789,14 +9937,9 @@ class EquationManager {
|
|
9789
9937
|
if(MODEL.equations_dataset) {
|
9790
9938
|
const
|
9791
9939
|
m = MODEL.equations_dataset.modifiers[UI.nameToID(id)] || null,
|
9792
|
-
|
9793
|
-
dt = now - this.last_time_selected,
|
9794
|
-
// Consider click to be "double" if it occurred less than 300 ms ago
|
9795
|
-
edit = event.altKey || (m === this.selected_modifier && dt < 300);
|
9796
|
-
this.last_time_selected = now;
|
9940
|
+
edit = event.altKey || this.doubleClicked(m);
|
9797
9941
|
this.selected_modifier = m;
|
9798
9942
|
if(m && edit) {
|
9799
|
-
this.last_time_selected = 0;
|
9800
9943
|
if(x) {
|
9801
9944
|
this.editEquation();
|
9802
9945
|
} else {
|
@@ -10088,6 +10231,31 @@ class GUIChartManager extends ChartManager {
|
|
10088
10231
|
this.last_time_selected = 0;
|
10089
10232
|
}
|
10090
10233
|
|
10234
|
+
enterKey() {
|
10235
|
+
// Open "edit" dialog for the selected chart variable
|
10236
|
+
const srl = this.variables_table.getElementsByClassName('sel-set');
|
10237
|
+
if(srl.length > 0) {
|
10238
|
+
const r = this.variables_table.rows[srl[0].rowIndex];
|
10239
|
+
if(r) {
|
10240
|
+
// Emulate a double-click to edit the variable properties
|
10241
|
+
this.last_time_selected = Date.now();
|
10242
|
+
r.dispatchEvent(new Event('click'));
|
10243
|
+
}
|
10244
|
+
}
|
10245
|
+
}
|
10246
|
+
|
10247
|
+
upDownKey(dir) {
|
10248
|
+
// Select row above or below the selected one (if possible)
|
10249
|
+
const srl = this.variables_table.getElementsByClassName('sel-set');
|
10250
|
+
if(srl.length > 0) {
|
10251
|
+
const r = this.variables_table.rows[srl[0].rowIndex + dir];
|
10252
|
+
if(r) {
|
10253
|
+
UI.scrollIntoView(r);
|
10254
|
+
r.dispatchEvent(new Event('click'));
|
10255
|
+
}
|
10256
|
+
}
|
10257
|
+
}
|
10258
|
+
|
10091
10259
|
setRunsChart(show) {
|
10092
10260
|
// Indicates whether the chart manager should display a run result chart
|
10093
10261
|
this.runs_chart = show;
|
@@ -11552,7 +11720,10 @@ class GUIExperimentManager extends ExperimentManager {
|
|
11552
11720
|
this.default_message = document.getElementById('experiment-default-message');
|
11553
11721
|
|
11554
11722
|
this.design = document.getElementById('experiment-design');
|
11723
|
+
this.experiment_table = document.getElementById('experiment-table');
|
11555
11724
|
this.params_div = document.getElementById('experiment-params-div');
|
11725
|
+
this.dimension_table = document.getElementById('experiment-dim-table');
|
11726
|
+
this.chart_table = document.getElementById('experiment-chart-table');
|
11556
11727
|
// NOTE: the Exclude input field responds to several events
|
11557
11728
|
this.exclude = document.getElementById('experiment-exclude');
|
11558
11729
|
this.exclude.addEventListener(
|
@@ -11749,9 +11920,22 @@ class GUIExperimentManager extends ExperimentManager {
|
|
11749
11920
|
this.edited_dimension_index = -1;
|
11750
11921
|
this.edited_combi_selector_index = -1;
|
11751
11922
|
this.color_scale = new ColorScale('no');
|
11923
|
+
this.focal_table = null;
|
11752
11924
|
this.designMode();
|
11753
11925
|
}
|
11754
11926
|
|
11927
|
+
upDownKey(dir) {
|
11928
|
+
// Select row above or below the selected one (if possible)
|
11929
|
+
const srl = this.focal_table.getElementsByClassName('sel-set');
|
11930
|
+
if(srl.length > 0) {
|
11931
|
+
const r = this.focal_table.rows[srl[0].rowIndex + dir];
|
11932
|
+
if(r) {
|
11933
|
+
UI.scrollIntoView(r);
|
11934
|
+
r.dispatchEvent(new Event('click'));
|
11935
|
+
}
|
11936
|
+
}
|
11937
|
+
}
|
11938
|
+
|
11755
11939
|
updateDialog() {
|
11756
11940
|
this.updateChartList();
|
11757
11941
|
// Warn modeler if no meaningful experiments can be defined
|
@@ -11785,7 +11969,7 @@ class GUIExperimentManager extends ExperimentManager {
|
|
11785
11969
|
'\');" onmouseover="EXPERIMENT_MANAGER.showInfo(', xi,
|
11786
11970
|
', event.shiftKey);"><td>', x.title, '</td></tr>'].join(''));
|
11787
11971
|
}
|
11788
|
-
|
11972
|
+
this.experiment_table.innerHTML = xl.join('');
|
11789
11973
|
const
|
11790
11974
|
btns = 'xp-rename xp-view xp-delete xp-ignore',
|
11791
11975
|
icnt = document.getElementById('xp-ignore-count');
|
@@ -11849,7 +12033,7 @@ class GUIExperimentManager extends ExperimentManager {
|
|
11849
12033
|
setString(x.dimensions[i]),
|
11850
12034
|
'</td></tr>'].join(''));
|
11851
12035
|
}
|
11852
|
-
|
12036
|
+
this.dimension_table.innerHTML = tr.join('');
|
11853
12037
|
// Add button must be enabled only if there still are unused dimensions
|
11854
12038
|
if(x.available_dimensions.length > 0) {
|
11855
12039
|
document.getElementById('xp-d-add-btn').classList.remove('v-disab');
|
@@ -11865,7 +12049,7 @@ class GUIExperimentManager extends ExperimentManager {
|
|
11865
12049
|
i, '\');"><td>',
|
11866
12050
|
x.charts[i].title, '</td></tr>'].join(''));
|
11867
12051
|
}
|
11868
|
-
|
12052
|
+
this.chart_table.innerHTML = tr.join('');
|
11869
12053
|
if(x.charts.length === 0) canview = false;
|
11870
12054
|
if(tr.length >= this.suitable_charts.length) {
|
11871
12055
|
document.getElementById('xp-c-add-btn').classList.add('v-disab');
|
@@ -12525,6 +12709,8 @@ N = ${rr.N}, vector length = ${rr.vector.length}` : '')].join('');
|
|
12525
12709
|
|
12526
12710
|
selectParameter(p) {
|
12527
12711
|
this.selected_parameter = p;
|
12712
|
+
this.focal_table = (p.startsWith('d') ? this.dimension_table :
|
12713
|
+
this.chart_table);
|
12528
12714
|
this.updateDialog();
|
12529
12715
|
}
|
12530
12716
|
|
@@ -14142,7 +14328,10 @@ class Finder {
|
|
14142
14328
|
this.copy_btn = document.getElementById('finder-copy-btn');
|
14143
14329
|
this.copy_btn.addEventListener(
|
14144
14330
|
'click', (event) => FINDER.copyAttributesToClipboard(event.shiftKey));
|
14145
|
-
|
14331
|
+
this.entity_table = document.getElementById('finder-table');
|
14332
|
+
this.item_table = document.getElementById('finder-item-table');
|
14333
|
+
this.expression_table = document.getElementById('finder-expression-table');
|
14334
|
+
|
14146
14335
|
// Attribute headers are used by Finder to output entity attribute values
|
14147
14336
|
this.attribute_headers = {
|
14148
14337
|
A: 'ACTORS:\tWeight\tCash IN\tCash OUT\tCash FLOW',
|
@@ -14174,6 +14363,47 @@ class Finder {
|
|
14174
14363
|
this.product_cluster_index = 0;
|
14175
14364
|
}
|
14176
14365
|
|
14366
|
+
doubleClicked(obj) {
|
14367
|
+
const
|
14368
|
+
now = Date.now(),
|
14369
|
+
dt = now - this.last_time_clicked;
|
14370
|
+
this.last_time_clicked = now;
|
14371
|
+
if(obj === this.clicked_object) {
|
14372
|
+
// Consider click to be "double" if it occurred less than 300 ms ago
|
14373
|
+
if(dt < 300) {
|
14374
|
+
this.last_time_clicked = 0;
|
14375
|
+
return true;
|
14376
|
+
}
|
14377
|
+
}
|
14378
|
+
this.clicked_object = obj;
|
14379
|
+
return false;
|
14380
|
+
}
|
14381
|
+
|
14382
|
+
enterKey() {
|
14383
|
+
// Open "edit properties" dialog for the selected entity
|
14384
|
+
const srl = this.entity_table.getElementsByClassName('sel-set');
|
14385
|
+
if(srl.length > 0) {
|
14386
|
+
const r = this.entity_table.rows[srl[0].rowIndex];
|
14387
|
+
if(r) {
|
14388
|
+
const e = new Event('click');
|
14389
|
+
e.altKey = true;
|
14390
|
+
r.dispatchEvent(e);
|
14391
|
+
}
|
14392
|
+
}
|
14393
|
+
}
|
14394
|
+
|
14395
|
+
upDownKey(dir) {
|
14396
|
+
// Select row above or below the selected one (if possible)
|
14397
|
+
const srl = this.entity_table.getElementsByClassName('sel-set');
|
14398
|
+
if(srl.length > 0) {
|
14399
|
+
const r = this.entity_table.rows[srl[0].rowIndex + dir];
|
14400
|
+
if(r) {
|
14401
|
+
UI.scrollIntoView(r);
|
14402
|
+
r.dispatchEvent(new Event('click'));
|
14403
|
+
}
|
14404
|
+
}
|
14405
|
+
}
|
14406
|
+
|
14177
14407
|
updateDialog() {
|
14178
14408
|
const
|
14179
14409
|
el = [],
|
@@ -14289,7 +14519,7 @@ class Finder {
|
|
14289
14519
|
if(e === se) seid += i;
|
14290
14520
|
el.push(['<tr id="etr', i, '" class="dataset',
|
14291
14521
|
(e === se ? ' sel-set' : ''), '" onclick="FINDER.selectEntity(\'',
|
14292
|
-
enl[i], '\');" onmouseover="FINDER.showInfo(\'', enl[i],
|
14522
|
+
enl[i], '\', event.altKey);" onmouseover="FINDER.showInfo(\'', enl[i],
|
14293
14523
|
'\', event.shiftKey);"><td draggable="true" ',
|
14294
14524
|
'ondragstart="FINDER.drag(event);"><img class="finder" src="images/',
|
14295
14525
|
e.type.toLowerCase(), '.png">', e.displayName,
|
@@ -14297,7 +14527,7 @@ class Finder {
|
|
14297
14527
|
}
|
14298
14528
|
// NOTE: reset `selected_entity` if not in the new list
|
14299
14529
|
if(seid === 'etr') this.selected_entity = null;
|
14300
|
-
|
14530
|
+
this.entity_table.innerHTML = el.join('');
|
14301
14531
|
UI.scrollIntoView(document.getElementById(seid));
|
14302
14532
|
document.getElementById('finder-count').innerHTML = pluralS(
|
14303
14533
|
el.length, 'entity', 'entities');
|
@@ -14452,7 +14682,7 @@ class Finder {
|
|
14452
14682
|
e.type.toLowerCase(), '.png">', e.displayName,
|
14453
14683
|
'</td></tr>'].join(''));
|
14454
14684
|
}
|
14455
|
-
|
14685
|
+
this.item_table.innerHTML = el.join('');
|
14456
14686
|
// Clear the table row list
|
14457
14687
|
el.length = 0;
|
14458
14688
|
// Now fill it with entity+attribute having a matching expression
|
@@ -14480,7 +14710,7 @@ class Finder {
|
|
14480
14710
|
'<img class="finder" src="images/', img, '.png">', td, '</td></tr>'
|
14481
14711
|
].join(''));
|
14482
14712
|
}
|
14483
|
-
|
14713
|
+
this.expression_table.innerHTML = el.join('');
|
14484
14714
|
document.getElementById('finder-expression-hdr').innerHTML =
|
14485
14715
|
pluralS(el.length, 'expression');
|
14486
14716
|
}
|
@@ -14515,10 +14745,37 @@ class Finder {
|
|
14515
14745
|
if(e) DOCUMENTATION_MANAGER.update(e, shift);
|
14516
14746
|
}
|
14517
14747
|
|
14518
|
-
selectEntity(id) {
|
14519
|
-
// Looks up entity, selects it in the left pane, and updates the
|
14520
|
-
|
14748
|
+
selectEntity(id, alt=false) {
|
14749
|
+
// Looks up entity, selects it in the left pane, and updates the
|
14750
|
+
// right pane; opens the "edit properties" modal dialog on double-click
|
14751
|
+
// and Alt-click if the entity is editable
|
14752
|
+
const obj = MODEL.objectByID(id);
|
14753
|
+
this.selected_entity = obj;
|
14521
14754
|
this.updateDialog();
|
14755
|
+
if(!obj) return;
|
14756
|
+
if(alt || this.doubleClicked(obj)) {
|
14757
|
+
if(obj instanceof Process) {
|
14758
|
+
UI.showProcessPropertiesDialog(obj);
|
14759
|
+
} else if(obj instanceof Product) {
|
14760
|
+
UI.showProductPropertiesDialog(obj);
|
14761
|
+
} else if(obj instanceof Link) {
|
14762
|
+
UI.showLinkPropertiesDialog(obj);
|
14763
|
+
} else if(obj instanceof Note) {
|
14764
|
+
obj.showNotePropertiesDialog();
|
14765
|
+
} else if(obj instanceof Dataset) {
|
14766
|
+
if(UI.hidden('dataset-dlg')) {
|
14767
|
+
UI.buttons.dataset.dispatchEvent(new Event('click'));
|
14768
|
+
}
|
14769
|
+
DATASET_MANAGER.selected_dataset = obj;
|
14770
|
+
DATASET_MANAGER.updateDialog();
|
14771
|
+
} else if(obj instanceof DatasetModifier) {
|
14772
|
+
if(UI.hidden('equation-dlg')) {
|
14773
|
+
UI.buttons.equation.dispatchEvent(new Event('click'));
|
14774
|
+
}
|
14775
|
+
EQUATION_MANAGER.selected_modifier = obj;
|
14776
|
+
EQUATION_MANAGER.updateDialog();
|
14777
|
+
}
|
14778
|
+
}
|
14522
14779
|
}
|
14523
14780
|
|
14524
14781
|
reveal(id) {
|
@@ -14569,22 +14826,12 @@ class Finder {
|
|
14569
14826
|
// NOTE: return the object to save a second lookup by revealExpression
|
14570
14827
|
return obj;
|
14571
14828
|
}
|
14572
|
-
|
14829
|
+
|
14573
14830
|
revealExpression(id, attr, shift=false, alt=false) {
|
14574
|
-
const
|
14575
|
-
|
14576
|
-
|
14577
|
-
|
14578
|
-
this.last_time_clicked = now;
|
14579
|
-
if(obj === this.clicked_object) {
|
14580
|
-
// Consider click to be "double" if it occurred less than 300 ms ago
|
14581
|
-
if(dt < 300) {
|
14582
|
-
this.last_time_clicked = 0;
|
14583
|
-
shift = true;
|
14584
|
-
}
|
14585
|
-
}
|
14586
|
-
this.clicked_object = obj;
|
14587
|
-
if(obj && attr && (shift || alt)) {
|
14831
|
+
const obj = this.reveal(id);
|
14832
|
+
if(!obj) return;
|
14833
|
+
shift = shift || this.doubleClicked(obj);
|
14834
|
+
if(attr && (shift || alt)) {
|
14588
14835
|
if(obj instanceof Process) {
|
14589
14836
|
// NOTE: the second argument makes the dialog focus on the specified
|
14590
14837
|
// attribute input field; the third makes it open the expression editor
|
@@ -689,8 +689,8 @@ class LinnyRModel {
|
|
689
689
|
// Merge into dimension if there are shared selectors
|
690
690
|
for(let i = 0; i < this.dimensions.length; i++) {
|
691
691
|
const c = complement(sl, this.dimensions[i]);
|
692
|
-
if(c.length
|
693
|
-
this.dimensions[i].push(...c);
|
692
|
+
if(c.length < sl.length) {
|
693
|
+
if(c.length > 0) this.dimensions[i].push(...c);
|
694
694
|
newdim = false;
|
695
695
|
break;
|
696
696
|
}
|
@@ -1181,8 +1181,8 @@ class LinnyRModel {
|
|
1181
1181
|
}
|
1182
1182
|
const id = UI.nameToID(name);
|
1183
1183
|
let d = this.namedObjectByID(id);
|
1184
|
-
if(d) {
|
1185
|
-
if(IO_CONTEXT
|
1184
|
+
if(d && d !== this.equations_dataset) {
|
1185
|
+
if(IO_CONTEXT) {
|
1186
1186
|
IO_CONTEXT.supersede(d);
|
1187
1187
|
} else {
|
1188
1188
|
// Preserve name uniqueness
|
@@ -1205,8 +1205,6 @@ class LinnyRModel {
|
|
1205
1205
|
if(eqds) {
|
1206
1206
|
// Restore pointer to original equations dataset
|
1207
1207
|
this.equations_dataset = eqds;
|
1208
|
-
// Add included equations with prefixed names
|
1209
|
-
console.log('HERE', d);
|
1210
1208
|
// Return the extended equations dataset
|
1211
1209
|
return eqds;
|
1212
1210
|
} else {
|
@@ -4773,7 +4771,14 @@ class NodeBox extends ObjectWithXYWH {
|
|
4773
4771
|
get numberContext() {
|
4774
4772
|
// Returns the string to be used to evaluate #, so for clusters, processes
|
4775
4773
|
// and products this is the string of trailing digits (or empty if none)
|
4776
|
-
|
4774
|
+
// of the node name, or if that does not end with a number, the trailing
|
4775
|
+
// digits of the first prefix (from right to left) that does
|
4776
|
+
const sn = this.name.split(UI.PREFIXER);
|
4777
|
+
let nc = endsWithDigits(sn.pop());
|
4778
|
+
while(!nc && sn.length > 0) {
|
4779
|
+
nc = endsWithDigits(sn.pop());
|
4780
|
+
}
|
4781
|
+
return nc;
|
4777
4782
|
}
|
4778
4783
|
|
4779
4784
|
rename(name, actor_name) {
|
@@ -7593,8 +7598,8 @@ class Link {
|
|
7593
7598
|
tn = this.from_node;
|
7594
7599
|
}
|
7595
7600
|
// Otherwise, the FROM node is checked first
|
7596
|
-
let nc =
|
7597
|
-
if(!nc) nc =
|
7601
|
+
let nc = fn.numberContext;
|
7602
|
+
if(!nc) nc = tn.numberContext;
|
7598
7603
|
return nc;
|
7599
7604
|
}
|
7600
7605
|
|
@@ -7847,7 +7852,12 @@ class Dataset {
|
|
7847
7852
|
|
7848
7853
|
get numberContext() {
|
7849
7854
|
// Returns the string to be used to evaluate # (empty string if undefined)
|
7850
|
-
|
7855
|
+
const sn = this.name.split(UI.PREFIXER);
|
7856
|
+
let nc = endsWithDigits(sn.pop());
|
7857
|
+
while(!nc && sn.length > 0) {
|
7858
|
+
nc = endsWithDigits(sn.pop());
|
7859
|
+
}
|
7860
|
+
return nc;
|
7851
7861
|
}
|
7852
7862
|
|
7853
7863
|
get selectorList() {
|