UNPKG

14.2 kBJavaScriptView Raw
1/* -----------------------------------------------------------------------------
2| Copyright (c) Jupyter Development Team.
3| Distributed under the terms of the Modified BSD License.
4|----------------------------------------------------------------------------*/
5import { Signal } from '@lumino/signaling';
6import { AttachmentsModel } from '@jupyterlab/attachments';
7import { CodeEditor } from '@jupyterlab/codeeditor';
8import { OutputAreaModel } from '@jupyterlab/outputarea';
9import { createMutex, createStandaloneCell } from '@jupyter/ydoc';
10const globalModelDBMutex = createMutex();
11export function isCodeCellModel(model) {
12 return model.type === 'code';
13}
14export function isMarkdownCellModel(model) {
15 return model.type === 'markdown';
16}
17export function isRawCellModel(model) {
18 return model.type === 'raw';
19}
20/**
21 * An implementation of the cell model.
22 */
23export class CellModel extends CodeEditor.Model {
24 constructor(options = {}) {
25 const { cell_type, sharedModel, ...others } = options;
26 super({
27 sharedModel: sharedModel !== null && sharedModel !== void 0 ? sharedModel : createStandaloneCell({
28 cell_type: cell_type !== null && cell_type !== void 0 ? cell_type : 'raw',
29 id: options.id
30 }),
31 ...others
32 });
33 /**
34 * A signal emitted when the state of the model changes.
35 */
36 this.contentChanged = new Signal(this);
37 /**
38 * A signal emitted when a model state changes.
39 */
40 this.stateChanged = new Signal(this);
41 this._metadataChanged = new Signal(this);
42 this._trusted = false;
43 this.standaloneModel = typeof options.sharedModel === 'undefined';
44 this.trusted = !!this.getMetadata('trusted') || !!options.trusted;
45 this.sharedModel.changed.connect(this.onGenericChange, this);
46 this.sharedModel.metadataChanged.connect(this._onMetadataChanged, this);
47 }
48 /**
49 * Signal emitted when cell metadata changes.
50 */
51 get metadataChanged() {
52 return this._metadataChanged;
53 }
54 /**
55 * The id for the cell.
56 */
57 get id() {
58 return this.sharedModel.getId();
59 }
60 /**
61 * The metadata associated with the cell.
62 */
63 get metadata() {
64 return this.sharedModel.metadata;
65 }
66 /**
67 * The trusted state of the model.
68 */
69 get trusted() {
70 return this._trusted;
71 }
72 set trusted(newValue) {
73 const oldValue = this.trusted;
74 if (oldValue !== newValue) {
75 this._trusted = newValue;
76 this.onTrustedChanged(this, { newValue, oldValue });
77 }
78 }
79 /**
80 * Dispose of the resources held by the model.
81 */
82 dispose() {
83 if (this.isDisposed) {
84 return;
85 }
86 this.sharedModel.changed.disconnect(this.onGenericChange, this);
87 this.sharedModel.metadataChanged.disconnect(this._onMetadataChanged, this);
88 super.dispose();
89 }
90 /**
91 * Handle a change to the trusted state.
92 *
93 * The default implementation is a no-op.
94 */
95 onTrustedChanged(trusted, args) {
96 /* no-op */
97 }
98 /**
99 * Delete a metadata
100 *
101 * @param key Metadata key
102 */
103 deleteMetadata(key) {
104 return this.sharedModel.deleteMetadata(key);
105 }
106 /**
107 * Get a metadata
108 *
109 * ### Notes
110 * This returns a copy of the key value.
111 *
112 * @param key Metadata key
113 */
114 getMetadata(key) {
115 return this.sharedModel.getMetadata(key);
116 }
117 /**
118 * Set a metadata
119 *
120 * @param key Metadata key
121 * @param value Metadata value
122 */
123 setMetadata(key, value) {
124 if (typeof value === 'undefined') {
125 this.sharedModel.deleteMetadata(key);
126 }
127 else {
128 this.sharedModel.setMetadata(key, value);
129 }
130 }
131 /**
132 * Serialize the model to JSON.
133 */
134 toJSON() {
135 return this.sharedModel.toJSON();
136 }
137 /**
138 * Handle a change to the observable value.
139 */
140 onGenericChange() {
141 this.contentChanged.emit(void 0);
142 }
143 _onMetadataChanged(sender, change) {
144 this._metadataChanged.emit(change);
145 }
146}
147/**
148 * A base implementation for cell models with attachments.
149 */
150export class AttachmentsCellModel extends CellModel {
151 /**
152 * Construct a new cell with optional attachments.
153 */
154 constructor(options) {
155 var _a;
156 super(options);
157 const factory = (_a = options.contentFactory) !== null && _a !== void 0 ? _a : AttachmentsCellModel.defaultContentFactory;
158 const values = this.sharedModel.getAttachments();
159 this._attachments = factory.createAttachmentsModel({ values });
160 this._attachments.stateChanged.connect(this.onGenericChange, this);
161 this._attachments.changed.connect(this._onAttachmentsChange, this);
162 this.sharedModel.changed.connect(this._onSharedModelChanged, this);
163 }
164 /**
165 * Get the attachments of the model.
166 */
167 get attachments() {
168 return this._attachments;
169 }
170 /**
171 * Dispose of the resources held by the model.
172 */
173 dispose() {
174 if (this.isDisposed) {
175 return;
176 }
177 this._attachments.stateChanged.disconnect(this.onGenericChange, this);
178 this._attachments.changed.disconnect(this._onAttachmentsChange, this);
179 this._attachments.dispose();
180 this.sharedModel.changed.disconnect(this._onSharedModelChanged, this);
181 super.dispose();
182 }
183 /**
184 * Serialize the model to JSON.
185 */
186 toJSON() {
187 return super.toJSON();
188 }
189 /**
190 * Handle a change to the cell outputs modelDB and reflect it in the shared model.
191 */
192 _onAttachmentsChange(sender, event) {
193 const cell = this.sharedModel;
194 globalModelDBMutex(() => cell.setAttachments(sender.toJSON()));
195 }
196 /**
197 * Handle a change to the code cell value.
198 */
199 _onSharedModelChanged(slot, change) {
200 if (change.attachmentsChange) {
201 const cell = this.sharedModel;
202 globalModelDBMutex(() => { var _a; return this._attachments.fromJSON((_a = cell.getAttachments()) !== null && _a !== void 0 ? _a : {}); });
203 }
204 }
205}
206/**
207 * The namespace for `AttachmentsCellModel` statics.
208 */
209(function (AttachmentsCellModel) {
210 /**
211 * The default implementation of an `IContentFactory`.
212 */
213 class ContentFactory {
214 /**
215 * Create an attachments model.
216 */
217 createAttachmentsModel(options) {
218 return new AttachmentsModel(options);
219 }
220 }
221 AttachmentsCellModel.ContentFactory = ContentFactory;
222 /**
223 * The shared `ContentFactory` instance.
224 */
225 AttachmentsCellModel.defaultContentFactory = new ContentFactory();
226})(AttachmentsCellModel || (AttachmentsCellModel = {}));
227/**
228 * An implementation of a raw cell model.
229 */
230export class RawCellModel extends AttachmentsCellModel {
231 /**
232 * Construct a raw cell model from optional shared model.
233 */
234 constructor(options = {}) {
235 super({
236 cell_type: 'raw',
237 ...options
238 });
239 }
240 /**
241 * The type of the cell.
242 */
243 get type() {
244 return 'raw';
245 }
246 /**
247 * Serialize the model to JSON.
248 */
249 toJSON() {
250 return super.toJSON();
251 }
252}
253/**
254 * An implementation of a markdown cell model.
255 */
256export class MarkdownCellModel extends AttachmentsCellModel {
257 /**
258 * Construct a markdown cell model from optional shared model.
259 */
260 constructor(options = {}) {
261 super({
262 cell_type: 'markdown',
263 ...options
264 });
265 // Use the Github-flavored markdown mode.
266 this.mimeType = 'text/x-ipythongfm';
267 }
268 /**
269 * The type of the cell.
270 */
271 get type() {
272 return 'markdown';
273 }
274 /**
275 * Serialize the model to JSON.
276 */
277 toJSON() {
278 return super.toJSON();
279 }
280}
281/**
282 * An implementation of a code cell Model.
283 */
284export class CodeCellModel extends CellModel {
285 /**
286 * Construct a new code cell with optional original cell content.
287 */
288 constructor(options = {}) {
289 var _a;
290 super({
291 cell_type: 'code',
292 ...options
293 });
294 this._executedCode = '';
295 this._isDirty = false;
296 const factory = (_a = options === null || options === void 0 ? void 0 : options.contentFactory) !== null && _a !== void 0 ? _a : CodeCellModel.defaultContentFactory;
297 const trusted = this.trusted;
298 const outputs = this.sharedModel.getOutputs();
299 this._outputs = factory.createOutputArea({ trusted, values: outputs });
300 this.sharedModel.changed.connect(this._onSharedModelChanged, this);
301 this._outputs.changed.connect(this.onGenericChange, this);
302 this._outputs.changed.connect(this.onOutputsChange, this);
303 }
304 /**
305 * The type of the cell.
306 */
307 get type() {
308 return 'code';
309 }
310 /**
311 * The execution count of the cell.
312 */
313 get executionCount() {
314 return this.sharedModel.execution_count || null;
315 }
316 set executionCount(newValue) {
317 this.sharedModel.execution_count = newValue || null;
318 }
319 /**
320 * Whether the cell is dirty or not.
321 *
322 * A cell is dirty if it is output is not empty and does not
323 * result of the input code execution.
324 */
325 get isDirty() {
326 // Test could be done dynamically with this._executedCode
327 // but for performance reason, the diff status is stored in a boolean.
328 return this._isDirty;
329 }
330 /**
331 * The cell outputs.
332 */
333 get outputs() {
334 return this._outputs;
335 }
336 clearExecution() {
337 this.outputs.clear();
338 this.executionCount = null;
339 this._setDirty(false);
340 this.sharedModel.deleteMetadata('execution');
341 // We trust this cell as it no longer has any outputs.
342 this.trusted = true;
343 }
344 /**
345 * Dispose of the resources held by the model.
346 */
347 dispose() {
348 if (this.isDisposed) {
349 return;
350 }
351 this.sharedModel.changed.disconnect(this._onSharedModelChanged, this);
352 this._outputs.changed.disconnect(this.onGenericChange, this);
353 this._outputs.changed.disconnect(this.onOutputsChange, this);
354 this._outputs.dispose();
355 this._outputs = null;
356 super.dispose();
357 }
358 /**
359 * Handle a change to the trusted state.
360 */
361 onTrustedChanged(trusted, args) {
362 const newTrusted = args.newValue;
363 if (this._outputs) {
364 this._outputs.trusted = newTrusted;
365 }
366 if (newTrusted) {
367 const codeCell = this.sharedModel;
368 const metadata = codeCell.getMetadata();
369 metadata.trusted = true;
370 codeCell.setMetadata(metadata);
371 }
372 this.stateChanged.emit({
373 name: 'trusted',
374 oldValue: args.oldValue,
375 newValue: newTrusted
376 });
377 }
378 /**
379 * Serialize the model to JSON.
380 */
381 toJSON() {
382 return super.toJSON();
383 }
384 /**
385 * Handle a change to the cell outputs modelDB and reflect it in the shared model.
386 */
387 onOutputsChange(sender, event) {
388 const codeCell = this.sharedModel;
389 globalModelDBMutex(() => {
390 switch (event.type) {
391 case 'add': {
392 const outputs = event.newValues.map(output => output.toJSON());
393 codeCell.updateOutputs(event.newIndex, event.newIndex, outputs);
394 break;
395 }
396 case 'set': {
397 const newValues = event.newValues.map(output => output.toJSON());
398 codeCell.updateOutputs(event.oldIndex, event.oldIndex + newValues.length, newValues);
399 break;
400 }
401 case 'remove':
402 codeCell.updateOutputs(event.oldIndex, event.oldValues.length);
403 break;
404 default:
405 throw new Error(`Invalid event type: ${event.type}`);
406 }
407 });
408 }
409 /**
410 * Handle a change to the code cell value.
411 */
412 _onSharedModelChanged(slot, change) {
413 if (change.outputsChange) {
414 globalModelDBMutex(() => {
415 this.outputs.clear();
416 slot.getOutputs().forEach(output => this._outputs.add(output));
417 });
418 }
419 if (change.executionCountChange) {
420 if (change.executionCountChange.newValue &&
421 (this.isDirty || !change.executionCountChange.oldValue)) {
422 this._setDirty(false);
423 }
424 this.stateChanged.emit({
425 name: 'executionCount',
426 oldValue: change.executionCountChange.oldValue,
427 newValue: change.executionCountChange.newValue
428 });
429 }
430 if (change.sourceChange && this.executionCount !== null) {
431 this._setDirty(this._executedCode !== this.sharedModel.getSource().trim());
432 }
433 }
434 /**
435 * Set whether the cell is dirty or not.
436 */
437 _setDirty(v) {
438 if (!v) {
439 this._executedCode = this.sharedModel.getSource().trim();
440 }
441 if (v !== this._isDirty) {
442 this._isDirty = v;
443 this.stateChanged.emit({
444 name: 'isDirty',
445 oldValue: !v,
446 newValue: v
447 });
448 }
449 }
450}
451/**
452 * The namespace for `CodeCellModel` statics.
453 */
454(function (CodeCellModel) {
455 /**
456 * The default implementation of an `IContentFactory`.
457 */
458 class ContentFactory {
459 /**
460 * Create an output area.
461 */
462 createOutputArea(options) {
463 return new OutputAreaModel(options);
464 }
465 }
466 CodeCellModel.ContentFactory = ContentFactory;
467 /**
468 * The shared `ContentFactory` instance.
469 */
470 CodeCellModel.defaultContentFactory = new ContentFactory();
471})(CodeCellModel || (CodeCellModel = {}));
472//# sourceMappingURL=model.js.map
\No newline at end of file