UNPKG

@jupyterlab/notebook

Version:
362 lines 11.5 kB
// Copyright (c) Jupyter Development Team. // Distributed under the terms of the Modified BSD License. import { Dialog, showDialog } from '@jupyterlab/apputils'; import * as nbformat from '@jupyterlab/nbformat'; import { YNotebook } from '@jupyter/ydoc'; import { nullTranslator } from '@jupyterlab/translation'; import { JSONExt } from '@lumino/coreutils'; import { Signal } from '@lumino/signaling'; import { CellList } from './celllist'; /** * An implementation of a notebook Model. */ export class NotebookModel { /** * Construct a new notebook model. */ constructor(options = {}) { var _a, _b; /** * Whether the model should disposed the shared model on disposal or not. */ this.standaloneModel = false; this._dirty = false; this._readOnly = false; this._contentChanged = new Signal(this); this._stateChanged = new Signal(this); this._isDisposed = false; this._metadataChanged = new Signal(this); this.standaloneModel = typeof options.sharedModel === 'undefined'; if (options.sharedModel) { this.sharedModel = options.sharedModel; } else { this.sharedModel = YNotebook.create({ disableDocumentWideUndoRedo: (_a = options.disableDocumentWideUndoRedo) !== null && _a !== void 0 ? _a : true, data: { nbformat: nbformat.MAJOR_VERSION, nbformat_minor: nbformat.MINOR_VERSION, metadata: { kernelspec: { name: '', display_name: '' }, language_info: { name: (_b = options.languagePreference) !== null && _b !== void 0 ? _b : '' } } } }); } this._cells = new CellList(this.sharedModel); this._trans = (options.translator || nullTranslator).load('jupyterlab'); this._deletedCells = []; this._collaborationEnabled = !!(options === null || options === void 0 ? void 0 : options.collaborationEnabled); this._cells.changed.connect(this._onCellsChanged, this); this.sharedModel.changed.connect(this._onStateChanged, this); this.sharedModel.metadataChanged.connect(this._onMetadataChanged, this); } /** * A signal emitted when the document content changes. */ get contentChanged() { return this._contentChanged; } /** * Signal emitted when notebook metadata changes. */ get metadataChanged() { return this._metadataChanged; } /** * A signal emitted when the document state changes. */ get stateChanged() { return this._stateChanged; } /** * Get the observable list of notebook cells. */ get cells() { return this._cells; } /** * The dirty state of the document. */ get dirty() { return this._dirty; } set dirty(newValue) { const oldValue = this._dirty; if (newValue === oldValue) { return; } this._dirty = newValue; this.triggerStateChange({ name: 'dirty', oldValue, newValue }); } /** * The read only state of the document. */ get readOnly() { return this._readOnly; } set readOnly(newValue) { if (newValue === this._readOnly) { return; } const oldValue = this._readOnly; this._readOnly = newValue; this.triggerStateChange({ name: 'readOnly', oldValue, newValue }); } /** * The metadata associated with the notebook. * * ### Notes * This is a copy of the metadata. Changing a part of it * won't affect the model. * As this returns a copy of all metadata, it is advised to * use `getMetadata` to speed up the process of getting a single key. */ get metadata() { return this.sharedModel.metadata; } /** * The major version number of the nbformat. */ get nbformat() { return this.sharedModel.nbformat; } /** * The minor version number of the nbformat. */ get nbformatMinor() { return this.sharedModel.nbformat_minor; } /** * The default kernel name of the document. */ get defaultKernelName() { var _a; const spec = this.getMetadata('kernelspec'); return (_a = spec === null || spec === void 0 ? void 0 : spec.name) !== null && _a !== void 0 ? _a : ''; } /** * A list of deleted cells for the notebook.. */ get deletedCells() { return this._deletedCells; } /** * The default kernel language of the document. */ get defaultKernelLanguage() { var _a; const info = this.getMetadata('language_info'); return (_a = info === null || info === void 0 ? void 0 : info.name) !== null && _a !== void 0 ? _a : ''; } /** * Whether the model is collaborative or not. */ get collaborative() { return this._collaborationEnabled; } /** * Dispose of the resources held by the model. */ dispose() { // Do nothing if already disposed. if (this.isDisposed) { return; } this._isDisposed = true; const cells = this.cells; this._cells = null; cells.dispose(); if (this.standaloneModel) { this.sharedModel.dispose(); } Signal.clearData(this); } /** * Delete a metadata * * @param key Metadata key */ deleteMetadata(key) { return this.sharedModel.deleteMetadata(key); } /** * Get a metadata * * ### Notes * This returns a copy of the key value. * * @param key Metadata key */ getMetadata(key) { return this.sharedModel.getMetadata(key); } /** * Set a metadata * * @param key Metadata key * @param value Metadata value */ setMetadata(key, value) { if (typeof value === 'undefined') { this.sharedModel.deleteMetadata(key); } else { this.sharedModel.setMetadata(key, value); } } /** * Serialize the model to a string. */ toString() { return JSON.stringify(this.toJSON()); } /** * Deserialize the model from a string. * * #### Notes * Should emit a [contentChanged] signal. */ fromString(value) { this.fromJSON(JSON.parse(value)); } /** * Serialize the model to JSON. */ toJSON() { this._ensureMetadata(); return this.sharedModel.toJSON(); } /** * Deserialize the model from JSON. * * #### Notes * Should emit a [contentChanged] signal. */ fromJSON(value) { var _a, _b; const copy = JSONExt.deepCopy(value); const origNbformat = value.metadata.orig_nbformat; // Alert the user if the format changes. copy.nbformat = Math.max(value.nbformat, nbformat.MAJOR_VERSION); if (copy.nbformat !== value.nbformat || copy.nbformat_minor < nbformat.MINOR_VERSION) { copy.nbformat_minor = nbformat.MINOR_VERSION; } if (origNbformat !== undefined && copy.nbformat !== origNbformat) { const newer = copy.nbformat > origNbformat; let msg; if (newer) { msg = this._trans.__(`This notebook has been converted from an older notebook format (v%1) to the current notebook format (v%2). The next time you save this notebook, the current notebook format (v%2) will be used. 'Older versions of Jupyter may not be able to read the new format.' To preserve the original format version, close the notebook without saving it.`, origNbformat, copy.nbformat); } else { msg = this._trans.__(`This notebook has been converted from an newer notebook format (v%1) to the current notebook format (v%2). The next time you save this notebook, the current notebook format (v%2) will be used. Some features of the original notebook may not be available.' To preserve the original format version, close the notebook without saving it.`, origNbformat, copy.nbformat); } void showDialog({ title: this._trans.__('Notebook converted'), body: msg, buttons: [Dialog.okButton({ label: this._trans.__('Ok') })] }); } // Ensure there is at least one cell if (((_b = (_a = copy.cells) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) === 0) { copy['cells'] = [ { cell_type: 'code', source: '', metadata: { trusted: true } } ]; } this.sharedModel.fromJSON(copy); this._ensureMetadata(); this.dirty = true; } /** * Handle a change in the cells list. */ _onCellsChanged(list, change) { switch (change.type) { case 'add': change.newValues.forEach(cell => { cell.contentChanged.connect(this.triggerContentChange, this); }); break; case 'remove': break; case 'set': change.newValues.forEach(cell => { cell.contentChanged.connect(this.triggerContentChange, this); }); break; default: break; } this.triggerContentChange(); } _onMetadataChanged(sender, changes) { this._metadataChanged.emit(changes); this.triggerContentChange(); } _onStateChanged(sender, changes) { if (changes.stateChange) { changes.stateChange.forEach(value => { if (value.name === 'dirty') { // Setting `dirty` will trigger the state change. // We always set `dirty` because the shared model state // and the local attribute are synchronized one way shared model -> _dirty this.dirty = value.newValue; } else if (value.oldValue !== value.newValue) { this.triggerStateChange({ newValue: undefined, oldValue: undefined, ...value }); } }); } } /** * Make sure we have the required metadata fields. */ _ensureMetadata(languageName = '') { if (!this.getMetadata('language_info')) { this.sharedModel.setMetadata('language_info', { name: languageName }); } if (!this.getMetadata('kernelspec')) { this.sharedModel.setMetadata('kernelspec', { name: '', display_name: '' }); } } /** * Trigger a state change signal. */ triggerStateChange(args) { this._stateChanged.emit(args); } /** * Trigger a content changed signal. */ triggerContentChange() { this._contentChanged.emit(void 0); this.dirty = true; } /** * Whether the model is disposed. */ get isDisposed() { return this._isDisposed; } } //# sourceMappingURL=model.js.map