1 |
|
2 |
|
3 |
|
4 |
|
5 | import { URLExt } from '@jupyterlab/coreutils';
|
6 | import { nullTranslator } from '@jupyterlab/translation';
|
7 | import escape from 'lodash.escape';
|
8 | import { removeMath, replaceMath } from './latex';
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 | export function renderHTML(options) {
|
17 |
|
18 | let { host, source, trusted, sanitizer, resolver, linkHandler, shouldTypeset, latexTypesetter, translator } = options;
|
19 | translator = translator || nullTranslator;
|
20 | const trans = translator === null || translator === void 0 ? void 0 : translator.load('jupyterlab');
|
21 | let originalSource = source;
|
22 |
|
23 | if (!source) {
|
24 | host.textContent = '';
|
25 | return Promise.resolve(undefined);
|
26 | }
|
27 |
|
28 |
|
29 | if (!trusted) {
|
30 | originalSource = `${source}`;
|
31 | source = sanitizer.sanitize(source);
|
32 | }
|
33 |
|
34 | host.innerHTML = source;
|
35 | if (host.getElementsByTagName('script').length > 0) {
|
36 |
|
37 |
|
38 |
|
39 | if (trusted) {
|
40 | Private.evalInnerHTMLScriptTags(host);
|
41 | }
|
42 | else {
|
43 | const container = document.createElement('div');
|
44 | const warning = document.createElement('pre');
|
45 | warning.textContent = trans.__('This HTML output contains inline scripts. Are you sure that you want to run arbitrary Javascript within your JupyterLab session?');
|
46 | const runButton = document.createElement('button');
|
47 | runButton.textContent = trans.__('Run');
|
48 | runButton.onclick = event => {
|
49 | host.innerHTML = originalSource;
|
50 | Private.evalInnerHTMLScriptTags(host);
|
51 | if (host.firstChild) {
|
52 | host.removeChild(host.firstChild);
|
53 | }
|
54 | };
|
55 | container.appendChild(warning);
|
56 | container.appendChild(runButton);
|
57 | host.insertBefore(container, host.firstChild);
|
58 | }
|
59 | }
|
60 |
|
61 | Private.handleDefaults(host, resolver);
|
62 |
|
63 | let promise;
|
64 | if (resolver) {
|
65 | promise = Private.handleUrls(host, resolver, linkHandler);
|
66 | }
|
67 | else {
|
68 | promise = Promise.resolve(undefined);
|
69 | }
|
70 |
|
71 | return promise.then(() => {
|
72 | if (shouldTypeset && latexTypesetter) {
|
73 | latexTypesetter.typeset(host);
|
74 | }
|
75 | });
|
76 | }
|
77 |
|
78 |
|
79 |
|
80 |
|
81 |
|
82 |
|
83 |
|
84 | export function renderImage(options) {
|
85 |
|
86 | const { host, mimeType, source, width, height, needsBackground, unconfined } = options;
|
87 |
|
88 | host.textContent = '';
|
89 |
|
90 | const img = document.createElement('img');
|
91 |
|
92 | img.src = `data:${mimeType};base64,${source}`;
|
93 |
|
94 | if (typeof height === 'number') {
|
95 | img.height = height;
|
96 | }
|
97 | if (typeof width === 'number') {
|
98 | img.width = width;
|
99 | }
|
100 | if (needsBackground === 'light') {
|
101 | img.classList.add('jp-needs-light-background');
|
102 | }
|
103 | else if (needsBackground === 'dark') {
|
104 | img.classList.add('jp-needs-dark-background');
|
105 | }
|
106 | if (unconfined === true) {
|
107 | img.classList.add('jp-mod-unconfined');
|
108 | }
|
109 |
|
110 | host.appendChild(img);
|
111 |
|
112 | return Promise.resolve(undefined);
|
113 | }
|
114 |
|
115 |
|
116 |
|
117 |
|
118 |
|
119 |
|
120 |
|
121 | export function renderLatex(options) {
|
122 |
|
123 | const { host, source, shouldTypeset, latexTypesetter } = options;
|
124 |
|
125 | host.textContent = source;
|
126 |
|
127 | if (shouldTypeset && latexTypesetter) {
|
128 | latexTypesetter.typeset(host);
|
129 | }
|
130 |
|
131 | return Promise.resolve(undefined);
|
132 | }
|
133 |
|
134 |
|
135 |
|
136 |
|
137 |
|
138 |
|
139 |
|
140 | export async function renderMarkdown(options) {
|
141 |
|
142 | const { host, source, markdownParser, ...others } = options;
|
143 |
|
144 | if (!source) {
|
145 | host.textContent = '';
|
146 | return;
|
147 | }
|
148 | let html = '';
|
149 | if (markdownParser) {
|
150 |
|
151 | const parts = removeMath(source);
|
152 |
|
153 | html = await markdownParser.render(parts['text']);
|
154 |
|
155 | html = replaceMath(html, parts['math']);
|
156 | }
|
157 | else {
|
158 |
|
159 | html = `<pre>${source}</pre>`;
|
160 | }
|
161 |
|
162 | await renderHTML({
|
163 | host,
|
164 | source: html,
|
165 | ...others
|
166 | });
|
167 |
|
168 | Private.headerAnchors(host);
|
169 | }
|
170 |
|
171 |
|
172 |
|
173 | (function (renderMarkdown) {
|
174 | |
175 |
|
176 |
|
177 |
|
178 |
|
179 |
|
180 | function createHeaderId(header) {
|
181 | var _a;
|
182 | return ((_a = header.textContent) !== null && _a !== void 0 ? _a : '').replace(/ /g, '-');
|
183 | }
|
184 | renderMarkdown.createHeaderId = createHeaderId;
|
185 | })(renderMarkdown || (renderMarkdown = {}));
|
186 |
|
187 |
|
188 |
|
189 |
|
190 |
|
191 |
|
192 |
|
193 | export function renderSVG(options) {
|
194 |
|
195 | let { host, source, trusted, unconfined } = options;
|
196 |
|
197 | if (!source) {
|
198 | host.textContent = '';
|
199 | return Promise.resolve(undefined);
|
200 | }
|
201 |
|
202 | if (!trusted) {
|
203 | host.textContent =
|
204 | 'Cannot display an untrusted SVG. Maybe you need to run the cell?';
|
205 | return Promise.resolve(undefined);
|
206 | }
|
207 |
|
208 | const patt = '<svg[^>]+xmlns=[^>]+svg';
|
209 | if (source.search(patt) < 0) {
|
210 | source = source.replace('<svg', '<svg xmlns="http://www.w3.org/2000/svg"');
|
211 | }
|
212 |
|
213 | const img = new Image();
|
214 | img.src = `data:image/svg+xml,${encodeURIComponent(source)}`;
|
215 | host.appendChild(img);
|
216 | if (unconfined === true) {
|
217 | host.classList.add('jp-mod-unconfined');
|
218 | }
|
219 | return Promise.resolve();
|
220 | }
|
221 | var ILinker;
|
222 | (function (ILinker) {
|
223 |
|
224 |
|
225 | const controlCodes = '\\u0000-\\u0020\\u007f-\\u009f';
|
226 | ILinker.webLinkRegex = new RegExp('(?<path>(?:[a-zA-Z][a-zA-Z0-9+.-]{2,}:\\/\\/|data:|www\\.)[^\\s' +
|
227 | controlCodes +
|
228 | '"]{2,}[^\\s' +
|
229 | controlCodes +
|
230 | '"\'(){}\\[\\],:;.!?])', 'ug');
|
231 |
|
232 |
|
233 | const winAbsPathRegex = /(?:[a-zA-Z]:(?:(?:\\|\/)[\w\.-]*)+)/;
|
234 | const winRelPathRegex = /(?:(?:\~|\.)(?:(?:\\|\/)[\w\.-]*)+)/;
|
235 | const winPathRegex = new RegExp(`(${winAbsPathRegex.source}|${winRelPathRegex.source})`);
|
236 | const posixPathRegex = /((?:\~|\.)?(?:\/[\w\.-]*)+)/;
|
237 | const lineColumnRegex = /(?:(?:\:|", line )(?<line>[\d]+))?(?:\:(?<column>[\d]+))?/;
|
238 |
|
239 | const isWindows = navigator.userAgent.indexOf('Windows') >= 0;
|
240 | ILinker.pathLinkRegex = new RegExp(`(?<path>${isWindows ? winPathRegex.source : posixPathRegex.source})${lineColumnRegex.source}`, 'g');
|
241 | })(ILinker || (ILinker = {}));
|
242 |
|
243 |
|
244 |
|
245 | class WebLinker {
|
246 | constructor() {
|
247 | this.regex = ILinker.webLinkRegex;
|
248 | }
|
249 | createAnchor(url, label) {
|
250 | const anchor = document.createElement('a');
|
251 | anchor.href = url.startsWith('www.') ? 'https://' + url : url;
|
252 | anchor.rel = 'noopener';
|
253 | anchor.target = '_blank';
|
254 | anchor.appendChild(document.createTextNode(label));
|
255 | return anchor;
|
256 | }
|
257 | processPath(url) {
|
258 |
|
259 | const lastChars = url.slice(-1);
|
260 | const endsWithGtLt = ['>', '<'].indexOf(lastChars) !== -1;
|
261 | const len = endsWithGtLt ? url.length - 1 : url.length;
|
262 | url = url.slice(0, len);
|
263 | return url;
|
264 | }
|
265 | processLabel(url) {
|
266 | return this.processPath(url);
|
267 | }
|
268 | }
|
269 |
|
270 |
|
271 |
|
272 | class PathLinker {
|
273 | constructor() {
|
274 | this.regex = ILinker.pathLinkRegex;
|
275 | }
|
276 | createAnchor(path, label, locators) {
|
277 | const anchor = document.createElement('a');
|
278 |
|
279 |
|
280 |
|
281 | anchor.dataset.path = path;
|
282 |
|
283 |
|
284 | const line = parseInt(locators['line'], 10);
|
285 | let locator = !isNaN(line) ? `line=${line - 1}` : '';
|
286 | anchor.dataset.locator = locator;
|
287 | anchor.appendChild(document.createTextNode(label));
|
288 | return anchor;
|
289 | }
|
290 | }
|
291 | function autolink(content, options) {
|
292 | const linkers = [];
|
293 | if (options.checkWeb) {
|
294 | linkers.push(new WebLinker());
|
295 | }
|
296 | if (options.checkPaths) {
|
297 | linkers.push(new PathLinker());
|
298 | }
|
299 | const nodes = [];
|
300 |
|
301 |
|
302 |
|
303 | const linkify = (content, regexIndex) => {
|
304 | if (regexIndex >= linkers.length) {
|
305 | nodes.push(document.createTextNode(content));
|
306 | return;
|
307 | }
|
308 | const linker = linkers[regexIndex];
|
309 | let match;
|
310 | let currentIndex = 0;
|
311 | const regex = linker.regex;
|
312 |
|
313 | regex.lastIndex = 0;
|
314 | while (null != (match = regex.exec(content))) {
|
315 | const stringBeforeMatch = content.substring(currentIndex, match.index);
|
316 | if (stringBeforeMatch) {
|
317 | linkify(stringBeforeMatch, regexIndex + 1);
|
318 | }
|
319 | const { path, ...locators } = match.groups;
|
320 | const value = linker.processPath ? linker.processPath(path) : path;
|
321 | const label = linker.processLabel
|
322 | ? linker.processLabel(match[0])
|
323 | : match[0];
|
324 | nodes.push(linker.createAnchor(value, label, locators));
|
325 | currentIndex = match.index + label.length;
|
326 | }
|
327 | const stringAfterMatches = content.substring(currentIndex);
|
328 | if (stringAfterMatches) {
|
329 | linkify(stringAfterMatches, regexIndex + 1);
|
330 | }
|
331 | };
|
332 | linkify(content, 0);
|
333 | return nodes;
|
334 | }
|
335 |
|
336 |
|
337 |
|
338 |
|
339 |
|
340 |
|
341 | function splitShallowNode(node, at) {
|
342 | var _a, _b;
|
343 | const pre = node.cloneNode();
|
344 | pre.textContent = (_a = node.textContent) === null || _a === void 0 ? void 0 : _a.slice(0, at);
|
345 | const post = node.cloneNode();
|
346 | post.textContent = (_b = node.textContent) === null || _b === void 0 ? void 0 : _b.slice(at);
|
347 | return {
|
348 | pre,
|
349 | post
|
350 | };
|
351 | }
|
352 |
|
353 |
|
354 |
|
355 | function* nodeIter(nodes) {
|
356 | var _a;
|
357 | let start = 0;
|
358 | let end;
|
359 | for (let node of nodes) {
|
360 | end = start + (((_a = node.textContent) === null || _a === void 0 ? void 0 : _a.length) || 0);
|
361 | yield {
|
362 | node,
|
363 | start,
|
364 | end,
|
365 | isText: node.nodeType === Node.TEXT_NODE
|
366 | };
|
367 | start = end;
|
368 | }
|
369 | }
|
370 |
|
371 |
|
372 |
|
373 |
|
374 |
|
375 |
|
376 | function* alignedNodes(a, b) {
|
377 | var _a, _b;
|
378 | let iterA = nodeIter(a);
|
379 | let iterB = nodeIter(b);
|
380 | let nA = iterA.next();
|
381 | let nB = iterB.next();
|
382 | while (!nA.done && !nB.done) {
|
383 | let A = nA.value;
|
384 | let B = nB.value;
|
385 | if (A.isText && A.start <= B.start && A.end >= B.end) {
|
386 |
|
387 | yield [null, B.node];
|
388 | nB = iterB.next();
|
389 | }
|
390 | else if (B.isText && B.start <= A.start && B.end >= A.end) {
|
391 |
|
392 | yield [A.node, null];
|
393 | nA = iterA.next();
|
394 | }
|
395 | else {
|
396 |
|
397 | if (A.end === B.end && A.start === B.start) {
|
398 | yield [A.node, B.node];
|
399 | nA = iterA.next();
|
400 | nB = iterB.next();
|
401 | }
|
402 | else if (A.end > B.end) {
|
403 | |
404 |
|
405 |
|
406 |
|
407 |
|
408 |
|
409 | let { pre, post } = splitShallowNode(A.node, B.end - A.start);
|
410 | if (B.start < A.start) {
|
411 |
|
412 | B.node.textContent = (_a = B.node.textContent) === null || _a === void 0 ? void 0 : _a.slice(A.start - B.start);
|
413 | }
|
414 | yield [pre, B.node];
|
415 |
|
416 | A.node = post;
|
417 | A.start = B.end;
|
418 | nB = iterB.next();
|
419 | }
|
420 | else if (B.end > A.end) {
|
421 | let { pre, post } = splitShallowNode(B.node, A.end - B.start);
|
422 | if (A.start < B.start) {
|
423 |
|
424 | A.node.textContent = (_b = A.node.textContent) === null || _b === void 0 ? void 0 : _b.slice(B.start - A.start);
|
425 | }
|
426 | yield [A.node, pre];
|
427 |
|
428 | B.node = post;
|
429 | B.start = A.end;
|
430 | nA = iterA.next();
|
431 | }
|
432 | else {
|
433 | throw new Error(`Unexpected intersection: ${JSON.stringify(A)} ${JSON.stringify(B)}`);
|
434 | }
|
435 | }
|
436 | }
|
437 | }
|
438 |
|
439 |
|
440 |
|
441 |
|
442 |
|
443 |
|
444 |
|
445 | export function renderText(options) {
|
446 | var _a, _b;
|
447 |
|
448 | const { host, sanitizer, source } = options;
|
449 |
|
450 | const content = sanitizer.sanitize(Private.ansiSpan(source), {
|
451 | allowedTags: ['span']
|
452 | });
|
453 |
|
454 | const pre = document.createElement('pre');
|
455 | pre.innerHTML = content;
|
456 | const preTextContent = pre.textContent;
|
457 | let ret;
|
458 | if (preTextContent) {
|
459 |
|
460 | const linkedNodes = ((_b = (_a = sanitizer.getAutolink) === null || _a === void 0 ? void 0 : _a.call(sanitizer)) !== null && _b !== void 0 ? _b : true)
|
461 | ? autolink(preTextContent, {
|
462 | checkWeb: true,
|
463 | checkPaths: false
|
464 | })
|
465 | : [document.createTextNode(content)];
|
466 | const preNodes = Array.from(pre.childNodes);
|
467 | ret = mergeNodes(preNodes, linkedNodes);
|
468 | }
|
469 | else {
|
470 | ret = document.createElement('pre');
|
471 | }
|
472 | host.appendChild(ret);
|
473 |
|
474 | return Promise.resolve(undefined);
|
475 | }
|
476 |
|
477 |
|
478 |
|
479 |
|
480 |
|
481 |
|
482 |
|
483 | export function renderError(options) {
|
484 | var _a, _b;
|
485 |
|
486 | const { host, linkHandler, sanitizer, resolver, source } = options;
|
487 |
|
488 | const content = sanitizer.sanitize(Private.ansiSpan(source), {
|
489 | allowedTags: ['span']
|
490 | });
|
491 |
|
492 | const pre = document.createElement('pre');
|
493 | pre.innerHTML = content;
|
494 | const preTextContent = pre.textContent;
|
495 | let ret;
|
496 | if (preTextContent) {
|
497 |
|
498 | const linkedNodes = ((_b = (_a = sanitizer.getAutolink) === null || _a === void 0 ? void 0 : _a.call(sanitizer)) !== null && _b !== void 0 ? _b : true)
|
499 | ? autolink(preTextContent, {
|
500 | checkWeb: true,
|
501 | checkPaths: true
|
502 | })
|
503 | : [document.createTextNode(content)];
|
504 | const preNodes = Array.from(pre.childNodes);
|
505 | ret = mergeNodes(preNodes, linkedNodes);
|
506 | }
|
507 | else {
|
508 | ret = document.createElement('pre');
|
509 | }
|
510 | host.appendChild(ret);
|
511 |
|
512 | let promise;
|
513 | if (resolver) {
|
514 | promise = Private.handlePaths(host, resolver, linkHandler);
|
515 | }
|
516 | else {
|
517 | promise = Promise.resolve(undefined);
|
518 | }
|
519 |
|
520 | return promise;
|
521 | }
|
522 |
|
523 |
|
524 |
|
525 | function mergeNodes(preNodes, linkedNodes) {
|
526 | const ret = document.createElement('pre');
|
527 | let inAnchorElement = false;
|
528 | const combinedNodes = [];
|
529 | for (let nodes of alignedNodes(preNodes, linkedNodes)) {
|
530 | if (!nodes[0]) {
|
531 | combinedNodes.push(nodes[1]);
|
532 | inAnchorElement = nodes[1].nodeType !== Node.TEXT_NODE;
|
533 | continue;
|
534 | }
|
535 | else if (!nodes[1]) {
|
536 | combinedNodes.push(nodes[0]);
|
537 | inAnchorElement = false;
|
538 | continue;
|
539 | }
|
540 | let [preNode, linkNode] = nodes;
|
541 | const lastCombined = combinedNodes[combinedNodes.length - 1];
|
542 |
|
543 |
|
544 |
|
545 | if (inAnchorElement &&
|
546 | linkNode.href ===
|
547 | lastCombined.href) {
|
548 | lastCombined.appendChild(preNode);
|
549 | }
|
550 | else {
|
551 |
|
552 | const isAnchor = linkNode.nodeType !== Node.TEXT_NODE;
|
553 |
|
554 | if (!isAnchor) {
|
555 | combinedNodes.push(preNode);
|
556 | inAnchorElement = false;
|
557 | }
|
558 | else {
|
559 |
|
560 |
|
561 |
|
562 | linkNode.textContent = '';
|
563 | linkNode.appendChild(preNode);
|
564 | combinedNodes.push(linkNode);
|
565 | inAnchorElement = true;
|
566 | }
|
567 | }
|
568 | }
|
569 |
|
570 | for (const child of combinedNodes) {
|
571 | ret.appendChild(child);
|
572 | }
|
573 | return ret;
|
574 | }
|
575 |
|
576 |
|
577 |
|
578 | var Private;
|
579 | (function (Private) {
|
580 | |
581 |
|
582 |
|
583 |
|
584 |
|
585 |
|
586 |
|
587 |
|
588 | function evalInnerHTMLScriptTags(host) {
|
589 |
|
590 | const scripts = Array.from(host.getElementsByTagName('script'));
|
591 |
|
592 | for (const script of scripts) {
|
593 |
|
594 | if (!script.parentNode) {
|
595 | continue;
|
596 | }
|
597 |
|
598 | const clone = document.createElement('script');
|
599 |
|
600 | const attrs = script.attributes;
|
601 | for (let i = 0, n = attrs.length; i < n; ++i) {
|
602 | const { name, value } = attrs[i];
|
603 | clone.setAttribute(name, value);
|
604 | }
|
605 |
|
606 | clone.textContent = script.textContent;
|
607 |
|
608 | script.parentNode.replaceChild(clone, script);
|
609 | }
|
610 | }
|
611 | Private.evalInnerHTMLScriptTags = evalInnerHTMLScriptTags;
|
612 | |
613 |
|
614 |
|
615 | function handleDefaults(node, resolver) {
|
616 |
|
617 | const anchors = node.getElementsByTagName('a');
|
618 | for (let i = 0; i < anchors.length; i++) {
|
619 | const el = anchors[i];
|
620 |
|
621 |
|
622 | if (!(el instanceof HTMLAnchorElement)) {
|
623 | continue;
|
624 | }
|
625 | const path = el.href;
|
626 | const isLocal = resolver && resolver.isLocal
|
627 | ? resolver.isLocal(path)
|
628 | : URLExt.isLocal(path);
|
629 |
|
630 | if (!el.target) {
|
631 | el.target = isLocal ? '_self' : '_blank';
|
632 | }
|
633 |
|
634 | if (!isLocal) {
|
635 | el.rel = 'noopener';
|
636 | }
|
637 | }
|
638 |
|
639 | const imgs = node.getElementsByTagName('img');
|
640 | for (let i = 0; i < imgs.length; i++) {
|
641 | if (!imgs[i].alt) {
|
642 | imgs[i].alt = 'Image';
|
643 | }
|
644 | }
|
645 | }
|
646 | Private.handleDefaults = handleDefaults;
|
647 | |
648 |
|
649 |
|
650 |
|
651 |
|
652 |
|
653 |
|
654 |
|
655 |
|
656 |
|
657 |
|
658 | function handleUrls(node, resolver, linkHandler) {
|
659 |
|
660 | const promises = [];
|
661 |
|
662 | const nodes = node.querySelectorAll('*[src]');
|
663 | for (let i = 0; i < nodes.length; i++) {
|
664 | promises.push(handleAttr(nodes[i], 'src', resolver));
|
665 | }
|
666 |
|
667 | const anchors = node.getElementsByTagName('a');
|
668 | for (let i = 0; i < anchors.length; i++) {
|
669 | promises.push(handleAnchor(anchors[i], resolver, linkHandler));
|
670 | }
|
671 |
|
672 | const links = node.getElementsByTagName('link');
|
673 | for (let i = 0; i < links.length; i++) {
|
674 | promises.push(handleAttr(links[i], 'href', resolver));
|
675 | }
|
676 |
|
677 | return Promise.all(promises).then(() => undefined);
|
678 | }
|
679 | Private.handleUrls = handleUrls;
|
680 | |
681 |
|
682 |
|
683 |
|
684 |
|
685 |
|
686 |
|
687 |
|
688 |
|
689 |
|
690 |
|
691 | async function handlePaths(node, resolver, linkHandler) {
|
692 |
|
693 | const anchors = node.getElementsByTagName('a');
|
694 | for (let i = 0; i < anchors.length; i++) {
|
695 | await handlePathAnchor(anchors[i], resolver, linkHandler);
|
696 | }
|
697 | }
|
698 | Private.handlePaths = handlePaths;
|
699 | |
700 |
|
701 |
|
702 | function headerAnchors(node) {
|
703 | const headerNames = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
|
704 | for (const headerType of headerNames) {
|
705 | const headers = node.getElementsByTagName(headerType);
|
706 | for (let i = 0; i < headers.length; i++) {
|
707 | const header = headers[i];
|
708 | header.id = renderMarkdown.createHeaderId(header);
|
709 | const anchor = document.createElement('a');
|
710 | anchor.target = '_self';
|
711 | anchor.textContent = '¶';
|
712 | anchor.href = '#' + header.id;
|
713 | anchor.classList.add('jp-InternalAnchorLink');
|
714 | header.appendChild(anchor);
|
715 | }
|
716 | }
|
717 | }
|
718 | Private.headerAnchors = headerAnchors;
|
719 | |
720 |
|
721 |
|
722 | async function handleAttr(node, name, resolver) {
|
723 | const source = node.getAttribute(name) || '';
|
724 | const isLocal = resolver.isLocal
|
725 | ? resolver.isLocal(source)
|
726 | : URLExt.isLocal(source);
|
727 | if (!source || !isLocal) {
|
728 | return;
|
729 | }
|
730 | try {
|
731 | const urlPath = await resolver.resolveUrl(source);
|
732 | let url = await resolver.getDownloadUrl(urlPath);
|
733 | if (URLExt.parse(url).protocol !== 'data:') {
|
734 |
|
735 |
|
736 | url += (/\?/.test(url) ? '&' : '?') + new Date().getTime();
|
737 | }
|
738 | node.setAttribute(name, url);
|
739 | }
|
740 | catch (err) {
|
741 |
|
742 |
|
743 | node.setAttribute(name, '');
|
744 | throw err;
|
745 | }
|
746 | }
|
747 | |
748 |
|
749 |
|
750 | function handleAnchor(anchor, resolver, linkHandler) {
|
751 |
|
752 |
|
753 | let href = anchor.getAttribute('href') || '';
|
754 | const isLocal = resolver.isLocal
|
755 | ? resolver.isLocal(href)
|
756 | : URLExt.isLocal(href);
|
757 |
|
758 | if (!href || !isLocal) {
|
759 | return Promise.resolve(undefined);
|
760 | }
|
761 |
|
762 | const hash = anchor.hash;
|
763 | if (hash) {
|
764 |
|
765 | if (hash === href) {
|
766 | anchor.target = '_self';
|
767 | return Promise.resolve(undefined);
|
768 | }
|
769 |
|
770 | href = href.replace(hash, '');
|
771 | }
|
772 |
|
773 | return resolver
|
774 | .resolveUrl(href)
|
775 | .then(urlPath => {
|
776 |
|
777 | const path = decodeURIComponent(urlPath);
|
778 |
|
779 | if (linkHandler) {
|
780 | linkHandler.handleLink(anchor, path, hash);
|
781 | }
|
782 |
|
783 | return resolver.getDownloadUrl(urlPath);
|
784 | })
|
785 | .then(url => {
|
786 |
|
787 | anchor.href = url + hash;
|
788 | })
|
789 | .catch(err => {
|
790 |
|
791 |
|
792 | anchor.href = '';
|
793 | });
|
794 | }
|
795 | |
796 |
|
797 |
|
798 | async function handlePathAnchor(anchor, resolver, linkHandler) {
|
799 | let path = anchor.dataset.path || '';
|
800 | let locator = anchor.dataset.locator ? '#' + anchor.dataset.locator : '';
|
801 | delete anchor.dataset.path;
|
802 | delete anchor.dataset.locator;
|
803 | const allowRoot = true;
|
804 | const isLocal = resolver.isLocal
|
805 | ? resolver.isLocal(path, allowRoot)
|
806 | : URLExt.isLocal(path, allowRoot);
|
807 |
|
808 |
|
809 |
|
810 |
|
811 | if (!path ||
|
812 | !isLocal ||
|
813 | !resolver.resolvePath ||
|
814 | !linkHandler ||
|
815 | !linkHandler.handlePath) {
|
816 | anchor.replaceWith(...anchor.childNodes);
|
817 | return Promise.resolve(undefined);
|
818 | }
|
819 | try {
|
820 |
|
821 | const resolution = await resolver.resolvePath(path);
|
822 | if (!resolution) {
|
823 |
|
824 | console.log('Path resolution bailing: does not exist');
|
825 | return Promise.resolve(undefined);
|
826 | }
|
827 |
|
828 | linkHandler.handlePath(anchor, resolution.path, resolution.scope, locator);
|
829 |
|
830 | anchor.href = resolution.path + locator;
|
831 | }
|
832 | catch (err) {
|
833 |
|
834 |
|
835 | console.warn('Path anchor error:', err);
|
836 | anchor.href = '#linking-failed-see-console';
|
837 | }
|
838 | }
|
839 | const ANSI_COLORS = [
|
840 | 'ansi-black',
|
841 | 'ansi-red',
|
842 | 'ansi-green',
|
843 | 'ansi-yellow',
|
844 | 'ansi-blue',
|
845 | 'ansi-magenta',
|
846 | 'ansi-cyan',
|
847 | 'ansi-white',
|
848 | 'ansi-black-intense',
|
849 | 'ansi-red-intense',
|
850 | 'ansi-green-intense',
|
851 | 'ansi-yellow-intense',
|
852 | 'ansi-blue-intense',
|
853 | 'ansi-magenta-intense',
|
854 | 'ansi-cyan-intense',
|
855 | 'ansi-white-intense'
|
856 | ];
|
857 | |
858 |
|
859 |
|
860 |
|
861 | function pushColoredChunk(chunk, fg, bg, bold, underline, inverse, out) {
|
862 | if (chunk) {
|
863 | const classes = [];
|
864 | const styles = [];
|
865 | if (bold && typeof fg === 'number' && 0 <= fg && fg < 8) {
|
866 | fg += 8;
|
867 | }
|
868 | if (inverse) {
|
869 | [fg, bg] = [bg, fg];
|
870 | }
|
871 | if (typeof fg === 'number') {
|
872 | classes.push(ANSI_COLORS[fg] + '-fg');
|
873 | }
|
874 | else if (fg.length) {
|
875 | styles.push(`color: rgb(${fg})`);
|
876 | }
|
877 | else if (inverse) {
|
878 | classes.push('ansi-default-inverse-fg');
|
879 | }
|
880 | if (typeof bg === 'number') {
|
881 | classes.push(ANSI_COLORS[bg] + '-bg');
|
882 | }
|
883 | else if (bg.length) {
|
884 | styles.push(`background-color: rgb(${bg})`);
|
885 | }
|
886 | else if (inverse) {
|
887 | classes.push('ansi-default-inverse-bg');
|
888 | }
|
889 | if (bold) {
|
890 | classes.push('ansi-bold');
|
891 | }
|
892 | if (underline) {
|
893 | classes.push('ansi-underline');
|
894 | }
|
895 | if (classes.length || styles.length) {
|
896 | out.push('<span');
|
897 | if (classes.length) {
|
898 | out.push(` class="${classes.join(' ')}"`);
|
899 | }
|
900 | if (styles.length) {
|
901 | out.push(` style="${styles.join('; ')}"`);
|
902 | }
|
903 | out.push('>');
|
904 | out.push(chunk);
|
905 | out.push('</span>');
|
906 | }
|
907 | else {
|
908 | out.push(chunk);
|
909 | }
|
910 | }
|
911 | }
|
912 | |
913 |
|
914 |
|
915 | function getExtendedColors(numbers) {
|
916 | let r;
|
917 | let g;
|
918 | let b;
|
919 | const n = numbers.shift();
|
920 | if (n === 2 && numbers.length >= 3) {
|
921 |
|
922 | r = numbers.shift();
|
923 | g = numbers.shift();
|
924 | b = numbers.shift();
|
925 | if ([r, g, b].some(c => c < 0 || 255 < c)) {
|
926 | throw new RangeError('Invalid range for RGB colors');
|
927 | }
|
928 | }
|
929 | else if (n === 5 && numbers.length >= 1) {
|
930 |
|
931 | const idx = numbers.shift();
|
932 | if (idx < 0) {
|
933 | throw new RangeError('Color index must be >= 0');
|
934 | }
|
935 | else if (idx < 16) {
|
936 |
|
937 | return idx;
|
938 | }
|
939 | else if (idx < 232) {
|
940 |
|
941 | r = Math.floor((idx - 16) / 36);
|
942 | r = r > 0 ? 55 + r * 40 : 0;
|
943 | g = Math.floor(((idx - 16) % 36) / 6);
|
944 | g = g > 0 ? 55 + g * 40 : 0;
|
945 | b = (idx - 16) % 6;
|
946 | b = b > 0 ? 55 + b * 40 : 0;
|
947 | }
|
948 | else if (idx < 256) {
|
949 |
|
950 | r = g = b = (idx - 232) * 10 + 8;
|
951 | }
|
952 | else {
|
953 | throw new RangeError('Color index must be < 256');
|
954 | }
|
955 | }
|
956 | else {
|
957 | throw new RangeError('Invalid extended color specification');
|
958 | }
|
959 | return [r, g, b];
|
960 | }
|
961 | |
962 |
|
963 |
|
964 |
|
965 |
|
966 |
|
967 |
|
968 | function ansiSpan(str) {
|
969 | const ansiRe = /\x1b\[(.*?)([@-~])/g;
|
970 | let fg = [];
|
971 | let bg = [];
|
972 | let bold = false;
|
973 | let underline = false;
|
974 | let inverse = false;
|
975 | let match;
|
976 | const out = [];
|
977 | const numbers = [];
|
978 | let start = 0;
|
979 | str = escape(str);
|
980 | str += '\x1b[m';
|
981 |
|
982 | while ((match = ansiRe.exec(str))) {
|
983 | if (match[2] === 'm') {
|
984 | const items = match[1].split(';');
|
985 | for (let i = 0; i < items.length; i++) {
|
986 | const item = items[i];
|
987 | if (item === '') {
|
988 | numbers.push(0);
|
989 | }
|
990 | else if (item.search(/^\d+$/) !== -1) {
|
991 | numbers.push(parseInt(item, 10));
|
992 | }
|
993 | else {
|
994 |
|
995 | numbers.length = 0;
|
996 | break;
|
997 | }
|
998 | }
|
999 | }
|
1000 | else {
|
1001 |
|
1002 | }
|
1003 | const chunk = str.substring(start, match.index);
|
1004 | pushColoredChunk(chunk, fg, bg, bold, underline, inverse, out);
|
1005 | start = ansiRe.lastIndex;
|
1006 | while (numbers.length) {
|
1007 | const n = numbers.shift();
|
1008 | switch (n) {
|
1009 | case 0:
|
1010 | fg = bg = [];
|
1011 | bold = false;
|
1012 | underline = false;
|
1013 | inverse = false;
|
1014 | break;
|
1015 | case 1:
|
1016 | case 5:
|
1017 | bold = true;
|
1018 | break;
|
1019 | case 4:
|
1020 | underline = true;
|
1021 | break;
|
1022 | case 7:
|
1023 | inverse = true;
|
1024 | break;
|
1025 | case 21:
|
1026 | case 22:
|
1027 | bold = false;
|
1028 | break;
|
1029 | case 24:
|
1030 | underline = false;
|
1031 | break;
|
1032 | case 27:
|
1033 | inverse = false;
|
1034 | break;
|
1035 | case 30:
|
1036 | case 31:
|
1037 | case 32:
|
1038 | case 33:
|
1039 | case 34:
|
1040 | case 35:
|
1041 | case 36:
|
1042 | case 37:
|
1043 | fg = n - 30;
|
1044 | break;
|
1045 | case 38:
|
1046 | try {
|
1047 | fg = getExtendedColors(numbers);
|
1048 | }
|
1049 | catch (e) {
|
1050 | numbers.length = 0;
|
1051 | }
|
1052 | break;
|
1053 | case 39:
|
1054 | fg = [];
|
1055 | break;
|
1056 | case 40:
|
1057 | case 41:
|
1058 | case 42:
|
1059 | case 43:
|
1060 | case 44:
|
1061 | case 45:
|
1062 | case 46:
|
1063 | case 47:
|
1064 | bg = n - 40;
|
1065 | break;
|
1066 | case 48:
|
1067 | try {
|
1068 | bg = getExtendedColors(numbers);
|
1069 | }
|
1070 | catch (e) {
|
1071 | numbers.length = 0;
|
1072 | }
|
1073 | break;
|
1074 | case 49:
|
1075 | bg = [];
|
1076 | break;
|
1077 | case 90:
|
1078 | case 91:
|
1079 | case 92:
|
1080 | case 93:
|
1081 | case 94:
|
1082 | case 95:
|
1083 | case 96:
|
1084 | case 97:
|
1085 | fg = n - 90 + 8;
|
1086 | break;
|
1087 | case 100:
|
1088 | case 101:
|
1089 | case 102:
|
1090 | case 103:
|
1091 | case 104:
|
1092 | case 105:
|
1093 | case 106:
|
1094 | case 107:
|
1095 | bg = n - 100 + 8;
|
1096 | break;
|
1097 | default:
|
1098 |
|
1099 | }
|
1100 | }
|
1101 | }
|
1102 | return out.join('');
|
1103 | }
|
1104 | Private.ansiSpan = ansiSpan;
|
1105 | })(Private || (Private = {}));
|
1106 |
|
\ | No newline at end of file |