Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagecell
Path: blob/master/js/interact_data.js
447 views
1
import $ from "jquery";
2
import utils from "./utils";
3
4
var ce = utils.createElement;
5
6
function InteractControl(dirty_update) {
7
return function (control) {
8
this.control = control;
9
if (typeof dirty_update === "undefined") {
10
this.dirty_update = !this.control.update;
11
} else {
12
this.dirty_update = dirty_update;
13
}
14
this.eventCount = this.ignoreNext = 0;
15
};
16
}
17
18
var Button = InteractControl();
19
20
Button.prototype.rendered = function (id) {
21
this.button = ce("button", { id: id }, [this.control.text]);
22
this.button.style.width = this.control.width;
23
var that = this;
24
$(this.button).click(function () {
25
that.clicked = true;
26
$(that.button).trigger("clickdone");
27
});
28
$(this.button).button();
29
this.clicked = false;
30
return this.button;
31
};
32
33
Button.prototype.changeHandlers = function () {
34
return { clickdone: this.button };
35
};
36
37
Button.prototype.json_value = function () {
38
var c = this.clicked;
39
this.clicked = false;
40
return c;
41
};
42
43
Button.prototype.disable = function () {
44
$(this.button).button("option", "disabled", true);
45
};
46
47
var ButtonBar = InteractControl();
48
49
ButtonBar.prototype.rendered = function (id) {
50
var table = ce("table", { style: "width: auto;" });
51
var i = -1;
52
this.buttons = $();
53
var that = this;
54
for (var row = 0; row < this.control.nrows; row++) {
55
var tr = ce("tr");
56
for (var col = 0; col < this.control.ncols; col++) {
57
var button = ce("button", {}, [this.control.value_labels[++i]]);
58
button.style.width = this.control.width;
59
$(button).click(
60
(function (i) {
61
return function (event) {
62
that.index = i;
63
$(event.target).trigger("clickdone");
64
};
65
})(i)
66
);
67
this.buttons = this.buttons.add(button);
68
tr.appendChild(ce("td", {}, [button]));
69
}
70
table.appendChild(tr);
71
}
72
this.buttons.first().attr("id", id);
73
this.index = null;
74
this.buttons.button();
75
return table;
76
};
77
78
ButtonBar.prototype.changeHandlers = function () {
79
return { clickdone: this.buttons };
80
};
81
82
ButtonBar.prototype.json_value = function () {
83
var i = this.index;
84
this.index = null;
85
return i;
86
};
87
88
ButtonBar.prototype.disable = function () {
89
this.buttons.button("option", "disabled", true);
90
};
91
92
var Checkbox = InteractControl();
93
94
Checkbox.prototype.rendered = function (id) {
95
this.input = ce("input", { type: "checkbox", id: id });
96
this.input.checked = this.control["default"];
97
return this.input;
98
};
99
100
Checkbox.prototype.changeHandlers = function () {
101
return { change: this.input };
102
};
103
104
Checkbox.prototype.json_value = function () {
105
return this.input.checked;
106
};
107
108
Checkbox.prototype.update = function (value) {
109
this.input.checked = value;
110
};
111
112
Checkbox.prototype.disable = function () {
113
this.input.disabled = true;
114
};
115
116
var ColorSelector = InteractControl();
117
118
ColorSelector.prototype.rendered = function () {
119
this.selector = ce("span", { class: "sagecell_colorSelector" });
120
var text = document.createTextNode(this.control["default"]);
121
this.span = ce("span", {}, [this.selector]);
122
if (!this.control.hide_input) {
123
this.selector.style.marginRight = "10px";
124
this.span.appendChild(text);
125
}
126
this.selector.style.backgroundColor = this.control["default"];
127
var that = this;
128
$(this.selector).ColorPicker({
129
color: this.control["default"],
130
onChange: (this.change = function (hsb, hex, rgb, el) {
131
text.nodeValue =
132
that.color =
133
that.selector.style.backgroundColor =
134
"#" + hex;
135
}),
136
onHide: function () {
137
$(that.span).change();
138
},
139
});
140
return this.span;
141
};
142
143
ColorSelector.prototype.changeHandlers = function () {
144
return { change: this.span };
145
};
146
147
ColorSelector.prototype.json_value = function () {
148
return this.color;
149
};
150
151
ColorSelector.prototype.update = function (value) {
152
$(this.selector).ColorPickerSetColor(value);
153
this.change(undefined, value.substr(1));
154
};
155
156
ColorSelector.prototype.disable = function () {
157
$(this.span.firstChild).off("click");
158
this.span.firstChild.style.cursor = "default";
159
};
160
161
var HtmlBox = InteractControl(false);
162
163
HtmlBox.prototype.rendered = function () {
164
this.div = ce("div");
165
this.value = this.control.value;
166
$(this.div).html(this.control.value);
167
return this.div;
168
};
169
170
HtmlBox.prototype.changeHandlers = function () {
171
return {};
172
};
173
174
HtmlBox.prototype.json_value = function () {
175
return this.value;
176
};
177
178
HtmlBox.prototype.update = function (value) {
179
this.value = value;
180
$(this.div).html(value);
181
};
182
183
var InputBox = InteractControl();
184
185
InputBox.prototype.rendered = function (id) {
186
if (this.control.subtype === "textarea") {
187
this.textbox = ce("textarea", {
188
rows: this.control.height,
189
cols: this.control.width,
190
});
191
} else if (this.control.subtype === "input") {
192
this.textbox = ce(
193
"input",
194
/* Most of the time these will be Sage expressions, so turn all "helpful" features */
195
{
196
size: this.control.width,
197
autocapitalize: "off",
198
autocorrect: "off",
199
autocomplete: "off",
200
}
201
);
202
}
203
this.textbox.value = this.control["default"];
204
this.textbox.id = id;
205
if (this.control.evaluate) {
206
this.textbox.style.fontFamily = "monospace";
207
}
208
this.event = this.control.keypress ? "keyup" : "change";
209
return this.textbox;
210
};
211
212
InputBox.prototype.changeHandlers = function () {
213
var h = {};
214
h[this.event] = this.textbox;
215
return h;
216
};
217
218
InputBox.prototype.json_value = function () {
219
return this.textbox.value;
220
};
221
222
InputBox.prototype.update = function (value) {
223
this.textbox.value = value;
224
};
225
226
InputBox.prototype.disable = function () {
227
this.textbox.disabled = true;
228
};
229
230
var InputGrid = InteractControl();
231
232
InputGrid.prototype.rendered = function (id) {
233
this.textboxes = $();
234
var table = ce("table", {
235
style: "width: auto; vertical-align: middle; display: inline-table;",
236
});
237
this.button = ce("button", { style: "vertical-align: middle;" }, [
238
"Submit",
239
]);
240
var div = ce("div", {}, [table, this.button]);
241
var i = -1;
242
for (var row = 0; row < this.control.nrows; row++) {
243
var tr = ce("tr");
244
for (var col = 0; col < this.control.ncols; col++) {
245
var textbox = ce("input", {
246
value: this.control["default"][row][col],
247
size: this.control.width,
248
autocapitalize: "off",
249
autocorrect: "off",
250
autocomplete: "off",
251
});
252
textbox.id = id + "_" + ++i;
253
if (this.control.evaluate) {
254
textbox.style.fontFamily = "monospace";
255
}
256
this.textboxes = this.textboxes.add(textbox);
257
tr.appendChild(ce("td", {}, [textbox]));
258
}
259
table.appendChild(tr);
260
}
261
return div;
262
};
263
264
InputGrid.prototype.changeHandlers = function () {
265
return { click: this.button };
266
};
267
268
InputGrid.prototype.json_value = function () {
269
var value = [];
270
for (var row = 0; row < this.control.nrows; row++) {
271
var rowlist = [];
272
for (var col = 0; col < this.control.ncols; col++) {
273
rowlist.push(this.textboxes[row * this.control.ncols + col].value);
274
}
275
value.push(rowlist);
276
}
277
return value;
278
};
279
280
InputGrid.prototype.update = function (value, index) {
281
if (index === undefined) {
282
var i = -1;
283
for (var row = 0; row < value.length; row++) {
284
for (var col = 0; col < value[row].length; col++) {
285
this.textboxes[++i].value = value[row][col];
286
}
287
}
288
} else {
289
this.textboxes[index[0] * this.control.ncols + index[1]].value = value;
290
}
291
};
292
293
InputGrid.prototype.disable = function () {
294
this.textboxes.prop("disabled", true);
295
};
296
297
var MultiSlider = InteractControl();
298
299
MultiSlider.prototype.rendered = function () {
300
var div = ce("div");
301
this.sliders = $();
302
this.value_boxes = $();
303
this.values = this.control["default"].slice();
304
this.eventCount = 1;
305
for (var i = 0; i < this.control.sliders; i++) {
306
var column = ce("div");
307
column.style.width = "50px";
308
column.style.cssFloat = "left";
309
column.style.textAlign = "center";
310
var slider = ce("span", { class: "sagecell_multiSliderControl" });
311
slider.style.display = "block";
312
slider.style.margin = "0.25em 0.5em 1em 0.8em";
313
column.appendChild(slider);
314
var that = this;
315
if (this.control.subtype === "continuous") {
316
var textbox = ce("input", {
317
class: "sagecell_interactValueBox",
318
type: "number",
319
min: this.control.range[i][0],
320
max: this.control.range[i][1],
321
step: "any",
322
});
323
textbox.value = this.values[i].toString();
324
textbox.size = textbox.value.length + 1;
325
textbox.style.display = this.control.display_values ? "" : "none";
326
$(textbox).change(
327
(function (i) {
328
return function (event) {
329
var textbox = event.target;
330
var val = parseFloat(textbox.value);
331
if (
332
that.control.range[i][0] <= val &&
333
val <= that.control.range[i][1]
334
) {
335
that.values[i] = val;
336
$(that.sliders[i]).slider("option", "value", val);
337
textbox.value = val.toString();
338
} else {
339
textbox.value = that.values[i].toString();
340
}
341
textbox.size = textbox.value.length + 1;
342
};
343
})(i)
344
);
345
$(textbox).keyup(function (event) {
346
event.target.size = event.target.value.length + 1;
347
});
348
that.value_boxes = that.value_boxes.add(textbox);
349
column.appendChild(textbox);
350
} else {
351
var span = ce("span", {}, [
352
this.control.values[i][this.values[i]].toString(),
353
]);
354
span.style.fontFamily = "monospace";
355
span.style.display = this.control.display_values ? "" : "none";
356
that.value_boxes = that.value_boxes.add(span);
357
column.appendChild(span);
358
}
359
var slide_handler = (function (i) {
360
return function (event, ui) {
361
that.values[i] = ui.value;
362
var value_box = that.value_boxes[i];
363
if (that.control.subtype === "continuous") {
364
value_box.value = ui.value.toString();
365
value_box.size = value_box.value.length + 1;
366
$(value_box).data("old_value", value_box.value);
367
} else {
368
$(value_box).text(that.control.values[i][ui.value]);
369
}
370
};
371
})(i);
372
$(slider).slider({
373
orientation: "vertical",
374
value: this.control["default"][i],
375
min: this.control.range[i][0],
376
max: this.control.range[i][1],
377
step: this.control.step[i],
378
});
379
$(slider).on("slide", slide_handler);
380
this.sliders = this.sliders.add(slider);
381
div.appendChild(column);
382
}
383
return div;
384
};
385
386
MultiSlider.prototype.changeHandlers = function () {
387
return { slidechange: this.sliders };
388
};
389
390
MultiSlider.prototype.json_value = function () {
391
return this.values.slice();
392
};
393
394
MultiSlider.prototype.update = function (value, index) {
395
if (index === undefined) {
396
this.ignoreNext = value.length;
397
for (var i = 0; i < value.length; i++) {
398
$(this.sliders[i]).slider("option", "value", value[i]);
399
$(this.sliders[i]).trigger("slide", { value: value[i] });
400
}
401
} else {
402
$(this.sliders[index]).slider("option", "value", value);
403
$(this.sliders[index]).trigger("slide", { value: value });
404
}
405
};
406
407
MultiSlider.prototype.disable = function () {
408
this.sliders.slider("option", "disabled", true);
409
this.value_boxes.prop("disabled", true);
410
};
411
412
var Selector = InteractControl();
413
414
Selector.prototype.rendered = function (id) {
415
var that = this;
416
if (this.control.subtype === "list") {
417
var select = ce("select");
418
for (var i = 0; i < this.control.values; i++) {
419
select.appendChild(
420
ce("option", {}, [this.control.value_labels[i]])
421
);
422
}
423
this.value = select.selectedIndex = this.control["default"];
424
$(select).change(function (event) {
425
that.value = event.target.selectedIndex;
426
$(event.target).trigger("changedone");
427
});
428
select.style.width = this.control.width;
429
select.id = id;
430
this.changing = select;
431
return select;
432
} else if (
433
this.control.subtype === "radio" ||
434
this.control.subtype === "button"
435
) {
436
this.changing = $();
437
var table = ce("table", { style: "width: auto;" });
438
var i = -1;
439
for (var row = 0; row < this.control.nrows; row++) {
440
var tr = ce("tr");
441
for (var col = 0; col < this.control.ncols; col++) {
442
var radio_id = id + "_" + ++i;
443
var option = ce("input", {
444
type: "radio",
445
name: id,
446
id: radio_id,
447
});
448
if (i === this.control["default"]) {
449
option.checked = true;
450
this.value = i;
451
}
452
var label = ce("label", { for: radio_id }, [
453
this.control.value_labels[i],
454
]);
455
label.style.width = this.control.width;
456
$(option).change(
457
(function (i) {
458
return function (event) {
459
that.value = i;
460
$(event.target).trigger("changedone");
461
};
462
})(i)
463
);
464
this.changing = this.changing.add(option);
465
tr.appendChild(ce("td", {}, [option, label]));
466
}
467
table.appendChild(tr);
468
}
469
if (this.control.subtype === "button") {
470
this.changing.button();
471
}
472
return table;
473
}
474
};
475
476
Selector.prototype.changeHandlers = function () {
477
return { changedone: this.changing };
478
};
479
480
Selector.prototype.json_value = function () {
481
return this.value;
482
};
483
484
Selector.prototype.update = function (value) {
485
if (this.control.subtype === "list") {
486
this.changing.selectedIndex = value;
487
} else {
488
this.changing[value].checked = true;
489
this.changing.button("refresh");
490
}
491
this.value = value;
492
};
493
494
Selector.prototype.disable = function () {
495
if (this.control.subtype === "list") {
496
this.changing.disabled = true;
497
} else if (this.control.subtype === "radio") {
498
this.changing.prop("disabled", true);
499
} else {
500
this.changing.button("option", "disabled", true);
501
}
502
};
503
504
var Slider = InteractControl();
505
506
Slider.prototype.rendered = function () {
507
this.continuous =
508
this.control.subtype === "continuous" ||
509
this.control.subtype === "continuous_range";
510
this.range =
511
this.control.subtype === "discrete_range" ||
512
this.control.subtype === "continuous_range";
513
var cell1 = ce("div"),
514
cell2 = ce("div");
515
var container = ce("div", { class: "sagecell_sliderContainer" }, [
516
cell1,
517
cell2,
518
]);
519
this.value_boxes = $();
520
this.eventCount = this.range ? 2 : 1;
521
this.slider = ce("div", { class: "sagecell_sliderControl" });
522
cell1.appendChild(this.slider);
523
var that = this;
524
if (this.continuous) {
525
if (this.range) {
526
this.values = this.control["default"].slice();
527
$(this.slider).slider({
528
min: this.control.range[0],
529
max: this.control.range[1],
530
step: this.control.step,
531
range: true,
532
values: this.values,
533
});
534
var min_text = ce("input", {
535
class: "sagecell_interactValueBox",
536
type: "number",
537
value: this.values[0].toString(),
538
min: this.control.range[0],
539
max: this.control.range[1],
540
step: "any",
541
});
542
var max_text = min_text.cloneNode();
543
max_text.value = this.values[1].toString();
544
min_text.size = min_text.value.length;
545
max_text.size = max_text.value.length;
546
$(this.slider).on("slide", function (event, ui) {
547
that.values = ui.values.slice();
548
min_text.value = that.values[0].toString();
549
max_text.value = that.values[1].toString();
550
min_text.size = min_text.value.length;
551
max_text.size = max_text.value.length;
552
});
553
$(min_text).change(function () {
554
var val = parseFloat(min_text.value);
555
if (
556
that.control.range[0] <= val &&
557
val <= $(that.slider).slider("option", "values")[1]
558
) {
559
that.values[0] = val;
560
$(that.slider).slider("option", "values", that.values);
561
min_text.value = val.toString();
562
} else {
563
min_text.value = that.values[0].toString();
564
}
565
min_text.size = min_text.value.length + 1;
566
});
567
$(max_text).change(function () {
568
var val = parseFloat(max_text.value);
569
if (
570
$(that.slider).slider("option", "values")[0] <= val &&
571
val <= that.control.range[1]
572
) {
573
that.values[1] = val;
574
$(that.slider).slider("option", "values", that.values);
575
max_text.value = val.toString();
576
} else {
577
max_text.value = that.values[1].toString();
578
}
579
max_text.size = max_text.value.length + 1;
580
});
581
$([min_text, max_text]).keyup(function (event) {
582
event.target.size = event.target.value.length + 1;
583
});
584
$([min_text, max_text]).focus(function (event) {
585
event.target.size = event.target.value.length + 1;
586
});
587
$([min_text, max_text]).blur(function (event) {
588
event.target.size = event.target.value.length;
589
});
590
var div = ce("div", {}, ["(", min_text, ",", max_text, ")"]);
591
div.style.whiteSpace = "nowrap";
592
this.value_boxes = $([min_text, max_text]);
593
div.style.fontFamily = "monospace";
594
cell2.appendChild(div);
595
} else {
596
this.value = this.control["default"];
597
$(this.slider).slider({
598
min: this.control.range[0],
599
max: this.control.range[1],
600
step: this.control.step,
601
value: this.value,
602
});
603
var textbox = ce("input", {
604
class: "sagecell_interactValueBox",
605
type: "number",
606
value: this.value.toString(),
607
min: this.control.range[0],
608
max: this.control.range[1],
609
step: "any",
610
});
611
textbox.size = textbox.value.length + 1;
612
$(this.slider).on("slide", function (event, ui) {
613
textbox.value = (that.value = ui.value).toString();
614
textbox.size = textbox.value.length + 1;
615
});
616
$(textbox).change(function () {
617
var val = parseFloat(textbox.value);
618
if (
619
that.control.range[0] <= val &&
620
val <= that.control.range[1]
621
) {
622
that.value = val;
623
$(that.slider).slider("option", "value", that.value);
624
textbox.value = val.toString();
625
} else {
626
textbox.value = that.value.toString();
627
}
628
textbox.size = textbox.value.length + 1;
629
});
630
$(textbox).keyup(function (event) {
631
textbox.size = textbox.value.length + 1;
632
});
633
cell2.appendChild(textbox);
634
this.value_boxes = $(textbox);
635
}
636
} else if (this.range) {
637
this.values = this.control["default"].slice();
638
$(this.slider).slider({
639
min: this.control.range[0],
640
max: this.control.range[1],
641
step: this.control.step,
642
range: true,
643
values: this.values,
644
});
645
var div = ce("div", {}, [
646
"(" +
647
this.control.values[this.values[0]] +
648
", " +
649
this.control.values[this.values[1]] +
650
")",
651
]);
652
div.style.fontFamily = "monospace";
653
div.style.whiteSpace = "nowrap";
654
$(this.slider).on("slide", function (event, ui) {
655
that.values = ui.values.slice();
656
this.values = ui.values.slice();
657
$(div).text(
658
"(" +
659
that.control.values[that.values[0]] +
660
", " +
661
that.control.values[that.values[1]] +
662
")"
663
);
664
});
665
cell2.appendChild(div);
666
} else {
667
this.value = this.control["default"];
668
$(this.slider).slider({
669
min: this.control.range[0],
670
max: this.control.range[1],
671
step: this.control.step,
672
value: this.value,
673
});
674
var div = ce("div", {}, [this.control.values[this.value].toString()]);
675
div.style.fontFamily = "monospace";
676
$(this.slider).on("slide", function (event, ui) {
677
$(div).text(
678
that.control.values[(that.value = ui.value)].toString()
679
);
680
});
681
cell2.appendChild(div);
682
}
683
return container;
684
};
685
686
Slider.prototype.changeHandlers = function () {
687
return { slidechange: this.slider };
688
};
689
690
Slider.prototype.json_value = function () {
691
if (this.range) {
692
return this.values.slice();
693
} else {
694
return this.value;
695
}
696
};
697
698
Slider.prototype.update = function (value) {
699
if (this.range) {
700
value = value.slice();
701
}
702
$(this.slider).slider("option", this.range ? "values" : "value", value);
703
var ui = {};
704
ui[this.range ? "values" : "value"] = value;
705
$(this.slider).trigger("slide", ui);
706
};
707
708
Slider.prototype.disable = function () {
709
$(this.slider).slider("option", "disabled", true);
710
$(this.value_boxes).prop("disabled", true);
711
};
712
713
export default {
714
button: Button,
715
button_bar: ButtonBar,
716
checkbox: Checkbox,
717
color_selector: ColorSelector,
718
html_box: HtmlBox,
719
input_box: InputBox,
720
input_grid: InputGrid,
721
multi_slider: MultiSlider,
722
selector: Selector,
723
slider: Slider,
724
};
725
726