@jupyterlab/docregistry
Version:
JupyterLab - Document Registry
249 lines • 8.57 kB
JavaScript
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
import { Printing, showErrorMessage } from '@jupyterlab/apputils';
import { IEditorMimeTypeService } from '@jupyterlab/codeeditor';
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]
: IEditorMimeTypeService.defaultMimeType;
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,
markdownParser: rendermime.markdownParser
});
}
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 {
...options,
readOnly: true
};
}
Private.createRegistryOptions = createRegistryOptions;
})(Private || (Private = {}));
//# sourceMappingURL=mimedocument.js.map