@jupyterlab/rendermime 0.19.1-alpha.0 → 0.19.1

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/lib/renderers.js CHANGED
@@ -1,537 +1,537 @@
1
- "use strict";
2
- /*-----------------------------------------------------------------------------
3
- | Copyright (c) Jupyter Development Team.
4
- | Distributed under the terms of the Modified BSD License.
5
- |----------------------------------------------------------------------------*/
6
- var __importDefault = (this && this.__importDefault) || function (mod) {
7
- return (mod && mod.__esModule) ? mod : { "default": mod };
8
- };
9
- Object.defineProperty(exports, "__esModule", { value: true });
10
- const ansi_up_1 = __importDefault(require("ansi_up"));
11
- const marked_1 = __importDefault(require("marked"));
12
- const codemirror_1 = require("@jupyterlab/codemirror");
13
- const coreutils_1 = require("@jupyterlab/coreutils");
14
- const algorithm_1 = require("@phosphor/algorithm");
15
- const latex_1 = require("./latex");
16
- /**
17
- * Render HTML into a host node.
18
- *
19
- * @params options - The options for rendering.
20
- *
21
- * @returns A promise which resolves when rendering is complete.
22
- */
23
- function renderHTML(options) {
24
- // Unpack the options.
25
- let { host, source, trusted, sanitizer, resolver, linkHandler, shouldTypeset, latexTypesetter } = options;
26
- let originalSource = source;
27
- // Bail early if the source is empty.
28
- if (!source) {
29
- host.textContent = '';
30
- return Promise.resolve(undefined);
31
- }
32
- // Sanitize the source if it is not trusted. This removes all
33
- // `<script>` tags as well as other potentially harmful HTML.
34
- if (!trusted) {
35
- originalSource = `${source}`;
36
- source = sanitizer.sanitize(source);
37
- }
38
- // Set the inner HTML of the host.
39
- host.innerHTML = source;
40
- if (host.getElementsByTagName('script').length > 0) {
41
- // If output it trusted, eval any script tags contained in the HTML.
42
- // This is not done automatically by the browser when script tags are
43
- // created by setting `innerHTML`.
44
- if (trusted) {
45
- Private.evalInnerHTMLScriptTags(host);
46
- }
47
- else {
48
- const container = document.createElement('div');
49
- const warning = document.createElement('pre');
50
- warning.textContent =
51
- 'This HTML output contains inline scripts. Are you sure that you want to run arbitrary Javascript within your JupyterLab session?';
52
- const runButton = document.createElement('button');
53
- runButton.textContent = 'Run';
54
- runButton.onclick = event => {
55
- host.innerHTML = originalSource;
56
- Private.evalInnerHTMLScriptTags(host);
57
- host.removeChild(host.firstChild);
58
- };
59
- container.appendChild(warning);
60
- container.appendChild(runButton);
61
- host.insertBefore(container, host.firstChild);
62
- }
63
- }
64
- // Handle default behavior of nodes.
65
- Private.handleDefaults(host, resolver);
66
- // Patch the urls if a resolver is available.
67
- let promise;
68
- if (resolver) {
69
- promise = Private.handleUrls(host, resolver, linkHandler);
70
- }
71
- else {
72
- promise = Promise.resolve(undefined);
73
- }
74
- // Return the final rendered promise.
75
- return promise.then(() => {
76
- if (shouldTypeset && latexTypesetter) {
77
- latexTypesetter.typeset(host);
78
- }
79
- });
80
- }
81
- exports.renderHTML = renderHTML;
82
- /**
83
- * Render an image into a host node.
84
- *
85
- * @params options - The options for rendering.
86
- *
87
- * @returns A promise which resolves when rendering is complete.
88
- */
89
- function renderImage(options) {
90
- // Unpack the options.
91
- let { host, mimeType, source, width, height, needsBackground, unconfined } = options;
92
- // Clear the content in the host.
93
- host.textContent = '';
94
- // Create the image element.
95
- let img = document.createElement('img');
96
- // Set the source of the image.
97
- img.src = `data:${mimeType};base64,${source}`;
98
- // Set the size of the image if provided.
99
- if (typeof height === 'number') {
100
- img.height = height;
101
- }
102
- if (typeof width === 'number') {
103
- img.width = width;
104
- }
105
- if (needsBackground === 'light') {
106
- img.classList.add('jp-needs-light-background');
107
- }
108
- else if (needsBackground === 'dark') {
109
- img.classList.add('jp-needs-dark-background');
110
- }
111
- if (unconfined === true) {
112
- img.classList.add('jp-mod-unconfined');
113
- }
114
- // Add the image to the host.
115
- host.appendChild(img);
116
- // Return the rendered promise.
117
- return Promise.resolve(undefined);
118
- }
119
- exports.renderImage = renderImage;
120
- /**
121
- * Render LaTeX into a host node.
122
- *
123
- * @params options - The options for rendering.
124
- *
125
- * @returns A promise which resolves when rendering is complete.
126
- */
127
- function renderLatex(options) {
128
- // Unpack the options.
129
- let { host, source, shouldTypeset, latexTypesetter } = options;
130
- // Set the source on the node.
131
- host.textContent = source;
132
- // Typeset the node if needed.
133
- if (shouldTypeset && latexTypesetter) {
134
- latexTypesetter.typeset(host);
135
- }
136
- // Return the rendered promise.
137
- return Promise.resolve(undefined);
138
- }
139
- exports.renderLatex = renderLatex;
140
- /**
141
- * Render Markdown into a host node.
142
- *
143
- * @params options - The options for rendering.
144
- *
145
- * @returns A promise which resolves when rendering is complete.
146
- */
147
- function renderMarkdown(options) {
148
- // Unpack the options.
149
- let { host, source, trusted, sanitizer, resolver, linkHandler, latexTypesetter, shouldTypeset } = options;
150
- // Clear the content if there is no source.
151
- if (!source) {
152
- host.textContent = '';
153
- return Promise.resolve(undefined);
154
- }
155
- // Separate math from normal markdown text.
156
- let parts = latex_1.removeMath(source);
157
- // Render the markdown and handle sanitization.
158
- return Private.renderMarked(parts['text'])
159
- .then(content => {
160
- // Restore the math content in the rendered markdown.
161
- content = latex_1.replaceMath(content, parts['math']);
162
- let originalContent = content;
163
- // Sanitize the content it is not trusted.
164
- if (!trusted) {
165
- originalContent = `${content}`;
166
- content = sanitizer.sanitize(content);
167
- }
168
- // Set the inner HTML of the host.
169
- host.innerHTML = content;
170
- if (host.getElementsByTagName('script').length > 0) {
171
- // If output it trusted, eval any script tags contained in the HTML.
172
- // This is not done automatically by the browser when script tags are
173
- // created by setting `innerHTML`.
174
- if (trusted) {
175
- Private.evalInnerHTMLScriptTags(host);
176
- }
177
- else {
178
- const container = document.createElement('div');
179
- const warning = document.createElement('pre');
180
- warning.textContent =
181
- 'This HTML output contains inline scripts. Are you sure that you want to run arbitrary Javascript within your JupyterLab session?';
182
- const runButton = document.createElement('button');
183
- runButton.textContent = 'Run';
184
- runButton.onclick = event => {
185
- host.innerHTML = originalContent;
186
- Private.evalInnerHTMLScriptTags(host);
187
- host.removeChild(host.firstChild);
188
- };
189
- container.appendChild(warning);
190
- container.appendChild(runButton);
191
- host.insertBefore(container, host.firstChild);
192
- }
193
- }
194
- // Handle default behavior of nodes.
195
- Private.handleDefaults(host, resolver);
196
- // Apply ids to the header nodes.
197
- Private.headerAnchors(host);
198
- // Patch the urls if a resolver is available.
199
- let promise;
200
- if (resolver) {
201
- promise = Private.handleUrls(host, resolver, linkHandler);
202
- }
203
- else {
204
- promise = Promise.resolve(undefined);
205
- }
206
- // Return the rendered promise.
207
- return promise;
208
- })
209
- .then(() => {
210
- if (shouldTypeset && latexTypesetter) {
211
- latexTypesetter.typeset(host);
212
- }
213
- });
214
- }
215
- exports.renderMarkdown = renderMarkdown;
216
- /**
217
- * Render SVG into a host node.
218
- *
219
- * @params options - The options for rendering.
220
- *
221
- * @returns A promise which resolves when rendering is complete.
222
- */
223
- function renderSVG(options) {
224
- // Unpack the options.
225
- let { host, source, trusted, unconfined } = options;
226
- // Clear the content if there is no source.
227
- if (!source) {
228
- host.textContent = '';
229
- return Promise.resolve(undefined);
230
- }
231
- // Display a message if the source is not trusted.
232
- if (!trusted) {
233
- host.textContent =
234
- 'Cannot display an untrusted SVG. Maybe you need to run the cell?';
235
- return Promise.resolve(undefined);
236
- }
237
- // Render in img so that user can save it easily
238
- const img = new Image();
239
- img.src = `data:image/svg+xml,${encodeURIComponent(source)}`;
240
- host.appendChild(img);
241
- if (unconfined === true) {
242
- host.classList.add('jp-mod-unconfined');
243
- }
244
- return Promise.resolve();
245
- }
246
- exports.renderSVG = renderSVG;
247
- /**
248
- * Render text into a host node.
249
- *
250
- * @params options - The options for rendering.
251
- *
252
- * @returns A promise which resolves when rendering is complete.
253
- */
254
- function renderText(options) {
255
- // Unpack the options.
256
- let { host, source } = options;
257
- const ansiUp = new ansi_up_1.default();
258
- ansiUp.escape_for_html = true;
259
- ansiUp.use_classes = true;
260
- // Create the HTML content.
261
- let content = ansiUp.ansi_to_html(source);
262
- // Set the inner HTML for the host node.
263
- host.innerHTML = `<pre>${content}</pre>`;
264
- // Return the rendered promise.
265
- return Promise.resolve(undefined);
266
- }
267
- exports.renderText = renderText;
268
- /**
269
- * The namespace for module implementation details.
270
- */
271
- var Private;
272
- (function (Private) {
273
- /**
274
- * Eval the script tags contained in a host populated by `innerHTML`.
275
- *
276
- * When script tags are created via `innerHTML`, the browser does not
277
- * evaluate them when they are added to the page. This function works
278
- * around that by creating new equivalent script nodes manually, and
279
- * replacing the originals.
280
- */
281
- function evalInnerHTMLScriptTags(host) {
282
- // Create a snapshot of the current script nodes.
283
- let scripts = algorithm_1.toArray(host.getElementsByTagName('script'));
284
- // Loop over each script node.
285
- for (let script of scripts) {
286
- // Skip any scripts which no longer have a parent.
287
- if (!script.parentNode) {
288
- continue;
289
- }
290
- // Create a new script node which will be clone.
291
- let clone = document.createElement('script');
292
- // Copy the attributes into the clone.
293
- let attrs = script.attributes;
294
- for (let i = 0, n = attrs.length; i < n; ++i) {
295
- let { name, value } = attrs[i];
296
- clone.setAttribute(name, value);
297
- }
298
- // Copy the text content into the clone.
299
- clone.textContent = script.textContent;
300
- // Replace the old script in the parent.
301
- script.parentNode.replaceChild(clone, script);
302
- }
303
- }
304
- Private.evalInnerHTMLScriptTags = evalInnerHTMLScriptTags;
305
- /**
306
- * Render markdown for the specified content.
307
- *
308
- * @param content - The string of markdown to render.
309
- *
310
- * @return A promise which resolves with the rendered content.
311
- */
312
- function renderMarked(content) {
313
- initializeMarked();
314
- return new Promise((resolve, reject) => {
315
- marked_1.default(content, (err, content) => {
316
- if (err) {
317
- reject(err);
318
- }
319
- else {
320
- resolve(content);
321
- }
322
- });
323
- });
324
- }
325
- Private.renderMarked = renderMarked;
326
- /**
327
- * Handle the default behavior of nodes.
328
- */
329
- function handleDefaults(node, resolver) {
330
- // Handle anchor elements.
331
- let anchors = node.getElementsByTagName('a');
332
- for (let i = 0; i < anchors.length; i++) {
333
- let path = anchors[i].href || '';
334
- const isLocal = resolver && resolver.isLocal
335
- ? resolver.isLocal(path)
336
- : coreutils_1.URLExt.isLocal(path);
337
- if (isLocal) {
338
- anchors[i].target = '_self';
339
- }
340
- else {
341
- anchors[i].target = '_blank';
342
- }
343
- }
344
- // Handle image elements.
345
- let imgs = node.getElementsByTagName('img');
346
- for (let i = 0; i < imgs.length; i++) {
347
- if (!imgs[i].alt) {
348
- imgs[i].alt = 'Image';
349
- }
350
- }
351
- }
352
- Private.handleDefaults = handleDefaults;
353
- /**
354
- * Resolve the relative urls in element `src` and `href` attributes.
355
- *
356
- * @param node - The head html element.
357
- *
358
- * @param resolver - A url resolver.
359
- *
360
- * @param linkHandler - An optional link handler for nodes.
361
- *
362
- * @returns a promise fulfilled when the relative urls have been resolved.
363
- */
364
- function handleUrls(node, resolver, linkHandler) {
365
- // Set up an array to collect promises.
366
- let promises = [];
367
- // Handle HTML Elements with src attributes.
368
- let nodes = node.querySelectorAll('*[src]');
369
- for (let i = 0; i < nodes.length; i++) {
370
- promises.push(handleAttr(nodes[i], 'src', resolver));
371
- }
372
- // Handle anchor elements.
373
- let anchors = node.getElementsByTagName('a');
374
- for (let i = 0; i < anchors.length; i++) {
375
- promises.push(handleAnchor(anchors[i], resolver, linkHandler));
376
- }
377
- // Handle link elements.
378
- let links = node.getElementsByTagName('link');
379
- for (let i = 0; i < links.length; i++) {
380
- promises.push(handleAttr(links[i], 'href', resolver));
381
- }
382
- // Wait on all promises.
383
- return Promise.all(promises).then(() => undefined);
384
- }
385
- Private.handleUrls = handleUrls;
386
- /**
387
- * Apply ids to headers.
388
- */
389
- function headerAnchors(node) {
390
- let headerNames = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
391
- for (let headerType of headerNames) {
392
- let headers = node.getElementsByTagName(headerType);
393
- for (let i = 0; i < headers.length; i++) {
394
- let header = headers[i];
395
- header.id = encodeURIComponent(header.innerHTML.replace(/ /g, '-'));
396
- let anchor = document.createElement('a');
397
- anchor.target = '_self';
398
- anchor.textContent = '¶';
399
- anchor.href = '#' + header.id;
400
- anchor.classList.add('jp-InternalAnchorLink');
401
- header.appendChild(anchor);
402
- }
403
- }
404
- }
405
- Private.headerAnchors = headerAnchors;
406
- /**
407
- * Handle a node with a `src` or `href` attribute.
408
- */
409
- function handleAttr(node, name, resolver) {
410
- let source = node.getAttribute(name) || '';
411
- const isLocal = resolver.isLocal
412
- ? resolver.isLocal(source)
413
- : coreutils_1.URLExt.isLocal(source);
414
- if (!source || !isLocal) {
415
- return Promise.resolve(undefined);
416
- }
417
- node.setAttribute(name, '');
418
- return resolver
419
- .resolveUrl(source)
420
- .then(urlPath => {
421
- return resolver.getDownloadUrl(urlPath);
422
- })
423
- .then(url => {
424
- // Check protocol again in case it changed:
425
- if (coreutils_1.URLExt.parse(url).protocol !== 'data:') {
426
- // Bust caching for local src attrs.
427
- // https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest#Bypassing_the_cache
428
- url += (/\?/.test(url) ? '&' : '?') + new Date().getTime();
429
- }
430
- node.setAttribute(name, url);
431
- })
432
- .catch(err => {
433
- // If there was an error getting the url,
434
- // just make it an empty link.
435
- node.setAttribute(name, '');
436
- });
437
- }
438
- /**
439
- * Handle an anchor node.
440
- */
441
- function handleAnchor(anchor, resolver, linkHandler) {
442
- // Get the link path without the location prepended.
443
- // (e.g. "./foo.md#Header 1" vs "http://localhost:8888/foo.md#Header 1")
444
- let href = anchor.getAttribute('href') || '';
445
- const isLocal = resolver.isLocal
446
- ? resolver.isLocal(href)
447
- : coreutils_1.URLExt.isLocal(href);
448
- // Bail if it is not a file-like url.
449
- if (!href || !isLocal) {
450
- return Promise.resolve(undefined);
451
- }
452
- // Remove the hash until we can handle it.
453
- let hash = anchor.hash;
454
- if (hash) {
455
- // Handle internal link in the file.
456
- if (hash === href) {
457
- anchor.target = '_self';
458
- return Promise.resolve(undefined);
459
- }
460
- // For external links, remove the hash until we have hash handling.
461
- href = href.replace(hash, '');
462
- }
463
- // Get the appropriate file path.
464
- return resolver
465
- .resolveUrl(href)
466
- .then(urlPath => {
467
- // decode encoded url from url to api path
468
- const path = decodeURI(urlPath);
469
- // Handle the click override.
470
- if (linkHandler) {
471
- linkHandler.handleLink(anchor, path, hash);
472
- }
473
- // Get the appropriate file download path.
474
- return resolver.getDownloadUrl(urlPath);
475
- })
476
- .then(url => {
477
- // Set the visible anchor.
478
- anchor.href = url + hash;
479
- })
480
- .catch(err => {
481
- // If there was an error getting the url,
482
- // just make it an empty link.
483
- anchor.href = '';
484
- });
485
- }
486
- let markedInitialized = false;
487
- /**
488
- * Support GitHub flavored Markdown, leave sanitizing to external library.
489
- */
490
- function initializeMarked() {
491
- if (markedInitialized) {
492
- return;
493
- }
494
- markedInitialized = true;
495
- marked_1.default.setOptions({
496
- gfm: true,
497
- sanitize: false,
498
- tables: true,
499
- // breaks: true; We can't use GFM breaks as it causes problems with tables
500
- langPrefix: `cm-s-${codemirror_1.CodeMirrorEditor.defaultConfig.theme} language-`,
501
- highlight: (code, lang, callback) => {
502
- let cb = (err, code) => {
503
- if (callback) {
504
- callback(err, code);
505
- }
506
- return code;
507
- };
508
- if (!lang) {
509
- // no language, no highlight
510
- return cb(null, code);
511
- }
512
- codemirror_1.Mode.ensure(lang)
513
- .then(spec => {
514
- let el = document.createElement('div');
515
- if (!spec) {
516
- console.log(`No CodeMirror mode: ${lang}`);
517
- return cb(null, code);
518
- }
519
- try {
520
- codemirror_1.Mode.run(code, spec.mime, el);
521
- return cb(null, el.innerHTML);
522
- }
523
- catch (err) {
524
- console.log(`Failed to highlight ${lang} code`, err);
525
- return cb(err, code);
526
- }
527
- })
528
- .catch(err => {
529
- console.log(`No CodeMirror mode: ${lang}`);
530
- console.log(`Require CodeMirror mode error: ${err}`);
531
- return cb(null, code);
532
- });
533
- return code;
534
- }
535
- });
536
- }
537
- })(Private || (Private = {}));
1
+ "use strict";
2
+ /*-----------------------------------------------------------------------------
3
+ | Copyright (c) Jupyter Development Team.
4
+ | Distributed under the terms of the Modified BSD License.
5
+ |----------------------------------------------------------------------------*/
6
+ var __importDefault = (this && this.__importDefault) || function (mod) {
7
+ return (mod && mod.__esModule) ? mod : { "default": mod };
8
+ };
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ const ansi_up_1 = __importDefault(require("ansi_up"));
11
+ const marked_1 = __importDefault(require("marked"));
12
+ const codemirror_1 = require("@jupyterlab/codemirror");
13
+ const coreutils_1 = require("@jupyterlab/coreutils");
14
+ const algorithm_1 = require("@phosphor/algorithm");
15
+ const latex_1 = require("./latex");
16
+ /**
17
+ * Render HTML into a host node.
18
+ *
19
+ * @params options - The options for rendering.
20
+ *
21
+ * @returns A promise which resolves when rendering is complete.
22
+ */
23
+ function renderHTML(options) {
24
+ // Unpack the options.
25
+ let { host, source, trusted, sanitizer, resolver, linkHandler, shouldTypeset, latexTypesetter } = options;
26
+ let originalSource = source;
27
+ // Bail early if the source is empty.
28
+ if (!source) {
29
+ host.textContent = '';
30
+ return Promise.resolve(undefined);
31
+ }
32
+ // Sanitize the source if it is not trusted. This removes all
33
+ // `<script>` tags as well as other potentially harmful HTML.
34
+ if (!trusted) {
35
+ originalSource = `${source}`;
36
+ source = sanitizer.sanitize(source);
37
+ }
38
+ // Set the inner HTML of the host.
39
+ host.innerHTML = source;
40
+ if (host.getElementsByTagName('script').length > 0) {
41
+ // If output it trusted, eval any script tags contained in the HTML.
42
+ // This is not done automatically by the browser when script tags are
43
+ // created by setting `innerHTML`.
44
+ if (trusted) {
45
+ Private.evalInnerHTMLScriptTags(host);
46
+ }
47
+ else {
48
+ const container = document.createElement('div');
49
+ const warning = document.createElement('pre');
50
+ warning.textContent =
51
+ 'This HTML output contains inline scripts. Are you sure that you want to run arbitrary Javascript within your JupyterLab session?';
52
+ const runButton = document.createElement('button');
53
+ runButton.textContent = 'Run';
54
+ runButton.onclick = event => {
55
+ host.innerHTML = originalSource;
56
+ Private.evalInnerHTMLScriptTags(host);
57
+ host.removeChild(host.firstChild);
58
+ };
59
+ container.appendChild(warning);
60
+ container.appendChild(runButton);
61
+ host.insertBefore(container, host.firstChild);
62
+ }
63
+ }
64
+ // Handle default behavior of nodes.
65
+ Private.handleDefaults(host, resolver);
66
+ // Patch the urls if a resolver is available.
67
+ let promise;
68
+ if (resolver) {
69
+ promise = Private.handleUrls(host, resolver, linkHandler);
70
+ }
71
+ else {
72
+ promise = Promise.resolve(undefined);
73
+ }
74
+ // Return the final rendered promise.
75
+ return promise.then(() => {
76
+ if (shouldTypeset && latexTypesetter) {
77
+ latexTypesetter.typeset(host);
78
+ }
79
+ });
80
+ }
81
+ exports.renderHTML = renderHTML;
82
+ /**
83
+ * Render an image into a host node.
84
+ *
85
+ * @params options - The options for rendering.
86
+ *
87
+ * @returns A promise which resolves when rendering is complete.
88
+ */
89
+ function renderImage(options) {
90
+ // Unpack the options.
91
+ let { host, mimeType, source, width, height, needsBackground, unconfined } = options;
92
+ // Clear the content in the host.
93
+ host.textContent = '';
94
+ // Create the image element.
95
+ let img = document.createElement('img');
96
+ // Set the source of the image.
97
+ img.src = `data:${mimeType};base64,${source}`;
98
+ // Set the size of the image if provided.
99
+ if (typeof height === 'number') {
100
+ img.height = height;
101
+ }
102
+ if (typeof width === 'number') {
103
+ img.width = width;
104
+ }
105
+ if (needsBackground === 'light') {
106
+ img.classList.add('jp-needs-light-background');
107
+ }
108
+ else if (needsBackground === 'dark') {
109
+ img.classList.add('jp-needs-dark-background');
110
+ }
111
+ if (unconfined === true) {
112
+ img.classList.add('jp-mod-unconfined');
113
+ }
114
+ // Add the image to the host.
115
+ host.appendChild(img);
116
+ // Return the rendered promise.
117
+ return Promise.resolve(undefined);
118
+ }
119
+ exports.renderImage = renderImage;
120
+ /**
121
+ * Render LaTeX into a host node.
122
+ *
123
+ * @params options - The options for rendering.
124
+ *
125
+ * @returns A promise which resolves when rendering is complete.
126
+ */
127
+ function renderLatex(options) {
128
+ // Unpack the options.
129
+ let { host, source, shouldTypeset, latexTypesetter } = options;
130
+ // Set the source on the node.
131
+ host.textContent = source;
132
+ // Typeset the node if needed.
133
+ if (shouldTypeset && latexTypesetter) {
134
+ latexTypesetter.typeset(host);
135
+ }
136
+ // Return the rendered promise.
137
+ return Promise.resolve(undefined);
138
+ }
139
+ exports.renderLatex = renderLatex;
140
+ /**
141
+ * Render Markdown into a host node.
142
+ *
143
+ * @params options - The options for rendering.
144
+ *
145
+ * @returns A promise which resolves when rendering is complete.
146
+ */
147
+ function renderMarkdown(options) {
148
+ // Unpack the options.
149
+ let { host, source, trusted, sanitizer, resolver, linkHandler, latexTypesetter, shouldTypeset } = options;
150
+ // Clear the content if there is no source.
151
+ if (!source) {
152
+ host.textContent = '';
153
+ return Promise.resolve(undefined);
154
+ }
155
+ // Separate math from normal markdown text.
156
+ let parts = latex_1.removeMath(source);
157
+ // Render the markdown and handle sanitization.
158
+ return Private.renderMarked(parts['text'])
159
+ .then(content => {
160
+ // Restore the math content in the rendered markdown.
161
+ content = latex_1.replaceMath(content, parts['math']);
162
+ let originalContent = content;
163
+ // Sanitize the content it is not trusted.
164
+ if (!trusted) {
165
+ originalContent = `${content}`;
166
+ content = sanitizer.sanitize(content);
167
+ }
168
+ // Set the inner HTML of the host.
169
+ host.innerHTML = content;
170
+ if (host.getElementsByTagName('script').length > 0) {
171
+ // If output it trusted, eval any script tags contained in the HTML.
172
+ // This is not done automatically by the browser when script tags are
173
+ // created by setting `innerHTML`.
174
+ if (trusted) {
175
+ Private.evalInnerHTMLScriptTags(host);
176
+ }
177
+ else {
178
+ const container = document.createElement('div');
179
+ const warning = document.createElement('pre');
180
+ warning.textContent =
181
+ 'This HTML output contains inline scripts. Are you sure that you want to run arbitrary Javascript within your JupyterLab session?';
182
+ const runButton = document.createElement('button');
183
+ runButton.textContent = 'Run';
184
+ runButton.onclick = event => {
185
+ host.innerHTML = originalContent;
186
+ Private.evalInnerHTMLScriptTags(host);
187
+ host.removeChild(host.firstChild);
188
+ };
189
+ container.appendChild(warning);
190
+ container.appendChild(runButton);
191
+ host.insertBefore(container, host.firstChild);
192
+ }
193
+ }
194
+ // Handle default behavior of nodes.
195
+ Private.handleDefaults(host, resolver);
196
+ // Apply ids to the header nodes.
197
+ Private.headerAnchors(host);
198
+ // Patch the urls if a resolver is available.
199
+ let promise;
200
+ if (resolver) {
201
+ promise = Private.handleUrls(host, resolver, linkHandler);
202
+ }
203
+ else {
204
+ promise = Promise.resolve(undefined);
205
+ }
206
+ // Return the rendered promise.
207
+ return promise;
208
+ })
209
+ .then(() => {
210
+ if (shouldTypeset && latexTypesetter) {
211
+ latexTypesetter.typeset(host);
212
+ }
213
+ });
214
+ }
215
+ exports.renderMarkdown = renderMarkdown;
216
+ /**
217
+ * Render SVG into a host node.
218
+ *
219
+ * @params options - The options for rendering.
220
+ *
221
+ * @returns A promise which resolves when rendering is complete.
222
+ */
223
+ function renderSVG(options) {
224
+ // Unpack the options.
225
+ let { host, source, trusted, unconfined } = options;
226
+ // Clear the content if there is no source.
227
+ if (!source) {
228
+ host.textContent = '';
229
+ return Promise.resolve(undefined);
230
+ }
231
+ // Display a message if the source is not trusted.
232
+ if (!trusted) {
233
+ host.textContent =
234
+ 'Cannot display an untrusted SVG. Maybe you need to run the cell?';
235
+ return Promise.resolve(undefined);
236
+ }
237
+ // Render in img so that user can save it easily
238
+ const img = new Image();
239
+ img.src = `data:image/svg+xml,${encodeURIComponent(source)}`;
240
+ host.appendChild(img);
241
+ if (unconfined === true) {
242
+ host.classList.add('jp-mod-unconfined');
243
+ }
244
+ return Promise.resolve();
245
+ }
246
+ exports.renderSVG = renderSVG;
247
+ /**
248
+ * Render text into a host node.
249
+ *
250
+ * @params options - The options for rendering.
251
+ *
252
+ * @returns A promise which resolves when rendering is complete.
253
+ */
254
+ function renderText(options) {
255
+ // Unpack the options.
256
+ let { host, source } = options;
257
+ const ansiUp = new ansi_up_1.default();
258
+ ansiUp.escape_for_html = true;
259
+ ansiUp.use_classes = true;
260
+ // Create the HTML content.
261
+ let content = ansiUp.ansi_to_html(source);
262
+ // Set the inner HTML for the host node.
263
+ host.innerHTML = `<pre>${content}</pre>`;
264
+ // Return the rendered promise.
265
+ return Promise.resolve(undefined);
266
+ }
267
+ exports.renderText = renderText;
268
+ /**
269
+ * The namespace for module implementation details.
270
+ */
271
+ var Private;
272
+ (function (Private) {
273
+ /**
274
+ * Eval the script tags contained in a host populated by `innerHTML`.
275
+ *
276
+ * When script tags are created via `innerHTML`, the browser does not
277
+ * evaluate them when they are added to the page. This function works
278
+ * around that by creating new equivalent script nodes manually, and
279
+ * replacing the originals.
280
+ */
281
+ function evalInnerHTMLScriptTags(host) {
282
+ // Create a snapshot of the current script nodes.
283
+ let scripts = algorithm_1.toArray(host.getElementsByTagName('script'));
284
+ // Loop over each script node.
285
+ for (let script of scripts) {
286
+ // Skip any scripts which no longer have a parent.
287
+ if (!script.parentNode) {
288
+ continue;
289
+ }
290
+ // Create a new script node which will be clone.
291
+ let clone = document.createElement('script');
292
+ // Copy the attributes into the clone.
293
+ let attrs = script.attributes;
294
+ for (let i = 0, n = attrs.length; i < n; ++i) {
295
+ let { name, value } = attrs[i];
296
+ clone.setAttribute(name, value);
297
+ }
298
+ // Copy the text content into the clone.
299
+ clone.textContent = script.textContent;
300
+ // Replace the old script in the parent.
301
+ script.parentNode.replaceChild(clone, script);
302
+ }
303
+ }
304
+ Private.evalInnerHTMLScriptTags = evalInnerHTMLScriptTags;
305
+ /**
306
+ * Render markdown for the specified content.
307
+ *
308
+ * @param content - The string of markdown to render.
309
+ *
310
+ * @return A promise which resolves with the rendered content.
311
+ */
312
+ function renderMarked(content) {
313
+ initializeMarked();
314
+ return new Promise((resolve, reject) => {
315
+ marked_1.default(content, (err, content) => {
316
+ if (err) {
317
+ reject(err);
318
+ }
319
+ else {
320
+ resolve(content);
321
+ }
322
+ });
323
+ });
324
+ }
325
+ Private.renderMarked = renderMarked;
326
+ /**
327
+ * Handle the default behavior of nodes.
328
+ */
329
+ function handleDefaults(node, resolver) {
330
+ // Handle anchor elements.
331
+ let anchors = node.getElementsByTagName('a');
332
+ for (let i = 0; i < anchors.length; i++) {
333
+ let path = anchors[i].href || '';
334
+ const isLocal = resolver && resolver.isLocal
335
+ ? resolver.isLocal(path)
336
+ : coreutils_1.URLExt.isLocal(path);
337
+ if (isLocal) {
338
+ anchors[i].target = '_self';
339
+ }
340
+ else {
341
+ anchors[i].target = '_blank';
342
+ }
343
+ }
344
+ // Handle image elements.
345
+ let imgs = node.getElementsByTagName('img');
346
+ for (let i = 0; i < imgs.length; i++) {
347
+ if (!imgs[i].alt) {
348
+ imgs[i].alt = 'Image';
349
+ }
350
+ }
351
+ }
352
+ Private.handleDefaults = handleDefaults;
353
+ /**
354
+ * Resolve the relative urls in element `src` and `href` attributes.
355
+ *
356
+ * @param node - The head html element.
357
+ *
358
+ * @param resolver - A url resolver.
359
+ *
360
+ * @param linkHandler - An optional link handler for nodes.
361
+ *
362
+ * @returns a promise fulfilled when the relative urls have been resolved.
363
+ */
364
+ function handleUrls(node, resolver, linkHandler) {
365
+ // Set up an array to collect promises.
366
+ let promises = [];
367
+ // Handle HTML Elements with src attributes.
368
+ let nodes = node.querySelectorAll('*[src]');
369
+ for (let i = 0; i < nodes.length; i++) {
370
+ promises.push(handleAttr(nodes[i], 'src', resolver));
371
+ }
372
+ // Handle anchor elements.
373
+ let anchors = node.getElementsByTagName('a');
374
+ for (let i = 0; i < anchors.length; i++) {
375
+ promises.push(handleAnchor(anchors[i], resolver, linkHandler));
376
+ }
377
+ // Handle link elements.
378
+ let links = node.getElementsByTagName('link');
379
+ for (let i = 0; i < links.length; i++) {
380
+ promises.push(handleAttr(links[i], 'href', resolver));
381
+ }
382
+ // Wait on all promises.
383
+ return Promise.all(promises).then(() => undefined);
384
+ }
385
+ Private.handleUrls = handleUrls;
386
+ /**
387
+ * Apply ids to headers.
388
+ */
389
+ function headerAnchors(node) {
390
+ let headerNames = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
391
+ for (let headerType of headerNames) {
392
+ let headers = node.getElementsByTagName(headerType);
393
+ for (let i = 0; i < headers.length; i++) {
394
+ let header = headers[i];
395
+ header.id = encodeURIComponent(header.innerHTML.replace(/ /g, '-'));
396
+ let anchor = document.createElement('a');
397
+ anchor.target = '_self';
398
+ anchor.textContent = '¶';
399
+ anchor.href = '#' + header.id;
400
+ anchor.classList.add('jp-InternalAnchorLink');
401
+ header.appendChild(anchor);
402
+ }
403
+ }
404
+ }
405
+ Private.headerAnchors = headerAnchors;
406
+ /**
407
+ * Handle a node with a `src` or `href` attribute.
408
+ */
409
+ function handleAttr(node, name, resolver) {
410
+ let source = node.getAttribute(name) || '';
411
+ const isLocal = resolver.isLocal
412
+ ? resolver.isLocal(source)
413
+ : coreutils_1.URLExt.isLocal(source);
414
+ if (!source || !isLocal) {
415
+ return Promise.resolve(undefined);
416
+ }
417
+ node.setAttribute(name, '');
418
+ return resolver
419
+ .resolveUrl(source)
420
+ .then(urlPath => {
421
+ return resolver.getDownloadUrl(urlPath);
422
+ })
423
+ .then(url => {
424
+ // Check protocol again in case it changed:
425
+ if (coreutils_1.URLExt.parse(url).protocol !== 'data:') {
426
+ // Bust caching for local src attrs.
427
+ // https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest#Bypassing_the_cache
428
+ url += (/\?/.test(url) ? '&' : '?') + new Date().getTime();
429
+ }
430
+ node.setAttribute(name, url);
431
+ })
432
+ .catch(err => {
433
+ // If there was an error getting the url,
434
+ // just make it an empty link.
435
+ node.setAttribute(name, '');
436
+ });
437
+ }
438
+ /**
439
+ * Handle an anchor node.
440
+ */
441
+ function handleAnchor(anchor, resolver, linkHandler) {
442
+ // Get the link path without the location prepended.
443
+ // (e.g. "./foo.md#Header 1" vs "http://localhost:8888/foo.md#Header 1")
444
+ let href = anchor.getAttribute('href') || '';
445
+ const isLocal = resolver.isLocal
446
+ ? resolver.isLocal(href)
447
+ : coreutils_1.URLExt.isLocal(href);
448
+ // Bail if it is not a file-like url.
449
+ if (!href || !isLocal) {
450
+ return Promise.resolve(undefined);
451
+ }
452
+ // Remove the hash until we can handle it.
453
+ let hash = anchor.hash;
454
+ if (hash) {
455
+ // Handle internal link in the file.
456
+ if (hash === href) {
457
+ anchor.target = '_self';
458
+ return Promise.resolve(undefined);
459
+ }
460
+ // For external links, remove the hash until we have hash handling.
461
+ href = href.replace(hash, '');
462
+ }
463
+ // Get the appropriate file path.
464
+ return resolver
465
+ .resolveUrl(href)
466
+ .then(urlPath => {
467
+ // decode encoded url from url to api path
468
+ const path = decodeURI(urlPath);
469
+ // Handle the click override.
470
+ if (linkHandler) {
471
+ linkHandler.handleLink(anchor, path, hash);
472
+ }
473
+ // Get the appropriate file download path.
474
+ return resolver.getDownloadUrl(urlPath);
475
+ })
476
+ .then(url => {
477
+ // Set the visible anchor.
478
+ anchor.href = url + hash;
479
+ })
480
+ .catch(err => {
481
+ // If there was an error getting the url,
482
+ // just make it an empty link.
483
+ anchor.href = '';
484
+ });
485
+ }
486
+ let markedInitialized = false;
487
+ /**
488
+ * Support GitHub flavored Markdown, leave sanitizing to external library.
489
+ */
490
+ function initializeMarked() {
491
+ if (markedInitialized) {
492
+ return;
493
+ }
494
+ markedInitialized = true;
495
+ marked_1.default.setOptions({
496
+ gfm: true,
497
+ sanitize: false,
498
+ tables: true,
499
+ // breaks: true; We can't use GFM breaks as it causes problems with tables
500
+ langPrefix: `cm-s-${codemirror_1.CodeMirrorEditor.defaultConfig.theme} language-`,
501
+ highlight: (code, lang, callback) => {
502
+ let cb = (err, code) => {
503
+ if (callback) {
504
+ callback(err, code);
505
+ }
506
+ return code;
507
+ };
508
+ if (!lang) {
509
+ // no language, no highlight
510
+ return cb(null, code);
511
+ }
512
+ codemirror_1.Mode.ensure(lang)
513
+ .then(spec => {
514
+ let el = document.createElement('div');
515
+ if (!spec) {
516
+ console.log(`No CodeMirror mode: ${lang}`);
517
+ return cb(null, code);
518
+ }
519
+ try {
520
+ codemirror_1.Mode.run(code, spec.mime, el);
521
+ return cb(null, el.innerHTML);
522
+ }
523
+ catch (err) {
524
+ console.log(`Failed to highlight ${lang} code`, err);
525
+ return cb(err, code);
526
+ }
527
+ })
528
+ .catch(err => {
529
+ console.log(`No CodeMirror mode: ${lang}`);
530
+ console.log(`Require CodeMirror mode error: ${err}`);
531
+ return cb(null, code);
532
+ });
533
+ return code;
534
+ }
535
+ });
536
+ }
537
+ })(Private || (Private = {}));