123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665 |
- // Copyright (c) Jupyter Development Team.
- // Distributed under the terms of the Modified BSD License.
- import {
- Mode
- } from '@jupyterlab/codemirror';
- import {
- Contents
- } from '@jupyterlab/services';
- import {
- JSONObject, JSONValue, PromiseDelegate
- } from '@phosphor/coreutils';
- import {
- Message
- } from '@phosphor/messaging';
- import {
- ISignal, Signal
- } from '@phosphor/signaling';
- import {
- PanelLayout, Widget
- } from '@phosphor/widgets';
- import {
- CodeEditor
- } from '@jupyterlab/codeeditor';
- import {
- ActivityMonitor, IChangedArgs, IModelDB
- } from '@jupyterlab/coreutils';
- import {
- IRenderMime, RenderMime, MimeModel
- } from '@jupyterlab/rendermime';
- import {
- DocumentRegistry
- } from './index';
- /**
- * The default implementation of a document model.
- */
- export
- class DocumentModel extends CodeEditor.Model implements DocumentRegistry.ICodeModel {
- /**
- * Construct a new document model.
- */
- constructor(languagePreference?: string, modelDB?: IModelDB) {
- super({modelDB});
- this._defaultLang = languagePreference || '';
- this.value.changed.connect(this.triggerContentChange, this);
- }
- /**
- * A signal emitted when the document content changes.
- */
- get contentChanged(): ISignal<this, void> {
- return this._contentChanged;
- }
- /**
- * A signal emitted when the document state changes.
- */
- get stateChanged(): ISignal<this, IChangedArgs<any>> {
- return this._stateChanged;
- }
- /**
- * The dirty state of the document.
- */
- get dirty(): boolean {
- return this._dirty;
- }
- set dirty(newValue: boolean) {
- if (newValue === this._dirty) {
- return;
- }
- let oldValue = this._dirty;
- this._dirty = newValue;
- this.triggerStateChange({ name: 'dirty', oldValue, newValue });
- }
- /**
- * The read only state of the document.
- */
- get readOnly(): boolean {
- return this._readOnly;
- }
- set readOnly(newValue: boolean) {
- if (newValue === this._readOnly) {
- return;
- }
- let oldValue = this._readOnly;
- this._readOnly = newValue;
- this.triggerStateChange({ name: 'readOnly', oldValue, newValue });
- }
- /**
- * The default kernel name of the document.
- *
- * #### Notes
- * This is a read-only property.
- */
- get defaultKernelName(): string {
- return '';
- }
- /**
- * The default kernel language of the document.
- *
- * #### Notes
- * This is a read-only property.
- */
- get defaultKernelLanguage(): string {
- return this._defaultLang;
- }
- /**
- * Serialize the model to a string.
- */
- toString(): string {
- return this.value.text;
- }
- /**
- * Deserialize the model from a string.
- *
- * #### Notes
- * Should emit a [contentChanged] signal.
- */
- fromString(value: string): void {
- this.value.text = value;
- }
- /**
- * Serialize the model to JSON.
- */
- toJSON(): JSONValue {
- return JSON.parse(this.value.text);
- }
- /**
- * Deserialize the model from JSON.
- *
- * #### Notes
- * Should emit a [contentChanged] signal.
- */
- fromJSON(value: JSONValue): void {
- this.fromString(JSON.stringify(value));
- }
- /**
- * Trigger a state change signal.
- */
- protected triggerStateChange(args: IChangedArgs<any>): void {
- this._stateChanged.emit(args);
- }
- /**
- * Trigger a content changed signal.
- */
- protected triggerContentChange(): void {
- this._contentChanged.emit(void 0);
- this.dirty = true;
- }
- private _defaultLang = '';
- private _dirty = false;
- private _readOnly = false;
- private _contentChanged = new Signal<this, void>(this);
- private _stateChanged = new Signal<this, IChangedArgs<any>>(this);
- }
- /**
- * An implementation of a model factory for text files.
- */
- export
- class TextModelFactory implements DocumentRegistry.CodeModelFactory {
- /**
- * The name of the model type.
- *
- * #### Notes
- * This is a read-only property.
- */
- get name(): string {
- return 'text';
- }
- /**
- * The type of the file.
- *
- * #### Notes
- * This is a read-only property.
- */
- get contentType(): Contents.ContentType {
- return 'file';
- }
- /**
- * The format of the file.
- *
- * This is a read-only property.
- */
- get fileFormat(): Contents.FileFormat {
- return 'text';
- }
- /**
- * Get whether the model factory has been disposed.
- */
- get isDisposed(): boolean {
- return this._isDisposed;
- }
- /**
- * Dispose of the resources held by the model factory.
- */
- dispose(): void {
- this._isDisposed = true;
- }
- /**
- * Create a new model.
- *
- * @param languagePreference - An optional kernel language preference.
- *
- * @returns A new document model.
- */
- createNew(languagePreference?: string, modelDB?: IModelDB): DocumentRegistry.ICodeModel {
- return new DocumentModel(languagePreference, modelDB);
- }
- /**
- * Get the preferred kernel language given an extension.
- */
- preferredLanguage(ext: string): string {
- let mode = Mode.findByExtension(ext.slice(1));
- return mode && mode.mode;
- }
- private _isDisposed = false;
- }
- /**
- * An implementation of a model factory for base64 files.
- */
- export
- class Base64ModelFactory extends TextModelFactory {
- /**
- * The name of the model type.
- *
- * #### Notes
- * This is a read-only property.
- */
- get name(): string {
- return 'base64';
- }
- /**
- * The type of the file.
- *
- * #### Notes
- * This is a read-only property.
- */
- get contentType(): Contents.ContentType {
- return 'file';
- }
- /**
- * The format of the file.
- *
- * This is a read-only property.
- */
- get fileFormat(): Contents.FileFormat {
- return 'base64';
- }
- }
- /**
- * The default implemetation of a widget factory.
- */
- export
- abstract class ABCWidgetFactory<T extends DocumentRegistry.IReadyWidget, U extends DocumentRegistry.IModel> implements DocumentRegistry.IWidgetFactory<T, U> {
- /**
- * Construct a new `ABCWidgetFactory`.
- */
- constructor(options: DocumentRegistry.IWidgetFactoryOptions) {
- this._name = options.name;
- this._readOnly = options.readOnly === undefined ? false : options.readOnly;
- this._defaultFor = options.defaultFor ? options.defaultFor.slice() : [];
- this._fileExtensions = options.fileExtensions.slice();
- this._modelName = options.modelName || 'text';
- this._preferKernel = !!options.preferKernel;
- this._canStartKernel = !!options.canStartKernel;
- }
- /**
- * A signal emitted when a widget is created.
- */
- get widgetCreated(): ISignal<DocumentRegistry.IWidgetFactory<T, U>, T> {
- return this._widgetCreated;
- }
- /**
- * Get whether the model factory has been disposed.
- */
- get isDisposed(): boolean {
- return this._isDisposed;
- }
- /**
- * Dispose of the resources held by the document manager.
- */
- dispose(): void {
- this._isDisposed = true;
- }
- /**
- * Whether the widget factory is read only.
- */
- get readOnly(): boolean {
- return this._readOnly;
- }
- /**
- * The name of the widget to display in dialogs.
- */
- get name(): string {
- return this._name;
- }
- /**
- * The file extensions the widget can view.
- */
- get fileExtensions(): string[] {
- return this._fileExtensions.slice();
- }
- /**
- * The registered name of the model type used to create the widgets.
- */
- get modelName(): string {
- return this._modelName;
- }
- /**
- * The file extensions for which the factory should be the default.
- */
- get defaultFor(): string[] {
- return this._defaultFor.slice();
- }
- /**
- * Whether the widgets prefer having a kernel started.
- */
- get preferKernel(): boolean {
- return this._preferKernel;
- }
- /**
- * Whether the widgets can start a kernel when opened.
- */
- get canStartKernel(): boolean {
- return this._canStartKernel;
- }
- /**
- * Create a new widget given a document model and a context.
- *
- * #### Notes
- * It should emit the [widgetCreated] signal with the new widget.
- */
- createNew(context: DocumentRegistry.IContext<U>): T {
- let widget = this.createNewWidget(context);
- this._widgetCreated.emit(widget);
- return widget;
- }
- /**
- * Create a widget for a context.
- */
- protected abstract createNewWidget(context: DocumentRegistry.IContext<U>): T;
- private _isDisposed = false;
- private _name: string;
- private _readOnly: boolean;
- private _canStartKernel: boolean;
- private _preferKernel: boolean;
- private _modelName: string;
- private _fileExtensions: string[];
- private _defaultFor: string[];
- private _widgetCreated = new Signal<DocumentRegistry.IWidgetFactory<T, U>, T>(this);
- }
- /**
- * A widget for rendered mimetype.
- */
- export
- class MimeRenderer extends Widget implements DocumentRegistry.IReadyWidget {
- /**
- * Construct a new markdown widget.
- */
- constructor(options: MimeRenderer.IOptions) {
- super();
- this.addClass('jp-MimeRenderer');
- let layout = this.layout = new PanelLayout();
- let toolbar = new Widget();
- toolbar.addClass('jp-Toolbar');
- layout.addWidget(toolbar);
- let context = options.context;
- this.title.label = context.path.split('/').pop();
- this.rendermime = options.rendermime.clone({ resolver: context });
- this._context = context;
- this._mimeType = options.mimeType;
- this._dataType = options.dataType;
- context.pathChanged.connect(this._onPathChanged, this);
- this._context.ready.then(() => {
- if (this.isDisposed) {
- return;
- }
- return this._render().then();
- }).then(() => {
- // Throttle the rendering rate of the widget.
- this._monitor = new ActivityMonitor({
- signal: context.model.contentChanged,
- timeout: options.renderTimeout
- });
- this._monitor.activityStopped.connect(this.update, this);
- this._ready.resolve(undefined);
- });
- }
- /**
- * The markdown widget's context.
- */
- get context(): DocumentRegistry.Context {
- return this._context;
- }
- /**
- * The rendermime instance associated with the widget.
- */
- readonly rendermime: RenderMime;
- /**
- * A promise that resolves when the widget is ready.
- */
- get ready(): Promise<void> {
- return this._ready.promise;
- }
- /**
- * Dispose of the resources held by the widget.
- */
- dispose(): void {
- if (this.isDisposed) {
- return;
- }
- if (this._monitor) {
- this._monitor.dispose();
- }
- super.dispose();
- }
- /**
- * Handle `'activate-request'` messages.
- */
- protected onActivateRequest(msg: Message): void {
- this.node.tabIndex = -1;
- this.node.focus();
- }
- /**
- * Handle an `update-request` message to the widget.
- */
- protected onUpdateRequest(msg: Message): void {
- this._render();
- }
- /**
- * Render the mime content.
- */
- private _render(): Promise<void> {
- let context = this._context;
- let model = context.model;
- let data: JSONObject = {};
- if (this._dataType === 'string') {
- data[this._mimeType] = model.toString();
- } else {
- data[this._mimeType] = model.toJSON();
- }
- let mimeModel = new MimeModel({ data });
- if (!this._renderer) {
- this._renderer = this.rendermime.createRenderer(this._mimeType);
- (this.layout as PanelLayout).addWidget(this._renderer);
- }
- return this._renderer.renderModel(mimeModel);
- }
- /**
- * Handle a path change.
- */
- private _onPathChanged(): void {
- this.title.label = this._context.path.split('/').pop();
- }
- private _context: DocumentRegistry.Context = null;
- private _monitor: ActivityMonitor<any, any> = null;
- private _renderer: IRenderMime.IRenderer;
- private _mimeType: string;
- private _ready = new PromiseDelegate<void>();
- private _dataType: 'string' | 'json';
- }
- /**
- * The namespace for MimeRenderer class statics.
- */
- export
- namespace MimeRenderer {
- /**
- * The options used to initialize a MimeRenderer.
- */
- export
- interface IOptions {
- /**
- * The document context.
- */
- context: DocumentRegistry.Context;
- /**
- * The rendermime instance.
- */
- rendermime: RenderMime;
- /**
- * The mime type.
- */
- mimeType: string;
- /**
- * The render timeout.
- */
- renderTimeout: number;
- /**
- * Preferred data type from the model.
- */
- dataType?: 'string' | 'json';
- }
- }
- /**
- * An implementation of a widget factory for a rendered mimetype.
- */
- export
- class MimeRendererFactory extends ABCWidgetFactory<MimeRenderer, DocumentRegistry.IModel> {
- /**
- * Construct a new markdown widget factory.
- */
- constructor(options: MimeRendererFactory.IOptions) {
- super(Private.createRegistryOptions(options));
- this._rendermime = options.rendermime;
- this._mimeType = options.mimeType;
- this._renderTimeout = options.renderTimeout || 1000;
- this._dataType = options.dataType || 'string';
- this._iconClass = options.iconClass || '';
- this._iconLabel = options.iconLabel || '';
- }
- /**
- * Create a new widget given a context.
- */
- protected createNewWidget(context: DocumentRegistry.Context): MimeRenderer {
- let widget = new MimeRenderer({
- context,
- rendermime: this._rendermime.clone(),
- mimeType: this._mimeType,
- renderTimeout: this._renderTimeout,
- dataType: this._dataType,
- });
- widget.title.iconClass = this._iconClass;
- widget.title.iconLabel = this._iconLabel;
- return widget;
- }
- private _rendermime: RenderMime = null;
- private _mimeType: string;
- private _renderTimeout: number;
- private _dataType: 'string' | 'json';
- private _iconLabel: string;
- private _iconClass: string;
- }
- /**
- * The namespace for MimeRendererFactory class statics.
- */
- export
- namespace MimeRendererFactory {
- /**
- * The options used to initialize a MimeRendererFactory.
- */
- export
- interface IOptions extends DocumentRegistry.IWidgetFactoryOptions {
- /**
- * The rendermime instance.
- */
- rendermime: RenderMime;
- /**
- * The mime type.
- */
- mimeType: string;
- /**
- * The render timeout.
- */
- renderTimeout?: number;
- /**
- * The icon class name for the widget.
- */
- iconClass?: string;
- /**
- * The icon label for the widget.
- */
- iconLabel?: string;
- /**
- * Preferred data type from the model.
- */
- dataType?: 'string' | 'json';
- }
- }
- /**
- * The namespace for the module implementation details.
- */
- namespace Private {
- /**
- * Create the document registry options.
- */
- export
- function createRegistryOptions(options: MimeRendererFactory.IOptions): DocumentRegistry.IWidgetFactoryOptions {
- return { ...options, readOnly: true } as DocumentRegistry.IWidgetFactoryOptions;
- }
- }
|