UNPKG

8.4 kBJavaScriptView Raw
1// Copyright (c) Jupyter Development Team.
2// Distributed under the terms of the Modified BSD License.
3import { Printing, showErrorMessage } from '@jupyterlab/apputils';
4import { ActivityMonitor } from '@jupyterlab/coreutils';
5import { MimeModel } from '@jupyterlab/rendermime';
6import { nullTranslator } from '@jupyterlab/translation';
7import { JSONExt, PromiseDelegate } from '@lumino/coreutils';
8import { MessageLoop } from '@lumino/messaging';
9import { StackedLayout, Widget } from '@lumino/widgets';
10import { ABCWidgetFactory, DocumentWidget } from './default';
11/**
12 * A content widget for a rendered mimetype document.
13 */
14export class MimeContent extends Widget {
15 /**
16 * Construct a new widget.
17 */
18 constructor(options) {
19 super();
20 /**
21 * A bound change callback.
22 */
23 this._changeCallback = (options) => {
24 if (!options.data || !options.data[this.mimeType]) {
25 return;
26 }
27 const data = options.data[this.mimeType];
28 if (typeof data === 'string') {
29 if (data !== this._context.model.toString()) {
30 this._context.model.fromString(data);
31 }
32 }
33 else if (data !== null &&
34 data !== undefined &&
35 !JSONExt.deepEqual(data, this._context.model.toJSON())) {
36 this._context.model.fromJSON(data);
37 }
38 };
39 this._fragment = '';
40 this._ready = new PromiseDelegate();
41 this._isRendering = false;
42 this._renderRequested = false;
43 this.addClass('jp-MimeDocument');
44 this.translator = options.translator || nullTranslator;
45 this._trans = this.translator.load('jupyterlab');
46 this.mimeType = options.mimeType;
47 this._dataType = options.dataType || 'string';
48 this._context = options.context;
49 this.renderer = options.renderer;
50 const layout = (this.layout = new StackedLayout());
51 layout.addWidget(this.renderer);
52 this._context.ready
53 .then(() => {
54 return this._render();
55 })
56 .then(() => {
57 // After rendering for the first time, send an activation request if we
58 // are currently focused.
59 if (this.node === document.activeElement) {
60 // We want to synchronously send (not post) the activate message, while
61 // we know this node still has focus.
62 MessageLoop.sendMessage(this.renderer, Widget.Msg.ActivateRequest);
63 }
64 // Throttle the rendering rate of the widget.
65 this._monitor = new ActivityMonitor({
66 signal: this._context.model.contentChanged,
67 timeout: options.renderTimeout
68 });
69 this._monitor.activityStopped.connect(this.update, this);
70 this._ready.resolve(undefined);
71 })
72 .catch(reason => {
73 // Dispose the document if rendering fails.
74 requestAnimationFrame(() => {
75 this.dispose();
76 });
77 void showErrorMessage(this._trans.__('Renderer Failure: %1', this._context.path), reason);
78 });
79 }
80 /**
81 * Print method. Deferred to the renderer.
82 */
83 [Printing.symbol]() {
84 return Printing.getPrintFunction(this.renderer);
85 }
86 /**
87 * A promise that resolves when the widget is ready.
88 */
89 get ready() {
90 return this._ready.promise;
91 }
92 /**
93 * Set URI fragment identifier.
94 */
95 setFragment(fragment) {
96 this._fragment = fragment;
97 this.update();
98 }
99 /**
100 * Dispose of the resources held by the widget.
101 */
102 dispose() {
103 if (this.isDisposed) {
104 return;
105 }
106 if (this._monitor) {
107 this._monitor.dispose();
108 }
109 this._monitor = null;
110 super.dispose();
111 }
112 /**
113 * Handle an `update-request` message to the widget.
114 */
115 onUpdateRequest(msg) {
116 if (this._context.isReady) {
117 void this._render();
118 this._fragment = '';
119 }
120 }
121 /**
122 * Render the mime content.
123 */
124 async _render() {
125 if (this.isDisposed) {
126 return;
127 }
128 // Since rendering is async, we note render requests that happen while we
129 // actually are rendering for a future rendering.
130 if (this._isRendering) {
131 this._renderRequested = true;
132 return;
133 }
134 // Set up for this rendering pass.
135 this._renderRequested = false;
136 const context = this._context;
137 const model = context.model;
138 const data = {};
139 if (this._dataType === 'string') {
140 data[this.mimeType] = model.toString();
141 }
142 else {
143 data[this.mimeType] = model.toJSON();
144 }
145 const mimeModel = new MimeModel({
146 data,
147 callback: this._changeCallback,
148 metadata: { fragment: this._fragment }
149 });
150 try {
151 // Do the rendering asynchronously.
152 this._isRendering = true;
153 await this.renderer.renderModel(mimeModel);
154 this._isRendering = false;
155 // If there is an outstanding request to render, go ahead and render
156 if (this._renderRequested) {
157 return this._render();
158 }
159 }
160 catch (reason) {
161 // Dispose the document if rendering fails.
162 requestAnimationFrame(() => {
163 this.dispose();
164 });
165 void showErrorMessage(this._trans.__('Renderer Failure: %1', context.path), reason);
166 }
167 }
168}
169/**
170 * A document widget for mime content.
171 */
172export class MimeDocument extends DocumentWidget {
173 setFragment(fragment) {
174 this.content.setFragment(fragment);
175 }
176}
177/**
178 * An implementation of a widget factory for a rendered mimetype document.
179 */
180export class MimeDocumentFactory extends ABCWidgetFactory {
181 /**
182 * Construct a new mimetype widget factory.
183 */
184 constructor(options) {
185 super(Private.createRegistryOptions(options));
186 this._rendermime = options.rendermime;
187 this._renderTimeout = options.renderTimeout || 1000;
188 this._dataType = options.dataType || 'string';
189 this._fileType = options.primaryFileType;
190 this._factory = options.factory;
191 }
192 /**
193 * Create a new widget given a context.
194 */
195 createNewWidget(context) {
196 var _a, _b;
197 const ft = this._fileType;
198 const mimeType = (ft === null || ft === void 0 ? void 0 : ft.mimeTypes.length) ? ft.mimeTypes[0] : 'text/plain';
199 const rendermime = this._rendermime.clone({
200 resolver: context.urlResolver
201 });
202 let renderer;
203 if (this._factory && this._factory.mimeTypes.includes(mimeType)) {
204 renderer = this._factory.createRenderer({
205 mimeType,
206 resolver: rendermime.resolver,
207 sanitizer: rendermime.sanitizer,
208 linkHandler: rendermime.linkHandler,
209 latexTypesetter: rendermime.latexTypesetter
210 });
211 }
212 else {
213 renderer = rendermime.createRenderer(mimeType);
214 }
215 const content = new MimeContent({
216 context,
217 renderer,
218 mimeType,
219 renderTimeout: this._renderTimeout,
220 dataType: this._dataType
221 });
222 content.title.icon = ft === null || ft === void 0 ? void 0 : ft.icon;
223 content.title.iconClass = (_a = ft === null || ft === void 0 ? void 0 : ft.iconClass) !== null && _a !== void 0 ? _a : '';
224 content.title.iconLabel = (_b = ft === null || ft === void 0 ? void 0 : ft.iconLabel) !== null && _b !== void 0 ? _b : '';
225 const widget = new MimeDocument({ content, context });
226 return widget;
227 }
228}
229/**
230 * The namespace for the module implementation details.
231 */
232var Private;
233(function (Private) {
234 /**
235 * Create the document registry options.
236 */
237 function createRegistryOptions(options) {
238 return Object.assign(Object.assign({}, options), { readOnly: true });
239 }
240 Private.createRegistryOptions = createRegistryOptions;
241})(Private || (Private = {}));
242//# sourceMappingURL=mimedocument.js.map
\No newline at end of file