UNPKG

10.8 kBJavaScriptView Raw
1/* -----------------------------------------------------------------------------
2| Copyright (c) Jupyter Development Team.
3| Distributed under the terms of the Modified BSD License.
4|----------------------------------------------------------------------------*/
5import { defaultSanitizer } from '@jupyterlab/apputils';
6import { PathExt, URLExt } from '@jupyterlab/coreutils';
7import { nullTranslator } from '@jupyterlab/translation';
8import { MimeModel } from './mimemodel';
9/**
10 * An object which manages mime renderer factories.
11 *
12 * This object is used to render mime models using registered mime
13 * renderers, selecting the preferred mime renderer to render the
14 * model into a widget.
15 *
16 * #### Notes
17 * This class is not intended to be subclassed.
18 */
19export class RenderMimeRegistry {
20 /**
21 * Construct a new rendermime.
22 *
23 * @param options - The options for initializing the instance.
24 */
25 constructor(options = {}) {
26 this._id = 0;
27 this._ranks = {};
28 this._types = null;
29 this._factories = {};
30 // Parse the options.
31 this.translator = options.translator || nullTranslator;
32 this.resolver = options.resolver || null;
33 this.linkHandler = options.linkHandler || null;
34 this.latexTypesetter = options.latexTypesetter || null;
35 this.sanitizer = options.sanitizer || defaultSanitizer;
36 // Add the initial factories.
37 if (options.initialFactories) {
38 for (const factory of options.initialFactories) {
39 this.addFactory(factory);
40 }
41 }
42 }
43 /**
44 * The ordered list of mimeTypes.
45 */
46 get mimeTypes() {
47 return this._types || (this._types = Private.sortedTypes(this._ranks));
48 }
49 /**
50 * Find the preferred mime type for a mime bundle.
51 *
52 * @param bundle - The bundle of mime data.
53 *
54 * @param safe - How to consider safe/unsafe factories. If 'ensure',
55 * it will only consider safe factories. If 'any', any factory will be
56 * considered. If 'prefer', unsafe factories will be considered, but
57 * only after the safe options have been exhausted.
58 *
59 * @returns The preferred mime type from the available factories,
60 * or `undefined` if the mime type cannot be rendered.
61 */
62 preferredMimeType(bundle, safe = 'ensure') {
63 // Try to find a safe factory first, if preferred.
64 if (safe === 'ensure' || safe === 'prefer') {
65 for (const mt of this.mimeTypes) {
66 if (mt in bundle && this._factories[mt].safe) {
67 return mt;
68 }
69 }
70 }
71 if (safe !== 'ensure') {
72 // Otherwise, search for the best factory among all factories.
73 for (const mt of this.mimeTypes) {
74 if (mt in bundle) {
75 return mt;
76 }
77 }
78 }
79 // Otherwise, no matching mime type exists.
80 return undefined;
81 }
82 /**
83 * Create a renderer for a mime type.
84 *
85 * @param mimeType - The mime type of interest.
86 *
87 * @returns A new renderer for the given mime type.
88 *
89 * @throws An error if no factory exists for the mime type.
90 */
91 createRenderer(mimeType) {
92 // Throw an error if no factory exists for the mime type.
93 if (!(mimeType in this._factories)) {
94 throw new Error(`No factory for mime type: '${mimeType}'`);
95 }
96 // Invoke the best factory for the given mime type.
97 return this._factories[mimeType].createRenderer({
98 mimeType,
99 resolver: this.resolver,
100 sanitizer: this.sanitizer,
101 linkHandler: this.linkHandler,
102 latexTypesetter: this.latexTypesetter,
103 translator: this.translator
104 });
105 }
106 /**
107 * Create a new mime model. This is a convenience method.
108 *
109 * @options - The options used to create the model.
110 *
111 * @returns A new mime model.
112 */
113 createModel(options = {}) {
114 return new MimeModel(options);
115 }
116 /**
117 * Create a clone of this rendermime instance.
118 *
119 * @param options - The options for configuring the clone.
120 *
121 * @returns A new independent clone of the rendermime.
122 */
123 clone(options = {}) {
124 // Create the clone.
125 const clone = new RenderMimeRegistry({
126 resolver: options.resolver || this.resolver || undefined,
127 sanitizer: options.sanitizer || this.sanitizer || undefined,
128 linkHandler: options.linkHandler || this.linkHandler || undefined,
129 latexTypesetter: options.latexTypesetter || this.latexTypesetter || undefined,
130 translator: this.translator
131 });
132 // Clone the internal state.
133 clone._factories = Object.assign({}, this._factories);
134 clone._ranks = Object.assign({}, this._ranks);
135 clone._id = this._id;
136 // Return the cloned object.
137 return clone;
138 }
139 /**
140 * Get the renderer factory registered for a mime type.
141 *
142 * @param mimeType - The mime type of interest.
143 *
144 * @returns The factory for the mime type, or `undefined`.
145 */
146 getFactory(mimeType) {
147 return this._factories[mimeType];
148 }
149 /**
150 * Add a renderer factory to the rendermime.
151 *
152 * @param factory - The renderer factory of interest.
153 *
154 * @param rank - The rank of the renderer. A lower rank indicates
155 * a higher priority for rendering. If not given, the rank will
156 * defer to the `defaultRank` of the factory. If no `defaultRank`
157 * is given, it will default to 100.
158 *
159 * #### Notes
160 * The renderer will replace an existing renderer for the given
161 * mimeType.
162 */
163 addFactory(factory, rank) {
164 if (rank === undefined) {
165 rank = factory.defaultRank;
166 if (rank === undefined) {
167 rank = 100;
168 }
169 }
170 for (const mt of factory.mimeTypes) {
171 this._factories[mt] = factory;
172 this._ranks[mt] = { rank, id: this._id++ };
173 }
174 this._types = null;
175 }
176 /**
177 * Remove a mime type.
178 *
179 * @param mimeType - The mime type of interest.
180 */
181 removeMimeType(mimeType) {
182 delete this._factories[mimeType];
183 delete this._ranks[mimeType];
184 this._types = null;
185 }
186 /**
187 * Get the rank for a given mime type.
188 *
189 * @param mimeType - The mime type of interest.
190 *
191 * @returns The rank of the mime type or undefined.
192 */
193 getRank(mimeType) {
194 const rank = this._ranks[mimeType];
195 return rank && rank.rank;
196 }
197 /**
198 * Set the rank of a given mime type.
199 *
200 * @param mimeType - The mime type of interest.
201 *
202 * @param rank - The new rank to assign.
203 *
204 * #### Notes
205 * This is a no-op if the mime type is not registered.
206 */
207 setRank(mimeType, rank) {
208 if (!this._ranks[mimeType]) {
209 return;
210 }
211 const id = this._id++;
212 this._ranks[mimeType] = { rank, id };
213 this._types = null;
214 }
215}
216/**
217 * The namespace for `RenderMimeRegistry` class statics.
218 */
219(function (RenderMimeRegistry) {
220 /**
221 * A default resolver that uses a given reference path and a contents manager.
222 */
223 class UrlResolver {
224 /**
225 * Create a new url resolver.
226 */
227 constructor(options) {
228 if (options.path) {
229 this._path = options.path;
230 }
231 else if (options.session) {
232 this._session = options.session;
233 }
234 else {
235 throw new Error("Either 'path' or 'session' must be given as a constructor option");
236 }
237 this._contents = options.contents;
238 }
239 /**
240 * The path of the object, from which local urls can be derived.
241 */
242 get path() {
243 var _a;
244 return (_a = this._path) !== null && _a !== void 0 ? _a : this._session.path;
245 }
246 set path(value) {
247 this._path = value;
248 }
249 /**
250 * Resolve a relative url to an absolute url path.
251 */
252 async resolveUrl(url) {
253 if (this.isLocal(url)) {
254 const cwd = encodeURI(PathExt.dirname(this.path));
255 url = PathExt.resolve(cwd, url);
256 }
257 return url;
258 }
259 /**
260 * Get the download url of a given absolute url path.
261 *
262 * #### Notes
263 * The returned URL may include a query parameter.
264 */
265 async getDownloadUrl(urlPath) {
266 if (this.isLocal(urlPath)) {
267 // decode url->path before passing to contents api
268 return this._contents.getDownloadUrl(decodeURIComponent(urlPath));
269 }
270 return urlPath;
271 }
272 /**
273 * Whether the URL should be handled by the resolver
274 * or not.
275 *
276 * #### Notes
277 * This is similar to the `isLocal` check in `URLExt`,
278 * but it also checks whether the path points to any
279 * of the `IDrive`s that may be registered with the contents
280 * manager.
281 */
282 isLocal(url) {
283 if (this.isMalformed(url)) {
284 return false;
285 }
286 return URLExt.isLocal(url) || !!this._contents.driveName(decodeURI(url));
287 }
288 /**
289 * Whether the URL can be decoded using `decodeURI`.
290 */
291 isMalformed(url) {
292 try {
293 decodeURI(url);
294 return false;
295 }
296 catch (error) {
297 if (error instanceof URIError) {
298 return true;
299 }
300 throw error;
301 }
302 }
303 }
304 RenderMimeRegistry.UrlResolver = UrlResolver;
305})(RenderMimeRegistry || (RenderMimeRegistry = {}));
306/**
307 * The namespace for the module implementation details.
308 */
309var Private;
310(function (Private) {
311 /**
312 * Get the mime types in the map, ordered by rank.
313 */
314 function sortedTypes(map) {
315 return Object.keys(map).sort((a, b) => {
316 const p1 = map[a];
317 const p2 = map[b];
318 if (p1.rank !== p2.rank) {
319 return p1.rank - p2.rank;
320 }
321 return p1.id - p2.id;
322 });
323 }
324 Private.sortedTypes = sortedTypes;
325 function sessionConnection(s) {
326 return s.sessionChanged
327 ? s.session
328 : s;
329 }
330 Private.sessionConnection = sessionConnection;
331})(Private || (Private = {}));
332//# sourceMappingURL=registry.js.map
\No newline at end of file