Move notebooks to workbench (#4888)

* move notebooks under workbench

* fix style imports
This commit is contained in:
Anthony Dresser
2019-04-09 01:59:55 -07:00
committed by GitHub
parent 4ece9b0085
commit 2fb06e7f4f
87 changed files with 109 additions and 114 deletions

View File

@@ -0,0 +1,99 @@
/*-----------------------------------------------------------------------------
| Copyright (c) Jupyter Development Team.
| Distributed under the terms of the Modified BSD License.
|----------------------------------------------------------------------------*/
import { IRenderMime } from './renderMimeInterfaces';
import { ReadonlyJSONObject } from '../../models/jsonext';
import { IThemeService } from 'vs/platform/theme/common/themeService';
/**
* The default mime model implementation.
*/
export class MimeModel implements IRenderMime.IMimeModel {
/**
* Construct a new mime model.
*/
constructor(options: MimeModel.IOptions = {}) {
this.trusted = !!options.trusted;
this._data = options.data || {};
this._metadata = options.metadata || {};
this._callback = options.callback;
this._themeService = options.themeService;
}
/**
* Whether the model is trusted.
*/
readonly trusted: boolean;
/**
* The data associated with the model.
*/
get data(): ReadonlyJSONObject {
return this._data;
}
/**
* The metadata associated with the model.
*/
get metadata(): ReadonlyJSONObject {
return this._metadata;
}
get themeService(): IThemeService {
return this._themeService;
}
/**
* Set the data associated with the model.
*
* #### Notes
* Depending on the implementation of the mime model,
* this call may or may not have deferred effects,
*/
setData(options: IRenderMime.ISetDataOptions): void {
this._data = options.data || this._data;
this._metadata = options.metadata || this._metadata;
this._callback(options);
}
private _callback: (options: IRenderMime.ISetDataOptions) => void;
private _data: ReadonlyJSONObject;
private _metadata: ReadonlyJSONObject;
private _themeService: IThemeService;
}
/**
* The namespace for MimeModel class statics.
*/
export namespace MimeModel {
/**
* The options used to create a mime model.
*/
export interface IOptions {
/**
* Whether the model is trusted. Defaults to `false`.
*/
trusted?: boolean;
/**
* A callback function for when the data changes.
*/
callback?: (options: IRenderMime.ISetDataOptions) => void;
/**
* The initial mime data.
*/
data?: ReadonlyJSONObject;
/**
* The initial mime metadata.
*/
metadata?: ReadonlyJSONObject;
/**
* Theme service used to react to theme change events
*/
themeService?: IThemeService;
}
}

View File

@@ -0,0 +1,111 @@
/*-----------------------------------------------------------------------------
| Copyright (c) Jupyter Development Team.
| Distributed under the terms of the Modified BSD License.
|----------------------------------------------------------------------------*/
import { JSONObject, isPrimitive } from '../../models/jsonext';
import { MimeModel } from './mimemodel';
import { nbformat } from '../../models/nbformat';
import { nb } from 'azdata';
/**
* A multiline string.
*/
export type MultilineString = string | string[];
/**
* A mime-type keyed dictionary of data.
*/
export interface IMimeBundle extends JSONObject {
[key: string]: MultilineString | JSONObject;
}
/**
* Get the data from a notebook output.
*/
export function getData(output: nb.ICellOutput): JSONObject {
let bundle: IMimeBundle = {};
if (
nbformat.isExecuteResult(output) ||
nbformat.isDisplayData(output) ||
nbformat.isDisplayUpdate(output)
) {
bundle = (output as nbformat.IExecuteResult).data;
} else if (nbformat.isStream(output)) {
if (output.name === 'stderr') {
bundle['application/vnd.jupyter.stderr'] = output.text;
} else {
bundle['application/vnd.jupyter.stdout'] = output.text;
}
} else if (nbformat.isError(output)) {
let traceback = output.traceback ? output.traceback.join('\n') : undefined;
bundle['application/vnd.jupyter.stderr'] = undefined;
if (traceback && traceback !== '') {
bundle['application/vnd.jupyter.stderr'] = traceback;
} else if (output.evalue) {
bundle['application/vnd.jupyter.stderr'] = output.ename && output.ename !== '' ? `${output.ename}: ${output.evalue}` : `${output.evalue}`;
}
}
return convertBundle(bundle);
}
/**
* Get the metadata from an output message.
*/
export function getMetadata(output: nbformat.IOutput): JSONObject {
let value: JSONObject = Object.create(null);
if (nbformat.isExecuteResult(output) || nbformat.isDisplayData(output)) {
for (let key in output.metadata) {
value[key] = extract(output.metadata, key);
}
}
return value;
}
/**
* Get the bundle options given output model options.
*/
export function getBundleOptions(options: IOutputModelOptions): MimeModel.IOptions {
let data = getData(options.value);
let metadata = getMetadata(options.value);
let trusted = !!options.trusted;
return { data, metadata, trusted };
}
/**
* Extract a value from a JSONObject.
*/
export function extract(value: JSONObject, key: string): {} {
let item = value[key];
if (isPrimitive(item)) {
return item;
}
return JSON.parse(JSON.stringify(item));
}
/**
* Convert a mime bundle to mime data.
*/
function convertBundle(bundle: nbformat.IMimeBundle): JSONObject {
let map: JSONObject = Object.create(null);
for (let mimeType in bundle) {
map[mimeType] = extract(bundle, mimeType);
}
return map;
}
/**
* The options used to create a notebook output model.
*/
export interface IOutputModelOptions {
/**
* The raw output value.
*/
value: nbformat.IOutput;
/**
* Whether the output is trusted. The default is false.
*/
trusted?: boolean;
}

View File

@@ -0,0 +1,365 @@
/*-----------------------------------------------------------------------------
| Copyright (c) Jupyter Development Team.
| Distributed under the terms of the Modified BSD License.
|----------------------------------------------------------------------------*/
import { ReadonlyJSONObject } from '../../models/jsonext';
import { IThemeService } from 'vs/platform/theme/common/themeService';
/**
* A namespace for rendermime associated interfaces.
*/
export namespace IRenderMime {
/**
* A model for mime data.
*/
export interface IMimeModel {
/**
* Whether the data in the model is trusted.
*/
readonly trusted: boolean;
/**
* The data associated with the model.
*/
readonly data: ReadonlyJSONObject;
/**
* The metadata associated with the model.
*/
readonly metadata: ReadonlyJSONObject;
/**
* Set the data associated with the model.
*
* #### Notes
* Calling this function may trigger an asynchronous operation
* that could cause the renderer to be rendered with a new model
* containing the new data.
*/
setData(options: ISetDataOptions): void;
/**
* Theme service used to react to theme change events
*/
readonly themeService: IThemeService;
}
/**
* The options used to update a mime model.
*/
export interface ISetDataOptions {
/**
* The new data object.
*/
data?: ReadonlyJSONObject;
/**
* The new metadata object.
*/
metadata?: ReadonlyJSONObject;
}
/**
* The options used to initialize a document widget factory.
*
* This interface is intended to be used by mime renderer extensions
* to define a document opener that uses its renderer factory.
*/
export interface IDocumentWidgetFactoryOptions {
/**
* The name of the widget to display in dialogs.
*/
readonly name: string;
/**
* The name of the document model type.
*/
readonly modelName?: string;
/**
* The primary file type of the widget.
*/
readonly primaryFileType: string;
/**
* The file types the widget can view.
*/
readonly fileTypes: ReadonlyArray<string>;
/**
* The file types for which the factory should be the default.
*/
readonly defaultFor?: ReadonlyArray<string>;
/**
* The file types for which the factory should be the default for rendering,
* if that is different than the default factory (which may be for editing)
* If undefined, then it will fall back on the default file type.
*/
readonly defaultRendered?: ReadonlyArray<string>;
}
/**
* A file type to associate with the renderer.
*/
export interface IFileType {
/**
* The name of the file type.
*/
readonly name: string;
/**
* The mime types associated the file type.
*/
readonly mimeTypes: ReadonlyArray<string>;
/**
* The extensions of the file type (e.g. `".txt"`). Can be a compound
* extension (e.g. `".table.json`).
*/
readonly extensions: ReadonlyArray<string>;
/**
* An optional display name for the file type.
*/
readonly displayName?: string;
/**
* An optional pattern for a file name (e.g. `^Dockerfile$`).
*/
readonly pattern?: string;
/**
* The icon class name for the file type.
*/
readonly iconClass?: string;
/**
* The icon label for the file type.
*/
readonly iconLabel?: string;
/**
* The file format for the file type ('text', 'base64', or 'json').
*/
readonly fileFormat?: string;
}
/**
* An interface for using a RenderMime.IRenderer for output and read-only documents.
*/
export interface IExtension {
/**
* The ID of the extension.
*
* #### Notes
* The convention for extension IDs in JupyterLab is the full NPM package
* name followed by a colon and a unique string token, e.g.
* `'@jupyterlab/apputils-extension:settings'` or `'foo-extension:bar'`.
*/
readonly id: string;
/**
* A renderer factory to be registered to render the MIME type.
*/
readonly rendererFactory: IRendererFactory;
/**
* The rank passed to `RenderMime.addFactory`. If not given,
* defaults to the `defaultRank` of the factory.
*/
readonly rank?: number;
/**
* The timeout after user activity to re-render the data.
*/
readonly renderTimeout?: number;
/**
* Preferred data type from the model. Defaults to `string`.
*/
readonly dataType?: 'string' | 'json';
/**
* The options used to open a document with the renderer factory.
*/
readonly documentWidgetFactoryOptions?:
| IDocumentWidgetFactoryOptions
| ReadonlyArray<IDocumentWidgetFactoryOptions>;
/**
* The optional file type associated with the extension.
*/
readonly fileTypes?: ReadonlyArray<IFileType>;
}
/**
* The interface for a module that exports an extension or extensions as
* the default value.
*/
export interface IExtensionModule {
/**
* The default export.
*/
readonly default: IExtension | ReadonlyArray<IExtension>;
}
/**
* A widget which displays the contents of a mime model.
*/
export interface IRenderer {
/**
* Render a mime model.
*
* @param model - The mime model to render.
*
* @returns A promise which resolves when rendering is complete.
*
* #### Notes
* This method may be called multiple times during the lifetime
* of the widget to update it if and when new data is available.
*/
renderModel(model: IRenderMime.IMimeModel): Promise<void>;
/**
* Node to be updated by the renderer
*/
node: HTMLElement;
}
/**
* The interface for a renderer factory.
*/
export interface IRendererFactory {
/**
* Whether the factory is a "safe" factory.
*
* #### Notes
* A "safe" factory produces renderer widgets which can render
* untrusted model data in a usable way. *All* renderers must
* handle untrusted data safely, but some may simply failover
* with a "Run cell to view output" message. A "safe" renderer
* is an indication that its sanitized output will be useful.
*/
readonly safe: boolean;
/**
* The mime types handled by this factory.
*/
readonly mimeTypes: ReadonlyArray<string>;
/**
* The default rank of the factory. If not given, defaults to 100.
*/
readonly defaultRank?: number;
/**
* Create a renderer which displays the mime data.
*
* @param options - The options used to render the data.
*/
createRenderer(options: IRendererOptions): IRenderer;
}
/**
* The options used to create a renderer.
*/
export interface IRendererOptions {
/**
* The preferred mimeType to render.
*/
mimeType: string;
/**
* The html sanitizer.
*/
sanitizer: ISanitizer;
/**
* An optional url resolver.
*/
resolver: IResolver | null;
/**
* An optional link handler.
*/
linkHandler: ILinkHandler | null;
/**
* The LaTeX typesetter.
*/
latexTypesetter: ILatexTypesetter | null;
}
/**
* An object that handles html sanitization.
*/
export interface ISanitizer {
/**
* Sanitize an HTML string.
*/
sanitize(dirty: string): string;
}
/**
* An object that handles links on a node.
*/
export interface ILinkHandler {
/**
* Add the link handler to the node.
*
* @param node: the node for which to handle the link.
*
* @param path: the path to open when the link is clicked.
*
* @param id: an optional element id to scroll to when the path is opened.
*/
handleLink(node: HTMLElement, path: string, id?: string): void;
}
/**
* An object that resolves relative URLs.
*/
export interface IResolver {
/**
* Resolve a relative url to a correct server path.
*/
resolveUrl(url: string): Promise<string>;
/**
* Get the download url of a given absolute server path.
*/
getDownloadUrl(path: string): Promise<string>;
/**
* Whether the URL should be handled by the resolver
* or not.
*
* #### Notes
* This is similar to the `isLocal` check in `URLExt`,
* but can also perform additional checks on whether the
* resolver should handle a given URL.
*/
isLocal?: (url: string) => boolean;
}
/**
* The interface for a LaTeX typesetter.
*/
export interface ILatexTypesetter {
/**
* Typeset a DOM element.
*
* @param element - the DOM element to typeset. The typesetting may
* happen synchronously or asynchronously.
*
* #### Notes
* The application-wide rendermime object has a settable
* `latexTypesetter` property which is used wherever LaTeX
* typesetting is required. Extensions wishing to provide their
* own typesetter may replace that on the global `lab.rendermime`.
*/
typeset(element: HTMLElement): void;
}
}

View File

@@ -0,0 +1,181 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
import { JSONObject } from '../../models/jsonext';
import { URI } from 'vs/base/common/uri';
/**
* The namespace for URL-related functions.
*/
export namespace URLExt {
/**
* Normalize a url.
*/
export function normalize(url: string): string {
return URI.parse(url).toString();
}
/**
* Join a sequence of url components and normalizes as in node `path.join`.
*
* @param parts - The url components.
*
* @returns the joined url.
*/
export function join(...parts: string[]): string {
parts = parts || [];
// Isolate the top element.
const top = parts[0] || '';
// Check whether protocol shorthand is being used.
const shorthand = top.indexOf('//') === 0;
// Parse the top element into a header collection.
const header = top.match(/(\w+)(:)(\/\/)?/);
const protocol = header && header[1];
const colon = protocol && header[2];
const slashes = colon && header[3];
// Construct the URL prefix.
const prefix = shorthand
? '//'
: [protocol, colon, slashes].filter(str => str).join('');
// Construct the URL body omitting the prefix of the top value.
const body = [top.indexOf(prefix) === 0 ? top.replace(prefix, '') : top]
// Filter out top value if empty.
.filter(str => str)
// Remove leading slashes in all subsequent URL body elements.
.concat(parts.slice(1).map(str => str.replace(/^\//, '')))
.join('/')
// Replace multiple slashes with one.
.replace(/\/+/g, '/');
return prefix + body;
}
/**
* Encode the components of a multi-segment url.
*
* @param url - The url to encode.
*
* @returns the encoded url.
*
* #### Notes
* Preserves the `'/'` separators.
* Should not include the base url, since all parts are escaped.
*/
export function encodeParts(url: string): string {
return join(...url.split('/').map(encodeURIComponent));
}
/**
* Return a serialized object string suitable for a query.
*
* @param object - The source object.
*
* @returns an encoded url query.
*
* #### Notes
* Modified version of [stackoverflow](http://stackoverflow.com/a/30707423).
*/
export function objectToQueryString(value: JSONObject): string {
const keys = Object.keys(value);
if (!keys.length) {
return '';
}
return (
'?' +
keys
.map(key => {
const content = encodeURIComponent(String(value[key]));
return key + (content ? '=' + content : '');
})
.join('&')
);
}
/**
* Return a parsed object that represents the values in a query string.
*/
export function queryStringToObject(
value: string
): { [key: string]: string } {
return value
.replace(/^\?/, '')
.split('&')
.reduce(
(acc, val) => {
const [key, value] = val.split('=');
acc[key] = decodeURIComponent(value || '');
return acc;
},
{} as { [key: string]: string }
);
}
/**
* Test whether the url is a local url.
*
* #### Notes
* This function returns `false` for any fully qualified url, including
* `data:`, `file:`, and `//` protocol URLs.
*/
export function isLocal(url: string): boolean {
// If if doesn't have a scheme such as file: or http:// it's local
return !!URI.parse(url).scheme;
}
/**
* The interface for a URL object
*/
export interface IUrl {
/**
* The full URL string that was parsed with both the protocol and host
* components converted to lower-case.
*/
href?: string;
/**
* Identifies the URL's lower-cased protocol scheme.
*/
protocol?: string;
/**
* The full lower-cased host portion of the URL, including the port if
* specified.
*/
host?: string;
/**
* The lower-cased host name portion of the host component without the
* port included.
*/
hostname?: string;
/**
* The numeric port portion of the host component.
*/
port?: string;
/**
* The entire path section of the URL.
*/
pathname?: string;
/**
* The "fragment" portion of the URL including the leading ASCII hash
* `(#)` character
*/
hash?: string;
/**
* The search element, including leading question mark (`'?'`), if any,
* of the URL.
*/
search?: string;
}
}

View File

@@ -0,0 +1,105 @@
/*-----------------------------------------------------------------------------
| Copyright (c) Jupyter Development Team.
| Distributed under the terms of the Modified BSD License.
|----------------------------------------------------------------------------*/
import * as widgets from './widgets';
import { IRenderMime } from './common/renderMimeInterfaces';
/**
* A mime renderer factory for raw html.
*/
export const htmlRendererFactory: IRenderMime.IRendererFactory = {
safe: true,
mimeTypes: ['text/html'],
defaultRank: 50,
createRenderer: options => new widgets.RenderedHTML(options)
};
/**
* A mime renderer factory for images.
*/
export const imageRendererFactory: IRenderMime.IRendererFactory = {
safe: true,
mimeTypes: ['image/bmp', 'image/png', 'image/jpeg', 'image/gif'],
defaultRank: 90,
createRenderer: options => new widgets.RenderedImage(options)
};
// /**
// * A mime renderer factory for LaTeX.
// */
// export const latexRendererFactory: IRenderMime.IRendererFactory = {
// safe: true,
// mimeTypes: ['text/latex'],
// defaultRank: 70,
// createRenderer: options => new widgets.RenderedLatex(options)
// };
// /**
// * A mime renderer factory for Markdown.
// */
// export const markdownRendererFactory: IRenderMime.IRendererFactory = {
// safe: true,
// mimeTypes: ['text/markdown'],
// defaultRank: 60,
// createRenderer: options => new widgets.RenderedMarkdown(options)
// };
/**
* A mime renderer factory for svg.
*/
export const svgRendererFactory: IRenderMime.IRendererFactory = {
safe: false,
mimeTypes: ['image/svg+xml'],
defaultRank: 80,
createRenderer: options => new widgets.RenderedSVG(options)
};
/**
* A mime renderer factory for plain and jupyter console text data.
*/
export const textRendererFactory: IRenderMime.IRendererFactory = {
safe: true,
mimeTypes: [
'text/plain',
'application/vnd.jupyter.stdout',
'application/vnd.jupyter.stderr'
],
defaultRank: 120,
createRenderer: options => new widgets.RenderedText(options)
};
/**
* A placeholder factory for deprecated rendered JavaScript.
*/
export const javaScriptRendererFactory: IRenderMime.IRendererFactory = {
safe: false,
mimeTypes: ['text/javascript', 'application/javascript'],
defaultRank: 110,
createRenderer: options => new widgets.RenderedJavaScript(options)
};
export const dataResourceRendererFactory: IRenderMime.IRendererFactory = {
safe: true,
mimeTypes: [
'application/vnd.dataresource+json',
'application/vnd.dataresource'
],
defaultRank: 40,
createRenderer: options => new widgets.RenderedDataResource(options)
};
/**
* The standard factories provided by the rendermime package.
*/
export const standardRendererFactories: ReadonlyArray<IRenderMime.IRendererFactory> = [
htmlRendererFactory,
// markdownRendererFactory,
// latexRendererFactory,
svgRendererFactory,
imageRendererFactory,
javaScriptRendererFactory,
textRendererFactory,
dataResourceRendererFactory
];

View File

@@ -0,0 +1,352 @@
/*-----------------------------------------------------------------------------
| Copyright (c) Jupyter Development Team.
| Distributed under the terms of the Modified BSD License.
|----------------------------------------------------------------------------*/
import { IRenderMime } from './common/renderMimeInterfaces';
import { MimeModel } from './common/mimemodel';
import { ReadonlyJSONObject } from '../models/jsonext';
import { defaultSanitizer } from './sanitizer';
/**
* An object which manages mime renderer factories.
*
* This object is used to render mime models using registered mime
* renderers, selecting the preferred mime renderer to render the
* model into a widget.
*
* #### Notes
* This class is not intended to be subclassed.
*/
export class RenderMimeRegistry {
/**
* Construct a new rendermime.
*
* @param options - The options for initializing the instance.
*/
constructor(options: RenderMimeRegistry.IOptions = {}) {
// Parse the options.
this.resolver = options.resolver || null;
this.linkHandler = options.linkHandler || null;
this.latexTypesetter = options.latexTypesetter || null;
this.sanitizer = options.sanitizer || defaultSanitizer;
// Add the initial factories.
if (options.initialFactories) {
for (let factory of options.initialFactories) {
this.addFactory(factory);
}
}
}
/**
* The sanitizer used by the rendermime instance.
*/
readonly sanitizer: IRenderMime.ISanitizer;
/**
* The object used to resolve relative urls for the rendermime instance.
*/
readonly resolver: IRenderMime.IResolver | null;
/**
* The object used to handle path opening links.
*/
readonly linkHandler: IRenderMime.ILinkHandler | null;
/**
* The LaTeX typesetter for the rendermime.
*/
readonly latexTypesetter: IRenderMime.ILatexTypesetter | null;
/**
* The ordered list of mimeTypes.
*/
get mimeTypes(): ReadonlyArray<string> {
return this._types || (this._types = Private.sortedTypes(this._ranks));
}
/**
* Find the preferred mime type for a mime bundle.
*
* @param bundle - The bundle of mime data.
*
* @param safe - How to consider safe/unsafe factories. If 'ensure',
* it will only consider safe factories. If 'any', any factory will be
* considered. If 'prefer', unsafe factories will be considered, but
* only after the safe options have been exhausted.
*
* @returns The preferred mime type from the available factories,
* or `undefined` if the mime type cannot be rendered.
*/
preferredMimeType(
bundle: ReadonlyJSONObject,
safe: 'ensure' | 'prefer' | 'any' = 'ensure'
): string | undefined {
// Try to find a safe factory first, if preferred.
if (safe === 'ensure' || safe === 'prefer') {
for (let mt of this.mimeTypes) {
if (mt in bundle && this._factories[mt].safe) {
return mt;
}
}
}
if (safe !== 'ensure') {
// Otherwise, search for the best factory among all factories.
for (let mt of this.mimeTypes) {
if (mt in bundle) {
return mt;
}
}
}
// Otherwise, no matching mime type exists.
return undefined;
}
/**
* Create a renderer for a mime type.
*
* @param mimeType - The mime type of interest.
*
* @returns A new renderer for the given mime type.
*
* @throws An error if no factory exists for the mime type.
*/
createRenderer(mimeType: string): IRenderMime.IRenderer {
// Throw an error if no factory exists for the mime type.
if (!(mimeType in this._factories)) {
throw new Error(`No factory for mime type: '${mimeType}'`);
}
// Invoke the best factory for the given mime type.
return this._factories[mimeType].createRenderer({
mimeType,
resolver: this.resolver,
sanitizer: this.sanitizer,
linkHandler: this.linkHandler,
latexTypesetter: this.latexTypesetter
});
}
/**
* Create a new mime model. This is a convenience method.
*
* @options - The options used to create the model.
*
* @returns A new mime model.
*/
createModel(options: MimeModel.IOptions = {}): MimeModel {
return new MimeModel(options);
}
/**
* Create a clone of this rendermime instance.
*
* @param options - The options for configuring the clone.
*
* @returns A new independent clone of the rendermime.
*/
clone(options: RenderMimeRegistry.ICloneOptions = {}): RenderMimeRegistry {
// Create the clone.
let clone = new RenderMimeRegistry({
resolver: options.resolver || this.resolver || undefined,
sanitizer: options.sanitizer || this.sanitizer || undefined,
linkHandler: options.linkHandler || this.linkHandler || undefined,
latexTypesetter: options.latexTypesetter || this.latexTypesetter
});
// Clone the internal state.
clone._factories = { ...this._factories };
clone._ranks = { ...this._ranks };
clone._id = this._id;
// Return the cloned object.
return clone;
}
/**
* Get the renderer factory registered for a mime type.
*
* @param mimeType - The mime type of interest.
*
* @returns The factory for the mime type, or `undefined`.
*/
getFactory(mimeType: string): IRenderMime.IRendererFactory | undefined {
return this._factories[mimeType];
}
/**
* Add a renderer factory to the rendermime.
*
* @param factory - The renderer factory of interest.
*
* @param rank - The rank of the renderer. A lower rank indicates
* a higher priority for rendering. If not given, the rank will
* defer to the `defaultRank` of the factory. If no `defaultRank`
* is given, it will default to 100.
*
* #### Notes
* The renderer will replace an existing renderer for the given
* mimeType.
*/
addFactory(factory: IRenderMime.IRendererFactory, rank?: number): void {
if (rank === undefined) {
rank = factory.defaultRank;
if (rank === undefined) {
rank = 100;
}
}
for (let mt of factory.mimeTypes) {
this._factories[mt] = factory;
this._ranks[mt] = { rank, id: this._id++ };
}
this._types = null;
}
/**
* Remove a mime type.
*
* @param mimeType - The mime type of interest.
*/
removeMimeType(mimeType: string): void {
delete this._factories[mimeType];
delete this._ranks[mimeType];
this._types = null;
}
/**
* Get the rank for a given mime type.
*
* @param mimeType - The mime type of interest.
*
* @returns The rank of the mime type or undefined.
*/
getRank(mimeType: string): number | undefined {
let rank = this._ranks[mimeType];
return rank && rank.rank;
}
/**
* Set the rank of a given mime type.
*
* @param mimeType - The mime type of interest.
*
* @param rank - The new rank to assign.
*
* #### Notes
* This is a no-op if the mime type is not registered.
*/
setRank(mimeType: string, rank: number): void {
if (!this._ranks[mimeType]) {
return;
}
let id = this._id++;
this._ranks[mimeType] = { rank, id };
this._types = null;
}
private _id = 0;
private _ranks: Private.RankMap = {};
private _types: string[] | null = null;
private _factories: Private.FactoryMap = {};
}
/**
* The namespace for `RenderMimeRegistry` class statics.
*/
export namespace RenderMimeRegistry {
/**
* The options used to initialize a rendermime instance.
*/
export interface IOptions {
/**
* Initial factories to add to the rendermime instance.
*/
initialFactories?: ReadonlyArray<IRenderMime.IRendererFactory>;
/**
* The sanitizer used to sanitize untrusted html inputs.
*
* If not given, a default sanitizer will be used.
*/
sanitizer?: IRenderMime.ISanitizer;
/**
* The initial resolver object.
*
* The default is `null`.
*/
resolver?: IRenderMime.IResolver;
/**
* An optional path handler.
*/
linkHandler?: IRenderMime.ILinkHandler;
/**
* An optional LaTeX typesetter.
*/
latexTypesetter?: IRenderMime.ILatexTypesetter;
}
/**
* The options used to clone a rendermime instance.
*/
export interface ICloneOptions {
/**
* The new sanitizer used to sanitize untrusted html inputs.
*/
sanitizer?: IRenderMime.ISanitizer;
/**
* The new resolver object.
*/
resolver?: IRenderMime.IResolver;
/**
* The new path handler.
*/
linkHandler?: IRenderMime.ILinkHandler;
/**
* The new LaTeX typesetter.
*/
latexTypesetter?: IRenderMime.ILatexTypesetter;
}
}
/**
* The namespace for the module implementation details.
*/
namespace Private {
/**
* A type alias for a mime rank and tie-breaking id.
*/
export type RankPair = { readonly id: number; readonly rank: number };
/**
* A type alias for a mapping of mime type -> rank pair.
*/
export type RankMap = { [key: string]: RankPair };
/**
* A type alias for a mapping of mime type -> ordered factories.
*/
export type FactoryMap = { [key: string]: IRenderMime.IRendererFactory };
/**
* Get the mime types in the map, ordered by rank.
*/
export function sortedTypes(map: RankMap): string[] {
return Object.keys(map).sort((a, b) => {
let p1 = map[a];
let p2 = map[b];
if (p1.rank !== p2.rank) {
return p1.rank - p2.rank;
}
return p1.id - p2.id;
});
}
}

View File

@@ -0,0 +1,649 @@
/*-----------------------------------------------------------------------------
| Copyright (c) Jupyter Development Team.
| Distributed under the terms of the Modified BSD License.
|----------------------------------------------------------------------------*/
import { default as AnsiUp } from 'ansi_up';
import { IRenderMime } from './common/renderMimeInterfaces';
import { URLExt } from './common/url';
import { URI } from 'vs/base/common/uri';
/**
* Render HTML into a host node.
*
* @params options - The options for rendering.
*
* @returns A promise which resolves when rendering is complete.
*/
export function renderHTML(options: renderHTML.IOptions): Promise<void> {
// Unpack the options.
let {
host,
source,
trusted,
sanitizer,
resolver,
linkHandler,
shouldTypeset,
latexTypesetter
} = options;
let originalSource = source;
// Bail early if the source is empty.
if (!source) {
host.textContent = '';
return Promise.resolve(undefined);
}
// Sanitize the source if it is not trusted. This removes all
// `<script>` tags as well as other potentially harmful HTML.
if (!trusted) {
originalSource = `${source}`;
source = sanitizer.sanitize(source);
}
// Set the inner HTML of the host.
host.innerHTML = source;
if (host.getElementsByTagName('script').length > 0) {
// If output it trusted, eval any script tags contained in the HTML.
// This is not done automatically by the browser when script tags are
// created by setting `innerHTML`.
if (trusted) {
Private.evalInnerHTMLScriptTags(host);
} else {
const container = document.createElement('div');
const warning = document.createElement('pre');
warning.textContent =
'This HTML output contains inline scripts. Are you sure that you want to run arbitrary Javascript within your Notebook session?';
const runButton = document.createElement('button');
runButton.textContent = 'Run';
runButton.onclick = event => {
host.innerHTML = originalSource;
Private.evalInnerHTMLScriptTags(host);
host.removeChild(host.firstChild);
};
container.appendChild(warning);
container.appendChild(runButton);
host.insertBefore(container, host.firstChild);
}
}
// Handle default behavior of nodes.
Private.handleDefaults(host, resolver);
// Patch the urls if a resolver is available.
let promise: Promise<void>;
if (resolver) {
promise = Private.handleUrls(host, resolver, linkHandler);
} else {
promise = Promise.resolve(undefined);
}
// Return the final rendered promise.
return promise.then(() => {
if (shouldTypeset && latexTypesetter) {
latexTypesetter.typeset(host);
}
});
}
/**
* The namespace for the `renderHTML` function statics.
*/
export namespace renderHTML {
/**
* The options for the `renderHTML` function.
*/
export interface IOptions {
/**
* The host node for the rendered HTML.
*/
host: HTMLElement;
/**
* The HTML source to render.
*/
source: string;
/**
* Whether the source is trusted.
*/
trusted: boolean;
/**
* The html sanitizer for untrusted source.
*/
sanitizer: IRenderMime.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;
/**
* The LaTeX typesetter for the application.
*/
latexTypesetter: IRenderMime.ILatexTypesetter | null;
}
}
/**
* Render an image into a host node.
*
* @params options - The options for rendering.
*
* @returns A promise which resolves when rendering is complete.
*/
export function renderImage(
options: renderImage.IRenderOptions
): Promise<void> {
// Unpack the options.
let {
host,
mimeType,
source,
width,
height,
needsBackground,
unconfined
} = options;
// Clear the content in the host.
host.textContent = '';
// Create the image element.
let img = document.createElement('img');
// Set the source of the image.
img.src = `data:${mimeType};base64,${source}`;
// Set the size of the image if provided.
if (typeof height === 'number') {
img.height = height;
}
if (typeof width === 'number') {
img.width = width;
}
if (needsBackground === 'light') {
img.classList.add('jp-needs-light-background');
} else if (needsBackground === 'dark') {
img.classList.add('jp-needs-dark-background');
}
if (unconfined === true) {
img.classList.add('jp-mod-unconfined');
}
// Add the image to the host.
host.appendChild(img);
// Return the rendered promise.
return Promise.resolve(undefined);
}
/**
* The namespace for the `renderImage` function statics.
*/
export namespace renderImage {
/**
* The options for the `renderImage` function.
*/
export interface IRenderOptions {
/**
* The image node to update with the content.
*/
host: HTMLElement;
/**
* The mime type for the image.
*/
mimeType: string;
/**
* The base64 encoded source for the image.
*/
source: string;
/**
* The optional width for the image.
*/
width?: number;
/**
* The optional height for the image.
*/
height?: number;
/**
* Whether an image requires a background for legibility.
*/
needsBackground?: string;
/**
* Whether the image should be unconfined.
*/
unconfined?: boolean;
}
}
/**
* Render LaTeX into a host node.
*
* @params options - The options for rendering.
*
* @returns A promise which resolves when rendering is complete.
*/
export function renderLatex(
options: renderLatex.IRenderOptions
): Promise<void> {
// Unpack the options.
let { host, source, shouldTypeset, latexTypesetter } = options;
// Set the source on the node.
host.textContent = source;
// Typeset the node if needed.
if (shouldTypeset && latexTypesetter) {
latexTypesetter.typeset(host);
}
// Return the rendered promise.
return Promise.resolve(undefined);
}
/**
* The namespace for the `renderLatex` function statics.
*/
export namespace renderLatex {
/**
* The options for the `renderLatex` function.
*/
export interface IRenderOptions {
/**
* The host node for the rendered LaTeX.
*/
host: HTMLElement;
/**
* The LaTeX source to render.
*/
source: string;
/**
* Whether the node should be typeset.
*/
shouldTypeset: boolean;
/**
* The LaTeX typesetter for the application.
*/
latexTypesetter: IRenderMime.ILatexTypesetter | null;
}
}
/**
* The namespace for the `renderDataResource` function statics.
*/
export namespace renderDataResource {
/**
* The options for the `renderDataResource` function.
*/
export interface IRenderOptions {
/**
* The host node for the rendered LaTeX.
*/
host: HTMLElement;
/**
* The DataResource source to render.
*/
source: string;
}
}
/**
* Render SVG into a host node.
*
* @params options - The options for rendering.
*
* @returns A promise which resolves when rendering is complete.
*/
export function renderSVG(options: renderSVG.IRenderOptions): Promise<void> {
// Unpack the options.
let { host, source, trusted, unconfined } = options;
// Clear the content if there is no source.
if (!source) {
host.textContent = '';
return Promise.resolve(undefined);
}
// Display a message if the source is not trusted.
if (!trusted) {
host.textContent =
'Cannot display an untrusted SVG. Maybe you need to run the cell?';
return Promise.resolve(undefined);
}
// Render in img so that user can save it easily
const img = new Image();
img.src = `data:image/svg+xml,${encodeURIComponent(source)}`;
host.appendChild(img);
if (unconfined === true) {
host.classList.add('jp-mod-unconfined');
}
return Promise.resolve();
}
/**
* The namespace for the `renderSVG` function statics.
*/
export namespace renderSVG {
/**
* The options for the `renderSVG` function.
*/
export interface IRenderOptions {
/**
* The host node for the rendered SVG.
*/
host: HTMLElement;
/**
* The SVG source.
*/
source: string;
/**
* Whether the source is trusted.
*/
trusted: boolean;
/**
* Whether the svg should be unconfined.
*/
unconfined?: boolean;
}
}
/**
* Render text into a host node.
*
* @params options - The options for rendering.
*
* @returns A promise which resolves when rendering is complete.
*/
export function renderText(options: renderText.IRenderOptions): Promise<void> {
// Unpack the options.
let { host, source } = options;
const ansiUp = new AnsiUp();
ansiUp.escape_for_html = true;
ansiUp.use_classes = true;
// Create the HTML content.
let content = ansiUp.ansi_to_html(source);
// Set the inner HTML for the host node.
host.innerHTML = `<pre>${content}</pre>`;
// Return the rendered promise.
return Promise.resolve(undefined);
}
/**
* The namespace for the `renderText` function statics.
*/
export namespace renderText {
/**
* The options for the `renderText` function.
*/
export interface IRenderOptions {
/**
* The host node for the text content.
*/
host: HTMLElement;
/**
* The source text to render.
*/
source: string;
}
}
/**
* The namespace for module implementation details.
*/
namespace Private {
/**
* Eval the script tags contained in a host populated by `innerHTML`.
*
* When script tags are created via `innerHTML`, the browser does not
* evaluate them when they are added to the page. This function works
* around that by creating new equivalent script nodes manually, and
* replacing the originals.
*/
export function evalInnerHTMLScriptTags(host: HTMLElement): void {
// Create a snapshot of the current script nodes.
let scripts = host.getElementsByTagName('script');
// Loop over each script node.
for (let i = 0; i < scripts.length; i++) {
let script = scripts.item(i);
// Skip any scripts which no longer have a parent.
if (!script.parentNode) {
continue;
}
// Create a new script node which will be clone.
let clone = document.createElement('script');
// Copy the attributes into the clone.
let attrs = script.attributes;
for (let i = 0, n = attrs.length; i < n; ++i) {
let { name, value } = attrs[i];
clone.setAttribute(name, value);
}
// Copy the text content into the clone.
clone.textContent = script.textContent;
// Replace the old script in the parent.
script.parentNode.replaceChild(clone, script);
}
}
/**
* Handle the default behavior of nodes.
*/
export function handleDefaults(
node: HTMLElement,
resolver?: IRenderMime.IResolver
): void {
// Handle anchor elements.
let anchors = node.getElementsByTagName('a');
for (let i = 0; i < anchors.length; i++) {
let path = anchors[i].href || '';
const isLocal =
resolver && resolver.isLocal
? resolver.isLocal(path)
: URLExt.isLocal(path);
if (isLocal) {
anchors[i].target = '_self';
} else {
anchors[i].target = '_blank';
}
}
// Handle image elements.
let imgs = node.getElementsByTagName('img');
for (let i = 0; i < imgs.length; i++) {
if (!imgs[i].alt) {
imgs[i].alt = 'Image';
}
}
}
/**
* 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 | null
): Promise<void> {
// Set up an array to collect promises.
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));
}
// Handle anchor elements.
let anchors = node.getElementsByTagName('a');
for (let i = 0; i < anchors.length; i++) {
promises.push(handleAnchor(anchors[i], resolver, linkHandler));
}
// Handle link elements.
let links = node.getElementsByTagName('link');
for (let i = 0; i < links.length; i++) {
promises.push(handleAttr(links[i], 'href', resolver));
}
// Wait on all promises.
return Promise.all(promises).then(() => undefined);
}
/**
* 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 = encodeURIComponent(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);
}
}
}
/**
* 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) || '';
const isLocal = resolver.isLocal
? resolver.isLocal(source)
: URLExt.isLocal(source);
if (!source || !isLocal) {
return Promise.resolve(undefined);
}
node.setAttribute(name, '');
return resolver
.resolveUrl(source)
.then(path => {
return resolver.getDownloadUrl(path);
})
.then(url => {
// Check protocol again in case it changed:
if (URI.parse(url).scheme !== 'data:') {
// Bust caching for local src attrs.
// https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest#Bypassing_the_cache
url += (/\?/.test(url) ? '&' : '?') + new Date().getTime();
}
node.setAttribute(name, url);
})
.catch(err => {
// If there was an error getting the url,
// just make it an empty link.
node.setAttribute(name, '');
});
}
/**
* Handle an anchor node.
*/
function handleAnchor(
anchor: HTMLAnchorElement,
resolver: IRenderMime.IResolver,
linkHandler: IRenderMime.ILinkHandler | null
): Promise<void> {
// 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') || '';
const isLocal = resolver.isLocal
? resolver.isLocal(href)
: URLExt.isLocal(href);
// Bail if it is not a file-like url.
if (!href || !isLocal) {
return Promise.resolve(undefined);
}
// 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(undefined);
}
// For external links, remove the hash until we have hash handling.
href = href.replace(hash, '');
}
// Get the appropriate file path.
return resolver
.resolveUrl(href)
.then(path => {
// Handle the click override.
if (linkHandler) {
linkHandler.handleLink(anchor, path, hash);
}
// Get the appropriate file download path.
return resolver.getDownloadUrl(path);
})
.then(url => {
// Set the visible anchor.
anchor.href = url + hash;
})
.catch(err => {
// If there was an error getting the url,
// just make it an empty link.
anchor.href = '';
});
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,138 @@
/*-----------------------------------------------------------------------------
| Copyright (c) Jupyter Development Team.
| Distributed under the terms of the Modified BSD License.
|----------------------------------------------------------------------------*/
import { TableDataView } from 'sql/base/browser/ui/table/tableDataView';
import { Table } from 'sql/base/browser/ui/table/table';
import { textFormatter } from 'sql/parts/grid/services/sharedServices';
import { RowNumberColumn } from 'sql/base/browser/ui/table/plugins/rowNumberColumn.plugin';
import { escape } from 'sql/base/common/strings';
import { IDataResource } from 'sql/workbench/services/notebook/sql/sqlSessionManager';
import { attachTableStyler } from 'sql/platform/theme/common/styler';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { MouseWheelSupport } from 'sql/base/browser/ui/table/plugins/mousewheelTableScroll.plugin';
import { AutoColumnSize } from 'sql/base/browser/ui/table/plugins/autoSizeColumns.plugin';
import { AdditionalKeyBindings } from 'sql/base/browser/ui/table/plugins/additionalKeyBindings.plugin';
/**
* Render DataResource as a grid into a host node.
*
* @params options - The options for rendering.
*
* @returns A promise which resolves when rendering is complete.
*/
export function renderDataResource(
options: renderDataResource.IRenderOptions
): Promise<void> {
// Unpack the options.
let { host, source } = options;
let sourceObject: IDataResource = JSON.parse(source);
// Before doing anything, avoid re-rendering the table multiple
// times (as can be the case when going untrusted -> trusted)
while (host.firstChild) {
host.removeChild(host.firstChild);
}
// Now create the table container
let tableContainer = document.createElement('div');
tableContainer.className = 'notebook-cellTable';
const ROW_HEIGHT = 29;
const BOTTOM_PADDING_AND_SCROLLBAR = 14;
let tableResultsData = new TableDataView();
let columns: string[] = sourceObject.schema.fields.map(val => val.name);
// Table object requires passed in columns to be of datatype Slick.Column
let columnsTransformed = transformColumns(columns);
// In order to show row numbers, we need to put the row number column
// ahead of all of the other columns, and register the plugin below
let rowNumberColumn = new RowNumberColumn({ numberOfRows: source.length });
columnsTransformed.unshift(rowNumberColumn.getColumnDefinition());
let transformedData = transformData(sourceObject.data, columnsTransformed);
tableResultsData.push(transformedData);
let detailTable = new Table(tableContainer, {
dataProvider: tableResultsData, columns: columnsTransformed
}, {
rowHeight: ROW_HEIGHT,
forceFitColumns: false,
defaultColumnWidth: 120
});
detailTable.registerPlugin(rowNumberColumn);
detailTable.registerPlugin(new MouseWheelSupport());
detailTable.registerPlugin(new AutoColumnSize({ autoSizeOnRender: true }));
detailTable.registerPlugin(new AdditionalKeyBindings());
let numRows = detailTable.grid.getDataLength();
// Need to include column headers and scrollbar, so that's why 1 needs to be added
let rowsHeight = (numRows + 1) * ROW_HEIGHT + BOTTOM_PADDING_AND_SCROLLBAR;
// if no rows are in the grid, set height to 100% of the container's height
if (numRows === 0) {
tableContainer.style.height = '100%';
} else {
// Set the height dynamically if the grid's height is < 500px high; otherwise, set height to 500px
tableContainer.style.height = rowsHeight >= 500 ? '500px' : rowsHeight.toString() + 'px';
}
attachTableStyler(detailTable, options.themeService);
host.appendChild(tableContainer);
detailTable.resizeCanvas();
// Return the rendered promise.
return Promise.resolve(undefined);
}
// SlickGrid requires columns and data to be in a very specific format; this code was adapted from tableInsight.component.ts
function transformData(rows: any[], columns: Slick.Column<any>[]): { [key: string]: string }[] {
return rows.map(row => {
let dataWithSchema = {};
Object.keys(row).forEach((val, index) => {
let displayValue = String(Object.values(row)[index]);
// Since the columns[0] represents the row number, start at 1
dataWithSchema[columns[index + 1].field] = {
displayValue: displayValue,
ariaLabel: escape(displayValue),
isNull: false
};
});
return dataWithSchema;
});
}
function transformColumns(columns: string[]): Slick.Column<any>[] {
return columns.map((col, index) => {
return <Slick.Column<any>>{
name: col,
id: col,
field: index.toString(),
formatter: textFormatter
};
});
}
/**
* The namespace for the `renderDataResource` function statics.
*/
export namespace renderDataResource {
/**
* The options for the `renderDataResource` function.
*/
export interface IRenderOptions {
/**
* The host node for the rendered LaTeX.
*/
host: HTMLElement;
/**
* The DataResource source to render.
*/
source: string;
/**
* Theme service used to react to theme change events
*/
themeService?: IThemeService;
}
}

View File

@@ -0,0 +1,380 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as renderers from './renderers';
import { IRenderMime } from './common/renderMimeInterfaces';
import { ReadonlyJSONObject } from '../models/jsonext';
import * as tableRenderers from 'sql/workbench/parts/notebook/outputs/tableRenderers';
import { IThemeService } from 'vs/platform/theme/common/themeService';
/**
* A common base class for mime renderers.
*/
export abstract class RenderedCommon implements IRenderMime.IRenderer {
private _node: HTMLElement;
private cachedClasses: string[] = [];
/**
* Construct a new rendered common widget.
*
* @param options - The options for initializing the widget.
*/
constructor(options: IRenderMime.IRendererOptions) {
this.mimeType = options.mimeType;
this.sanitizer = options.sanitizer;
this.resolver = options.resolver;
this.linkHandler = options.linkHandler;
this.latexTypesetter = options.latexTypesetter;
}
public get node(): HTMLElement {
return this._node;
}
public set node(value: HTMLElement) {
this._node = value;
value.dataset['mimeType'] = this.mimeType;
this._node.classList.add(...this.cachedClasses);
this.cachedClasses = [];
}
toggleClass(className: string, enabled: boolean): void {
if (enabled) {
this.addClass(className);
} else {
this.removeClass(className);
}
}
addClass(className: string): void {
if (!this._node) {
this.cachedClasses.push(className);
} else if (!this._node.classList.contains(className)) {
this._node.classList.add(className);
}
}
removeClass(className: string): void {
if (!this._node) {
this.cachedClasses = this.cachedClasses.filter(c => c !== className);
} else if (this._node.classList.contains(className)) {
this._node.classList.remove(className);
}
}
/**
* The mimetype being rendered.
*/
readonly mimeType: string;
/**
* The sanitizer used to sanitize untrusted html inputs.
*/
readonly sanitizer: IRenderMime.ISanitizer;
/**
* The resolver object.
*/
readonly resolver: IRenderMime.IResolver | null;
/**
* The link handler.
*/
readonly linkHandler: IRenderMime.ILinkHandler | null;
/**
* The latexTypesetter.
*/
readonly latexTypesetter: IRenderMime.ILatexTypesetter;
/**
* 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?
// Toggle the trusted class on the widget.
this.toggleClass('jp-mod-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 render(model: IRenderMime.IMimeModel): Promise<void>;
}
/**
* A common base class for HTML mime renderers.
*/
export abstract class RenderedHTMLCommon extends RenderedCommon {
/**
* Construct a new rendered HTML common widget.
*
* @param options - The options for initializing the widget.
*/
constructor(options: IRenderMime.IRendererOptions) {
super(options);
this.addClass('jp-RenderedHTMLCommon');
}
}
/**
* A mime renderer for displaying HTML and math.
*/
export class RenderedHTML extends RenderedHTMLCommon {
/**
* Construct a new rendered HTML widget.
*
* @param options - The options for initializing the widget.
*/
constructor(options: IRenderMime.IRendererOptions) {
super(options);
this.addClass('jp-RenderedHTML');
}
/**
* Render a mime model.
*
* @param model - The mime model to render.
*
* @returns A promise which resolves when rendering is complete.
*/
render(model: IRenderMime.IMimeModel): Promise<void> {
return renderers.renderHTML({
host: this.node,
source: String(model.data[this.mimeType]),
trusted: model.trusted,
resolver: this.resolver,
sanitizer: this.sanitizer,
linkHandler: this.linkHandler,
shouldTypeset: true, //this.isAttached,
latexTypesetter: this.latexTypesetter
});
}
// /**
// * A message handler invoked on an `'after-attach'` message.
// */
// onAfterAttach(msg: Message): void {
// if (this.latexTypesetter) {
// this.latexTypesetter.typeset(this.node);
// }
// }
}
// /**
// * A mime renderer for displaying LaTeX output.
// */
// export class RenderedLatex extends RenderedCommon {
// /**
// * Construct a new rendered LaTeX widget.
// *
// * @param options - The options for initializing the widget.
// */
// constructor(options: IRenderMime.IRendererOptions) {
// super(options);
// this.addClass('jp-RenderedLatex');
// }
// /**
// * Render a mime model.
// *
// * @param model - The mime model to render.
// *
// * @returns A promise which resolves when rendering is complete.
// */
// render(model: IRenderMime.IMimeModel): Promise<void> {
// return renderers.renderLatex({
// host: this.node,
// source: String(model.data[this.mimeType]),
// shouldTypeset: this.isAttached,
// latexTypesetter: this.latexTypesetter
// });
// }
// /**
// * A message handler invoked on an `'after-attach'` message.
// */
// onAfterAttach(msg: Message): void {
// if (this.latexTypesetter) {
// this.latexTypesetter.typeset(this.node);
// }
// }
// }
/**
* A mime renderer for displaying images.
*/
export class RenderedImage extends RenderedCommon {
/**
* Construct a new rendered image widget.
*
* @param options - The options for initializing the widget.
*/
constructor(options: IRenderMime.IRendererOptions) {
super(options);
this.addClass('jp-RenderedImage');
}
/**
* Render a mime model.
*
* @param model - The mime model to render.
*
* @returns A promise which resolves when rendering is complete.
*/
render(model: IRenderMime.IMimeModel): Promise<void> {
let metadata = model.metadata[this.mimeType] as ReadonlyJSONObject;
return renderers.renderImage({
host: this.node,
mimeType: this.mimeType,
source: String(model.data[this.mimeType]),
width: metadata && (metadata.width as number | undefined),
height: metadata && (metadata.height as number | undefined),
needsBackground: model.metadata['needs_background'] as string | undefined,
unconfined: metadata && (metadata.unconfined as boolean | undefined)
});
}
}
/**
* A widget for displaying SVG content.
*/
export class RenderedSVG extends RenderedCommon {
/**
* Construct a new rendered SVG widget.
*
* @param options - The options for initializing the widget.
*/
constructor(options: IRenderMime.IRendererOptions) {
super(options);
this.addClass('jp-RenderedSVG');
}
/**
* Render a mime model.
*
* @param model - The mime model to render.
*
* @returns A promise which resolves when rendering is complete.
*/
render(model: IRenderMime.IMimeModel): Promise<void> {
let metadata = model.metadata[this.mimeType] as ReadonlyJSONObject;
return renderers.renderSVG({
host: this.node,
source: String(model.data[this.mimeType]),
trusted: model.trusted,
unconfined: metadata && (metadata.unconfined as boolean | undefined)
});
}
// /**
// * A message handler invoked on an `'after-attach'` message.
// */
// onAfterAttach(msg: Message): void {
// if (this.latexTypesetter) {
// this.latexTypesetter.typeset(this.node);
// }
// }
}
/**
* A widget for displaying plain text and console text.
*/
export class RenderedText extends RenderedCommon {
/**
* Construct a new rendered text widget.
*
* @param options - The options for initializing the widget.
*/
constructor(options: IRenderMime.IRendererOptions) {
super(options);
this.addClass('jp-RenderedText');
}
/**
* Render a mime model.
*
* @param model - The mime model to render.
*
* @returns A promise which resolves when rendering is complete.
*/
render(model: IRenderMime.IMimeModel): Promise<void> {
return renderers.renderText({
host: this.node,
source: String(model.data[this.mimeType])
});
}
}
/**
* A widget for displaying deprecated JavaScript output.
*/
export class RenderedJavaScript extends RenderedCommon {
/**
* Construct a new rendered text widget.
*
* @param options - The options for initializing the widget.
*/
constructor(options: IRenderMime.IRendererOptions) {
super(options);
this.addClass('jp-RenderedJavaScript');
}
/**
* Render a mime model.
*
* @param model - The mime model to render.
*
* @returns A promise which resolves when rendering is complete.
*/
render(model: IRenderMime.IMimeModel): Promise<void> {
return renderers.renderText({
host: this.node,
source: 'JavaScript output is disabled in Notebooks'
});
}
}
/**
* A widget for displaying Data Resource schemas and data.
*/
export class RenderedDataResource extends RenderedCommon {
/**
* Construct a new rendered data resource widget.
*
* @param options - The options for initializing the widget.
*/
constructor(options: IRenderMime.IRendererOptions) {
super(options);
this.addClass('jp-RenderedDataResource');
}
/**
* Render a mime model.
*
* @param model - The mime model to render.
*
* @returns A promise which resolves when rendering is complete.
*/
render(model: IRenderMime.IMimeModel): Promise<void> {
return tableRenderers.renderDataResource({
host: this.node,
source: JSON.stringify(model.data[this.mimeType]),
themeService: model.themeService
});
}
}