Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagecell
Path: blob/master/js/interact_cell.js
447 views
1
import $ from "jquery";
2
import InteractData from "./interact_data";
3
import utils from "./utils";
4
5
import "base/js/events";
6
7
var ce = utils.createElement;
8
9
var stop = function (event) {
10
event.stopPropagation();
11
};
12
var close = null;
13
14
function InteractCell(session, data, parent_block) {
15
this.interact_id = data.new_interact_id;
16
this.function_code = data.function_code;
17
this.controls = {};
18
this.session = session;
19
this.parent_block = parent_block;
20
this.layout = data.layout;
21
this.locations = data.locations;
22
this.msg_id = data.msg_id;
23
this.changed = [];
24
25
var controls = data.controls;
26
for (var name in controls) {
27
if (controls.hasOwnProperty(name)) {
28
this.controls[name] = new InteractData[controls[name].control_type](
29
controls[name]
30
);
31
}
32
}
33
this.session.interact_pl.style.display = "block";
34
this.renderCanvas(parent_block);
35
this.bindChange();
36
if (data.readonly) {
37
this.disable();
38
}
39
}
40
41
InteractCell.prototype.newControl = function (data) {
42
this.controls[data.name] = new InteractData[data.control.control_type](
43
data.control
44
);
45
this.placeControl(data.name);
46
this.bindChange(data.name);
47
if (this.output_block && this.controls[data.name].dirty_update) {
48
$(this.cells[data.name]).addClass("sagecell_dirtyControl");
49
}
50
if (this.parent_block === null) {
51
this.session.updateLinks(true);
52
}
53
};
54
55
InteractCell.prototype.delControl = function (data) {
56
delete this.controls[data.name];
57
var tr = this.cells[data.name].parentNode;
58
tr.parentNode.removeChild(tr);
59
delete this.cells[data.name];
60
if (this.parent_block === null) {
61
this.session.updateLinks(true);
62
}
63
};
64
65
InteractCell.prototype.bindChange = function (cname) {
66
var that = this;
67
var handler = function (event, ui) {
68
if (that.controls[event.data.name].ignoreNext > 0) {
69
that.controls[event.data.name].ignoreNext--;
70
return;
71
}
72
if (that.changed.indexOf(event.data.name) === -1) {
73
that.changed.push(event.data.name);
74
}
75
var msg_dict = {
76
interact_id: that.interact_id,
77
values: {},
78
update_last: false,
79
user_expressions: { _sagecell_files: "sys._sage_.new_files()" },
80
};
81
msg_dict.values[event.data.name] =
82
that.controls[event.data.name].json_value(ui);
83
if (that.controls[event.data.name].dirty_update) {
84
$(that.cells[event.data.name]).addClass("sagecell_dirtyControl");
85
}
86
var callbacks = {
87
iopub: {
88
output: $.proxy(that.session.handle_output, that.session),
89
},
90
shell: {
91
reply: $.proxy(that.session.handle_execute_reply, that.session),
92
},
93
};
94
that.session.send_message(
95
"sagenb.interact.update_interact",
96
msg_dict,
97
callbacks
98
);
99
if (this.parent_block === null) {
100
that.session.updateLinks(true);
101
}
102
};
103
if (cname === undefined) {
104
for (var name in this.controls) {
105
if (this.controls.hasOwnProperty(name)) {
106
var events = this.controls[name].changeHandlers();
107
for (var e in events) {
108
if (events.hasOwnProperty(e)) {
109
$(events[e]).on(e, { name: name }, handler);
110
}
111
}
112
}
113
}
114
} else {
115
var events = this.controls[cname].changeHandlers();
116
for (var e in events) {
117
if (events.hasOwnProperty(e)) {
118
$(events[e]).on(e, { name: cname }, handler);
119
}
120
}
121
}
122
};
123
124
InteractCell.prototype.placeControl = function (name) {
125
var control = this.controls[name];
126
var id = this.interact_id + "_" + name;
127
var div = this.cells[name];
128
if (div === undefined) {
129
var rdiv = ce("div");
130
div = this.cells[name] = ce("div", {
131
class: "sagecell_interactControlCell",
132
});
133
div.style.width = "90%";
134
rdiv.appendChild(div);
135
if (this.output_block) {
136
var outRow = this.output_block.parentNode.parentNode;
137
outRow.parentNode.insertBefore(rdiv, outRow);
138
} else {
139
$(this.container).append(rdiv);
140
}
141
}
142
if (control.control.label.length > 0) {
143
div.appendChild(
144
ce(
145
"label",
146
{
147
class: "sagecell_interactControlLabel",
148
for: id,
149
title: name,
150
},
151
[control.control.label]
152
)
153
);
154
}
155
div.appendChild(
156
ce("div", { class: "sagecell_interactControl" }, [control.rendered(id)])
157
);
158
};
159
160
var textboxItem = function (defaultVal, callback) {
161
var input = ce("input", {
162
value: defaultVal,
163
placeholder: "Bookmark name",
164
});
165
input.addEventListener("keydown", stop);
166
input.addEventListener("keypress", function (event) {
167
if (event.keyCode === 13) {
168
callback();
169
event.preventDefault();
170
}
171
event.stopPropagation();
172
});
173
var div = ce("div", {
174
title: "Add bookmark",
175
tabindex: "0",
176
role: "button",
177
});
178
div.addEventListener("click", callback);
179
return ce("li", { class: "ui-state-disabled" }, [
180
ce("a", {}, [input, div]),
181
]);
182
};
183
184
var selectAll = function (txt) {
185
txt.selectionStart = 0;
186
txt.selectionEnd = txt.value.length;
187
txt.selectionDirection = "forward";
188
};
189
190
InteractCell.prototype.renderCanvas = function (parent_block) {
191
this.cells = {};
192
this.container = ce("div", { class: "sagecell_interactContainer" });
193
if (this.layout && this.layout.length > 0) {
194
for (var row = 0; row < this.layout.length; row++) {
195
var rdiv = ce("div");
196
var total = 0;
197
for (var col = 0; col < this.layout[row].length; col++) {
198
total += this.layout[row][col][1];
199
}
200
for (var col = 0; col < this.layout[row].length; col++) {
201
var cdiv = ce("div", {
202
class: "sagecell_interactControlCell",
203
});
204
cdiv.style.width =
205
(100 * this.layout[row][col][1]) / total + "%";
206
if (this.layout[row][col] !== undefined) {
207
this.cells[this.layout[row][col][0]] = cdiv;
208
if (this.layout[row][col][0] === "_output") {
209
this.output_block = ce("div", {
210
class: "sagecell_interactOutput",
211
});
212
cdiv.appendChild(this.output_block);
213
}
214
}
215
rdiv.appendChild(cdiv);
216
}
217
this.container.appendChild(rdiv);
218
}
219
}
220
if (this.locations) {
221
for (var name in this.locations) {
222
if (this.locations.hasOwnProperty(name)) {
223
this.cells[name] = $("body")
224
.find(this.locations[name])
225
.slice(0, 1)
226
.empty()[0];
227
if (name === "_output") {
228
this.output_block = this.cells[name];
229
$(this.output_block).addClass("sagecell_interactOutput");
230
} else if (name === "_bookmarks") {
231
this.bookmark_container = this.cells[name];
232
}
233
}
234
}
235
}
236
for (var name in this.controls) {
237
if (this.controls.hasOwnProperty(name)) {
238
this.placeControl(name);
239
}
240
}
241
var menuBar = ce("div", { class: "sagecell_bookmarks" });
242
var expText = ce("input", {
243
title: "Pass this string to the interact proxy\u2019s _set_bookmarks method.",
244
readonly: "",
245
});
246
var expButton = ce("div", {
247
title: "Export bookmarks",
248
tabindex: "0",
249
role: "button",
250
});
251
expText.style.display = "none";
252
expText.addEventListener("focus", function (event) {
253
selectAll(expText);
254
});
255
var starButton = ce("div", {
256
title: "Bookmarks",
257
tabindex: "0",
258
role: "button",
259
});
260
this.set_export = function () {
261
var b = [];
262
for (var i = 0; i < this.bookmarks.childNodes.length; i++) {
263
var li = this.bookmarks.childNodes[i];
264
var node = li.firstChild.firstChild.firstChild;
265
if (node !== null) {
266
b.push([node.nodeValue, $(li).data("values")]);
267
}
268
}
269
expText.value = JSON.stringify(JSON.stringify(b));
270
};
271
var list = ce("ul", { class: "sagecell_bookmarks_list" });
272
var that = this;
273
menuBar.addEventListener("mousedown", stop);
274
expButton.addEventListener("click", function () {
275
expText.style.display = "";
276
expText.focus();
277
selectAll(expText);
278
$(expButton).removeClass("sagecell_export");
279
});
280
menuBar.appendChild(expButton);
281
menuBar.appendChild(expText);
282
menuBar.appendChild(starButton);
283
this.bookmark_container = this.bookmark_container || this.container;
284
this.bookmark_container.appendChild(menuBar);
285
this.bookmarks = list;
286
list.addEventListener("mousedown", stop, true);
287
this.set_export();
288
var visible = false;
289
var tb;
290
var hide_box = function hide_box() {
291
list.parentNode.removeChild(list);
292
list.removeChild(tb);
293
$(expButton).removeClass("sagecell_export");
294
expText.style.display = "none";
295
window.removeEventListener("mousedown", hide_box);
296
visible = false;
297
close = null;
298
};
299
$(list).menu({
300
select: function (event, ui) {
301
that.state(ui.item.data("values"));
302
hide_box();
303
},
304
});
305
var handler = function (event) {
306
if (visible) {
307
return;
308
}
309
(function addTextbox() {
310
var n = 1;
311
while (true) {
312
for (var i = 0; i < list.childNodes.length; i++) {
313
if (
314
list.childNodes[i].firstChild.firstChild.firstChild
315
.nodeValue ===
316
"Bookmark " + n
317
) {
318
break;
319
}
320
}
321
if (i === list.childNodes.length) {
322
break;
323
}
324
n++;
325
}
326
tb = textboxItem("Bookmark " + n, function () {
327
list.removeChild(tb);
328
that.createBookmark(tb.firstChild.firstChild.value);
329
addTextbox();
330
});
331
list.appendChild(tb);
332
$(list).menu("refresh");
333
setTimeout(function () {
334
var input = list.lastChild.firstChild.firstChild;
335
input.selectionStart = 0;
336
input.selectionEnd = input.value.length;
337
input.selectionDirection = "forward";
338
input.focus();
339
}, 0);
340
})();
341
visible = true;
342
that.session.outputDiv.append(list);
343
if (close) {
344
close();
345
}
346
close = hide_box;
347
$(list).position({
348
my: "right top",
349
at: "right bottom+5px",
350
of: starButton,
351
});
352
$(expButton).addClass("sagecell_export");
353
expButton.style.display = "inline-block";
354
window.addEventListener("mousedown", hide_box);
355
event.stopPropagation();
356
};
357
starButton.addEventListener("mousedown", handler);
358
this.disable_bookmarks = function () {
359
starButton.removeEventListener("mousedown", handler);
360
starButton.setAttribute("aria-disabled", "true");
361
starButton.removeAttribute("tabindex");
362
};
363
if (this.layout && this.layout.length > 0) {
364
this.session.output(this.container, parent_block);
365
}
366
};
367
368
InteractCell.prototype.updateControl = function (data) {
369
if (this.controls[data.control].update) {
370
this.controls[data.control].ignoreNext =
371
this.controls[data.control].eventCount;
372
this.controls[data.control].update(data.value, data.index);
373
if (this.output_block && this.controls[data.control].dirty_update) {
374
$(this.cells[data.control]).addClass("sagecell_dirtyControl");
375
}
376
if (this.parent_block === null) {
377
this.session.updateLinks(true);
378
}
379
}
380
};
381
382
InteractCell.prototype.state = function (vals, callback) {
383
if (vals === undefined) {
384
vals = {};
385
for (var n in this.controls) {
386
if (this.controls.hasOwnProperty(n) && this.controls[n].update) {
387
vals[n] = this.controls[n].json_value();
388
}
389
}
390
return vals;
391
} else {
392
for (var n in vals) {
393
if (vals.hasOwnProperty(n) && this.controls.hasOwnProperty(n)) {
394
this.controls[n].ignoreNext = this.controls[n].eventCount;
395
this.controls[n].update(vals[n]);
396
}
397
}
398
var msg_dict = {
399
interact_id: this.interact_id,
400
values: vals,
401
update_last: true,
402
};
403
var callbacks = {
404
iopub: {
405
output: $.proxy(this.session.handle_output, this.session),
406
},
407
shell: {
408
"sagenb.interact.update_interact_reply":
409
callback ||
410
$.proxy(this.session.handle_message_reply, this.session),
411
},
412
};
413
this.session.send_message(
414
"sagenb.interact.update_interact",
415
msg_dict,
416
callbacks
417
);
418
}
419
};
420
421
InteractCell.prototype.createBookmark = function (name, vals) {
422
if (vals === undefined) {
423
vals = this.state();
424
}
425
var del = ce("div", {
426
title: "Delete bookmark",
427
tabindex: "0",
428
role: "button",
429
});
430
var entry = ce("li", {}, [ce("a", {}, [ce("div", {}, [name]), del])]);
431
var that = this;
432
del.addEventListener("click", function (event) {
433
that.bookmarks.removeChild(entry);
434
if (that.parent_block === null) {
435
that.session.updateLinks(true);
436
}
437
that.set_export();
438
event.stopPropagation();
439
});
440
$(entry).data({ values: vals });
441
var tbEntry;
442
if (
443
this.bookmarks.hasChildNodes() &&
444
!this.bookmarks.lastChild.firstChild.firstChild.hasChildNodes()
445
) {
446
tbEntry = this.bookmarks.removeChild(this.bookmarks.lastChild);
447
}
448
this.bookmarks.appendChild(entry);
449
if (tbEntry) {
450
this.bookmarks.appendChild(tbEntry);
451
}
452
$(this.bookmarks).menu("refresh");
453
if (this.parent_block === null) {
454
this.session.updateLinks(true);
455
}
456
this.set_export();
457
};
458
459
InteractCell.prototype.clearBookmarks = function () {
460
var tbEntry;
461
if (
462
this.bookmarks.hasChildNodes() &&
463
!this.bookmarks.lastChild.firstChild.firstChild.hasChildNodes()
464
) {
465
tbEntry = this.bookmarks.removeChild(this.bookmarks.lastChild);
466
}
467
while (this.bookmarks.hasChildNodes()) {
468
this.bookmarks.removeChild(this.bookmarks.firstChild);
469
}
470
if (tbEntry) {
471
this.bookmarks.appendChild(tbEntry);
472
}
473
};
474
475
InteractCell.prototype.disable = function () {
476
this.disable_bookmarks();
477
for (var name in this.controls) {
478
if (this.controls.hasOwnProperty(name) && this.controls[name].disable) {
479
this.controls[name].disable();
480
}
481
}
482
};
483
484
export default InteractCell;
485
export { InteractCell };
486
487