Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagecell
Path: blob/master/js/cell.js
447 views
1
import $ from "jquery";
2
import sagecell from "./sagecell";
3
import editor from "./editor";
4
import Session from "./session";
5
import utils from "./utils";
6
import domReady from "domready";
7
import { initializeURLs, URLs } from "./urls";
8
9
// Imports for side-effects only
10
import "webpack-jquery-ui";
11
import "jsmol";
12
import "colorpicker";
13
14
// The contents of these files is imported as strings
15
//import css from "all.min.css";
16
import cell_body from "./cell_body.html";
17
18
import { css } from "./css";
19
import { console } from "./console";
20
21
sagecell.modes = {
22
sage: "python",
23
python: "python",
24
html: "htmlmixed",
25
r: "r",
26
};
27
28
domReady(function () {
29
initializeURLs();
30
31
var style = document.createElement("style");
32
style.innerHTML = css.replace(
33
/(url\(["']?(?!["']?data:))/g,
34
"$1" + URLs.root + "static/"
35
);
36
var fs = document.getElementsByTagName("script")[0];
37
fs.parentNode.insertBefore(style, fs);
38
39
if (window.MathJax === undefined) {
40
// MathJax 3
41
var script = document.createElement("script");
42
script.type = "text/javascript";
43
script.text = `window.MathJax = {
44
tex: {
45
inlineMath: [["$", "$"], ["\\\\(", "\\\\)"]],
46
displayMath: [["$$", "$$"], ["\\\\[", "\\\\]"]],
47
processEscapes: true,
48
processEnvironments: true,
49
},
50
options: {
51
renderActions: { /* remove when dropping MathJax2 compatibility */
52
find_script_mathtex: [10, function (doc) {
53
for (const node of document.querySelectorAll('script[type^="math/tex"]')) {
54
const display = !!node.type.match(/; *mode=display/);
55
const math = new doc.options.MathItem(node.textContent, doc.inputJax[0], display);
56
const text = document.createTextNode('');
57
node.parentNode.replaceChild(text, node);
58
math.start = {node: text, delim: '', n: 0};
59
math.end = {node: text, delim: '', n: 0};
60
doc.math.push(math);
61
}
62
}, '']
63
}
64
}
65
};`;
66
fs.parentNode.insertBefore(script, fs);
67
script = document.createElement("script");
68
script.type = "text/javascript";
69
script.src =
70
"https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml-full.js";
71
fs.parentNode.insertBefore(script, fs);
72
}
73
74
// Preload images
75
new Image().src = URLs.spinner;
76
});
77
78
sagecell.kernels = sagecell.kernels || [];
79
80
function make(args, cellInfo, k) {
81
if (args.inputLocation === undefined) {
82
throw "Must specify an inputLocation!";
83
}
84
// Cannot be run before the dom is ready since this function
85
// searches the dom for sagecell elements to replace.
86
domReady(function () {
87
var input = $(args.inputLocation);
88
if (input.length === 0) {
89
return;
90
}
91
if (input.length > 1) {
92
if (args.outputLocation !== undefined) {
93
throw "inputLocation must be unique if outputLocation is specified";
94
}
95
cellInfo.array = [];
96
if (args.linked && k === undefined) {
97
args.autoeval = false;
98
k = sagecell.kernels.push(null) - 1;
99
}
100
for (var i = 0, i_max = input.length; i < i_max; i++) {
101
var args_i = $.extend({}, args);
102
args_i.inputLocation = input[i];
103
var cellInfo_i = {};
104
make(args_i, cellInfo_i, k);
105
cellInfo.array.push(cellInfo_i);
106
}
107
return;
108
}
109
if (input.hasClass("sagecell")) {
110
// Do not process again the same locations.
111
return;
112
}
113
if (k === undefined) {
114
k = sagecell.kernels.push(null) - 1;
115
}
116
if (args.outputLocation === undefined) {
117
args.outputLocation = args.inputLocation;
118
}
119
if (args.code === undefined) {
120
if (args.codeLocation !== undefined) {
121
args.code = $(args.codeLocation).html();
122
} else if (input.children("script").length > 0) {
123
args.code = input.children("script").html();
124
} else if (input.is("textarea")) {
125
args.code = input.val();
126
} else {
127
args.code = input.text();
128
}
129
args.code = $.trim(args.code);
130
}
131
var defaults = {
132
editor: "codemirror",
133
evalButtonText: "Evaluate",
134
hide: ["messages"],
135
mode: "normal",
136
replaceOutput: true,
137
languages: ["sage"],
138
};
139
$.extend(cellInfo, defaults, args.template, args);
140
// Since hide is an array, it is not actually merged as intended
141
var hide = (cellInfo.hide = $.merge([], defaults.hide));
142
if (args.template !== undefined && args.template.hide !== undefined) {
143
$.merge(hide, args.template.hide);
144
}
145
if (args.hide !== undefined) {
146
$.merge(hide, args.hide);
147
}
148
if ($.inArray(cellInfo.defaultLanguage, cellInfo.languages) === -1) {
149
cellInfo.defaultLanguage = cellInfo.languages[0];
150
}
151
if (cellInfo.languages.length === 1) {
152
hide.push("language");
153
}
154
if (cellInfo.linked) {
155
hide.push("permalink");
156
}
157
158
var output = $(cellInfo.outputLocation);
159
160
if (input.is("textarea")) {
161
var ta = input;
162
input = $(document.createElement("div")).insertBefore(input);
163
input.html(cell_body);
164
ta.addClass("sagecell_commands");
165
ta.attr({
166
autocapitalize: "off",
167
autocorrect: "off",
168
autocomplete: "off",
169
});
170
input.find(".sagecell_commands").replaceWith(ta);
171
var id = "input_" + utils.uuid();
172
input[0].id = id;
173
if (input === output) {
174
output = $((cellInfo.outputLocation = "#" + id));
175
}
176
cellInfo.inputLocation = "#" + id;
177
} else {
178
input.html(cell_body);
179
}
180
input.addClass("sagecell");
181
output.addClass("sagecell");
182
input.find(".sagecell_commands").val(cellInfo.code);
183
if (input !== output) {
184
input.find(".sagecell_output_elements").appendTo(output);
185
}
186
output.find(".sagecell_output_elements").hide();
187
hide.push("files"); // TODO: Delete this line when this feature is implemented.
188
if (cellInfo.mode === "debug") {
189
console.info("Running SageMathCell in debug mode");
190
} else {
191
var hideAdvanced = {};
192
var hideable = {
193
in: {
194
editor: true,
195
files: true,
196
evalButton: true,
197
language: true,
198
},
199
out: {
200
output: true,
201
messages: true,
202
sessionFiles: true,
203
permalink: true,
204
},
205
};
206
var hidden_out = [];
207
var out_class = "output_" + utils.uuid();
208
output.addClass(out_class);
209
for (var i = 0, i_max = hide.length; i < i_max; i++) {
210
if (hide[i] in hideable["in"]) {
211
input.find(".sagecell_" + hide[i]).css("display", "none");
212
// TODO: make the advancedFrame an option to hide, then delete
213
// this hideAdvanced hack
214
if (hide[i] === "files") {
215
hideAdvanced[hide[i]] = true;
216
}
217
} else if (hide[i] in hideable["out"]) {
218
hidden_out.push("." + out_class + " .sagecell_" + hide[i]);
219
}
220
}
221
var langOpts = input.find(".sagecell_language option");
222
langOpts
223
.not(function () {
224
return $.inArray(this.value, cellInfo.languages) !== -1;
225
})
226
.css("display", "none");
227
langOpts[0].parentNode.value = cellInfo.defaultLanguage;
228
if (hideAdvanced.files) {
229
input.find(".sagecell_advancedFrame").css("display", "none");
230
}
231
if (hidden_out.length > 0) {
232
var s = document.createElement("style");
233
var css = hidden_out.join(", ") + " {display: none;}";
234
s.setAttribute("type", "text/css");
235
if (s.styleSheet) {
236
s.styleSheet.cssText = css;
237
} else {
238
s.appendChild(document.createTextNode(css));
239
}
240
document.head.appendChild(s);
241
}
242
}
243
input.find(".sagecell_evalButton").text(cellInfo.evalButtonText);
244
init(cellInfo, k);
245
if (hide.indexOf("fullScreen") != -1) {
246
input.find(".sagecell_fullScreen").css("display", "none");
247
}
248
});
249
}
250
251
var accepted_tos = localStorage.accepted_tos;
252
var deferred_eval = [];
253
254
function deferredEvaluation() {
255
for (var i = 0; i < deferred_eval.length; i++) {
256
deferred_eval[i][0](deferred_eval[i][1]);
257
}
258
}
259
260
var last_session = {};
261
var ce = utils.createElement;
262
263
function init(cellInfo, k) {
264
var input = $(cellInfo.inputLocation);
265
var output = $(cellInfo.outputLocation);
266
var langSelect = input.find(".sagecell_language select");
267
//var files = [];
268
var temp = editor.render(cellInfo.editor, input, cellInfo.collapse);
269
var editorType = temp[0];
270
var editorData = temp[1];
271
cellInfo.editorData = editorData;
272
editorData.k = k;
273
input.find(".sagecell_advancedTitle").on("click", function () {
274
input.find(".sagecell_advancedFields").slideToggle();
275
return false;
276
});
277
langSelect.on("change", function () {
278
var mode = langSelect[0].value;
279
editorData.setOption("mode", sagecell.modes[mode]);
280
});
281
function startEvaluation(evt) {
282
if (last_session[evt.data.id]) {
283
if (!last_session[evt.data.id].linked) {
284
last_session[evt.data.id].kernel.kill();
285
}
286
if (cellInfo.replaceOutput) {
287
last_session[evt.data.id].destroy();
288
}
289
}
290
if (
291
editorType.lastIndexOf("codemirror", 0) ===
292
0 /* efficient .startswith('codemirror')*/
293
) {
294
editorData.save();
295
}
296
297
var code = input.find(".sagecell_commands").val();
298
var language = langSelect[0].value;
299
var session = new Session(
300
output,
301
language,
302
cellInfo.interacts || [],
303
k,
304
cellInfo.linked || false
305
);
306
cellInfo.session = session;
307
cellInfo.interacts = [];
308
session.execute(code);
309
last_session[evt.data.id] = session;
310
output.find(".sagecell_output_elements").show();
311
}
312
cellInfo.submit = function (evt) {
313
if (accepted_tos) {
314
startEvaluation(evt);
315
return false;
316
}
317
deferred_eval.push([startEvaluation, evt]);
318
if (deferred_eval.length === 1) {
319
utils.sendRequest("POST", URLs.terms, {}, function (data) {
320
if (data.length === 0) {
321
accepted_tos = true;
322
deferredEvaluation();
323
} else {
324
var terms = $(document.createElement("div"));
325
terms.html(data);
326
terms.dialog({
327
modal: true,
328
height: 400,
329
width: 600,
330
appendTo: input,
331
title: "Terms of Service",
332
buttons: {
333
Accept: function () {
334
$(this).dialog("close");
335
accepted_tos = true;
336
localStorage.accepted_tos = true;
337
deferredEvaluation();
338
},
339
Cancel: function () {
340
$(this).dialog("close");
341
},
342
},
343
});
344
}
345
});
346
}
347
// return false to make *sure* any containing form doesn't submit
348
return false;
349
};
350
var button = input.find(".sagecell_evalButton").button();
351
button.on("click", { id: utils.uuid() }, cellInfo.submit);
352
if (cellInfo.code && cellInfo.autoeval) {
353
button.click();
354
}
355
if (cellInfo.callback) {
356
cellInfo.callback();
357
}
358
return cellInfo;
359
}
360
361
const cell = {
362
make: make,
363
delete: function (cellInfo) {
364
$(cellInfo.inputLocation).remove();
365
$(cellInfo.outputLocation).remove();
366
},
367
moveInputForm: function (cellInfo) {
368
var moved = ce("div", { id: "sagecell_moved" });
369
moved.style.display = "none";
370
$(document.body).append(moved);
371
$(cellInfo.inputLocation).contents().appendTo($(moved));
372
},
373
restore: function (cellInfo) {
374
var moved = $("#sagecell_moved");
375
moved.contents().appendTo(cellInfo.inputLocation);
376
moved.remove();
377
},
378
};
379
380
export default cell;
381
382