Skip to content

Commit a145c6d

Browse files
committed
Use custom markdown renderer to implement external links
1 parent 9743785 commit a145c6d

File tree

1 file changed

+38
-10
lines changed

1 file changed

+38
-10
lines changed

jupyter_notebook/static/phosphor-notebook/src/NotebookComponent.ts

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -62,24 +62,52 @@ export var JupyterError = createFactory(JupyterErrorComponent)
6262
var renderer = new (<any>marked).Renderer();
6363
renderer.heading = function (text: string, level: number) {
6464
var escapedText = text.toLowerCase().replace(/[^\w]+/g, '-');
65-
return '<h' + level + '><a name="' +
66-
escapedText +
67-
'" class="anchor" href="#' +
68-
escapedText +
69-
'"><span class="header-link"></span></a>' +
70-
text + '</h' + level + '>';
65+
return `<h${level} id="${escapedText}">${text}<a class="anchor-link" href="#${escapedText}">¶</a></h${level}>`;
7166
}
72-
// TODO: make links open new tabs
73-
// links in markdown cells should open in new tabs
74-
// html.find("a[href]").not('[href^="#"]').attr("target", "_blank");
7567

68+
renderer.unescape = function(html: string): string {
69+
// from https://github.com/chjj/marked/blob/2b5802f258c5e23e48366f2377fbb4c807f47658/lib/marked.js#L1085
70+
return html.replace(/&([#\w]+);/g, function(_, n) {
71+
n = n.toLowerCase();
72+
if (n === 'colon') return ':';
73+
if (n.charAt(0) === '#') {
74+
return n.charAt(1) === 'x'
75+
? String.fromCharCode(parseInt(n.substring(2), 16))
76+
: String.fromCharCode(+n.substring(1));
77+
}
78+
return '';
79+
});
80+
}
81+
82+
renderer.check_url = function(href: string): boolean {
83+
try {
84+
var prot = decodeURIComponent(this.unescape(href))
85+
.replace(/[^\w:]/g, '')
86+
.toLowerCase();
87+
} catch (e) {
88+
return false;
89+
}
90+
if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
91+
return false;
92+
}
93+
return true;
94+
};
95+
96+
renderer.link = function(href: string, title: string, text: string) {
97+
//modified from the mark.js source to open all urls in new tabs
98+
if (this.options.sanitize && !this.check_url(href)) {
99+
return '';
100+
}
101+
return `<a href="${href}" ${title ? `title="${title}"` : ""} ${href[0] !== "#" ? "target=_blank" : ""}>${text}</a>`;
102+
};
76103

77104
class MarkdownCellComponent extends BaseComponent<nbformat.MarkdownCell> {
78105
onUpdateRequest(msg: IMessage): void {
79106
// replace the innerHTML of the node with the rendered markdown
80107
var t = mathjaxutils.remove_math(this.data.source);
81-
marked(t.html, { sanitize: true }, (err: any, html: string) => {
108+
marked(t.html, { sanitize: true, renderer: renderer}, (err: any, html: string) => {
82109
this.node.innerHTML = mathjaxutils.replace_math(html, t.math);
110+
// TODO: do some serious sanitization, using, for example, the caja sanitizer
83111
MathJax.Hub.Queue(["Typeset", MathJax.Hub, this.node]);
84112
});
85113
}

0 commit comments

Comments
 (0)