Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
jjtorrens
GitHub Repository: jjtorrens/learnlatex.github.io
Path: blob/main/assets/scripts/runlatex.js
3170 views
1
// runlatex.js for TeXLive.net and Overleaf
2
// Copyright 2020 2021 David Carlisle
3
// MIT Licence
4
5
// set here but local versions can be redefined after
6
// loading this file
7
8
// runlatex configuration object
9
var runlatex={}
10
11
runlatex.texts ={
12
"Open in Overleaf": "Open in Overleaf",
13
"TeXLive.net": "run at TeXLive.net", // or "run latex" or whatever
14
"Delete Output": "Delete Output",
15
"Compiling PDF": "Compiling PDF",
16
// The following not used on learnlatex.org
17
"edit": "edit",
18
"copy": "copy",
19
"Added Code": "Added code",
20
"End Added Code": "End Added code",
21
"Top Caption": "Edit and run this example:"
22
}
23
24
runlatex.editorlines=100;
25
runlatex.adddefaultpreamble=false;
26
runlatex.adddefaultengine=false;
27
runlatex.usecaptions=false;
28
runlatex.minrunlines=0;
29
runlatex.completionsURI="";
30
31
// debug by using https://httpbin.org/post
32
// set to null to omit from interface
33
runlatex.latexcgiURI="https://texlive.net/cgi-bin/latexcgi";
34
runlatex.overleafURI="https://www.overleaf.com/docs";
35
36
// per page setup
37
runlatex.preincludes={};
38
39
// Disable all use of cookies
40
// No cookies are set by this file even when true.
41
runlatex.usecookies=true;
42
43
// end of configuration
44
45
var editors=[];
46
47
const noeditregex = /^\s*[/%#\*]+ *!TEX.*[^a-zA-Z]noedit *(\n|$)/i;
48
const norunregex = /^\s*([/%#\*]+ *!TEX.*[^a-zA-Z]none *|[^% \t\\][^\\]*)(\n|$)/i;
49
const commentregex = / %.*/;
50
const engineregex = /% *!TEX.*[^a-zA-Z](((pdf|xe|lua|u?p)?latex(-dev)?)|context|(pdf|xe|lua|u?p)?tex) *\n/i;
51
const returnregex = /% *!TEX.*[^a-zA-Z](pdfjs|pdf|log|make4ht|latexml|lwarp) *\n/i;
52
const makeindexregex = /% *!TEX.*[^a-zA-Z]makeindex( [a-z0-9\.\- ]*)\n/ig;
53
54
var packageregex = [
55
[ /\\includegraphics/, "\\usepackage[demo]{graphicx}\n"],
56
[ /\\begin{equation|align|gather|flalign/,"\\usepackage{amsmath}\n" ],
57
[ /tikz|pgf/, "\\usepackage{tikz}\n" ],
58
[ /fancy/, "\\usepackage{fancyhdr}\n" ],
59
[ /addplot|axis/, "\\usepackage{pgfplots}\n" ],
60
[ /hyper|href|bookmark|\\url/, "\\usepackage{hyperref}\n" ],
61
[ /\\newcolumntype/, "\\usepackage{array}\n" ],
62
[ /listing/, "\\usepackage{listings}\n" ],
63
[ /\\blind/, "\\usepackage{blindtext}\n" ],
64
[ /\\lipsum/, "\\usepackage{lipsum}\n" ],
65
[ /color/, "\\usepackage{xcolor}\n" ],
66
[ /pspicture/, "\\usepackage{pstricks}\n" ]
67
];
68
69
70
var latexcompetions="";
71
72
73
var customCompleter = {
74
getCompletions: function(editor, session, pos, prefix, callback) {
75
var startToken = session.getTokenAt(pos.row, pos.column).value;
76
if (startToken.startsWith("\\")){
77
var cmplts=[];
78
var s=0;
79
for (let pkg in latexcompletions) {
80
var cs=latexcompletions[pkg];
81
s=s-1;
82
for(let i=0;i<cs.length;i++){
83
if(cs[i].startsWith(prefix)){
84
cmplts.push({name: cs[i], value:cs[i],score: s, meta: pkg});
85
}
86
}
87
}
88
callback(null, cmplts);
89
} else {
90
callback(null, []);
91
return
92
}
93
}
94
}
95
96
function llexamples() {
97
if(runlatex.completionsURI != ""){
98
let request = new XMLHttpRequest();
99
request.open('GET', runlatex.completionsURI);
100
request.responseType = 'json';
101
request.onload = function() {
102
latexcompletions = request.response;
103
}
104
request.send();
105
}
106
var p = document.getElementsByTagName("pre");
107
var editor;
108
var acemode;
109
for(var i=0;i<p.length;i++) {
110
acemode="ace/mode/latex";
111
p[i].setAttribute("id","pre" + i);
112
var pretext=p[i].innerText;
113
if(!pretext.match(noeditregex) && !p[i].classList.contains('noedit')) {
114
if((runlatex.adddefaultpreamble &&
115
(pretext.match(norunregex) || (pretext.match(/\n[^\n]/g) || '').length + 1 < runlatex.minrunlines )) ||
116
(!runlatex.adddefaultpreamble &&
117
pretext.indexOf("\\documentclass") == -1 && !pretext.match(engineregex)) ||
118
p[i].classList.contains('norun')) {
119
if(pretext.match(norunregex)) {
120
acemode="ace/mode/text";
121
}
122
} else {
123
// caption
124
if(runlatex.usecaptions && runlatex.texts["Top Caption"]) {
125
var cpt = document.createElement("div");
126
cpt.setAttribute("class",'lltopcaption');
127
cpt.innerHTML=runlatex.texts["Top Caption"];
128
p[i].parentNode.insertBefore(cpt, p[i]);
129
}
130
// space
131
var s = document.createElement("div");
132
s.setAttribute("class",'ace-spacer');
133
p[i].parentNode.insertBefore(s, p[i].nextSibling);
134
if(runlatex.latexcgiURI){
135
// texlive.net
136
var r = document.createElement("button");
137
r.innerText=runlatex.texts["TeXLive.net"];
138
r.setAttribute("class","llbutton");
139
r.setAttribute("onclick",'latexcgi("pre' + i + '")');
140
r.setAttribute("id","lo-pre" + i);
141
p[i].parentNode.insertBefore(r, p[i].nextSibling);
142
var f2=document.createElement("span");
143
f2.innerHTML="<form style=\"display:none\" id=\"form2-pre" + i +
144
"\" name=\"form2-pre" + i +
145
"\" enctype=\"multipart/form-data\" action=\"" +
146
runlatex.latexcgiURI +
147
"\" method=\"post\" target=\"pre" + i +
148
"ifr\"></form>";
149
p[i].parentNode.insertBefore(f2, p[i].nextSibling);
150
}
151
if(runlatex.overleafURI){
152
// overleaf
153
var o = document.createElement("button");
154
o.innerText=runlatex.texts["Open in Overleaf"];
155
o.setAttribute("class","llbutton");
156
o.setAttribute("onclick",'openinoverleaf("pre' + i + '")');
157
p[i].parentNode.insertBefore(o, p[i].nextSibling);
158
var f=document.createElement("span");
159
f.innerHTML="<form style=\"display:none\" id=\"form-pre" + i +
160
"\" action=\"" +
161
runlatex.overleafURI +
162
"\" method=\"post\" target=\"_blank\"></form>";
163
p[i].parentNode.insertBefore(f, p[i].nextSibling);
164
}
165
}
166
if(runlatex.adddefaultpreamble) {
167
pretext=pretext.replace(/^[ \t\u00A0]+$/gm,'');
168
}
169
p[i].textContent=pretext.replace(/\s+$/,'');
170
p[i].style.height="1em"; // force redisplay in Opera zoom
171
ace.config.set('basePath', 'https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.12') ;
172
editor = ace.edit(p[i]);
173
editor.setTheme(rlacetheme);
174
editor.getSession().setMode(acemode);
175
editor.setOption("minLines",runlatex);
176
editor.setOption("maxLines",runlatex.editorlines);
177
editor.setShowPrintMargin(false);
178
if(runlatex.completionsURI != ""){
179
langTools=ace.require("ace/ext/language_tools");
180
langTools.setCompleters([customCompleter]);
181
editor.setOptions({
182
enableBasicAutocompletion: true,
183
enableLiveAutocompletion: true
184
});
185
}
186
editor.resize();
187
editors["pre" + i]=editor;
188
}
189
}
190
}
191
192
193
function addinput(f,n,v) {
194
var inp=document.createElement("input");
195
inp.setAttribute("type","text");
196
inp.setAttribute("name",n);
197
inp.value =encodeURIComponent(v);
198
f.appendChild(inp);
199
}
200
201
function addinputnoenc(f,n,v) {
202
var inp=document.createElement("input");
203
inp.setAttribute("type","text");
204
inp.setAttribute("name",n);
205
inp.value =v;
206
f.appendChild(inp);
207
}
208
209
function addtextarea(f,n,v) {
210
var inp=document.createElement("textarea");
211
inp.setAttribute("type","text");
212
inp.setAttribute("name",n);
213
inp.textContent=v;
214
f.appendChild(inp);
215
}
216
217
function openinoverleaf(nd) {
218
var fm = document.getElementById('form-' + nd);
219
fm.innerHTML="";
220
var p = document.getElementById(nd);
221
var t = editors[nd].getValue();
222
223
var engv=rldefaultengine;
224
var eng=t.match(engineregex);
225
if(runlatex.adddefaultpreamble) {
226
if(t.indexOf("\\documentclass") == -1 && ( eng == null)) {
227
t=generatepreamble(t,editors[nd]);
228
}
229
}
230
if(eng != null) {
231
engv=eng[1].toLowerCase();
232
if(engv == "pdftex" || engv == "luatex" || engv == "xetex" || engv == "ptex" || engv == "uptex") {
233
addinput(fm,"main_document","document.tex");
234
}
235
}
236
addinput(fm,"encoded_snip[]","\n" + t);
237
addinput(fm,"snip_name[]","document.tex");
238
if(typeof(runlatex.preincludes) == "object") {
239
if(typeof(runlatex.preincludes[nd]) == "object") {
240
var incl=runlatex.preincludes[nd];
241
for(prop in incl) {
242
if(editors[prop]==null) {
243
addinput(fm,"encoded_snip[]",document.getElementById(prop).textContent);
244
} else {
245
addinput(fm,"encoded_snip[]",editors[prop].getValue());
246
}
247
addinput(fm,"snip_name[]",incl[prop]);
248
}
249
}
250
}
251
if(eng != null) {
252
if(engv.indexOf("platex") != -1 || engv.indexOf("ptex") != -1 || engv=="tex") {
253
addinput(fm,"encoded_snip[]","$latex = '" + engv + "';\n$bibtex = 'pbibtex';\n$dvipdf = 'dvipdfmx %O -o %D %S';");
254
addinput(fm,"snip_name[]","latexmkrc");
255
engv="latex_dvipdf";
256
} else if(engv == "pdftex" || engv == "luatex" || engv == "xetex") {
257
addinput(fm,"encoded_snip[]","$pdflatex = '" + engv + "';");
258
addinput(fm,"snip_name[]","latexmkrc");
259
engv=rldefaultengine;
260
}
261
262
}
263
addinput(fm,"engine",engv);
264
fm.submit();
265
}
266
267
function copytoclipboard(nd){
268
var p = document.getElementById(nd);
269
var nn=document.createElement("textarea");
270
nn.value=p.innerText;
271
document.body.appendChild(nn);
272
nn.select();
273
document.execCommand("copy");
274
document.body.removeChild(nn);
275
}
276
277
278
function allowedit(nd){
279
var p = document.getElementById(nd);
280
p.contentEditable="true";
281
p.setAttribute("spellcheck","false");
282
p.innerHTML=p.innerText;
283
p.style.border="solid thin green";
284
}
285
286
function deleteoutput(nd){
287
var b = document.getElementById('del-' + nd);
288
var ifr = document.getElementById(nd + 'ifr');
289
b.parentNode.removeChild(b);
290
ifr.parentNode.removeChild(ifr);
291
}
292
293
function generatepreamble(t,e) {
294
e.navigateFileStart();
295
if(t.match(/koma|KOMA|addsec|\\scr|scrheadings/)){
296
e.insert("\n% " + runlatex.texts["Added Code"] + "\n\\documentclass{scrartcl}\n");
297
} else {
298
e.insert("\n% " + runlatex.texts["Added Code"] + "\n\\documentclass{article}\n");
299
}
300
for(var i=0;i<packageregex.length; i++){
301
if(t.match(packageregex[i][0])) e.insert(packageregex[i][1]);
302
}
303
e.insert("\n\\begin{document}\n% " + runlatex.texts["End Added Code"] + "\n\n");
304
e.navigateFileEnd();
305
e.insert("\n\n% " +
306
runlatex.texts["Added Code"] +
307
"\n\\end{document}\n% " +
308
runlatex.texts["End Added Code"] +
309
"\n");
310
return e.getValue();
311
}
312
313
function defaultengine(t) {
314
if ((t.indexOf("\\usepackage{lua") !== -1) || (t.indexOf("\\directlua") !== -1) ){
315
return "lualatex";
316
} else if (t.indexOf("fontspec") !== -1) {
317
return "xelatex";
318
} else if (t.indexOf("pstricks") !==-1) {
319
return "latex";
320
} else return rldefaultengine;
321
}
322
323
function latexcgi(nd) {
324
var fm = document.getElementById('form2-' + nd);
325
fm.innerHTML="";
326
var p = document.getElementById(nd);
327
var t = editors[nd].getValue();
328
var engv=rldefaultengine;
329
var eng=t.match(engineregex);
330
if(runlatex.adddefaultpreamble) {
331
if(t.indexOf("\\documentclass") == -1 && ( eng == null)) {
332
t=generatepreamble(t,editors[nd]);
333
}
334
}
335
addtextarea(fm,"filecontents[]",t);
336
addinputnoenc(fm,"filename[]","document.tex");
337
if(typeof(runlatex.preincludes) == "object") {
338
if(typeof(runlatex.preincludes[nd]) == "object") {
339
var incl=runlatex.preincludes[nd];
340
for(prop in incl) {
341
if(editors[prop]==null) {
342
addtextarea(fm,"filecontents[]",document.getElementById(prop).textContent);
343
} else {
344
addtextarea(fm,"filecontents[]",editors[prop].getValue());
345
}
346
addinputnoenc(fm,"filename[]",incl[prop]);
347
}
348
}
349
}
350
if(eng != null) {
351
engv=eng[1].toLowerCase();
352
} else if(runlatex.adddefaultengine) {
353
engv=defaultengine(t);
354
}
355
addinput(fm,"engine",engv);
356
var rtn = t.match(returnregex);
357
var rtnv = "";
358
if(rtn == null) {
359
// ES6 / IE
360
if (typeof Symbol == "undefined") {
361
addinput(fm,"return","pdf");
362
} else {
363
addinput(fm,"return",rldefaultreturn);
364
}
365
} else {
366
rtnv=rtn[1].toLowerCase();
367
addinput(fm,"return",rtnv);
368
}
369
var mki = makeindexregex.exec(t);
370
while (mki != null) {
371
addinputnoenc(fm,"makeindex[]",mki[1]);
372
mki = makeindexregex.exec(t);
373
}
374
var b = document.getElementById('lo-' + nd);
375
var ifr= document.getElementById(nd + "ifr");
376
if(ifr == null) {
377
ifr=document.createElement("iframe");
378
ifr.setAttribute("width","100%");
379
ifr.setAttribute("height","500em");
380
ifr.setAttribute("id",nd + "ifr");
381
ifr.setAttribute("name",nd + "ifr");
382
p.parentNode.insertBefore(ifr, b.nextSibling);
383
d=document.createElement("button");
384
d.innerText=runlatex.texts["Delete Output"];
385
d.setAttribute("class","llbutton");
386
d.setAttribute("id","del-" + nd);
387
d.setAttribute("onclick",'deleteoutput("' + nd + '")');
388
p.parentNode.insertBefore(d, b.nextSibling);
389
}
390
var loading=document.createElement("div");
391
loading.id=nd+"load";
392
loading.textContent=runlatex.texts["Compiling PDF"] + " . . .";
393
p.parentNode.insertBefore(loading, ifr);
394
// scroll only if really close to the bottom
395
var rect = b.getBoundingClientRect();
396
if(document.documentElement.clientHeight - rect.bottom < 50){
397
window.scrollBy(0,150);
398
}
399
setTimeout(function () {
400
p.parentNode.removeChild(document.getElementById(nd+"load"));
401
}, 1000);
402
fm.submit();
403
}
404
405
406
407
408
409
var createCookie = function(name, value, days) {
410
if(runlatex.usecookies){
411
var expires;
412
if (days) {
413
var date = new Date();
414
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
415
expires = "; expires=" + date.toGMTString();
416
}
417
else {
418
expires = "";
419
}
420
document.cookie = name + "=" + value + expires + "; path=/" + "; SameSite=Lax";
421
}
422
}
423
424
function getCookie(c_name) {
425
if (runlatex.usecookies && document.cookie.length > 0) {
426
c_start = document.cookie.indexOf(c_name + "=");
427
if (c_start != -1) {
428
c_start = c_start + c_name.length + 1;
429
c_end = document.cookie.indexOf(";", c_start);
430
if (c_end == -1) {
431
c_end = document.cookie.length;
432
}
433
return unescape(document.cookie.substring(c_start, c_end));
434
}
435
}
436
return "";
437
}
438
439
function rlSetReturn(n) {
440
createCookie('runlatex-return',n,100);
441
}
442
443
var rldefaultreturn=getCookie('runlatex-return');
444
if(rldefaultreturn=="") rldefaultreturn="pdfjs";
445
446
function rlSetEngine(n) {
447
createCookie('runlatex-engine',n,100);
448
}
449
450
var rldefaultengine=getCookie('runlatex-engine');
451
if(rldefaultengine=="") rldefaultengine="pdflatex";
452
453
454
var rlacetheme=getCookie('runlatex-acetheme');
455
if(rlacetheme=="") rlacetheme="ace/theme/textmate";
456
457
function rlAllowCookies() {
458
createCookie('runlatex-cookies',"true",100);
459
window.location.reload(false);
460
}
461
462
function rlDeleteCookies() {
463
createCookie('runlatex-cookies',"",-999);
464
createCookie('runlatex-return',"",-999);
465
createCookie('runlatex-engine',"",-999);
466
createCookie('runlatex-acetheme',"",-999);
467
window.location.reload(false);
468
}
469
470
var rlallowcookies=getCookie('runlatex-cookies')=="true";
471
472
window.addEventListener('load', llexamples, false);
473
474