|
@@ -1,17 +1,7 @@
|
|
-// Copyright (c) Jupyter Development Team.
|
|
|
|
-// Distributed under the terms of the Modified BSD License.
|
|
|
|
-
|
|
|
|
-import {
|
|
|
|
- ansi_to_html, escape_for_html
|
|
|
|
-} from 'ansi_up';
|
|
|
|
-
|
|
|
|
-import {
|
|
|
|
- Mode, CodeMirrorEditor
|
|
|
|
-} from '@jupyterlab/codemirror';
|
|
|
|
-
|
|
|
|
-import * as marked
|
|
|
|
- from 'marked';
|
|
|
|
-
|
|
|
|
|
|
+/*-----------------------------------------------------------------------------
|
|
|
|
+| Copyright (c) Jupyter Development Team.
|
|
|
|
+| Distributed under the terms of the Modified BSD License.
|
|
|
|
+|----------------------------------------------------------------------------*/
|
|
import {
|
|
import {
|
|
Message
|
|
Message
|
|
} from '@phosphor/messaging';
|
|
} from '@phosphor/messaging';
|
|
@@ -20,86 +10,36 @@ import {
|
|
Widget
|
|
Widget
|
|
} from '@phosphor/widgets';
|
|
} from '@phosphor/widgets';
|
|
|
|
|
|
-import {
|
|
|
|
- JSONObject
|
|
|
|
-} from '@phosphor/coreutils';
|
|
|
|
-
|
|
|
|
-import {
|
|
|
|
- URLExt
|
|
|
|
-} from '@jupyterlab/coreutils';
|
|
|
|
-
|
|
|
|
import {
|
|
import {
|
|
IRenderMime
|
|
IRenderMime
|
|
} from '@jupyterlab/rendermime-interfaces';
|
|
} from '@jupyterlab/rendermime-interfaces';
|
|
|
|
|
|
import {
|
|
import {
|
|
- typeset, removeMath, replaceMath
|
|
|
|
-} from '.';
|
|
|
|
-
|
|
|
|
|
|
+ typeset
|
|
|
|
+} from './latex';
|
|
|
|
|
|
-/*
|
|
|
|
- * The class name added to common rendered HTML.
|
|
|
|
- */
|
|
|
|
-const HTML_COMMON_CLASS = 'jp-RenderedHTMLCommon';
|
|
|
|
-
|
|
|
|
-/*
|
|
|
|
- * The class name added to rendered HTML.
|
|
|
|
- */
|
|
|
|
-const HTML_CLASS = 'jp-RenderedHTML';
|
|
|
|
-
|
|
|
|
-/*
|
|
|
|
- * The class name added to rendered markdown.
|
|
|
|
- */
|
|
|
|
-const MARKDOWN_CLASS = 'jp-RenderedMarkdown';
|
|
|
|
-
|
|
|
|
-/*
|
|
|
|
- * The class name added to rendered Latex.
|
|
|
|
- */
|
|
|
|
-const LATEX_CLASS = 'jp-RenderedLatex';
|
|
|
|
|
|
+import {
|
|
|
|
+ RenderHelpers
|
|
|
|
+} from './renderhelpers';
|
|
|
|
|
|
-/*
|
|
|
|
- * The class name added to rendered images.
|
|
|
|
- */
|
|
|
|
-const IMAGE_CLASS = 'jp-RenderedImage';
|
|
|
|
-
|
|
|
|
-/*
|
|
|
|
- * The class name added to rendered text.
|
|
|
|
- */
|
|
|
|
-const TEXT_CLASS = 'jp-RenderedText';
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
- * The class name added to an error output.
|
|
|
|
- */
|
|
|
|
-const ERROR_CLASS = 'jp-mod-error';
|
|
|
|
-
|
|
|
|
-/*
|
|
|
|
- * The class name added to rendered javascript.
|
|
|
|
- */
|
|
|
|
-const JAVASCRIPT_CLASS = 'jp-RenderedJavascript';
|
|
|
|
-
|
|
|
|
-/*
|
|
|
|
- * The class name added to rendered SVG.
|
|
|
|
- */
|
|
|
|
-const SVG_CLASS = 'jp-RenderedSVG';
|
|
|
|
-
|
|
|
|
-/*
|
|
|
|
- * The class name added to rendered PDF.
|
|
|
|
- */
|
|
|
|
-const PDF_CLASS = 'jp-RenderedPDF';
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-/*
|
|
|
|
- * A widget for displaying any widget whoes representation is rendered HTML.
|
|
|
|
|
|
+ * A common base class for mime renderers.
|
|
*/
|
|
*/
|
|
export
|
|
export
|
|
abstract class RenderedCommon extends Widget implements IRenderMime.IRenderer {
|
|
abstract class RenderedCommon extends Widget implements IRenderMime.IRenderer {
|
|
- /* Construct a new rendered HTML common widget.*/
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Construct a new rendered common widget.
|
|
|
|
+ *
|
|
|
|
+ * @param options - The options for initializing the widget.
|
|
|
|
+ */
|
|
constructor(options: IRenderMime.IRendererOptions) {
|
|
constructor(options: IRenderMime.IRendererOptions) {
|
|
super();
|
|
super();
|
|
this.mimeType = options.mimeType;
|
|
this.mimeType = options.mimeType;
|
|
this.sanitizer = options.sanitizer;
|
|
this.sanitizer = options.sanitizer;
|
|
this.resolver = options.resolver;
|
|
this.resolver = options.resolver;
|
|
this.linkHandler = options.linkHandler;
|
|
this.linkHandler = options.linkHandler;
|
|
|
|
+ this.node.dataset['mime-type'] = this.mimeType;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -113,70 +53,92 @@ abstract class RenderedCommon extends Widget implements IRenderMime.IRenderer {
|
|
readonly sanitizer: IRenderMime.ISanitizer;
|
|
readonly sanitizer: IRenderMime.ISanitizer;
|
|
|
|
|
|
/**
|
|
/**
|
|
- * The link handler.
|
|
|
|
|
|
+ * The resolver object.
|
|
*/
|
|
*/
|
|
- readonly linkHandler: IRenderMime.ILinkHandler;
|
|
|
|
|
|
+ readonly resolver: IRenderMime.IResolver | null;
|
|
|
|
|
|
/**
|
|
/**
|
|
- * The resolver object.
|
|
|
|
|
|
+ * The link handler.
|
|
*/
|
|
*/
|
|
- readonly resolver: IRenderMime.IResolver | null;
|
|
|
|
|
|
+ readonly linkHandler: IRenderMime.ILinkHandler | null;
|
|
|
|
|
|
/**
|
|
/**
|
|
* Render a mime model.
|
|
* Render a mime model.
|
|
|
|
+ *
|
|
|
|
+ * @param model - The mime model to render.
|
|
|
|
+ *
|
|
|
|
+ * @returns A promise which resolves when rendering is complete.
|
|
|
|
+ */
|
|
|
|
+ renderModel(model: IRenderMime.IMimeModel): Promise<void> {
|
|
|
|
+ // TODO compare model against old model for early bail?
|
|
|
|
+
|
|
|
|
+ // Set the trusted flag on the node.
|
|
|
|
+ this.node.dataset['trusted'] = `${model.trusted}`;
|
|
|
|
+
|
|
|
|
+ // Render the actual content.
|
|
|
|
+ return this.render(model);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Render the mime model.
|
|
|
|
+ *
|
|
|
|
+ * @param model - The mime model to render.
|
|
|
|
+ *
|
|
|
|
+ * @returns A promise which resolves when rendering is complete.
|
|
*/
|
|
*/
|
|
- abstract renderModel(model: IRenderMime.IMimeModel): Promise<void>;
|
|
|
|
|
|
+ abstract render(model: IRenderMime.IMimeModel): Promise<void>;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
-/*
|
|
|
|
- * A widget for displaying any widget whoes representation is rendered HTML.
|
|
|
|
- * */
|
|
|
|
|
|
+/**
|
|
|
|
+ * A common base class for HTML mime renderers.
|
|
|
|
+ */
|
|
export
|
|
export
|
|
abstract class RenderedHTMLCommon extends RenderedCommon {
|
|
abstract class RenderedHTMLCommon extends RenderedCommon {
|
|
- /* Construct a new rendered HTML common widget.*/
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Construct a new rendered HTML common widget.
|
|
|
|
+ *
|
|
|
|
+ * @param options - The options for initializing the widget.
|
|
|
|
+ */
|
|
constructor(options: IRenderMime.IRendererOptions) {
|
|
constructor(options: IRenderMime.IRendererOptions) {
|
|
super(options);
|
|
super(options);
|
|
- this.addClass(HTML_COMMON_CLASS);
|
|
|
|
|
|
+ this.addClass('jp-RenderedHTMLCommon');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
- * A widget for displaying HTML and rendering math.
|
|
|
|
|
|
+ * A mime renderer for displaying HTML and math.
|
|
*/
|
|
*/
|
|
export
|
|
export
|
|
class RenderedHTML extends RenderedHTMLCommon {
|
|
class RenderedHTML extends RenderedHTMLCommon {
|
|
/**
|
|
/**
|
|
- * Construct a new html widget.
|
|
|
|
|
|
+ * Construct a new rendered HTML widget.
|
|
|
|
+ *
|
|
|
|
+ * @param options - The options for initializing the widget.
|
|
*/
|
|
*/
|
|
constructor(options: IRenderMime.IRendererOptions) {
|
|
constructor(options: IRenderMime.IRendererOptions) {
|
|
super(options);
|
|
super(options);
|
|
- this.addClass(HTML_CLASS);
|
|
|
|
|
|
+ this.addClass('jp-RenderedHTML');
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
* Render a mime model.
|
|
* Render a mime model.
|
|
- */
|
|
|
|
- renderModel(model: IRenderMime.IMimeModel): Promise<void> {
|
|
|
|
- let source = Private.getSource(model, this.mimeType);
|
|
|
|
- if (!model.trusted) {
|
|
|
|
- source = this.sanitizer.sanitize(source);
|
|
|
|
- }
|
|
|
|
- Private.setHtml(this.node, source);
|
|
|
|
- if (this.resolver) {
|
|
|
|
- return Private.handleUrls(
|
|
|
|
- this.node, this.resolver, this.linkHandler
|
|
|
|
- ).then(() => {
|
|
|
|
- if (this.isAttached) {
|
|
|
|
- typeset(this.node);
|
|
|
|
- }
|
|
|
|
- });
|
|
|
|
- }
|
|
|
|
- if (this.isAttached) {
|
|
|
|
- typeset(this.node);
|
|
|
|
- }
|
|
|
|
- return Promise.resolve(void 0);
|
|
|
|
|
|
+ *
|
|
|
|
+ * @param model - The mime model to render.
|
|
|
|
+ *
|
|
|
|
+ * @returns A promise which resolves when rendering is complete.
|
|
|
|
+ */
|
|
|
|
+ render(model: IRenderMime.IMimeModel): Promise<void> {
|
|
|
|
+ return RenderedHTML.renderHTML({
|
|
|
|
+ node: this.node,
|
|
|
|
+ source: String(model.data[this.mimeType]),
|
|
|
|
+ trusted: model.trusted,
|
|
|
|
+ resolver: this.resolver,
|
|
|
|
+ sanitizer: this.sanitizer,
|
|
|
|
+ linkHandler: this.linkHandler,
|
|
|
|
+ shouldTypeset: this.isAttached
|
|
|
|
+ });
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -189,57 +151,124 @@ class RenderedHTML extends RenderedHTMLCommon {
|
|
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
- * A widget for displaying Markdown with embeded latex.
|
|
|
|
|
|
+ * The namespace for the `RenderedHTML` class statics.
|
|
|
|
+ */
|
|
|
|
+export
|
|
|
|
+namespace RenderedHTML {
|
|
|
|
+ /**
|
|
|
|
+ * The options for the `render` function.
|
|
|
|
+ */
|
|
|
|
+ export
|
|
|
|
+ interface IRenderOptions {
|
|
|
|
+ /**
|
|
|
|
+ * The node to use as the host of the rendered HTML.
|
|
|
|
+ */
|
|
|
|
+ node: HTMLElement;
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * The HTML source to render.
|
|
|
|
+ */
|
|
|
|
+ source: string;
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Whether the source is trusted.
|
|
|
|
+ */
|
|
|
|
+ trusted: boolean;
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * The html sanitizer.
|
|
|
|
+ */
|
|
|
|
+ sanitizer: ISanitizer;
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * An optional url resolver.
|
|
|
|
+ */
|
|
|
|
+ resolver: IRenderMime.IResolver | null;
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * An optional link handler.
|
|
|
|
+ */
|
|
|
|
+ linkHandler: IRenderMime.ILinkHandler | null;
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Whether the node should be typeset.
|
|
|
|
+ */
|
|
|
|
+ shouldTypeset: boolean;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Render HTML into a host node.
|
|
|
|
+ *
|
|
|
|
+ * @params options - The options for rendering.
|
|
|
|
+ *
|
|
|
|
+ * @returns A promise which resolves when rendering is complete.
|
|
|
|
+ */
|
|
|
|
+ export
|
|
|
|
+ function render(options: renderHTML.IOptions): Promise<void> {
|
|
|
|
+ // Unpack the options.
|
|
|
|
+ let {
|
|
|
|
+ node, source, trusted, sanitizer, resolver, linkHandler, shouldTypeset
|
|
|
|
+ } = options;
|
|
|
|
+
|
|
|
|
+ // Clear the content if there is no source.
|
|
|
|
+ if (!source) {
|
|
|
|
+ node.textContent = '';
|
|
|
|
+ return Promise.resolve(undefined);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Sanitize the source if it is not trusted.
|
|
|
|
+ if (!trusted) {
|
|
|
|
+ source = sanitizer.sanitize(source);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Set the inner HTML to the source.
|
|
|
|
+ node.innerHTML = source;
|
|
|
|
+
|
|
|
|
+ // Patch the urls if a resolver is available.
|
|
|
|
+ let promise: Promise<void>;
|
|
|
|
+ if (resolver) {
|
|
|
|
+ promise = Private.handleUrls(node, resolver, linkHandler);
|
|
|
|
+ } else {
|
|
|
|
+ promise = Promise.resolve(undefined);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Return the final rendered promise.
|
|
|
|
+ return promise.then(() => { if (shouldTypeset) { typeset(node); } });
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * A mime renderer for displaying Markdown with embeded latex.
|
|
*/
|
|
*/
|
|
export
|
|
export
|
|
class RenderedMarkdown extends RenderedHTMLCommon {
|
|
class RenderedMarkdown extends RenderedHTMLCommon {
|
|
/**
|
|
/**
|
|
- * Construct a new markdown widget.
|
|
|
|
|
|
+ * Construct a new rendered markdown widget.
|
|
|
|
+ *
|
|
|
|
+ * @param options - The options for initializing the widget.
|
|
*/
|
|
*/
|
|
constructor(options: IRenderMime.IRendererOptions) {
|
|
constructor(options: IRenderMime.IRendererOptions) {
|
|
super(options);
|
|
super(options);
|
|
- this.addClass(MARKDOWN_CLASS);
|
|
|
|
-
|
|
|
|
- // Initialize the marked library if necessary.
|
|
|
|
- Private.initializeMarked();
|
|
|
|
|
|
+ this.addClass('jp-RenderedMarkdown');
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
* Render a mime model.
|
|
* Render a mime model.
|
|
- */
|
|
|
|
- renderModel(model: IRenderMime.IMimeModel): Promise<void> {
|
|
|
|
- return new Promise<void>((resolve, reject) => {
|
|
|
|
- let source = Private.getSource(model, this.mimeType);
|
|
|
|
- let parts = removeMath(source);
|
|
|
|
- // Add the markdown content asynchronously.
|
|
|
|
- marked(parts['text'], (err: any, content: string) => {
|
|
|
|
- if (err) {
|
|
|
|
- console.error(err);
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
- content = replaceMath(content, parts['math']);
|
|
|
|
- if (!model.trusted) {
|
|
|
|
- content = this.sanitizer.sanitize(content);
|
|
|
|
- }
|
|
|
|
- Private.setHtml(this.node, content);
|
|
|
|
- Private.headerAnchors(this.node);
|
|
|
|
- this.fit();
|
|
|
|
- if (this.resolver) {
|
|
|
|
- Private.handleUrls(
|
|
|
|
- this.node, this.resolver, this.linkHandler
|
|
|
|
- ).then(() => {
|
|
|
|
- if (this.isAttached) {
|
|
|
|
- typeset(this.node);
|
|
|
|
- }
|
|
|
|
- resolve(void 0);
|
|
|
|
- });
|
|
|
|
- } else {
|
|
|
|
- if (this.isAttached) {
|
|
|
|
- typeset(this.node);
|
|
|
|
- }
|
|
|
|
- resolve(void 0);
|
|
|
|
- }
|
|
|
|
- });
|
|
|
|
|
|
+ *
|
|
|
|
+ * @param model - The mime model to render.
|
|
|
|
+ *
|
|
|
|
+ * @returns A promise which resolves when rendering is complete.
|
|
|
|
+ */
|
|
|
|
+ render(model: IRenderMime.IMimeModel): Promise<void> {
|
|
|
|
+ return RenderHelpers.renderMarkdown({
|
|
|
|
+ node: this.node,
|
|
|
|
+ source: String(model.data[this.mimeType]),
|
|
|
|
+ trusted: model.trusted,
|
|
|
|
+ resolver: this.resolver,
|
|
|
|
+ sanitizer: this.sanitizer,
|
|
|
|
+ linkHandler: this.linkHandler,
|
|
|
|
+ shouldTypeset: this.isAttached
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
|
|
@@ -253,28 +282,33 @@ class RenderedMarkdown extends RenderedHTMLCommon {
|
|
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
- * A widget for displaying LaTeX output.
|
|
|
|
|
|
+ * A mime renderer for displaying LaTeX output.
|
|
*/
|
|
*/
|
|
export
|
|
export
|
|
class RenderedLatex extends RenderedCommon {
|
|
class RenderedLatex extends RenderedCommon {
|
|
/**
|
|
/**
|
|
- * Construct a new latex widget.
|
|
|
|
|
|
+ * Construct a new rendered Latex widget.
|
|
|
|
+ *
|
|
|
|
+ * @param options - The options for initializing the widget.
|
|
*/
|
|
*/
|
|
constructor(options: IRenderMime.IRendererOptions) {
|
|
constructor(options: IRenderMime.IRendererOptions) {
|
|
super(options);
|
|
super(options);
|
|
- this.addClass(LATEX_CLASS);
|
|
|
|
|
|
+ this.addClass('jp-RenderedLatex');
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
* Render a mime model.
|
|
* Render a mime model.
|
|
|
|
+ *
|
|
|
|
+ * @param model - The mime model to render.
|
|
|
|
+ *
|
|
|
|
+ * @returns A promise which resolves when rendering is complete.
|
|
*/
|
|
*/
|
|
- renderModel(model: IRenderMime.IMimeModel): Promise<void> {
|
|
|
|
- let source = Private.getSource(model, this.mimeType);
|
|
|
|
- this.node.textContent = source;
|
|
|
|
- if (this.isAttached) {
|
|
|
|
- typeset(this.node);
|
|
|
|
- }
|
|
|
|
- return Promise.resolve(void 0);
|
|
|
|
|
|
+ render(model: IRenderMime.IMimeModel): Promise<void> {
|
|
|
|
+ return RenderHelpers.renderLatex({
|
|
|
|
+ node: this.node,
|
|
|
|
+ source: String(model.data[this.mimeType]),
|
|
|
|
+ shouldTypeset: this.isAttached
|
|
|
|
+ });
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -287,134 +321,134 @@ class RenderedLatex extends RenderedCommon {
|
|
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
- * A widget for displaying rendered images.
|
|
|
|
|
|
+ * A mime renderer for displaying images.
|
|
*/
|
|
*/
|
|
export
|
|
export
|
|
class RenderedImage extends RenderedCommon {
|
|
class RenderedImage extends RenderedCommon {
|
|
/**
|
|
/**
|
|
* Construct a new rendered image widget.
|
|
* Construct a new rendered image widget.
|
|
|
|
+ *
|
|
|
|
+ * @param options - The options for initializing the widget.
|
|
*/
|
|
*/
|
|
constructor(options: IRenderMime.IRendererOptions) {
|
|
constructor(options: IRenderMime.IRendererOptions) {
|
|
super(options);
|
|
super(options);
|
|
- let img = document.createElement('img');
|
|
|
|
- this.node.appendChild(img);
|
|
|
|
- this.addClass(IMAGE_CLASS);
|
|
|
|
|
|
+ this.addClass('jp-RenderedImage');
|
|
|
|
+ this.node.appendChild(document.createElement('img'));
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
* Render a mime model.
|
|
* Render a mime model.
|
|
|
|
+ *
|
|
|
|
+ * @param model - The mime model to render.
|
|
|
|
+ *
|
|
|
|
+ * @returns A promise which resolves when rendering is complete.
|
|
*/
|
|
*/
|
|
- renderModel(model: IRenderMime.IMimeModel): Promise<void> {
|
|
|
|
- let source = Private.getSource(model, this.mimeType);
|
|
|
|
- let img = this.node.firstChild as HTMLImageElement;
|
|
|
|
- img.src = `data:${this.mimeType};base64,${source}`;
|
|
|
|
- let metadata = model.metadata[this.mimeType] as JSONObject;
|
|
|
|
- if (metadata) {
|
|
|
|
- let metaJSON = metadata as JSONObject;
|
|
|
|
- if (typeof metaJSON['height'] === 'number') {
|
|
|
|
- img.height = metaJSON['height'] as number;
|
|
|
|
- }
|
|
|
|
- if (typeof metaJSON['width'] === 'number') {
|
|
|
|
- img.width = metaJSON['width'] as number;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- return Promise.resolve(void 0);
|
|
|
|
|
|
+ render(model: IRenderMime.IMimeModel): Promise<void> {
|
|
|
|
+ return RenderHelpers.renderImage({
|
|
|
|
+ model,
|
|
|
|
+ mimeType: this.mimeType,
|
|
|
|
+ node: this.node.firstChild as HTMLImageElement
|
|
|
|
+ });
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
- * A widget for displaying rendered text.
|
|
|
|
|
|
+ * A widget for displaying plain text and console text.
|
|
*/
|
|
*/
|
|
export
|
|
export
|
|
class RenderedText extends RenderedCommon {
|
|
class RenderedText extends RenderedCommon {
|
|
/**
|
|
/**
|
|
* Construct a new rendered text widget.
|
|
* Construct a new rendered text widget.
|
|
|
|
+ *
|
|
|
|
+ * @param options - The options for initializing the widget.
|
|
*/
|
|
*/
|
|
constructor(options: IRenderMime.IRendererOptions) {
|
|
constructor(options: IRenderMime.IRendererOptions) {
|
|
super(options);
|
|
super(options);
|
|
- let pre = document.createElement('pre');
|
|
|
|
- this.node.appendChild(pre);
|
|
|
|
- this.addClass(TEXT_CLASS);
|
|
|
|
- if (this.mimeType === 'application/vnd.jupyter.stderr') {
|
|
|
|
- this.addClass(ERROR_CLASS);
|
|
|
|
- }
|
|
|
|
|
|
+ this.addClass('jp-RenderedText');
|
|
|
|
+ this.node.appendChild(document.createElement('pre'));
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
* Render a mime model.
|
|
* Render a mime model.
|
|
|
|
+ *
|
|
|
|
+ * @param model - The mime model to render.
|
|
|
|
+ *
|
|
|
|
+ * @returns A promise which resolves when rendering is complete.
|
|
*/
|
|
*/
|
|
- renderModel(model: IRenderMime.IMimeModel): Promise<void> {
|
|
|
|
- let source = Private.getSource(model, this.mimeType);
|
|
|
|
- let data = escape_for_html(source);
|
|
|
|
- let pre = this.node.firstChild as HTMLPreElement;
|
|
|
|
- while (pre.firstChild) {
|
|
|
|
- pre.removeChild(pre.firstChild);
|
|
|
|
- }
|
|
|
|
- pre.innerHTML = ansi_to_html(data, {use_classes: true});
|
|
|
|
- return Promise.resolve(void 0);
|
|
|
|
|
|
+ render(model: IRenderMime.IMimeModel): Promise<void> {
|
|
|
|
+ return RenderHelpers.renderText({
|
|
|
|
+ model,
|
|
|
|
+ mimeType: this.mimeType,
|
|
|
|
+ node: this.node.firstChild as HTMLElement
|
|
|
|
+ });
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
- * A widget for displaying rendered JavaScript.
|
|
|
|
|
|
+ * A widget for displaying/executing JavaScript.
|
|
*/
|
|
*/
|
|
export
|
|
export
|
|
class RenderedJavaScript extends RenderedCommon {
|
|
class RenderedJavaScript extends RenderedCommon {
|
|
/**
|
|
/**
|
|
- * Construct a new rendered JavaScript widget.
|
|
|
|
|
|
+ * Construct a new rendered Javascript widget.
|
|
|
|
+ *
|
|
|
|
+ * @param options - The options for initializing the widget.
|
|
*/
|
|
*/
|
|
constructor(options: IRenderMime.IRendererOptions) {
|
|
constructor(options: IRenderMime.IRendererOptions) {
|
|
super(options);
|
|
super(options);
|
|
- let s = document.createElement('script');
|
|
|
|
- s.type = options.mimeType;
|
|
|
|
- this.node.appendChild(s);
|
|
|
|
- this.addClass(JAVASCRIPT_CLASS);
|
|
|
|
|
|
+ this.addClass('jp-RenderedJavaScript');
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
* Render a mime model.
|
|
* Render a mime model.
|
|
|
|
+ *
|
|
|
|
+ * @param model - The mime model to render.
|
|
|
|
+ *
|
|
|
|
+ * @returns A promise which resolves when rendering is complete.
|
|
*/
|
|
*/
|
|
- renderModel(model: IRenderMime.IMimeModel): Promise<void> {
|
|
|
|
- let s = this.node.firstChild as HTMLScriptElement;
|
|
|
|
- let source = Private.getSource(model, this.mimeType);
|
|
|
|
- s.textContent = source;
|
|
|
|
- return Promise.resolve(void 0);
|
|
|
|
|
|
+ render(model: IRenderMime.IMimeModel): Promise<void> {
|
|
|
|
+ return RenderHelpers.renderJavaScript({
|
|
|
|
+ model,
|
|
|
|
+ node: this.node,
|
|
|
|
+ mimeType: this.mimeType
|
|
|
|
+ });
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
- * A widget for displaying rendered SVG content.
|
|
|
|
|
|
+ * A widget for displaying SVG content.
|
|
*/
|
|
*/
|
|
export
|
|
export
|
|
class RenderedSVG extends RenderedCommon {
|
|
class RenderedSVG extends RenderedCommon {
|
|
/**
|
|
/**
|
|
* Construct a new rendered SVG widget.
|
|
* Construct a new rendered SVG widget.
|
|
|
|
+ *
|
|
|
|
+ * @param options - The options for initializing the widget.
|
|
*/
|
|
*/
|
|
constructor(options: IRenderMime.IRendererOptions) {
|
|
constructor(options: IRenderMime.IRendererOptions) {
|
|
super(options);
|
|
super(options);
|
|
- this.addClass(SVG_CLASS);
|
|
|
|
|
|
+ this.addClass('jp-RenderedSVG');
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
* Render a mime model.
|
|
* Render a mime model.
|
|
- */
|
|
|
|
- renderModel(model: IRenderMime.IMimeModel): Promise<void> {
|
|
|
|
- let source = Private.getSource(model, this.mimeType);
|
|
|
|
- Private.setHtml(this.node, source);
|
|
|
|
- let svgElement = this.node.getElementsByTagName('svg')[0];
|
|
|
|
- if (!svgElement) {
|
|
|
|
- let msg = 'SVGRender: Error: Failed to create <svg> element';
|
|
|
|
- return Promise.reject(new Error(msg));
|
|
|
|
- }
|
|
|
|
- if (this.resolver) {
|
|
|
|
- return Private.handleUrls(
|
|
|
|
- this.node, this.resolver, this.linkHandler
|
|
|
|
- );
|
|
|
|
- }
|
|
|
|
- return Promise.resolve(void 0);
|
|
|
|
|
|
+ *
|
|
|
|
+ * @param model - The mime model to render.
|
|
|
|
+ *
|
|
|
|
+ * @returns A promise which resolves when rendering is complete.
|
|
|
|
+ */
|
|
|
|
+ render(model: IRenderMime.IMimeModel): Promise<void> {
|
|
|
|
+ return RenderHelpers.renderSVG({
|
|
|
|
+ model,
|
|
|
|
+ node: this.node,
|
|
|
|
+ mimeType: this.mimeType,
|
|
|
|
+ resolver: this.resolver,
|
|
|
|
+ linkHandler: this.linkHandler,
|
|
|
|
+ shouldTypeset: this.isAttached
|
|
|
|
+ });
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -433,215 +467,48 @@ export
|
|
class RenderedPDF extends RenderedCommon {
|
|
class RenderedPDF extends RenderedCommon {
|
|
/**
|
|
/**
|
|
* Construct a new rendered PDF widget.
|
|
* Construct a new rendered PDF widget.
|
|
|
|
+ *
|
|
|
|
+ * @param options - The options for initializing the widget.
|
|
*/
|
|
*/
|
|
constructor(options: IRenderMime.IRendererOptions) {
|
|
constructor(options: IRenderMime.IRendererOptions) {
|
|
super(options);
|
|
super(options);
|
|
- let a = document.createElement('a');
|
|
|
|
- a.target = '_blank';
|
|
|
|
- a.textContent = 'View PDF';
|
|
|
|
- this.node.appendChild(a);
|
|
|
|
- this.addClass(PDF_CLASS);
|
|
|
|
|
|
+ this.addClass('jp-RenderedPDF');
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
* Render a mime model.
|
|
* Render a mime model.
|
|
*/
|
|
*/
|
|
- renderModel(model: IRenderMime.IMimeModel): Promise<void> {
|
|
|
|
- let source = Private.getSource(model, this.mimeType);
|
|
|
|
- let a = this.node.firstChild as HTMLAnchorElement;
|
|
|
|
- a.href = `data:application/pdf;base64,${source}`;
|
|
|
|
- return Promise.resolve(void 0);
|
|
|
|
|
|
+ render(model: IRenderMime.IMimeModel): Promise<void> {
|
|
|
|
+ return RenderHelpers.renderPDF({
|
|
|
|
+ model,
|
|
|
|
+ node: this.node,
|
|
|
|
+ mimeType: this.mimeType
|
|
|
|
+ });
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
- * The namespace for module private data.
|
|
|
|
|
|
+ * The namespace for module implementation details.
|
|
*/
|
|
*/
|
|
namespace Private {
|
|
namespace Private {
|
|
/**
|
|
/**
|
|
- * Extract the source text from render options.
|
|
|
|
|
|
+ * Test whether two mime models are equivalent.
|
|
*/
|
|
*/
|
|
export
|
|
export
|
|
- function getSource(model: IRenderMime.IMimeModel, mimeType: string): string {
|
|
|
|
- return String(model.data[mimeType]);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Set trusted html to a node.
|
|
|
|
- */
|
|
|
|
- export
|
|
|
|
- function setHtml(node: HTMLElement, html: string): void {
|
|
|
|
- // Remove any existing child nodes.
|
|
|
|
- while (node.firstChild) {
|
|
|
|
- node.removeChild(node.firstChild);
|
|
|
|
- }
|
|
|
|
- try {
|
|
|
|
- let range = document.createRange();
|
|
|
|
- node.appendChild(range.createContextualFragment(html));
|
|
|
|
- } catch (error) {
|
|
|
|
- console.warn('Environment does not support Range ' +
|
|
|
|
- 'createContextualFragment, falling back on innerHTML');
|
|
|
|
- node.innerHTML = html;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Resolve the relative urls in element `src` and `href` attributes.
|
|
|
|
- *
|
|
|
|
- * @param node - The head html element.
|
|
|
|
- *
|
|
|
|
- * @param resolver - A url resolver.
|
|
|
|
- *
|
|
|
|
- * @param linkHandler - An optional link handler for nodes.
|
|
|
|
- *
|
|
|
|
- * @returns a promise fulfilled when the relative urls have been resolved.
|
|
|
|
- */
|
|
|
|
- export
|
|
|
|
- function handleUrls(node: HTMLElement, resolver: IRenderMime.IResolver, linkHandler?: IRenderMime.ILinkHandler): Promise<void> {
|
|
|
|
- let promises: Promise<void>[] = [];
|
|
|
|
- // Handle HTML Elements with src attributes.
|
|
|
|
- let nodes = node.querySelectorAll('*[src]');
|
|
|
|
- for (let i = 0; i < nodes.length; i++) {
|
|
|
|
- promises.push(handleAttr(nodes[i] as HTMLElement, 'src', resolver));
|
|
|
|
- }
|
|
|
|
- let anchors = node.getElementsByTagName('a');
|
|
|
|
- for (let i = 0; i < anchors.length; i++) {
|
|
|
|
- promises.push(handleAnchor(anchors[i], resolver, linkHandler || null));
|
|
|
|
- }
|
|
|
|
- let links = node.getElementsByTagName('link');
|
|
|
|
- for (let i = 0; i < links.length; i++) {
|
|
|
|
- promises.push(handleAttr(links[i], 'href', resolver));
|
|
|
|
- }
|
|
|
|
- return Promise.all(promises).then(() => { return void 0; });
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Handle a node with a `src` or `href` attribute.
|
|
|
|
- */
|
|
|
|
- function handleAttr(node: HTMLElement, name: 'src' | 'href', resolver: IRenderMime.IResolver): Promise<void> {
|
|
|
|
- let source = node.getAttribute(name);
|
|
|
|
- if (!source) {
|
|
|
|
- return Promise.resolve(void 0);
|
|
|
|
- }
|
|
|
|
- node.setAttribute(name, '');
|
|
|
|
- return resolver.resolveUrl(source).then(path => {
|
|
|
|
- return resolver.getDownloadUrl(path);
|
|
|
|
- }).then(url => {
|
|
|
|
- node.setAttribute(name, url);
|
|
|
|
- });
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Apply ids to headers.
|
|
|
|
- */
|
|
|
|
- export
|
|
|
|
- function headerAnchors(node: HTMLElement): void {
|
|
|
|
- let headerNames = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
|
|
|
|
- for (let headerType of headerNames){
|
|
|
|
- let headers = node.getElementsByTagName(headerType);
|
|
|
|
- for (let i=0; i < headers.length; i++) {
|
|
|
|
- let header = headers[i];
|
|
|
|
- header.id = header.innerHTML.replace(/ /g, '-');
|
|
|
|
- let anchor = document.createElement('a');
|
|
|
|
- anchor.target = '_self';
|
|
|
|
- anchor.textContent = '¶';
|
|
|
|
- anchor.href = '#' + header.id;
|
|
|
|
- anchor.classList.add('jp-InternalAnchorLink');
|
|
|
|
- header.appendChild(anchor);
|
|
|
|
- }
|
|
|
|
|
|
+ function equalModels(a: IRenderMime.IMimeModel, b: IRenderMime.IMimeModel): boolean {
|
|
|
|
+ if (a === b) {
|
|
|
|
+ return true;
|
|
}
|
|
}
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Handle an anchor node.
|
|
|
|
- */
|
|
|
|
- function handleAnchor(anchor: HTMLAnchorElement, resolver: IRenderMime.IResolver, linkHandler: IRenderMime.ILinkHandler | null): Promise<void> {
|
|
|
|
- anchor.target = '_blank';
|
|
|
|
- // Get the link path without the location prepended.
|
|
|
|
- // (e.g. "./foo.md#Header 1" vs "http://localhost:8888/foo.md#Header 1")
|
|
|
|
- let href = anchor.getAttribute('href');
|
|
|
|
- // Bail if it is not a file-like url.
|
|
|
|
- if (!href || href.indexOf('://') !== -1 && href.indexOf('//') === 0) {
|
|
|
|
- return Promise.resolve(void 0);
|
|
|
|
|
|
+ if (a.trusted !== b.trusted) {
|
|
|
|
+ return false;
|
|
}
|
|
}
|
|
- // Remove the hash until we can handle it.
|
|
|
|
- let hash = anchor.hash;
|
|
|
|
- if (hash) {
|
|
|
|
- // Handle internal link in the file.
|
|
|
|
- if (hash === href) {
|
|
|
|
- anchor.target = '_self';
|
|
|
|
- return Promise.resolve(void 0);
|
|
|
|
- }
|
|
|
|
- // For external links, remove the hash until we have hash handling.
|
|
|
|
- href = href.replace(hash, '');
|
|
|
|
|
|
+ if (a.data !== b.data) {
|
|
|
|
+ return false;
|
|
}
|
|
}
|
|
- // Get the appropriate file path.
|
|
|
|
- return resolver.resolveUrl(href).then(path => {
|
|
|
|
- // Handle the click override.
|
|
|
|
- if (linkHandler && URLExt.isLocal(path)) {
|
|
|
|
- linkHandler.handleLink(anchor, path);
|
|
|
|
- }
|
|
|
|
- // Get the appropriate file download path.
|
|
|
|
- return resolver.getDownloadUrl(path);
|
|
|
|
- }).then(url => {
|
|
|
|
- // Set the visible anchor.
|
|
|
|
- anchor.href = url + hash;
|
|
|
|
- });
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/**
|
|
|
|
- * A namespace for private module data.
|
|
|
|
- */
|
|
|
|
-namespace Private {
|
|
|
|
- let initialized = false;
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Support GitHub flavored Markdown, leave sanitizing to external library.
|
|
|
|
- */
|
|
|
|
- export
|
|
|
|
- function initializeMarked(): void {
|
|
|
|
- if (initialized) {
|
|
|
|
- return;
|
|
|
|
|
|
+ if (a.metadata !== b.metadata) {
|
|
|
|
+ return false;
|
|
}
|
|
}
|
|
- initialized = true;
|
|
|
|
- marked.setOptions({
|
|
|
|
- gfm: true,
|
|
|
|
- sanitize: false,
|
|
|
|
- tables: true,
|
|
|
|
- // breaks: true; We can't use GFM breaks as it causes problems with tables
|
|
|
|
- langPrefix: `cm-s-${CodeMirrorEditor.defaultConfig.theme} language-`,
|
|
|
|
- highlight: (code, lang, callback) => {
|
|
|
|
- if (!lang) {
|
|
|
|
- // no language, no highlight
|
|
|
|
- if (callback) {
|
|
|
|
- callback(null, code);
|
|
|
|
- return;
|
|
|
|
- } else {
|
|
|
|
- return code;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- Mode.ensure(lang).then(spec => {
|
|
|
|
- let el = document.createElement('div');
|
|
|
|
- if (!spec) {
|
|
|
|
- console.log(`No CodeMirror mode: ${lang}`);
|
|
|
|
- callback(null, code);
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
- try {
|
|
|
|
- Mode.run(code, spec.mime, el);
|
|
|
|
- callback(null, el.innerHTML);
|
|
|
|
- } catch (err) {
|
|
|
|
- console.log(`Failed to highlight ${lang} code`, err);
|
|
|
|
- callback(err, code);
|
|
|
|
- }
|
|
|
|
- }).catch(err => {
|
|
|
|
- console.log(`No CodeMirror mode: ${lang}`);
|
|
|
|
- console.log(`Require CodeMirror mode error: ${err}`);
|
|
|
|
- callback(null, code);
|
|
|
|
- });
|
|
|
|
- }
|
|
|
|
- });
|
|
|
|
|
|
+ return true;
|
|
}
|
|
}
|
|
}
|
|
}
|