UNPKG

sophon-notebook-notebook

Version:
349 lines 11.8 kB
// Copyright (c) Jupyter Development Team. // Distributed under the terms of the Modified BSD License. import { DocumentModel } from 'sophon-notebook-docregistry'; import { CodeCellModel, RawCellModel, MarkdownCellModel } from 'sophon-notebook-cells'; import { nbformat } from 'sophon-notebook-coreutils'; import { UUID } from '@phosphor/coreutils'; import { CellList } from './celllist'; import { showDialog, Dialog } from 'sophon-notebook-apputils'; /** * An implementation of a notebook Model. */ export class NotebookModel extends DocumentModel { /** * Construct a new notebook model. */ constructor(options = {}) { super(options.languagePreference, options.modelDB); this._nbformat = nbformat.MAJOR_VERSION; this._nbformatMinor = nbformat.MINOR_VERSION; let factory = options.contentFactory || NotebookModel.defaultContentFactory; this.contentFactory = factory.clone(this.modelDB.view('cells')); this._cells = new CellList(this.modelDB, this.contentFactory); this._cells.changed.connect(this._onCellsChanged, this); // Handle initial metadata. let metadata = this.modelDB.createMap('metadata'); if (!metadata.has('language_info')) { let name = options.languagePreference || ''; metadata.set('language_info', { name }); } this._ensureMetadata(); metadata.changed.connect(this.triggerContentChange, this); this._deletedCells = []; } /** * The metadata associated with the notebook. */ get metadata() { return this.modelDB.get('metadata'); } /** * Get the observable list of notebook cells. */ get cells() { return this._cells; } /** * The major version number of the nbformat. */ get nbformat() { return this._nbformat; } /** * The minor version number of the nbformat. */ get nbformatMinor() { return this._nbformatMinor; } /** * The default kernel name of the document. */ get defaultKernelName() { let spec = this.metadata.get('kernelspec'); return spec ? spec.name : ''; } /** * A list of deleted cells for the notebook.. */ get deletedCells() { return this._deletedCells; } /** * The default kernel language of the document. */ get defaultKernelLanguage() { let info = this.metadata.get('language_info'); return info ? info.name : ''; } /** * Dispose of the resources held by the model. */ dispose() { // Do nothing if already disposed. if (this.cells === null) { return; } let cells = this.cells; this._cells = null; cells.dispose(); super.dispose(); } /** * 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() { let cells = []; for (let i = 0; i < this.cells.length; i++) { let cell = this.cells.get(i); cells.push(cell.toJSON()); } this._ensureMetadata(); let metadata = Object.create(null); for (let key of this.metadata.keys()) { metadata[key] = JSON.parse(JSON.stringify(this.metadata.get(key))); } return { metadata, nbformat_minor: this._nbformatMinor, nbformat: this._nbformat, cells }; } /** * Deserialize the model from JSON. * * #### Notes * Should emit a [contentChanged] signal. */ fromJSON(value) { let cells = []; let factory = this.contentFactory; for (let cell of value.cells) { switch (cell.cell_type) { case 'code': cells.push(factory.createCodeCell({ cell })); break; case 'markdown': cells.push(factory.createMarkdownCell({ cell })); break; case 'raw': cells.push(factory.createRawCell({ cell })); break; default: continue; } } this.cells.beginCompoundOperation(); this.cells.clear(); this.cells.pushAll(cells); this.cells.endCompoundOperation(); let oldValue = 0; let newValue = 0; this._nbformatMinor = nbformat.MINOR_VERSION; this._nbformat = nbformat.MAJOR_VERSION; const origNbformat = value.metadata.orig_nbformat; if (value.nbformat !== this._nbformat) { oldValue = this._nbformat; this._nbformat = newValue = value.nbformat; this.triggerStateChange({ name: 'nbformat', oldValue, newValue }); } if (value.nbformat_minor > this._nbformatMinor) { oldValue = this._nbformatMinor; this._nbformatMinor = newValue = value.nbformat_minor; this.triggerStateChange({ name: 'nbformatMinor', oldValue, newValue }); } // Alert the user if the format changes. if (origNbformat !== undefined && this._nbformat !== origNbformat) { const newer = this._nbformat > origNbformat; const msg = `This notebook has been converted from ${newer ? 'an older' : 'a newer'} notebook format (v${origNbformat}) to the current notebook format (v${this._nbformat}). The next time you save this notebook, the current notebook format (v${this._nbformat}) will be used. ${newer ? 'Older versions of Jupyter may not be able to read the new format.' : 'Some features of the original notebook may not be available.'} To preserve the original format version, close the notebook without saving it.`; void showDialog({ title: 'Notebook converted', body: msg, buttons: [Dialog.okButton()] }); } // Update the metadata. this.metadata.clear(); let metadata = value.metadata; for (let key in metadata) { // orig_nbformat is not intended to be stored per spec. if (key === 'orig_nbformat') { continue; } this.metadata.set(key, metadata[key]); } this._ensureMetadata(); this.dirty = true; } /** * Initialize the model with its current state. */ initialize() { super.initialize(); this.cells.clearUndo(); } /** * 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(); } /** * Make sure we have the required metadata fields. */ _ensureMetadata() { let metadata = this.metadata; if (!metadata.has('language_info')) { metadata.set('language_info', { name: '' }); } if (!metadata.has('kernelspec')) { metadata.set('kernelspec', { name: '', display_name: '' }); } } } /** * The namespace for the `NotebookModel` class statics. */ (function (NotebookModel) { /** * The default implementation of an `IContentFactory`. */ class ContentFactory { /** * Create a new cell model factory. */ constructor(options) { this.codeCellContentFactory = options.codeCellContentFactory || CodeCellModel.defaultContentFactory; this.modelDB = options.modelDB; } /** * Create a new cell by cell type. * * @param type: the type of the cell to create. * * @param options: the cell creation options. * * #### Notes * This method is intended to be a convenience method to programmaticaly * call the other cell creation methods in the factory. */ createCell(type, opts) { switch (type) { case 'code': return this.createCodeCell(opts); break; case 'markdown': return this.createMarkdownCell(opts); break; case 'raw': default: return this.createRawCell(opts); } } /** * Create a new code cell. * * @param source - The data to use for the original source data. * * @returns A new code cell. If a source cell is provided, the * new cell will be initialized with the data from the source. * If the contentFactory is not provided, the instance * `codeCellContentFactory` will be used. */ createCodeCell(options) { if (options.contentFactory) { options.contentFactory = this.codeCellContentFactory; } if (this.modelDB) { if (!options.id) { options.id = UUID.uuid4(); } options.modelDB = this.modelDB.view(options.id); } return new CodeCellModel(options); } /** * Create a new markdown cell. * * @param source - The data to use for the original source data. * * @returns A new markdown cell. If a source cell is provided, the * new cell will be initialized with the data from the source. */ createMarkdownCell(options) { if (this.modelDB) { if (!options.id) { options.id = UUID.uuid4(); } options.modelDB = this.modelDB.view(options.id); } return new MarkdownCellModel(options); } /** * Create a new raw cell. * * @param source - The data to use for the original source data. * * @returns A new raw cell. If a source cell is provided, the * new cell will be initialized with the data from the source. */ createRawCell(options) { if (this.modelDB) { if (!options.id) { options.id = UUID.uuid4(); } options.modelDB = this.modelDB.view(options.id); } return new RawCellModel(options); } /** * Clone the content factory with a new IModelDB. */ clone(modelDB) { return new ContentFactory({ modelDB: modelDB, codeCellContentFactory: this.codeCellContentFactory }); } } NotebookModel.ContentFactory = ContentFactory; /** * The default `ContentFactory` instance. */ NotebookModel.defaultContentFactory = new ContentFactory({}); })(NotebookModel || (NotebookModel = {})); //# sourceMappingURL=model.js.map