UNPKG

@jupyterlab/docregistry

Version:
242 lines 8.4 kB
// Copyright (c) Jupyter Development Team. // Distributed under the terms of the Modified BSD License. import { Printing, showErrorMessage } from '@jupyterlab/apputils'; import { ActivityMonitor } from '@jupyterlab/coreutils'; import { MimeModel } from '@jupyterlab/rendermime'; import { nullTranslator } from '@jupyterlab/translation'; import { JSONExt, PromiseDelegate } from '@lumino/coreutils'; import { MessageLoop } from '@lumino/messaging'; import { StackedLayout, Widget } from '@lumino/widgets'; import { ABCWidgetFactory, DocumentWidget } from './default'; /** * A content widget for a rendered mimetype document. */ export class MimeContent extends Widget { /** * Construct a new widget. */ constructor(options) { super(); /** * A bound change callback. */ this._changeCallback = (options) => { if (!options.data || !options.data[this.mimeType]) { return; } const data = options.data[this.mimeType]; if (typeof data === 'string') { if (data !== this._context.model.toString()) { this._context.model.fromString(data); } } else if (data !== null && data !== undefined && !JSONExt.deepEqual(data, this._context.model.toJSON())) { this._context.model.fromJSON(data); } }; this._fragment = ''; this._ready = new PromiseDelegate(); this._isRendering = false; this._renderRequested = false; this.addClass('jp-MimeDocument'); this.translator = options.translator || nullTranslator; this._trans = this.translator.load('jupyterlab'); this.mimeType = options.mimeType; this._dataType = options.dataType || 'string'; this._context = options.context; this.renderer = options.renderer; const layout = (this.layout = new StackedLayout()); layout.addWidget(this.renderer); this._context.ready .then(() => { return this._render(); }) .then(() => { // After rendering for the first time, send an activation request if we // are currently focused. if (this.node === document.activeElement) { // We want to synchronously send (not post) the activate message, while // we know this node still has focus. MessageLoop.sendMessage(this.renderer, Widget.Msg.ActivateRequest); } // Throttle the rendering rate of the widget. this._monitor = new ActivityMonitor({ signal: this._context.model.contentChanged, timeout: options.renderTimeout }); this._monitor.activityStopped.connect(this.update, this); this._ready.resolve(undefined); }) .catch(reason => { // Dispose the document if rendering fails. requestAnimationFrame(() => { this.dispose(); }); void showErrorMessage(this._trans.__('Renderer Failure: %1', this._context.path), reason); }); } /** * Print method. Deferred to the renderer. */ [Printing.symbol]() { return Printing.getPrintFunction(this.renderer); } /** * A promise that resolves when the widget is ready. */ get ready() { return this._ready.promise; } /** * Set URI fragment identifier. */ setFragment(fragment) { this._fragment = fragment; this.update(); } /** * Dispose of the resources held by the widget. */ dispose() { if (this.isDisposed) { return; } if (this._monitor) { this._monitor.dispose(); } this._monitor = null; super.dispose(); } /** * Handle an `update-request` message to the widget. */ onUpdateRequest(msg) { if (this._context.isReady) { void this._render(); this._fragment = ''; } } /** * Render the mime content. */ async _render() { if (this.isDisposed) { return; } // Since rendering is async, we note render requests that happen while we // actually are rendering for a future rendering. if (this._isRendering) { this._renderRequested = true; return; } // Set up for this rendering pass. this._renderRequested = false; const context = this._context; const model = context.model; const data = {}; if (this._dataType === 'string') { data[this.mimeType] = model.toString(); } else { data[this.mimeType] = model.toJSON(); } const mimeModel = new MimeModel({ data, callback: this._changeCallback, metadata: { fragment: this._fragment } }); try { // Do the rendering asynchronously. this._isRendering = true; await this.renderer.renderModel(mimeModel); this._isRendering = false; // If there is an outstanding request to render, go ahead and render if (this._renderRequested) { return this._render(); } } catch (reason) { // Dispose the document if rendering fails. requestAnimationFrame(() => { this.dispose(); }); void showErrorMessage(this._trans.__('Renderer Failure: %1', context.path), reason); } } } /** * A document widget for mime content. */ export class MimeDocument extends DocumentWidget { setFragment(fragment) { this.content.setFragment(fragment); } } /** * An implementation of a widget factory for a rendered mimetype document. */ export class MimeDocumentFactory extends ABCWidgetFactory { /** * Construct a new mimetype widget factory. */ constructor(options) { super(Private.createRegistryOptions(options)); this._rendermime = options.rendermime; this._renderTimeout = options.renderTimeout || 1000; this._dataType = options.dataType || 'string'; this._fileType = options.primaryFileType; this._factory = options.factory; } /** * Create a new widget given a context. */ createNewWidget(context) { var _a, _b; const ft = this._fileType; const mimeType = (ft === null || ft === void 0 ? void 0 : ft.mimeTypes.length) ? ft.mimeTypes[0] : 'text/plain'; const rendermime = this._rendermime.clone({ resolver: context.urlResolver }); let renderer; if (this._factory && this._factory.mimeTypes.includes(mimeType)) { renderer = this._factory.createRenderer({ mimeType, resolver: rendermime.resolver, sanitizer: rendermime.sanitizer, linkHandler: rendermime.linkHandler, latexTypesetter: rendermime.latexTypesetter }); } else { renderer = rendermime.createRenderer(mimeType); } const content = new MimeContent({ context, renderer, mimeType, renderTimeout: this._renderTimeout, dataType: this._dataType }); content.title.icon = ft === null || ft === void 0 ? void 0 : ft.icon; content.title.iconClass = (_a = ft === null || ft === void 0 ? void 0 : ft.iconClass) !== null && _a !== void 0 ? _a : ''; content.title.iconLabel = (_b = ft === null || ft === void 0 ? void 0 : ft.iconLabel) !== null && _b !== void 0 ? _b : ''; const widget = new MimeDocument({ content, context }); return widget; } } /** * The namespace for the module implementation details. */ var Private; (function (Private) { /** * Create the document registry options. */ function createRegistryOptions(options) { return Object.assign(Object.assign({}, options), { readOnly: true }); } Private.createRegistryOptions = createRegistryOptions; })(Private || (Private = {})); //# sourceMappingURL=mimedocument.js.map