@jupyterlab/notebook
Version:
JupyterLab - Notebook
362 lines • 11.5 kB
JavaScript
// 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