From e31747d087146a816016e8d98e908f977e621b9e Mon Sep 17 00:00:00 2001 From: Kevin Cunnane Date: Mon, 7 Jan 2019 14:02:12 -0800 Subject: [PATCH] Refactor JSON and format files to model and fix tabs -> spaces issues (#3675) This is in prep for some work to reuse these code files inside the model, so pushing as its own PR to keep the next piece of work clean. --- .../{outputs/common => models}/jsonext.ts | 16 +- src/sql/parts/notebook/models/nbformat.ts | 494 ++++ .../notebook/outputs/common/mimemodel.ts | 124 +- .../parts/notebook/outputs/common/nbformat.ts | 494 ---- .../outputs/common/outputProcessor.ts | 106 +- .../outputs/common/renderMimeInterfaces.ts | 609 +++-- src/sql/parts/notebook/outputs/common/url.ts | 303 ++- src/sql/parts/notebook/outputs/factories.ts | 60 +- src/sql/parts/notebook/outputs/registry.ts | 574 ++--- src/sql/parts/notebook/outputs/renderers.ts | 938 ++++---- src/sql/parts/notebook/outputs/sanitizer.ts | 1994 ++++++++--------- src/sql/parts/notebook/outputs/widgets.ts | 474 ++-- 12 files changed, 3090 insertions(+), 3096 deletions(-) rename src/sql/parts/notebook/{outputs/common => models}/jsonext.ts (85%) create mode 100644 src/sql/parts/notebook/models/nbformat.ts delete mode 100644 src/sql/parts/notebook/outputs/common/nbformat.ts diff --git a/src/sql/parts/notebook/outputs/common/jsonext.ts b/src/sql/parts/notebook/models/jsonext.ts similarity index 85% rename from src/sql/parts/notebook/outputs/common/jsonext.ts rename to src/sql/parts/notebook/models/jsonext.ts index 95de8717f1..88dbddd3da 100644 --- a/src/sql/parts/notebook/outputs/common/jsonext.ts +++ b/src/sql/parts/notebook/models/jsonext.ts @@ -15,7 +15,7 @@ export declare type JSONValue = JSONPrimitive | JSONObject | JSONArray; * A type definition for a JSON object. */ export interface JSONObject { - [key: string]: JSONValue; + [key: string]: JSONValue; } /** * A type definition for a JSON array. @@ -26,7 +26,7 @@ export interface JSONArray extends Array { * A type definition for a readonly JSON object. */ export interface ReadonlyJSONObject { - readonly [key: string]: ReadonlyJSONValue; + readonly [key: string]: ReadonlyJSONValue; } /** * A type definition for a readonly JSON array. @@ -45,10 +45,10 @@ export declare type ReadonlyJSONValue = JSONPrimitive | ReadonlyJSONObject | Rea * @returns `true` if the value is a primitive,`false` otherwise. */ export function isPrimitive(value: any): boolean { - return ( - value === null || - typeof value === 'boolean' || - typeof value === 'number' || - typeof value === 'string' - ); + return ( + value === null || + typeof value === 'boolean' || + typeof value === 'number' || + typeof value === 'string' + ); } diff --git a/src/sql/parts/notebook/models/nbformat.ts b/src/sql/parts/notebook/models/nbformat.ts new file mode 100644 index 0000000000..7ef637978a --- /dev/null +++ b/src/sql/parts/notebook/models/nbformat.ts @@ -0,0 +1,494 @@ +// Copyright (c) Jupyter Development Team. +// Distributed under the terms of the Modified BSD License. + +// Notebook format interfaces +// https://nbformat.readthedocs.io/en/latest/format_description.html +// https://github.com/jupyter/nbformat/blob/master/nbformat/v4/nbformat.v4.schema.json + + +import { JSONObject } from './jsonext'; +import { nb } from 'sqlops'; + +/** + * A namespace for nbformat interfaces. + */ +export namespace nbformat { + /** + * The major version of the notebook format. + */ + export const MAJOR_VERSION: number = 4; + + /** + * The minor version of the notebook format. + */ + export const MINOR_VERSION: number = 2; + + /** + * The kernelspec metadata. + */ + export interface IKernelspecMetadata extends JSONObject { + name: string; + display_name: string; + } + + /** + * The language info metatda + */ + export interface ILanguageInfoMetadata extends JSONObject { + name: string; + codemirror_mode?: string | JSONObject; + file_extension?: string; + mimetype?: string; + pygments_lexer?: string; + } + + /** + * The default metadata for the notebook. + */ + export interface INotebookMetadata extends JSONObject { + kernelspec?: IKernelspecMetadata; + language_info?: ILanguageInfoMetadata; + orig_nbformat: number; + } + + /** + * The notebook content. + */ + export interface INotebookContent { + metadata: INotebookMetadata; + nbformat_minor: number; + nbformat: number; + cells: ICell[]; + } + + /** + * A multiline string. + */ + export type MultilineString = string | string[]; + + /** + * A mime-type keyed dictionary of data. + */ + export interface IMimeBundle extends JSONObject { + [key: string]: MultilineString | JSONObject; + } + + /** + * Media attachments (e.g. inline images). + */ + export interface IAttachments { + [key: string]: IMimeBundle; + } + + /** + * The code cell's prompt number. Will be null if the cell has not been run. + */ + export type ExecutionCount = number | null; + + /** + * Cell output metadata. + */ + export type OutputMetadata = JSONObject; + + /** + * Validate a mime type/value pair. + * + * @param type - The mimetype name. + * + * @param value - The value associated with the type. + * + * @returns Whether the type/value pair are valid. + */ + export function validateMimeValue( + type: string, + value: MultilineString | JSONObject + ): boolean { + // Check if "application/json" or "application/foo+json" + const jsonTest = /^application\/(.*?)+\+json$/; + const isJSONType = type === 'application/json' || jsonTest.test(type); + + let isString = (x: any) => { + return Object.prototype.toString.call(x) === '[object String]'; + }; + + // If it is an array, make sure if is not a JSON type and it is an + // array of strings. + if (Array.isArray(value)) { + if (isJSONType) { + return false; + } + let valid = true; + (value as string[]).forEach(v => { + if (!isString(v)) { + valid = false; + } + }); + return valid; + } + + // If it is a string, make sure we are not a JSON type. + if (isString(value)) { + return !isJSONType; + } + + // It is not a string, make sure it is a JSON type. + if (!isJSONType) { + return false; + } + + // It is a JSON type, make sure it is a valid JSON object. + // return JSONExt.isObject(value); + return true; + } + + /** + * Cell-level metadata. + */ + export interface IBaseCellMetadata extends JSONObject { + /** + * Whether the cell is trusted. + * + * #### Notes + * This is not strictly part of the nbformat spec, but it is added by + * the contents manager. + * + * See https://jupyter-notebook.readthedocs.io/en/latest/security.html. + */ + trusted: boolean; + + /** + * The cell's name. If present, must be a non-empty string. + */ + name: string; + + /** + * The cell's tags. Tags must be unique, and must not contain commas. + */ + tags: string[]; + } + + /** + * The base cell interface. + */ + export interface IBaseCell { + /** + * String identifying the type of cell. + */ + cell_type: string; + + /** + * Contents of the cell, represented as an array of lines. + */ + source: MultilineString; + + /** + * Cell-level metadata. + */ + metadata: Partial; + } + + /** + * Metadata for the raw cell. + */ + export interface IRawCellMetadata extends IBaseCellMetadata { + /** + * Raw cell metadata format for nbconvert. + */ + format: string; + } + + /** + * A raw cell. + */ + export interface IRawCell extends IBaseCell { + /** + * String identifying the type of cell. + */ + cell_type: 'raw'; + + /** + * Cell-level metadata. + */ + metadata: Partial; + + /** + * Cell attachments. + */ + attachments?: IAttachments; + } + + /** + * A markdown cell. + */ + export interface IMarkdownCell extends IBaseCell { + /** + * String identifying the type of cell. + */ + cell_type: 'markdown'; + + /** + * Cell attachments. + */ + attachments?: IAttachments; + } + + /** + * Metadata for a code cell. + */ + export interface ICodeCellMetadata extends IBaseCellMetadata { + /** + * Whether the cell is collapsed/expanded. + */ + collapsed: boolean; + + /** + * Whether the cell's output is scrolled, unscrolled, or autoscrolled. + */ + scrolled: boolean | 'auto'; + } + + /** + * A code cell. + */ + export interface ICodeCell extends IBaseCell { + /** + * String identifying the type of cell. + */ + cell_type: 'code'; + + /** + * Cell-level metadata. + */ + metadata: Partial; + + /** + * Execution, display, or stream outputs. + */ + outputs: IOutput[]; + + /** + * The code cell's prompt number. Will be null if the cell has not been run. + */ + execution_count: ExecutionCount; + } + + /** + * An unrecognized cell. + */ + export interface IUnrecognizedCell extends IBaseCell { } + + /** + * A cell union type. + */ + export type ICell = IRawCell | IMarkdownCell | ICodeCell | IUnrecognizedCell; + + /** + * Test whether a cell is a raw cell. + */ + export function isRaw(cell: ICell): cell is IRawCell { + return cell.cell_type === 'raw'; + } + + /** + * Test whether a cell is a markdown cell. + */ + export function isMarkdown(cell: ICell): cell is IMarkdownCell { + return cell.cell_type === 'markdown'; + } + + /** + * Test whether a cell is a code cell. + */ + export function isCode(cell: ICell): cell is ICodeCell { + return cell.cell_type === 'code'; + } + + /** + * A union metadata type. + */ + export type ICellMetadata = + | IBaseCellMetadata + | IRawCellMetadata + | ICodeCellMetadata; + + /** + * The valid output types. + */ + export type OutputType = + | 'execute_result' + | 'display_data' + | 'stream' + | 'error' + | 'update_display_data'; + + + /** + * Result of executing a code cell. + */ + export interface IExecuteResult extends nb.ICellOutput { + /** + * Type of cell output. + */ + output_type: 'execute_result'; + + /** + * A result's prompt number. + */ + execution_count: ExecutionCount; + + /** + * A mime-type keyed dictionary of data. + */ + data: IMimeBundle; + + /** + * Cell output metadata. + */ + metadata: OutputMetadata; + } + + /** + * Data displayed as a result of code cell execution. + */ + export interface IDisplayData extends nb.ICellOutput { + /** + * Type of cell output. + */ + output_type: 'display_data'; + + /** + * A mime-type keyed dictionary of data. + */ + data: IMimeBundle; + + /** + * Cell output metadata. + */ + metadata: OutputMetadata; + } + + /** + * Data displayed as an update to existing display data. + */ + export interface IDisplayUpdate extends nb.ICellOutput { + /** + * Type of cell output. + */ + output_type: 'update_display_data'; + + /** + * A mime-type keyed dictionary of data. + */ + data: IMimeBundle; + + /** + * Cell output metadata. + */ + metadata: OutputMetadata; + } + + /** + * Stream output from a code cell. + */ + export interface IStream extends nb.ICellOutput { + /** + * Type of cell output. + */ + output_type: 'stream'; + + /** + * The name of the stream. + */ + name: StreamType; + + /** + * The stream's text output. + */ + text: MultilineString; + } + + /** + * An alias for a stream type. + */ + export type StreamType = 'stdout' | 'stderr'; + + /** + * Output of an error that occurred during code cell execution. + */ + export interface IError extends nb.ICellOutput { + /** + * Type of cell output. + */ + output_type: 'error'; + + /** + * The name of the error. + */ + ename: string; + + /** + * The value, or message, of the error. + */ + evalue: string; + + /** + * The error's traceback. + */ + traceback: string[]; + } + + /** + * Unrecognized output. + */ + export interface IUnrecognizedOutput extends nb.ICellOutput { } + + /** + * Test whether an output is an execute result. + */ + export function isExecuteResult(output: IOutput): output is IExecuteResult { + return output.output_type === 'execute_result'; + } + + /** + * Test whether an output is from display data. + */ + export function isDisplayData(output: IOutput): output is IDisplayData { + return output.output_type === 'display_data'; + } + + /** + * Test whether an output is from updated display data. + */ + export function isDisplayUpdate(output: IOutput): output is IDisplayUpdate { + return output.output_type === 'update_display_data'; + } + + /** + * Test whether an output is from a stream. + */ + export function isStream(output: IOutput): output is IStream { + return output.output_type === 'stream'; + } + + /** + * Test whether an output is from a stream. + */ + export function isError(output: IOutput): output is IError { + return output.output_type === 'error'; + } + + /** + * An output union type. + */ + export type IOutput = + | IUnrecognizedOutput + | IExecuteResult + | IDisplayData + | IStream + | IError; +} + +export interface ICellOutputWithIdAndTrust extends nb.ICellOutput { + id: number; + trusted: boolean; +} diff --git a/src/sql/parts/notebook/outputs/common/mimemodel.ts b/src/sql/parts/notebook/outputs/common/mimemodel.ts index 3b88c489d7..25dda250d4 100644 --- a/src/sql/parts/notebook/outputs/common/mimemodel.ts +++ b/src/sql/parts/notebook/outputs/common/mimemodel.ts @@ -3,85 +3,85 @@ | Distributed under the terms of the Modified BSD License. |----------------------------------------------------------------------------*/ import { IRenderMime } from './renderMimeInterfaces'; -import { ReadonlyJSONObject } from './jsonext'; +import { ReadonlyJSONObject } from '../../models/jsonext'; /** * 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; - } + /** + * 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; + } - /** - * Whether the model is trusted. - */ - readonly trusted: boolean; + /** + * Whether the model is trusted. + */ + readonly trusted: boolean; - /** - * The data associated with the model. - */ - get data(): ReadonlyJSONObject { - return this._data; - } + /** + * The data associated with the model. + */ + get data(): ReadonlyJSONObject { + return this._data; + } - /** - * The metadata associated with the model. - */ - get metadata(): ReadonlyJSONObject { - return this._metadata; - } + /** + * The metadata associated with the model. + */ + get metadata(): ReadonlyJSONObject { + return this._metadata; + } - /** - * 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); - } + /** + * 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 _callback: (options: IRenderMime.ISetDataOptions) => void; + private _data: ReadonlyJSONObject; + private _metadata: ReadonlyJSONObject; } /** * 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; + /** + * 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; + /** + * A callback function for when the data changes. + */ + callback?: (options: IRenderMime.ISetDataOptions) => void; - /** - * The initial mime data. - */ - data?: ReadonlyJSONObject; + /** + * The initial mime data. + */ + data?: ReadonlyJSONObject; - /** - * The initial mime metadata. - */ - metadata?: ReadonlyJSONObject; - } + /** + * The initial mime metadata. + */ + metadata?: ReadonlyJSONObject; + } } \ No newline at end of file diff --git a/src/sql/parts/notebook/outputs/common/nbformat.ts b/src/sql/parts/notebook/outputs/common/nbformat.ts deleted file mode 100644 index 12ebc816cb..0000000000 --- a/src/sql/parts/notebook/outputs/common/nbformat.ts +++ /dev/null @@ -1,494 +0,0 @@ -// Copyright (c) Jupyter Development Team. -// Distributed under the terms of the Modified BSD License. - -// Notebook format interfaces -// https://nbformat.readthedocs.io/en/latest/format_description.html -// https://github.com/jupyter/nbformat/blob/master/nbformat/v4/nbformat.v4.schema.json - - -import { JSONObject } from './jsonext'; -import { nb } from 'sqlops'; - -/** - * A namespace for nbformat interfaces. - */ -export namespace nbformat { - /** - * The major version of the notebook format. - */ - export const MAJOR_VERSION: number = 4; - - /** - * The minor version of the notebook format. - */ - export const MINOR_VERSION: number = 2; - - /** - * The kernelspec metadata. - */ - export interface IKernelspecMetadata extends JSONObject { - name: string; - display_name: string; - } - - /** - * The language info metatda - */ - export interface ILanguageInfoMetadata extends JSONObject { - name: string; - codemirror_mode?: string | JSONObject; - file_extension?: string; - mimetype?: string; - pygments_lexer?: string; - } - - /** - * The default metadata for the notebook. - */ - export interface INotebookMetadata extends JSONObject { - kernelspec?: IKernelspecMetadata; - language_info?: ILanguageInfoMetadata; - orig_nbformat: number; - } - - /** - * The notebook content. - */ - export interface INotebookContent { - metadata: INotebookMetadata; - nbformat_minor: number; - nbformat: number; - cells: ICell[]; - } - - /** - * A multiline string. - */ - export type MultilineString = string | string[]; - - /** - * A mime-type keyed dictionary of data. - */ - export interface IMimeBundle extends JSONObject { - [key: string]: MultilineString | JSONObject; - } - - /** - * Media attachments (e.g. inline images). - */ - export interface IAttachments { - [key: string]: IMimeBundle; - } - - /** - * The code cell's prompt number. Will be null if the cell has not been run. - */ - export type ExecutionCount = number | null; - - /** - * Cell output metadata. - */ - export type OutputMetadata = JSONObject; - - /** - * Validate a mime type/value pair. - * - * @param type - The mimetype name. - * - * @param value - The value associated with the type. - * - * @returns Whether the type/value pair are valid. - */ - export function validateMimeValue( - type: string, - value: MultilineString | JSONObject - ): boolean { - // Check if "application/json" or "application/foo+json" - const jsonTest = /^application\/(.*?)+\+json$/; - const isJSONType = type === 'application/json' || jsonTest.test(type); - - let isString = (x: any) => { - return Object.prototype.toString.call(x) === '[object String]'; - }; - - // If it is an array, make sure if is not a JSON type and it is an - // array of strings. - if (Array.isArray(value)) { - if (isJSONType) { - return false; - } - let valid = true; - (value as string[]).forEach(v => { - if (!isString(v)) { - valid = false; - } - }); - return valid; - } - - // If it is a string, make sure we are not a JSON type. - if (isString(value)) { - return !isJSONType; - } - - // It is not a string, make sure it is a JSON type. - if (!isJSONType) { - return false; - } - - // It is a JSON type, make sure it is a valid JSON object. - // return JSONExt.isObject(value); - return true; - } - - /** - * Cell-level metadata. - */ - export interface IBaseCellMetadata extends JSONObject { - /** - * Whether the cell is trusted. - * - * #### Notes - * This is not strictly part of the nbformat spec, but it is added by - * the contents manager. - * - * See https://jupyter-notebook.readthedocs.io/en/latest/security.html. - */ - trusted: boolean; - - /** - * The cell's name. If present, must be a non-empty string. - */ - name: string; - - /** - * The cell's tags. Tags must be unique, and must not contain commas. - */ - tags: string[]; - } - - /** - * The base cell interface. - */ - export interface IBaseCell { - /** - * String identifying the type of cell. - */ - cell_type: string; - - /** - * Contents of the cell, represented as an array of lines. - */ - source: MultilineString; - - /** - * Cell-level metadata. - */ - metadata: Partial; - } - - /** - * Metadata for the raw cell. - */ - export interface IRawCellMetadata extends IBaseCellMetadata { - /** - * Raw cell metadata format for nbconvert. - */ - format: string; - } - - /** - * A raw cell. - */ - export interface IRawCell extends IBaseCell { - /** - * String identifying the type of cell. - */ - cell_type: 'raw'; - - /** - * Cell-level metadata. - */ - metadata: Partial; - - /** - * Cell attachments. - */ - attachments?: IAttachments; - } - - /** - * A markdown cell. - */ - export interface IMarkdownCell extends IBaseCell { - /** - * String identifying the type of cell. - */ - cell_type: 'markdown'; - - /** - * Cell attachments. - */ - attachments?: IAttachments; - } - - /** - * Metadata for a code cell. - */ - export interface ICodeCellMetadata extends IBaseCellMetadata { - /** - * Whether the cell is collapsed/expanded. - */ - collapsed: boolean; - - /** - * Whether the cell's output is scrolled, unscrolled, or autoscrolled. - */ - scrolled: boolean | 'auto'; - } - - /** - * A code cell. - */ - export interface ICodeCell extends IBaseCell { - /** - * String identifying the type of cell. - */ - cell_type: 'code'; - - /** - * Cell-level metadata. - */ - metadata: Partial; - - /** - * Execution, display, or stream outputs. - */ - outputs: IOutput[]; - - /** - * The code cell's prompt number. Will be null if the cell has not been run. - */ - execution_count: ExecutionCount; - } - - /** - * An unrecognized cell. - */ - export interface IUnrecognizedCell extends IBaseCell { } - - /** - * A cell union type. - */ - export type ICell = IRawCell | IMarkdownCell | ICodeCell | IUnrecognizedCell; - - /** - * Test whether a cell is a raw cell. - */ - export function isRaw(cell: ICell): cell is IRawCell { - return cell.cell_type === 'raw'; - } - - /** - * Test whether a cell is a markdown cell. - */ - export function isMarkdown(cell: ICell): cell is IMarkdownCell { - return cell.cell_type === 'markdown'; - } - - /** - * Test whether a cell is a code cell. - */ - export function isCode(cell: ICell): cell is ICodeCell { - return cell.cell_type === 'code'; - } - - /** - * A union metadata type. - */ - export type ICellMetadata = - | IBaseCellMetadata - | IRawCellMetadata - | ICodeCellMetadata; - - /** - * The valid output types. - */ - export type OutputType = - | 'execute_result' - | 'display_data' - | 'stream' - | 'error' - | 'update_display_data'; - - - /** - * Result of executing a code cell. - */ - export interface IExecuteResult extends nb.ICellOutput { - /** - * Type of cell output. - */ - output_type: 'execute_result'; - - /** - * A result's prompt number. - */ - execution_count: ExecutionCount; - - /** - * A mime-type keyed dictionary of data. - */ - data: IMimeBundle; - - /** - * Cell output metadata. - */ - metadata: OutputMetadata; - } - - /** - * Data displayed as a result of code cell execution. - */ - export interface IDisplayData extends nb.ICellOutput { - /** - * Type of cell output. - */ - output_type: 'display_data'; - - /** - * A mime-type keyed dictionary of data. - */ - data: IMimeBundle; - - /** - * Cell output metadata. - */ - metadata: OutputMetadata; - } - - /** - * Data displayed as an update to existing display data. - */ - export interface IDisplayUpdate extends nb.ICellOutput { - /** - * Type of cell output. - */ - output_type: 'update_display_data'; - - /** - * A mime-type keyed dictionary of data. - */ - data: IMimeBundle; - - /** - * Cell output metadata. - */ - metadata: OutputMetadata; - } - - /** - * Stream output from a code cell. - */ - export interface IStream extends nb.ICellOutput { - /** - * Type of cell output. - */ - output_type: 'stream'; - - /** - * The name of the stream. - */ - name: StreamType; - - /** - * The stream's text output. - */ - text: MultilineString; - } - - /** - * An alias for a stream type. - */ - export type StreamType = 'stdout' | 'stderr'; - - /** - * Output of an error that occurred during code cell execution. - */ - export interface IError extends nb.ICellOutput { - /** - * Type of cell output. - */ - output_type: 'error'; - - /** - * The name of the error. - */ - ename: string; - - /** - * The value, or message, of the error. - */ - evalue: string; - - /** - * The error's traceback. - */ - traceback: string[]; - } - - /** - * Unrecognized output. - */ - export interface IUnrecognizedOutput extends nb.ICellOutput { } - - /** - * Test whether an output is an execute result. - */ - export function isExecuteResult(output: IOutput): output is IExecuteResult { - return output.output_type === 'execute_result'; - } - - /** - * Test whether an output is from display data. - */ - export function isDisplayData(output: IOutput): output is IDisplayData { - return output.output_type === 'display_data'; - } - - /** - * Test whether an output is from updated display data. - */ - export function isDisplayUpdate(output: IOutput): output is IDisplayUpdate { - return output.output_type === 'update_display_data'; - } - - /** - * Test whether an output is from a stream. - */ - export function isStream(output: IOutput): output is IStream { - return output.output_type === 'stream'; - } - - /** - * Test whether an output is from a stream. - */ - export function isError(output: IOutput): output is IError { - return output.output_type === 'error'; - } - - /** - * An output union type. - */ - export type IOutput = - | IUnrecognizedOutput - | IExecuteResult - | IDisplayData - | IStream - | IError; -} - -export interface ICellOutputWithIdAndTrust extends nb.ICellOutput { - id: number; - trusted: boolean; -} diff --git a/src/sql/parts/notebook/outputs/common/outputProcessor.ts b/src/sql/parts/notebook/outputs/common/outputProcessor.ts index ef2a9366c2..d08656dd2c 100644 --- a/src/sql/parts/notebook/outputs/common/outputProcessor.ts +++ b/src/sql/parts/notebook/outputs/common/outputProcessor.ts @@ -4,9 +4,9 @@ | Distributed under the terms of the Modified BSD License. |----------------------------------------------------------------------------*/ -import { JSONObject, isPrimitive } from './jsonext'; +import { JSONObject, isPrimitive } from '../../models/jsonext'; import { MimeModel } from './mimemodel'; -import { nbformat } from './nbformat'; +import { nbformat } from '../../models/nbformat'; import { nb } from 'sqlops'; /** @@ -18,92 +18,90 @@ export type MultilineString = string | string[]; * A mime-type keyed dictionary of data. */ export interface IMimeBundle extends JSONObject { - [key: string]: MultilineString | 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'] = - traceback || `${output.ename}: ${output.evalue}`; - } - return convertBundle(bundle); + 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'] = + traceback || `${output.ename}: ${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; + 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 }; +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)); + 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; + 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; + /** + * The raw output value. + */ + value: nbformat.IOutput; - /** - * Whether the output is trusted. The default is false. - */ - trusted?: boolean; + /** + * Whether the output is trusted. The default is false. + */ + trusted?: boolean; } \ No newline at end of file diff --git a/src/sql/parts/notebook/outputs/common/renderMimeInterfaces.ts b/src/sql/parts/notebook/outputs/common/renderMimeInterfaces.ts index 3585a0d112..0be3adc974 100644 --- a/src/sql/parts/notebook/outputs/common/renderMimeInterfaces.ts +++ b/src/sql/parts/notebook/outputs/common/renderMimeInterfaces.ts @@ -3,358 +3,357 @@ | Copyright (c) Jupyter Development Team. | Distributed under the terms of the Modified BSD License. |----------------------------------------------------------------------------*/ -import { ReadonlyJSONObject } from './jsonext'; +import { ReadonlyJSONObject } from '../../models/jsonext'; /** * 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; + /** + * 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 data associated with the model. + */ + readonly data: ReadonlyJSONObject; - /** - * The metadata associated with the model. - */ - readonly metadata: 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; - } + /** + * 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; + } - /** - * The options used to update a mime model. - */ - export interface ISetDataOptions { - /** - * The new data object. - */ - data?: ReadonlyJSONObject; + /** + * The options used to update a mime model. + */ + export interface ISetDataOptions { + /** + * The new data object. + */ + data?: ReadonlyJSONObject; - /** - * The new metadata object. - */ - metadata?: 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 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 name of the document model type. - */ - readonly modelName?: string; + /** + * The primary file type of the widget. + */ + readonly primaryFileType: string; - /** - * The primary file type of the widget. - */ - readonly primaryFileType: string; + /** + * The file types the widget can view. + */ + readonly fileTypes: ReadonlyArray; - /** - * The file types the widget can view. - */ - readonly fileTypes: ReadonlyArray; + /** + * The file types for which the factory should be the default. + */ + readonly defaultFor?: ReadonlyArray; - /** - * The file types for which the factory should be the default. - */ - readonly defaultFor?: ReadonlyArray; + /** + * 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; + } - /** - * 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; - } + /** + * A file type to associate with the renderer. + */ + export interface IFileType { + /** + * The name of the file type. + */ + readonly name: 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; - /** - * The mime types associated the file type. - */ - readonly mimeTypes: ReadonlyArray; + /** + * The extensions of the file type (e.g. `".txt"`). Can be a compound + * extension (e.g. `".table.json`). + */ + readonly extensions: ReadonlyArray; - /** - * The extensions of the file type (e.g. `".txt"`). Can be a compound - * extension (e.g. `".table.json`). - */ - readonly extensions: ReadonlyArray; + /** + * An optional display name for the file type. + */ + readonly displayName?: 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; - /** - * 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 class name for the file type. - */ - readonly iconClass?: string; + /** + * The icon label for the file type. + */ + readonly iconLabel?: 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; + } - /** - * 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; - /** - * 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; - /** - * 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 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; - /** - * 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'; - /** - * 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; - /** - * The options used to open a document with the renderer factory. - */ - readonly documentWidgetFactoryOptions?: - | IDocumentWidgetFactoryOptions - | ReadonlyArray; + /** + * The optional file type associated with the extension. + */ + readonly fileTypes?: ReadonlyArray; + } - /** - * The optional file type associated with the extension. - */ - readonly fileTypes?: ReadonlyArray; - } + /** + * 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; + } - /** - * 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; - } + /** + * 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; - /** - * 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; + /** + * Node to be updated by the renderer + */ + node: HTMLElement; + } - /** - * 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 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; - /** - * The mime types handled by this factory. - */ - readonly mimeTypes: ReadonlyArray; + /** + * The default rank of the factory. If not given, defaults to 100. + */ + readonly defaultRank?: number; - /** - * 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; + } - /** - * 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 options used to create a renderer. - */ - export interface IRendererOptions { - /** - * The preferred mimeType to render. - */ - mimeType: string; + /** + * The html sanitizer. + */ + sanitizer: ISanitizer; - /** - * The html sanitizer. - */ - sanitizer: ISanitizer; + /** + * An optional url resolver. + */ + resolver: IResolver | null; - /** - * An optional url resolver. - */ - resolver: IResolver | null; + /** + * An optional link handler. + */ + linkHandler: ILinkHandler | null; - /** - * An optional link handler. - */ - linkHandler: ILinkHandler | null; + /** + * The LaTeX typesetter. + */ + latexTypesetter: ILatexTypesetter | 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 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 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; - /** - * An object that resolves relative URLs. - */ - export interface IResolver { - /** - * Resolve a relative url to a correct server path. - */ - resolveUrl(url: string): Promise; + /** + * Get the download url of a given absolute server path. + */ + getDownloadUrl(path: string): Promise; - /** - * Get the download url of a given absolute server path. - */ - getDownloadUrl(path: string): Promise; + /** + * 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; + } - /** - * 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; - } + /** + * 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; + } } diff --git a/src/sql/parts/notebook/outputs/common/url.ts b/src/sql/parts/notebook/outputs/common/url.ts index 5386ee1daa..b9186a7999 100644 --- a/src/sql/parts/notebook/outputs/common/url.ts +++ b/src/sql/parts/notebook/outputs/common/url.ts @@ -1,184 +1,181 @@ // Copyright (c) Jupyter Development Team. // Distributed under the terms of the Modified BSD License. -import { JSONObject } from './jsonext'; +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(); + /** + * 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 { /** - * Join a sequence of url components and normalizes as in node `path.join`. - * - * @param parts - The url components. - * - * @returns the joined url. + * The full URL string that was parsed with both the protocol and host + * components converted to lower-case. */ - 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; - } + href?: string; /** - * 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. + * Identifies the URL's lower-cased protocol scheme. */ - export function encodeParts(url: string): string { - return join(...url.split('/').map(encodeURIComponent)); - } + protocol?: string; /** - * 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). + * The full lower-cased host portion of the URL, including the port if + * specified. */ - 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('&') - ); - } + host?: string; /** - * Return a parsed object that represents the values in a query string. + * The lower-cased host name portion of the host component without the + * port included. */ - 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 } - ); - } + hostname?: 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. + * The numeric port portion of the host component. */ - 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; - } + port?: string; /** - * The interface for a URL object + * The entire path section of the URL. */ - export interface IUrl { - /** - * The full URL string that was parsed with both the protocol and host - * components converted to lower-case. - */ - href?: string; + pathname?: string; - /** - * Identifies the URL's lower-cased protocol scheme. - */ - protocol?: string; + /** + * The "fragment" portion of the URL including the leading ASCII hash + * `(#)` character + */ + hash?: 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; - } + /** + * The search element, including leading question mark (`'?'`), if any, + * of the URL. + */ + search?: string; + } } diff --git a/src/sql/parts/notebook/outputs/factories.ts b/src/sql/parts/notebook/outputs/factories.ts index a34d1cffa1..145f2357a7 100644 --- a/src/sql/parts/notebook/outputs/factories.ts +++ b/src/sql/parts/notebook/outputs/factories.ts @@ -10,9 +10,9 @@ import { IRenderMime } from './common/renderMimeInterfaces'; * A mime renderer factory for raw html. */ export const htmlRendererFactory: IRenderMime.IRendererFactory = { - safe: true, - mimeTypes: ['text/html'], - defaultRank: 50, + safe: true, + mimeTypes: ['text/html'], + defaultRank: 50, createRenderer: options => new widgets.RenderedHTML(options) }; @@ -20,10 +20,10 @@ export const htmlRendererFactory: IRenderMime.IRendererFactory = { * 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) + safe: true, + mimeTypes: ['image/bmp', 'image/png', 'image/jpeg', 'image/gif'], + defaultRank: 90, + createRenderer: options => new widgets.RenderedImage(options) }; // /** @@ -50,45 +50,45 @@ export const imageRendererFactory: IRenderMime.IRendererFactory = { * 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) + 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) + 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) + safe: false, + mimeTypes: ['text/javascript', 'application/javascript'], + defaultRank: 110, + createRenderer: options => new widgets.RenderedJavaScript(options) }; /** * The standard factories provided by the rendermime package. */ export const standardRendererFactories: ReadonlyArray = [ - htmlRendererFactory, - // markdownRendererFactory, - // latexRendererFactory, - svgRendererFactory, - imageRendererFactory, - javaScriptRendererFactory, - textRendererFactory + htmlRendererFactory, + // markdownRendererFactory, + // latexRendererFactory, + svgRendererFactory, + imageRendererFactory, + javaScriptRendererFactory, + textRendererFactory ]; diff --git a/src/sql/parts/notebook/outputs/registry.ts b/src/sql/parts/notebook/outputs/registry.ts index b35004e696..4c1f302d5c 100644 --- a/src/sql/parts/notebook/outputs/registry.ts +++ b/src/sql/parts/notebook/outputs/registry.ts @@ -5,7 +5,7 @@ |----------------------------------------------------------------------------*/ import { IRenderMime } from './common/renderMimeInterfaces'; import { MimeModel } from './common/mimemodel'; -import { ReadonlyJSONObject } from './common/jsonext'; +import { ReadonlyJSONObject } from '../models/jsonext'; import { defaultSanitizer } from './sanitizer'; /** @@ -19,334 +19,334 @@ import { defaultSanitizer } from './sanitizer'; * 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; + /** + * 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); - } - } - } + // 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 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 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 object used to handle path opening links. + */ + readonly linkHandler: IRenderMime.ILinkHandler | null; - /** - * The LaTeX typesetter for the rendermime. - */ - readonly latexTypesetter: IRenderMime.ILatexTypesetter | null; + /** + * The LaTeX typesetter for the rendermime. + */ + readonly latexTypesetter: IRenderMime.ILatexTypesetter | null; - /** - * The ordered list of mimeTypes. - */ - get mimeTypes(): ReadonlyArray { - return this._types || (this._types = Private.sortedTypes(this._ranks)); - } + /** + * The ordered list of mimeTypes. + */ + get mimeTypes(): ReadonlyArray { + 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; - } - } - } + /** + * 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; - } - } - } + 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; - } + // 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}'`); - } + /** + * 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 - }); - } + // 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 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 - }); + /** + * 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; + // Clone the internal state. + clone._factories = { ...this._factories }; + clone._ranks = { ...this._ranks }; + clone._id = this._id; - // Return the cloned object. - return clone; - } + // 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]; - } + /** + * 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; - } + /** + * 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; - } + /** + * 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; - } + /** + * 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; - } + /** + * 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 = {}; + 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; + /** + * The options used to initialize a rendermime instance. + */ + export interface IOptions { + /** + * Initial factories to add to the rendermime instance. + */ + initialFactories?: ReadonlyArray; - /** - * The sanitizer used to sanitize untrusted html inputs. - * - * If not given, a default sanitizer will be used. - */ - sanitizer?: IRenderMime.ISanitizer; + /** + * 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; + /** + * The initial resolver object. + * + * The default is `null`. + */ + resolver?: IRenderMime.IResolver; - /** - * An optional path handler. - */ - linkHandler?: IRenderMime.ILinkHandler; + /** + * An optional path handler. + */ + linkHandler?: IRenderMime.ILinkHandler; - /** - * An optional LaTeX typesetter. - */ - latexTypesetter?: IRenderMime.ILatexTypesetter; - } + /** + * 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 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 resolver object. + */ + resolver?: IRenderMime.IResolver; - /** - * The new path handler. - */ - linkHandler?: IRenderMime.ILinkHandler; + /** + * The new path handler. + */ + linkHandler?: IRenderMime.ILinkHandler; - /** - * The new LaTeX typesetter. - */ - latexTypesetter?: IRenderMime.ILatexTypesetter; - } + /** + * 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 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 -> 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 }; + /** + * 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; - }); - } + /** + * 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; + }); + } } diff --git a/src/sql/parts/notebook/outputs/renderers.ts b/src/sql/parts/notebook/outputs/renderers.ts index 498ebe0435..753b19de86 100644 --- a/src/sql/parts/notebook/outputs/renderers.ts +++ b/src/sql/parts/notebook/outputs/renderers.ts @@ -17,127 +17,127 @@ import URI from 'vs/base/common/uri'; * @returns A promise which resolves when rendering is complete. */ export function renderHTML(options: renderHTML.IOptions): Promise { - // Unpack the options. - let { - host, - source, - trusted, - sanitizer, - resolver, - linkHandler, - shouldTypeset, - latexTypesetter - } = options; + // Unpack the options. + let { + host, + source, + trusted, + sanitizer, + resolver, + linkHandler, + shouldTypeset, + latexTypesetter + } = options; - let originalSource = source; + let originalSource = source; - // Bail early if the source is empty. - if (!source) { - host.textContent = ''; - return Promise.resolve(undefined); - } + // 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 - // `