linny-r 1.4.3 → 1.4.5
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/README.md +102 -48
- package/package.json +1 -1
- package/server.js +31 -6
- package/static/images/check-off-not-same-changed.png +0 -0
- package/static/images/check-off-not-same-not-changed.png +0 -0
- package/static/images/check-off-same-changed.png +0 -0
- package/static/images/check-off-same-not-changed.png +0 -0
- package/static/images/check-on-not-same-changed.png +0 -0
- package/static/images/check-on-not-same-not-changed.png +0 -0
- package/static/images/check-on-same-changed.png +0 -0
- package/static/images/check-on-same-not-changed.png +0 -0
- package/static/images/eq-not-same-changed.png +0 -0
- package/static/images/eq-not-same-not-changed.png +0 -0
- package/static/images/eq-same-changed.png +0 -0
- package/static/images/eq-same-not-changed.png +0 -0
- package/static/images/ne-not-same-changed.png +0 -0
- package/static/images/ne-not-same-not-changed.png +0 -0
- package/static/images/ne-same-changed.png +0 -0
- package/static/images/ne-same-not-changed.png +0 -0
- package/static/images/sort-asc-lead.png +0 -0
- package/static/images/sort-asc.png +0 -0
- package/static/images/sort-desc-lead.png +0 -0
- package/static/images/sort-desc.png +0 -0
- package/static/images/sort-not.png +0 -0
- package/static/index.html +51 -35
- package/static/linny-r.css +167 -53
- package/static/scripts/linny-r-gui-actor-manager.js +340 -0
- package/static/scripts/linny-r-gui-chart-manager.js +944 -0
- package/static/scripts/linny-r-gui-constraint-editor.js +681 -0
- package/static/scripts/linny-r-gui-controller.js +4005 -0
- package/static/scripts/linny-r-gui-dataset-manager.js +1176 -0
- package/static/scripts/linny-r-gui-documentation-manager.js +739 -0
- package/static/scripts/linny-r-gui-equation-manager.js +307 -0
- package/static/scripts/linny-r-gui-experiment-manager.js +1944 -0
- package/static/scripts/linny-r-gui-expression-editor.js +450 -0
- package/static/scripts/linny-r-gui-file-manager.js +392 -0
- package/static/scripts/linny-r-gui-finder.js +727 -0
- package/static/scripts/linny-r-gui-model-autosaver.js +230 -0
- package/static/scripts/linny-r-gui-monitor.js +448 -0
- package/static/scripts/linny-r-gui-paper.js +2789 -0
- package/static/scripts/linny-r-gui-receiver.js +323 -0
- package/static/scripts/linny-r-gui-repository-browser.js +819 -0
- package/static/scripts/linny-r-gui-scale-unit-manager.js +244 -0
- package/static/scripts/linny-r-gui-sensitivity-analysis.js +778 -0
- package/static/scripts/linny-r-gui-undo-redo.js +560 -0
- package/static/scripts/linny-r-model.js +34 -15
- package/static/scripts/linny-r-utils.js +11 -1
- package/static/scripts/linny-r-vm.js +21 -12
- package/static/scripts/linny-r-gui.js +0 -16908
@@ -0,0 +1,944 @@
|
|
1
|
+
/*
|
2
|
+
Linny-R is an executable graphical specification language for (mixed integer)
|
3
|
+
linear programming (MILP) problems, especially unit commitment problems (UCP).
|
4
|
+
The Linny-R language and tool have been developed by Pieter Bots at Delft
|
5
|
+
University of Technology, starting in 2009. The project to develop a browser-
|
6
|
+
based version started in 2017. See https://linny-r.org for more information.
|
7
|
+
|
8
|
+
This JavaScript file (linny-r-gui-chartmgr.js) provides the GUI functionality
|
9
|
+
for the Linny-R Chart Manager dialog.
|
10
|
+
|
11
|
+
*/
|
12
|
+
|
13
|
+
/*
|
14
|
+
Copyright (c) 2017-2023 Delft University of Technology
|
15
|
+
|
16
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
17
|
+
of this software and associated documentation files (the "Software"), to deal
|
18
|
+
in the Software without restriction, including without limitation the rights to
|
19
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
20
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
21
|
+
so, subject to the following conditions:
|
22
|
+
|
23
|
+
The above copyright notice and this permission notice shall be included in
|
24
|
+
all copies or substantial portions of the Software.
|
25
|
+
|
26
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
27
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
28
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
29
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
30
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
31
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
32
|
+
SOFTWARE.
|
33
|
+
*/
|
34
|
+
|
35
|
+
// CLASS GUIChartManager
|
36
|
+
class GUIChartManager extends ChartManager {
|
37
|
+
constructor() {
|
38
|
+
super();
|
39
|
+
this.dialog = UI.draggableDialog('chart');
|
40
|
+
UI.resizableDialog('chart', 'CHART_MANAGER');
|
41
|
+
this.dialog.addEventListener('mousemove',
|
42
|
+
(event) => CHART_MANAGER.showInfo(event.shiftKey));
|
43
|
+
this.dialog.addEventListener('dragover',
|
44
|
+
(event) => CHART_MANAGER.dragOver(event));
|
45
|
+
this.dialog.addEventListener('drop',
|
46
|
+
(event) => CHART_MANAGER.handleDrop(event));
|
47
|
+
// Toolbar buttons
|
48
|
+
document.getElementById('chart-close-btn').addEventListener(
|
49
|
+
'click', (event) => UI.toggleDialog(event));
|
50
|
+
document.getElementById('chart-rename-btn').addEventListener(
|
51
|
+
'click', () => CHART_MANAGER.promptForTitle());
|
52
|
+
document.getElementById('chart-clone-btn').addEventListener(
|
53
|
+
'click', () => CHART_MANAGER.cloneChart());
|
54
|
+
this.results_btn = document.getElementById('chart-results-btn');
|
55
|
+
this.results_btn.addEventListener(
|
56
|
+
'click', () => CHART_MANAGER.toggleRunResults());
|
57
|
+
document.getElementById('chart-delete-btn').addEventListener(
|
58
|
+
'click', () => CHART_MANAGER.deleteChart());
|
59
|
+
this.control_panel = document.getElementById('chart-control-panel');
|
60
|
+
this.chart_selector = document.getElementById('chart-selector');
|
61
|
+
this.chart_selector.addEventListener(
|
62
|
+
'change', () => CHART_MANAGER.selectChart());
|
63
|
+
document.getElementById('chart-histogram').addEventListener(
|
64
|
+
'click', () => CHART_MANAGER.toggleHistogram());
|
65
|
+
this.histogram_options = document.getElementById('chart-histogram-options');
|
66
|
+
this.bins_selector = document.getElementById('histogram-bins');
|
67
|
+
this.bins_selector.addEventListener(
|
68
|
+
'change', () => CHART_MANAGER.changeBins());
|
69
|
+
document.getElementById('chart-title').addEventListener(
|
70
|
+
'click', () => CHART_MANAGER.toggleTitle());
|
71
|
+
this.legend_selector = document.getElementById('chart-legend');
|
72
|
+
this.legend_selector.addEventListener(
|
73
|
+
'change', () => CHART_MANAGER.changeLegend());
|
74
|
+
document.getElementById('chart-add-variable-btn').addEventListener(
|
75
|
+
'click', (event) => CHART_MANAGER.promptForVariable(event.shiftKey));
|
76
|
+
document.getElementById('chart-variable-up-btn').addEventListener(
|
77
|
+
'click', () => CHART_MANAGER.moveVariable(-1));
|
78
|
+
document.getElementById('chart-variable-down-btn').addEventListener(
|
79
|
+
'click', () => CHART_MANAGER.moveVariable(1));
|
80
|
+
document.getElementById('chart-edit-variable-btn').addEventListener(
|
81
|
+
'click', () => CHART_MANAGER.editVariable());
|
82
|
+
document.getElementById('chart-delete-variable-btn').addEventListener(
|
83
|
+
'click', () => CHART_MANAGER.deleteVariable());
|
84
|
+
this.variables_table = document.getElementById('chart-variables-table');
|
85
|
+
this.display_panel = document.getElementById('chart-display-panel');
|
86
|
+
this.toggle_chevron = document.getElementById('chart-toggle-chevron');
|
87
|
+
this.table_panel = document.getElementById('chart-table-panel');
|
88
|
+
this.statistics_table = document.getElementById('chart-table');
|
89
|
+
this.svg_container = document.getElementById('chart-svg-container');
|
90
|
+
this.svg_container.addEventListener(
|
91
|
+
'mousemove', (event) => CHART_MANAGER.updateTimeStep(event, true));
|
92
|
+
this.svg_container.addEventListener(
|
93
|
+
'mouseleave', (event) => CHART_MANAGER.updateTimeStep(event, false));
|
94
|
+
this.time_step = document.getElementById('chart-time-step');
|
95
|
+
document.getElementById('chart-toggle-chevron').addEventListener(
|
96
|
+
'click', () => CHART_MANAGER.toggleControlPanel());
|
97
|
+
document.getElementById('chart-stats-btn').addEventListener(
|
98
|
+
'click', () => CHART_MANAGER.toggleStatistics());
|
99
|
+
document.getElementById('chart-copy-stats-btn').addEventListener(
|
100
|
+
'click', () => CHART_MANAGER.copyStatistics());
|
101
|
+
document.getElementById('chart-copy-data-btn').addEventListener(
|
102
|
+
'click', () => CHART_MANAGER.copyData());
|
103
|
+
document.getElementById('chart-copy-table-btn').addEventListener(
|
104
|
+
'click', () => CHART_MANAGER.copyTable());
|
105
|
+
document.getElementById('chart-save-btn').addEventListener(
|
106
|
+
'click', () => CHART_MANAGER.downloadChart());
|
107
|
+
document.getElementById('chart-render-btn').addEventListener(
|
108
|
+
'click', () => CHART_MANAGER.renderChartAsPNG());
|
109
|
+
document.getElementById('chart-widen-btn').addEventListener(
|
110
|
+
'click', () => CHART_MANAGER.stretchChart(1));
|
111
|
+
document.getElementById('chart-narrow-btn').addEventListener(
|
112
|
+
'click', () => CHART_MANAGER.stretchChart(-1));
|
113
|
+
|
114
|
+
// The Add variable modal
|
115
|
+
this.add_variable_modal = new ModalDialog('add-variable');
|
116
|
+
this.add_variable_modal.ok.addEventListener(
|
117
|
+
'click', () => CHART_MANAGER.addVariable());
|
118
|
+
this.add_variable_modal.cancel.addEventListener(
|
119
|
+
'click', () => CHART_MANAGER.add_variable_modal.hide());
|
120
|
+
// NOTE: uses methods of the Expression Editor
|
121
|
+
this.add_variable_modal.element('obj').addEventListener(
|
122
|
+
'change', () => X_EDIT.updateVariableBar('add-'));
|
123
|
+
this.add_variable_modal.element('name').addEventListener(
|
124
|
+
'change', () => X_EDIT.updateAttributeSelector('add-'));
|
125
|
+
|
126
|
+
// The Edit variable modal
|
127
|
+
this.variable_modal = new ModalDialog('variable');
|
128
|
+
this.variable_modal.ok.addEventListener(
|
129
|
+
'click', () => CHART_MANAGER.modifyVariable());
|
130
|
+
this.variable_modal.cancel.addEventListener(
|
131
|
+
'click', () => CHART_MANAGER.variable_modal.hide());
|
132
|
+
this.change_equation_btns = document.getElementById('change-equation-btns');
|
133
|
+
document.getElementById('chart-rename-equation-btn').addEventListener(
|
134
|
+
'click', () => CHART_MANAGER.renameEquation());
|
135
|
+
document.getElementById('chart-edit-equation-btn').addEventListener(
|
136
|
+
'click', () => CHART_MANAGER.editEquation());
|
137
|
+
document.getElementById('variable-color').addEventListener(
|
138
|
+
'mouseenter', () => CHART_MANAGER.showPasteColor());
|
139
|
+
document.getElementById('variable-color').addEventListener(
|
140
|
+
'mouseleave', () => CHART_MANAGER.hidePasteColor());
|
141
|
+
document.getElementById('variable-color').addEventListener(
|
142
|
+
'click', (event) => CHART_MANAGER.copyPasteColor(event));
|
143
|
+
// NOTE: uses the color picker developed by James Daniel
|
144
|
+
this.color_picker = new iro.ColorPicker("#color-picker", {
|
145
|
+
width: 92,
|
146
|
+
height: 92,
|
147
|
+
color: '#a00',
|
148
|
+
markerRadius: 10,
|
149
|
+
padding: 1,
|
150
|
+
sliderMargin: 6,
|
151
|
+
sliderHeight: 10,
|
152
|
+
borderWidth: 1,
|
153
|
+
borderColor: '#fff',
|
154
|
+
anticlockwise: true
|
155
|
+
});
|
156
|
+
this.color_picker.on('input:end',
|
157
|
+
() => {
|
158
|
+
document.getElementById('variable-color').style.backgroundColor =
|
159
|
+
CHART_MANAGER.color_picker.color.hexString;
|
160
|
+
});
|
161
|
+
|
162
|
+
// The Rename chart modal
|
163
|
+
this.rename_chart_modal = new ModalDialog('rename-chart');
|
164
|
+
this.rename_chart_modal.ok.addEventListener(
|
165
|
+
'click', () => CHART_MANAGER.renameChart());
|
166
|
+
this.rename_chart_modal.cancel.addEventListener(
|
167
|
+
'click', () => CHART_MANAGER.rename_chart_modal.hide());
|
168
|
+
|
169
|
+
// Do not display the time step until cursor moves over chart
|
170
|
+
this.time_step.style.display = 'none';
|
171
|
+
document.getElementById('table-only-buttons').style.display = 'none';
|
172
|
+
// Initialize properties
|
173
|
+
this.reset();
|
174
|
+
}
|
175
|
+
|
176
|
+
reset() {
|
177
|
+
// Basic reset (same as console-only class)
|
178
|
+
this.visible = false;
|
179
|
+
this.chart_index = -1;
|
180
|
+
this.variable_index = -1;
|
181
|
+
this.stretch_factor = 1;
|
182
|
+
this.drawing_graph = false;
|
183
|
+
this.runs_chart = false;
|
184
|
+
// Clear the model-related DOM elements
|
185
|
+
this.chart_selector.innerHTML = '';
|
186
|
+
this.variables_table.innerHTML = '';
|
187
|
+
this.options_shown = true;
|
188
|
+
this.setRunsChart(false);
|
189
|
+
this.last_time_selected = 0;
|
190
|
+
this.paste_color = '';
|
191
|
+
}
|
192
|
+
|
193
|
+
enterKey() {
|
194
|
+
// Open "edit" dialog for the selected chart variable
|
195
|
+
const srl = this.variables_table.getElementsByClassName('sel-set');
|
196
|
+
if(srl.length > 0) {
|
197
|
+
const r = this.variables_table.rows[srl[0].rowIndex];
|
198
|
+
if(r) {
|
199
|
+
// Emulate a double-click to edit the variable properties
|
200
|
+
this.last_time_selected = Date.now();
|
201
|
+
r.dispatchEvent(new Event('click'));
|
202
|
+
}
|
203
|
+
}
|
204
|
+
}
|
205
|
+
|
206
|
+
upDownKey(dir) {
|
207
|
+
// Select row above or below the selected one (if possible)
|
208
|
+
const srl = this.variables_table.getElementsByClassName('sel-set');
|
209
|
+
if(srl.length > 0) {
|
210
|
+
const r = this.variables_table.rows[srl[0].rowIndex + dir];
|
211
|
+
if(r) {
|
212
|
+
UI.scrollIntoView(r);
|
213
|
+
r.dispatchEvent(new Event('click'));
|
214
|
+
}
|
215
|
+
}
|
216
|
+
}
|
217
|
+
|
218
|
+
setRunsChart(show) {
|
219
|
+
// Indicates whether the chart manager should display a run result chart
|
220
|
+
this.runs_chart = show;
|
221
|
+
if(show) {
|
222
|
+
this.results_btn.classList.add('stay-activ');
|
223
|
+
} else {
|
224
|
+
this.results_btn.classList.remove('stay-activ');
|
225
|
+
}
|
226
|
+
}
|
227
|
+
|
228
|
+
showInfo(shift) {
|
229
|
+
if(this.chart_index >= 0) {
|
230
|
+
DOCUMENTATION_MANAGER.update(MODEL.charts[this.chart_index], shift);
|
231
|
+
}
|
232
|
+
}
|
233
|
+
|
234
|
+
dragOver(ev) {
|
235
|
+
const
|
236
|
+
n = ev.dataTransfer.getData('text'),
|
237
|
+
obj = MODEL.objectByID(n);
|
238
|
+
if(obj) ev.preventDefault();
|
239
|
+
}
|
240
|
+
|
241
|
+
handleDrop(ev) {
|
242
|
+
const
|
243
|
+
n = ev.dataTransfer.getData('text'),
|
244
|
+
obj = MODEL.objectByID(n);
|
245
|
+
ev.preventDefault();
|
246
|
+
if(!obj) {
|
247
|
+
UI.alert(`Unknown entity ID "${n}"`);
|
248
|
+
} else if(this.chart_index >= 0) {
|
249
|
+
if(obj instanceof DatasetModifier) {
|
250
|
+
// Equations can be added directly as chart variable
|
251
|
+
this.addVariable(obj.selector);
|
252
|
+
return;
|
253
|
+
}
|
254
|
+
// For other entities, the attribute must be specified
|
255
|
+
this.add_variable_modal.show();
|
256
|
+
const
|
257
|
+
tn = VM.object_types.indexOf(obj.type),
|
258
|
+
dn = obj.displayName;
|
259
|
+
this.add_variable_modal.element('obj').value = tn;
|
260
|
+
X_EDIT.updateVariableBar('add-');
|
261
|
+
const s = this.add_variable_modal.element('name');
|
262
|
+
let i = 0;
|
263
|
+
for(let k in s.options) if(s.options.hasOwnProperty(k)) {
|
264
|
+
if(s[k].text === dn) {
|
265
|
+
i = s[k].value;
|
266
|
+
break;
|
267
|
+
}
|
268
|
+
}
|
269
|
+
s.value = i;
|
270
|
+
X_EDIT.updateAttributeSelector('add-');
|
271
|
+
}
|
272
|
+
}
|
273
|
+
|
274
|
+
toggleControlPanel() {
|
275
|
+
if(this.options_shown) {
|
276
|
+
this.control_panel.style.display = 'none';
|
277
|
+
this.display_panel.style.left = '1px';
|
278
|
+
this.display_panel.style.width = 'calc(100% - 8px)';
|
279
|
+
this.toggle_chevron.innerHTML = '»';
|
280
|
+
this.toggle_chevron.title = 'Show control panel';
|
281
|
+
this.options_shown = false;
|
282
|
+
} else {
|
283
|
+
this.control_panel.style.display = 'block';
|
284
|
+
this.display_panel.style.left = '205px';
|
285
|
+
this.display_panel.style.width = 'calc(100% - 212px)';
|
286
|
+
this.toggle_chevron.innerHTML = '«';
|
287
|
+
this.toggle_chevron.title = 'Hide control panel';
|
288
|
+
this.options_shown = true;
|
289
|
+
}
|
290
|
+
this.stretchChart(0);
|
291
|
+
}
|
292
|
+
|
293
|
+
updateSelector() {
|
294
|
+
// Adds one option to the selector for each chart defined for the model
|
295
|
+
// NOTE: add the "new chart" option if still absent
|
296
|
+
MODEL.addChart(this.new_chart_title);
|
297
|
+
if(this.chart_index < 0) this.chart_index = 0;
|
298
|
+
const ol = [];
|
299
|
+
for(let i = 0; i < MODEL.charts.length; i++) {
|
300
|
+
ol.push('<option value="', i,
|
301
|
+
(i == this.chart_index ? '"selected="selected' : ''),
|
302
|
+
'">', MODEL.charts[i].title , '</option>');
|
303
|
+
}
|
304
|
+
this.chart_selector.innerHTML = ol.join('');
|
305
|
+
}
|
306
|
+
|
307
|
+
updateDialog() {
|
308
|
+
// Refreshes all dialog fields to display actual MODEL chart properties
|
309
|
+
this.updateSelector();
|
310
|
+
let c = null;
|
311
|
+
if(this.chart_index >= 0) {
|
312
|
+
c = MODEL.charts[this.chart_index];
|
313
|
+
UI.setBox('chart-histogram', c.histogram);
|
314
|
+
this.bins_selector.value = c.bins;
|
315
|
+
if(c.histogram) {
|
316
|
+
this.histogram_options.style.display = 'block';
|
317
|
+
} else {
|
318
|
+
this.histogram_options.style.display = 'none';
|
319
|
+
}
|
320
|
+
UI.setBox('chart-title', c.show_title);
|
321
|
+
const ol = [];
|
322
|
+
for(let i = 0; i < this.legend_options.length; i++) {
|
323
|
+
const opt = this.legend_options[i], val = opt.toLowerCase();
|
324
|
+
ol.push(['<option value="', val,
|
325
|
+
(c.legend_position === val ? '" selected="selected' : ''),
|
326
|
+
'">', opt, '</option>'].join(''));
|
327
|
+
}
|
328
|
+
this.legend_selector.innerHTML = ol.join('');
|
329
|
+
ol.length = 0;
|
330
|
+
for(let i = 0; i < c.variables.length; i++) {
|
331
|
+
const cv = c.variables[i];
|
332
|
+
ol.push(['<tr class="variable',
|
333
|
+
(i === this.variable_index ? ' sel-set' : ''),
|
334
|
+
'" title="', cv.displayName,
|
335
|
+
'" onclick="CHART_MANAGER.selectVariable(', i, ');">',
|
336
|
+
'<td class="v-box"><div id="v-box-', i, '" class="vbox',
|
337
|
+
(cv.visible ? ' checked' : ' clear'),
|
338
|
+
'" onclick="CHART_MANAGER.toggleVariable(', i,
|
339
|
+
');"></div></td><td class="v-name">', cv.displayName,
|
340
|
+
'</td></tr>'].join(''));
|
341
|
+
}
|
342
|
+
this.variables_table.innerHTML = ol.join('');
|
343
|
+
} else {
|
344
|
+
this.variable_index = -1;
|
345
|
+
}
|
346
|
+
const
|
347
|
+
u_btn = 'chart-variable-up ',
|
348
|
+
d_btn = 'chart-variable-down ',
|
349
|
+
ed_btns = 'chart-edit-variable chart-delete-variable ';
|
350
|
+
// Just in case variable index has not been adjusted after some
|
351
|
+
// variables have been deleted
|
352
|
+
if(this.variable_index >= c.variables.length) {
|
353
|
+
this.variable_index = -1;
|
354
|
+
}
|
355
|
+
if(this.variable_index < 0) {
|
356
|
+
UI.disableButtons(ed_btns + u_btn + d_btn);
|
357
|
+
} else {
|
358
|
+
UI.enableButtons(ed_btns);
|
359
|
+
if(this.variable_index > 0) {
|
360
|
+
UI.enableButtons(u_btn);
|
361
|
+
} else {
|
362
|
+
UI.disableButtons(u_btn);
|
363
|
+
}
|
364
|
+
if(c && this.variable_index < c.variables.length - 1) {
|
365
|
+
UI.enableButtons(d_btn);
|
366
|
+
} else {
|
367
|
+
UI.disableButtons(d_btn);
|
368
|
+
}
|
369
|
+
// If the Edit variable dialog is showing, update its header
|
370
|
+
if(this.variable_index >= 0 && !UI.hidden('variable-dlg')) {
|
371
|
+
document.getElementById('variable-dlg-name').innerHTML =
|
372
|
+
c.variables[this.variable_index].displayName;
|
373
|
+
}
|
374
|
+
}
|
375
|
+
this.add_variable_modal.element('obj').value = 0;
|
376
|
+
// Update variable dropdown list of the "add variable" modal
|
377
|
+
X_EDIT.updateVariableBar('add-');
|
378
|
+
this.stretchChart(0);
|
379
|
+
}
|
380
|
+
|
381
|
+
updateExperimentInfo() {
|
382
|
+
// Display selected experiment title in dialog header if run data are used
|
383
|
+
const
|
384
|
+
selx = EXPERIMENT_MANAGER.selected_experiment,
|
385
|
+
el = document.getElementById('chart-experiment-info');
|
386
|
+
if(selx && this.runs_chart) {
|
387
|
+
el.innerHTML = '<em>Experiment:</em> ' + selx.title;
|
388
|
+
} else {
|
389
|
+
el.innerHTML = '';
|
390
|
+
}
|
391
|
+
}
|
392
|
+
|
393
|
+
updateTimeStep(e, show) {
|
394
|
+
// Shows the time step corresponding to the horizontal cursor position,
|
395
|
+
// or hides it if the cursor is not over the chart area
|
396
|
+
const c = (this.chart_index >= 0 ? MODEL.charts[this.chart_index] : null);
|
397
|
+
if(show && c) {
|
398
|
+
const
|
399
|
+
scale = this.container_height / this.svg_height,
|
400
|
+
r = c.chart_area_rect,
|
401
|
+
ox = r.left * scale,
|
402
|
+
w = r.width * scale,
|
403
|
+
x = e.pageX -
|
404
|
+
this.svg_container.getBoundingClientRect().left + window.scrollX;
|
405
|
+
let n = '';
|
406
|
+
if(c.histogram) {
|
407
|
+
let vv = [];
|
408
|
+
for(let i = 0; i < c.variables.length; i++) {
|
409
|
+
if(c.variables[i].visible) vv.push(c.variables[i]);
|
410
|
+
}
|
411
|
+
const
|
412
|
+
l = vv.length,
|
413
|
+
bars = c.bins * l,
|
414
|
+
b = Math.max(0, Math.min(bars, Math.floor(bars * (x - ox) / w))),
|
415
|
+
v = vv[b % l],
|
416
|
+
t = Math.floor(b / l);
|
417
|
+
if(x > ox && b < bars) n = 'N = ' + v.bin_tallies[t];
|
418
|
+
} else {
|
419
|
+
const
|
420
|
+
runs = EXPERIMENT_MANAGER.selectedRuns(c),
|
421
|
+
p = c.total_time_steps,
|
422
|
+
first = (runs.length > 0 ? 1 : MODEL.start_period),
|
423
|
+
last = (runs.length > 0 ? p : MODEL.end_period),
|
424
|
+
t = Math.round(first - 0.5 + p * (x - ox) / w);
|
425
|
+
n = 't = ' + Math.max(0, Math.min(t, last));
|
426
|
+
}
|
427
|
+
this.time_step.innerHTML = n;
|
428
|
+
this.time_step.style.display = 'block';
|
429
|
+
} else {
|
430
|
+
this.time_step.style.display = 'none';
|
431
|
+
}
|
432
|
+
}
|
433
|
+
|
434
|
+
selectChart() {
|
435
|
+
// Sets the selected chart to be the "active" chart
|
436
|
+
const ci = parseInt(this.chart_selector.value);
|
437
|
+
// Deselect variable only if different chart is selected
|
438
|
+
if(ci !== this.chart_index) this.variable_index = -1;
|
439
|
+
this.chart_index = ci;
|
440
|
+
this.updateDialog();
|
441
|
+
}
|
442
|
+
|
443
|
+
promptForTitle() {
|
444
|
+
// Prompts modeler for a new title for the current chart
|
445
|
+
if(this.chart_index >= 0) {
|
446
|
+
this.rename_chart_modal.show();
|
447
|
+
const nct = document.getElementById('new-chart-title');
|
448
|
+
nct.value = MODEL.charts[this.chart_index].displayName;
|
449
|
+
nct.focus();
|
450
|
+
}
|
451
|
+
}
|
452
|
+
|
453
|
+
renameChart() {
|
454
|
+
// Renames the current chart
|
455
|
+
if(this.chart_index >= 0) {
|
456
|
+
const t = document.getElementById('new-chart-title').value.trim();
|
457
|
+
// Check if a chart with this title already exists
|
458
|
+
const ci = MODEL.indexOfChart(t);
|
459
|
+
if(ci >= 0 && ci != this.chart_index) {
|
460
|
+
UI.warn(`A chart with title "${t}" already exists`);
|
461
|
+
} else {
|
462
|
+
const c = MODEL.charts[this.chart_index];
|
463
|
+
// Remember the old title of the chart-to-be-renamed
|
464
|
+
const ot = c.title;
|
465
|
+
c.title = t;
|
466
|
+
// If the default '(new chart)' has been renamed, create a new one
|
467
|
+
if(ot === this.new_chart_title) {
|
468
|
+
MODEL.addChart(ot);
|
469
|
+
}
|
470
|
+
// Update the chart index so that it points to the renamed chart
|
471
|
+
this.chart_index = MODEL.indexOfChart(t);
|
472
|
+
this.updateSelector();
|
473
|
+
// Redraw the chart if title is shown
|
474
|
+
if(c.show_title) this.drawChart();
|
475
|
+
}
|
476
|
+
// Update experiment viewer in case its current experiment uses this chart
|
477
|
+
UI.updateControllerDialogs('CFX');
|
478
|
+
}
|
479
|
+
this.rename_chart_modal.hide();
|
480
|
+
}
|
481
|
+
|
482
|
+
cloneChart() {
|
483
|
+
// Creates a new chart that is identical to the current one
|
484
|
+
if(this.chart_index >= 0) {
|
485
|
+
let c = MODEL.charts[this.chart_index],
|
486
|
+
nt = c.title + '-copy';
|
487
|
+
while(MODEL.indexOfChart(nt) >= 0) {
|
488
|
+
nt += '-copy';
|
489
|
+
}
|
490
|
+
const nc = MODEL.addChart(nt);
|
491
|
+
// Copy properties of c to nc
|
492
|
+
nc.histogram = c.histogram;
|
493
|
+
nc.bins = c.bins;
|
494
|
+
nc.show_title = c.show_title;
|
495
|
+
nc.legend_position = c.legend_position;
|
496
|
+
for(let i = 0; i < c.variables.length; i++) {
|
497
|
+
const
|
498
|
+
cv = c.variables[i],
|
499
|
+
nv = new ChartVariable(nc);
|
500
|
+
nv.setProperties(cv.object, cv.attribute, cv.stacked,
|
501
|
+
cv.color, cv.scale_factor, cv.line_width);
|
502
|
+
nc.variables.push(nv);
|
503
|
+
}
|
504
|
+
this.chart_index = MODEL.indexOfChart(nc.title);
|
505
|
+
this.updateDialog();
|
506
|
+
}
|
507
|
+
}
|
508
|
+
|
509
|
+
toggleRunResults() {
|
510
|
+
// Toggles the Boolean property that signals charts that they must plot
|
511
|
+
// run results if they are part of the selected experiment chart set
|
512
|
+
this.setRunsChart(!this.runs_chart);
|
513
|
+
this.resetChartVectors();
|
514
|
+
this.updateDialog();
|
515
|
+
}
|
516
|
+
|
517
|
+
deleteChart() {
|
518
|
+
// Deletes the shown chart (if any)
|
519
|
+
if(this.chart_index >= 0) {
|
520
|
+
// NOTE: do not delete the default chart, but clear it
|
521
|
+
if(MODEL.charts[this.chart_index].title === this.new_chart_title) {
|
522
|
+
MODEL.charts[this.chart_index].reset();
|
523
|
+
} else {
|
524
|
+
MODEL.charts.splice(this.chart_index, 1);
|
525
|
+
this.chart_index = -1;
|
526
|
+
}
|
527
|
+
// Also update the experiment viewer (charts define the output variables)
|
528
|
+
UI.updateControllerDialogs('CFX');
|
529
|
+
}
|
530
|
+
}
|
531
|
+
|
532
|
+
changeBins() {
|
533
|
+
if(this.chart_index >= 0) {
|
534
|
+
const
|
535
|
+
c = MODEL.charts[this.chart_index],
|
536
|
+
b = parseInt(this.bins_selector.value);
|
537
|
+
if(b !== c.bins) {
|
538
|
+
c.bins = b;
|
539
|
+
this.drawChart();
|
540
|
+
}
|
541
|
+
}
|
542
|
+
}
|
543
|
+
|
544
|
+
toggleHistogram() {
|
545
|
+
if(this.chart_index >= 0) {
|
546
|
+
const c = MODEL.charts[this.chart_index];
|
547
|
+
c.histogram = !c.histogram;
|
548
|
+
if(c.histogram) {
|
549
|
+
this.histogram_options.style.display = 'block';
|
550
|
+
} else {
|
551
|
+
this.histogram_options.style.display = 'none';
|
552
|
+
}
|
553
|
+
this.drawChart();
|
554
|
+
}
|
555
|
+
}
|
556
|
+
|
557
|
+
toggleTitle() {
|
558
|
+
// window.event.stopPropagation();
|
559
|
+
if(this.chart_index >= 0) {
|
560
|
+
const c = MODEL.charts[this.chart_index];
|
561
|
+
c.show_title = !c.show_title;
|
562
|
+
this.drawChart();
|
563
|
+
}
|
564
|
+
}
|
565
|
+
|
566
|
+
changeLegend() {
|
567
|
+
if(this.chart_index >= 0) {
|
568
|
+
const c = MODEL.charts[this.chart_index];
|
569
|
+
c.legend_position = document.getElementById('chart-legend').value;
|
570
|
+
this.drawChart();
|
571
|
+
}
|
572
|
+
}
|
573
|
+
|
574
|
+
promptForVariable(shift) {
|
575
|
+
// Prompts for variable to add to chart
|
576
|
+
// NOTE: shortcut (Shift-click) to add a new equation to the chart
|
577
|
+
if(shift) {
|
578
|
+
if(UI.hidden('equation-dlg')) {
|
579
|
+
UI.buttons.equation.dispatchEvent(new Event('click'));
|
580
|
+
}
|
581
|
+
// NOTE: TRUE signals equation manager to add new equation to the chart
|
582
|
+
EQUATION_MANAGER.promptForEquation(true);
|
583
|
+
} else {
|
584
|
+
this.add_variable_modal.show();
|
585
|
+
}
|
586
|
+
}
|
587
|
+
|
588
|
+
addVariable(eq='') {
|
589
|
+
// Adds the variable specified by the add-variable-dialog to the chart
|
590
|
+
// NOTE: when defined, `eq` is the selector of the equation to be added
|
591
|
+
if(this.chart_index >= 0) {
|
592
|
+
let o = '',
|
593
|
+
a = eq;
|
594
|
+
if(!eq) {
|
595
|
+
o = this.add_variable_modal.selectedOption('name').text;
|
596
|
+
a = this.add_variable_modal.selectedOption('attr').text;
|
597
|
+
}
|
598
|
+
// NOTE: when equation is added, object specifier is empty string
|
599
|
+
if(!o && a) o = UI.EQUATIONS_DATASET_NAME;
|
600
|
+
this.variable_index = MODEL.charts[this.chart_index].addVariable(o, a);
|
601
|
+
if(this.variable_index >= 0) {
|
602
|
+
this.add_variable_modal.hide();
|
603
|
+
// Also update the experiment viewer (charts define the output variables)
|
604
|
+
if(EXPERIMENT_MANAGER.selected_experiment) {
|
605
|
+
EXPERIMENT_MANAGER.selected_experiment.inferVariables();
|
606
|
+
}
|
607
|
+
UI.updateControllerDialogs('CFX');
|
608
|
+
}
|
609
|
+
}
|
610
|
+
}
|
611
|
+
|
612
|
+
selectVariable(vi) {
|
613
|
+
// Select variable, and edit it when double-clicked
|
614
|
+
const
|
615
|
+
now = Date.now(),
|
616
|
+
dt = now - this.last_time_selected;
|
617
|
+
if(vi >= 0 && this.chart_index >= 0) {
|
618
|
+
this.last_time_selected = now;
|
619
|
+
if(vi === this.variable_index) {
|
620
|
+
// Consider click to be "double" if it occurred less than 300 ms ago
|
621
|
+
if(dt < 300) {
|
622
|
+
this.last_time_selected = 0;
|
623
|
+
this.editVariable();
|
624
|
+
return;
|
625
|
+
}
|
626
|
+
}
|
627
|
+
}
|
628
|
+
this.variable_index = vi;
|
629
|
+
this.updateDialog();
|
630
|
+
}
|
631
|
+
|
632
|
+
setColorPicker(color) {
|
633
|
+
// Robust way to set iro color picker color
|
634
|
+
try {
|
635
|
+
this.color_picker.color.hexString = color;
|
636
|
+
} catch(e) {
|
637
|
+
this.color_picker.color.rgbString = color;
|
638
|
+
}
|
639
|
+
}
|
640
|
+
|
641
|
+
editVariable() {
|
642
|
+
// Shows the edit (or rather: format) variable dialog
|
643
|
+
if(this.chart_index >= 0 && this.variable_index >= 0) {
|
644
|
+
const cv = MODEL.charts[this.chart_index].variables[this.variable_index];
|
645
|
+
document.getElementById('variable-dlg-name').innerHTML = cv.displayName;
|
646
|
+
UI.setBox('variable-stacked', cv.stacked);
|
647
|
+
this.variable_modal.element('scale').value = VM.sig4Dig(cv.scale_factor);
|
648
|
+
this.variable_modal.element('width').value = VM.sig4Dig(cv.line_width);
|
649
|
+
this.variable_modal.element('color').style.backgroundColor = cv.color;
|
650
|
+
this.setColorPicker(cv.color);
|
651
|
+
// Show change equation buttons only for equation variables
|
652
|
+
if(cv.object === MODEL.equations_dataset) {
|
653
|
+
this.change_equation_btns.style.display = 'block';
|
654
|
+
} else {
|
655
|
+
this.change_equation_btns.style.display = 'none';
|
656
|
+
}
|
657
|
+
this.variable_modal.show();
|
658
|
+
}
|
659
|
+
}
|
660
|
+
|
661
|
+
showPasteColor() {
|
662
|
+
// Show last copied color (if any) as smaller square next to color box
|
663
|
+
if(this.paste_color) {
|
664
|
+
const pc = this.variable_modal.element('paste-color');
|
665
|
+
pc.style.backgroundColor = this.paste_color;
|
666
|
+
pc.style.display = 'inline-block';
|
667
|
+
}
|
668
|
+
}
|
669
|
+
|
670
|
+
hidePasteColor() {
|
671
|
+
// Hide paste color box
|
672
|
+
this.variable_modal.element('paste-color').style.display = 'none';
|
673
|
+
}
|
674
|
+
|
675
|
+
copyPasteColor(event) {
|
676
|
+
// Store the current color as past color, or set it to the current
|
677
|
+
// paste color if this is defined and the Shift key was pressed
|
678
|
+
event.stopPropagation();
|
679
|
+
const cbox = this.variable_modal.element('color');
|
680
|
+
if(event.shiftKey && this.paste_color) {
|
681
|
+
cbox.style.backgroundColor = this.paste_color;
|
682
|
+
this.setColorPicker(this.paste_color);
|
683
|
+
} else {
|
684
|
+
this.paste_color = cbox.style.backgroundColor;
|
685
|
+
this.showPasteColor();
|
686
|
+
}
|
687
|
+
}
|
688
|
+
|
689
|
+
toggleVariable(vi) {
|
690
|
+
window.event.stopPropagation();
|
691
|
+
if(vi >= 0 && this.chart_index >= 0) {
|
692
|
+
const cv = MODEL.charts[this.chart_index].variables[vi];
|
693
|
+
// toggle visibility of the selected variable
|
694
|
+
cv.visible = !cv.visible;
|
695
|
+
// update the check box
|
696
|
+
UI.setBox('v-box-' + vi, cv.visible);
|
697
|
+
// redraw chart and table (with one variable more or less)
|
698
|
+
this.drawChart();
|
699
|
+
// Also update the experiment viewer (charts define the output variables)
|
700
|
+
if(EXPERIMENT_MANAGER.selected_experiment) {
|
701
|
+
EXPERIMENT_MANAGER.updateDialog();
|
702
|
+
}
|
703
|
+
}
|
704
|
+
}
|
705
|
+
|
706
|
+
moveVariable(dir) {
|
707
|
+
if(this.chart_index >= 0 && this.variable_index >= 0) {
|
708
|
+
const c = MODEL.charts[this.chart_index];
|
709
|
+
let vi = this.variable_index;
|
710
|
+
if((dir > 0 && vi < c.variables.length - 1) || (dir < 0 && vi > 0)) {
|
711
|
+
vi += dir;
|
712
|
+
const v = c.variables.splice(this.variable_index, 1)[0];
|
713
|
+
c.variables.splice(vi, 0, v);
|
714
|
+
this.variable_index = vi;
|
715
|
+
}
|
716
|
+
this.updateDialog();
|
717
|
+
}
|
718
|
+
}
|
719
|
+
|
720
|
+
modifyVariable() {
|
721
|
+
if(this.variable_index >= 0) {
|
722
|
+
const s = UI.validNumericInput('variable-scale', 'scale factor');
|
723
|
+
if(!s) return;
|
724
|
+
const w = UI.validNumericInput('variable-width', 'line width');
|
725
|
+
if(!w) return;
|
726
|
+
const
|
727
|
+
c = MODEL.charts[this.chart_index],
|
728
|
+
cv = c.variables[this.variable_index];
|
729
|
+
cv.stacked = UI.boxChecked('variable-stacked');
|
730
|
+
cv.scale_factor = s;
|
731
|
+
cv.line_width = w;
|
732
|
+
cv.color = this.color_picker.color.hexString;
|
733
|
+
// NOTE: clear the vector so it will be recalculated
|
734
|
+
cv.vector.length = 0;
|
735
|
+
}
|
736
|
+
this.variable_modal.hide();
|
737
|
+
this.updateDialog();
|
738
|
+
}
|
739
|
+
|
740
|
+
renameEquation() {
|
741
|
+
// Renames the selected variable (if it is an equation)
|
742
|
+
if(this.chart_index >= 0 && this.variable_index >= 0) {
|
743
|
+
const v = MODEL.charts[this.chart_index].variables[this.variable_index];
|
744
|
+
if(v.object === MODEL.equations_dataset) {
|
745
|
+
const m = MODEL.equations_dataset.modifiers[UI.nameToID(v.attribute)];
|
746
|
+
if(m instanceof DatasetModifier) {
|
747
|
+
EQUATION_MANAGER.selected_modifier = m;
|
748
|
+
EQUATION_MANAGER.promptForName();
|
749
|
+
}
|
750
|
+
}
|
751
|
+
}
|
752
|
+
}
|
753
|
+
|
754
|
+
editEquation() {
|
755
|
+
// Opens the expression editor for the selected variable (if equation)
|
756
|
+
if(this.chart_index >= 0 && this.variable_index >= 0) {
|
757
|
+
const v = MODEL.charts[this.chart_index].variables[this.variable_index];
|
758
|
+
if(v.object === MODEL.equations_dataset) {
|
759
|
+
const m = MODEL.equations_dataset.modifiers[UI.nameToID(v.attribute)];
|
760
|
+
if(m instanceof DatasetModifier) {
|
761
|
+
EQUATION_MANAGER.selected_modifier = m;
|
762
|
+
EQUATION_MANAGER.editEquation();
|
763
|
+
}
|
764
|
+
}
|
765
|
+
}
|
766
|
+
}
|
767
|
+
|
768
|
+
deleteVariable() {
|
769
|
+
// Deletes the selected variable from the chart
|
770
|
+
if(this.variable_index >= 0) {
|
771
|
+
MODEL.charts[this.chart_index].variables.splice(this.variable_index, 1);
|
772
|
+
this.variable_index = -1;
|
773
|
+
this.updateDialog();
|
774
|
+
// Also update the experiment viewer (charts define the output variables)
|
775
|
+
// and finder dialog
|
776
|
+
if(EXPERIMENT_MANAGER.selected_experiment) UI.updateControllerDialogs('FX');
|
777
|
+
}
|
778
|
+
this.variable_modal.hide();
|
779
|
+
}
|
780
|
+
|
781
|
+
showChartImage(c) {
|
782
|
+
// Displays the SVG image for chart `c` (computed by this Chart object)
|
783
|
+
if(c) document.getElementById('chart-svg').innerHTML = c.svg;
|
784
|
+
}
|
785
|
+
|
786
|
+
drawTable() {
|
787
|
+
// Shows the statistics on the chart variables
|
788
|
+
const html = [];
|
789
|
+
let vbl = [];
|
790
|
+
if(this.chart_index >= 0) vbl = MODEL.charts[this.chart_index].variables;
|
791
|
+
// First get the (potentially floating point) numbers so that their format
|
792
|
+
// can be made uniform per column
|
793
|
+
const data = [];
|
794
|
+
let nr = 0;
|
795
|
+
for(let i = 0; i < vbl.length; i++) {
|
796
|
+
const v = vbl[i];
|
797
|
+
if(v.visible) {
|
798
|
+
data.push([VM.sig4Dig(v.minimum), VM.sig4Dig(v.maximum),
|
799
|
+
VM.sig4Dig(v.mean), VM.sig4Dig(Math.sqrt(v.variance)),
|
800
|
+
VM.sig4Dig(v.sum)]);
|
801
|
+
nr++;
|
802
|
+
}
|
803
|
+
}
|
804
|
+
if(nr == 0 || this.drawing_chart) {
|
805
|
+
this.table_panel.html = '<div id="no-chart-data">No data</div>';
|
806
|
+
return;
|
807
|
+
}
|
808
|
+
// Process each of 5 columns separately
|
809
|
+
for(let c = 0; c < 5; c++) {
|
810
|
+
const col = [];
|
811
|
+
for(let r = 0; r < data.length; r++) {
|
812
|
+
col.push(data[r][c]);
|
813
|
+
}
|
814
|
+
uniformDecimals(col);
|
815
|
+
for(let r = 0; r < data.length; r++) {
|
816
|
+
data[r][c] = col[r];
|
817
|
+
}
|
818
|
+
}
|
819
|
+
html.push('<table id="chart-table">',
|
820
|
+
'<tr><th style="text-align: left">Variable</th>',
|
821
|
+
'<th>N</th><th style="font-size: 11px">MIN</th>',
|
822
|
+
'<th style="font-size: 11px">MAX</th>',
|
823
|
+
'<th>μ</th><th>σ</th><th>Σ</th>',
|
824
|
+
'<th>≠0</th><th>⚠</th></tr>');
|
825
|
+
nr = 0;
|
826
|
+
for(let i = 0; i < vbl.length; i++) {
|
827
|
+
const v = vbl[i];
|
828
|
+
if(v.visible) {
|
829
|
+
// NOTE: while still solving, display t-1 as N
|
830
|
+
const n = Math.max(0, v.N);
|
831
|
+
html.push('<tr><td class="v-name">',
|
832
|
+
[v.displayName, n, data[nr][0], data[nr][1], data[nr][2],
|
833
|
+
data[nr][3], data[nr][4],
|
834
|
+
v.non_zero_tally, v.exceptions].join('</td><td>'),
|
835
|
+
'</td></tr>');
|
836
|
+
nr++;
|
837
|
+
}
|
838
|
+
}
|
839
|
+
html.push('</table>');
|
840
|
+
this.table_panel.innerHTML = html.join('');
|
841
|
+
}
|
842
|
+
|
843
|
+
toggleStatistics() {
|
844
|
+
const btn = document.getElementById('chart-stats-btn');
|
845
|
+
let hs = 'Show';
|
846
|
+
if(btn.classList.contains('stay-activ')) {
|
847
|
+
btn.classList.remove('stay-activ');
|
848
|
+
} else {
|
849
|
+
btn.classList.add('stay-activ');
|
850
|
+
hs = 'Hide';
|
851
|
+
}
|
852
|
+
btn.title = hs + ' descriptive statistics';
|
853
|
+
UI.toggle('chart-only-buttons', 'inline-block');
|
854
|
+
UI.toggle('table-only-buttons', 'inline-block');
|
855
|
+
UI.toggle('chart-table-panel');
|
856
|
+
UI.toggle('chart-svg-scroller');
|
857
|
+
this.stretchChart(0);
|
858
|
+
}
|
859
|
+
|
860
|
+
stretchChart(delta) {
|
861
|
+
this.stretch_factor = Math.max(1, Math.min(10, this.stretch_factor + delta));
|
862
|
+
// NOTE: do not use 'auto', as this produces poor results
|
863
|
+
document.getElementById('chart-svg-scroller').style.overflowX =
|
864
|
+
(this.stretch_factor === 1 ? 'hidden' : 'scroll');
|
865
|
+
const csc = document.getElementById('chart-svg-container');
|
866
|
+
csc.style.width = (this.stretch_factor * 100 + '%');
|
867
|
+
// Size the chart proportional to its the display area
|
868
|
+
const style = window.getComputedStyle(csc);
|
869
|
+
this.container_width = parseFloat(style.width);
|
870
|
+
// If stretch factor > 1, the horizontal scroll bar takes up space,
|
871
|
+
// but this is accounted for by the container style!
|
872
|
+
this.container_height = parseFloat(style.height);
|
873
|
+
this.drawChart();
|
874
|
+
const
|
875
|
+
nbtn = document.getElementById('chart-narrow-btn'),
|
876
|
+
wbtn = document.getElementById('chart-widen-btn');
|
877
|
+
if(this.stretch_factor === 1) {
|
878
|
+
nbtn.classList.remove('enab');
|
879
|
+
nbtn.classList.add('disab');
|
880
|
+
} else if(this.stretch_factor === 2) {
|
881
|
+
nbtn.classList.remove('disab');
|
882
|
+
nbtn.classList.add('enab');
|
883
|
+
} else if(this.stretch_factor === 9) {
|
884
|
+
wbtn.classList.remove('disab');
|
885
|
+
wbtn.classList.add('enab');
|
886
|
+
} else if(this.stretch_factor === 10) {
|
887
|
+
wbtn.classList.remove('enab');
|
888
|
+
wbtn.classList.add('disab');
|
889
|
+
}
|
890
|
+
}
|
891
|
+
|
892
|
+
copyTable() {
|
893
|
+
UI.copyHtmlToClipboard(this.table_panel.innerHTML);
|
894
|
+
UI.notify('Table copied to clipboard (as HTML)');
|
895
|
+
}
|
896
|
+
|
897
|
+
copyStatistics() {
|
898
|
+
if(this.chart_index >= 0) {
|
899
|
+
UI.copyStringToClipboard(
|
900
|
+
MODEL.charts[this.chart_index].statisticsAsString);
|
901
|
+
}
|
902
|
+
}
|
903
|
+
|
904
|
+
copyData() {
|
905
|
+
if(this.chart_index >= 0) {
|
906
|
+
UI.copyStringToClipboard(
|
907
|
+
MODEL.charts[this.chart_index].dataAsString);
|
908
|
+
}
|
909
|
+
}
|
910
|
+
|
911
|
+
downloadChart() {
|
912
|
+
// Pushes the SVG of the selected chart as file to the browser
|
913
|
+
if(this.chart_index >= 0) {
|
914
|
+
FILE_MANAGER.pushOutSVG(MODEL.charts[this.chart_index].svg);
|
915
|
+
}
|
916
|
+
}
|
917
|
+
|
918
|
+
renderChartAsPNG() {
|
919
|
+
window.localStorage.removeItem('png-url');
|
920
|
+
FILE_MANAGER.renderSVGAsPNG(MODEL.charts[this.chart_index].svg);
|
921
|
+
}
|
922
|
+
|
923
|
+
drawChart() {
|
924
|
+
// Displays the selected chart unless an experiment is running, or already
|
925
|
+
// busy with an earlier drawChart call
|
926
|
+
if(MODEL.running_experiment) {
|
927
|
+
UI.notify(UI.NOTICE.NO_CHARTS);
|
928
|
+
} else if(this.chart_index >= 0 && !this.drawing_chart) {
|
929
|
+
this.drawing_chart = true;
|
930
|
+
CHART_MANAGER.actuallyDrawChart();
|
931
|
+
} else {
|
932
|
+
console.log(`Skipped drawing chart "${MODEL.charts[this.chart_index]}"`);
|
933
|
+
}
|
934
|
+
}
|
935
|
+
|
936
|
+
actuallyDrawChart() {
|
937
|
+
// Draws the chart, and resets the cursor when done
|
938
|
+
MODEL.charts[this.chart_index].draw();
|
939
|
+
this.drawing_chart = false;
|
940
|
+
this.drawTable();
|
941
|
+
}
|
942
|
+
|
943
|
+
} // END of class ChartManager
|
944
|
+
|