UNPKG

@jupyterlab/docregistry

Version:
519 lines 14.5 kB
// Copyright (c) Jupyter Development Team. // Distributed under the terms of the Modified BSD License. import { MainAreaWidget, setToolbar } from '@jupyterlab/apputils'; import { CodeEditor } from '@jupyterlab/codeeditor'; import { PathExt } from '@jupyterlab/coreutils'; import { nullTranslator } from '@jupyterlab/translation'; import { Signal } from '@lumino/signaling'; import { createReadonlyLabel } from './components'; /** * The default implementation of a document model. */ export class DocumentModel extends CodeEditor.Model { /** * Construct a new document model. */ constructor(options = {}) { var _a; super({ sharedModel: options.sharedModel }); this._defaultLang = ''; this._dirty = false; this._readOnly = false; this._contentChanged = new Signal(this); this._stateChanged = new Signal(this); this._defaultLang = (_a = options.languagePreference) !== null && _a !== void 0 ? _a : ''; this._collaborationEnabled = !!options.collaborationEnabled; this.sharedModel.changed.connect(this._onStateChanged, this); } /** * A signal emitted when the document content changes. */ get contentChanged() { return this._contentChanged; } /** * A signal emitted when the document state changes. */ get stateChanged() { return this._stateChanged; } /** * 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 default kernel name of the document. * * #### Notes * This is a read-only property. */ get defaultKernelName() { return ''; } /** * The default kernel language of the document. * * #### Notes * This is a read-only property. */ get defaultKernelLanguage() { return this._defaultLang; } /** * Whether the model is collaborative or not. */ get collaborative() { return this._collaborationEnabled; } /** * Serialize the model to a string. */ toString() { return this.sharedModel.getSource(); } /** * Deserialize the model from a string. * * #### Notes * Should emit a [contentChanged] signal. */ fromString(value) { this.sharedModel.setSource(value); } /** * Serialize the model to JSON. */ toJSON() { return JSON.parse(this.sharedModel.getSource() || 'null'); } /** * Deserialize the model from JSON. * * #### Notes * Should emit a [contentChanged] signal. */ fromJSON(value) { this.fromString(JSON.stringify(value)); } /** * Initialize the model with its current state. */ initialize() { return; } /** * 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; } _onStateChanged(sender, changes) { if (changes.sourceChange) { this.triggerContentChange(); } 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 }); } }); } } } /** * An implementation of a model factory for text files. */ export class TextModelFactory { /** * Instantiates a TextModelFactory. */ constructor(collaborative) { this._isDisposed = false; this._collaborative = collaborative !== null && collaborative !== void 0 ? collaborative : true; } /** * The name of the model type. * * #### Notes * This is a read-only property. */ get name() { return 'text'; } /** * The type of the file. * * #### Notes * This is a read-only property. */ get contentType() { return 'file'; } /** * The format of the file. * * This is a read-only property. */ get fileFormat() { return 'text'; } /** * Whether the model is collaborative or not. */ get collaborative() { return this._collaborative; } /** * Get whether the model factory has been disposed. */ get isDisposed() { return this._isDisposed; } /** * Dispose of the resources held by the model factory. */ dispose() { this._isDisposed = true; } /** * Create a new model. * * @param options - Model options. * * @returns A new document model. */ createNew(options = {}) { const collaborative = options.collaborationEnabled && this.collaborative; return new DocumentModel({ ...options, collaborationEnabled: collaborative }); } /** * Get the preferred kernel language given a file path. */ preferredLanguage(path) { return ''; } } /** * An implementation of a model factory for base64 files. */ export class Base64ModelFactory extends TextModelFactory { /** * The name of the model type. * * #### Notes * This is a read-only property. */ get name() { return 'base64'; } /** * The type of the file. * * #### Notes * This is a read-only property. */ get contentType() { return 'file'; } /** * The format of the file. * * This is a read-only property. */ get fileFormat() { return 'base64'; } } /** * The default implementation of a widget factory. */ export class ABCWidgetFactory { /** * Construct a new `ABCWidgetFactory`. */ constructor(options) { this._isDisposed = false; this._widgetCreated = new Signal(this); this._translator = options.translator || nullTranslator; this._name = options.name; this._label = options.label || options.name; this._readOnly = options.readOnly === undefined ? false : options.readOnly; this._defaultFor = options.defaultFor ? options.defaultFor.slice() : []; this._defaultRendered = (options.defaultRendered || []).slice(); this._fileTypes = options.fileTypes.slice(); this._modelName = options.modelName || 'text'; this._preferKernel = !!options.preferKernel; this._canStartKernel = !!options.canStartKernel; this._shutdownOnClose = !!options.shutdownOnClose; this._autoStartDefault = !!options.autoStartDefault; this._toolbarFactory = options.toolbarFactory; } /** * A signal emitted when a widget is created. */ get widgetCreated() { return this._widgetCreated; } /** * Get whether the model factory has been disposed. */ get isDisposed() { return this._isDisposed; } /** * Dispose of the resources used by the document manager. */ dispose() { if (this.isDisposed) { return; } this._isDisposed = true; Signal.clearData(this); } /** * Whether the widget factory is read only. */ get readOnly() { return this._readOnly; } /** * A unique name identifying of the widget. */ get name() { return this._name; } /** * The label of the widget to display in dialogs. * If not given, name is used instead. */ get label() { return this._label; } /** * The file types the widget can view. */ get fileTypes() { return this._fileTypes.slice(); } /** * The registered name of the model type used to create the widgets. */ get modelName() { return this._modelName; } /** * The file types for which the factory should be the default. */ get defaultFor() { return this._defaultFor.slice(); } /** * The file types for which the factory should be the default for * rendering a document model, if different from editing. */ get defaultRendered() { return this._defaultRendered.slice(); } /** * Whether the widgets prefer having a kernel started. */ get preferKernel() { return this._preferKernel; } /** * Whether the widgets can start a kernel when opened. */ get canStartKernel() { return this._canStartKernel; } /** * The application language translator. */ get translator() { return this._translator; } /** * Whether the kernel should be shutdown when the widget is closed. */ get shutdownOnClose() { return this._shutdownOnClose; } set shutdownOnClose(value) { this._shutdownOnClose = value; } /** * Whether to automatically select the preferred kernel during a kernel start */ get autoStartDefault() { return this._autoStartDefault; } set autoStartDefault(value) { this._autoStartDefault = value; } /** * Create a new widget given a document model and a context. * * #### Notes * It should emit the [widgetCreated] signal with the new widget. */ createNew(context, source) { var _a; // Create the new widget const widget = this.createNewWidget(context, source); // Add toolbar setToolbar(widget, (_a = this._toolbarFactory) !== null && _a !== void 0 ? _a : this.defaultToolbarFactory.bind(this)); // Emit widget created signal this._widgetCreated.emit(widget); return widget; } /** * Default factory for toolbar items to be added after the widget is created. */ defaultToolbarFactory(widget) { return []; } } /** * The class name added to a dirty widget. */ const DIRTY_CLASS = 'jp-mod-dirty'; /** * A document widget implementation. */ export class DocumentWidget extends MainAreaWidget { constructor(options) { var _a; // Include the context ready promise in the widget reveal promise options.reveal = Promise.all([options.reveal, options.context.ready]); super(options); this._trans = ((_a = options.translator) !== null && _a !== void 0 ? _a : nullTranslator).load('jupyterlab'); this.context = options.context; // Handle context path changes this.context.pathChanged.connect(this._onPathChanged, this); this._onPathChanged(this.context, this.context.path); // Listen for changes in the dirty state. this.context.model.stateChanged.connect(this._onModelStateChanged, this); void this.context.ready.then(() => { this._handleDirtyState(); }); // listen for changes to the title object this.title.changed.connect(this._onTitleChanged, this); } /** * Set URI fragment identifier. */ setFragment(fragment) { /* no-op */ } /** * Handle a title change. */ async _onTitleChanged(_sender) { const validNameExp = /[\/\\:]/; const name = this.title.label; // Use localPath to avoid the drive name const filename = this.context.localPath.split('/').pop() || this.context.localPath; if (name === filename) { return; } if (name.length > 0 && !validNameExp.test(name)) { const oldPath = this.context.path; await this.context.rename(name); if (this.context.path !== oldPath) { // Rename succeeded return; } } // Reset title if name is invalid or rename fails this.title.label = filename; } /** * Handle a path change. */ _onPathChanged(sender, path) { this.title.label = PathExt.basename(sender.localPath); // The document is not untitled any more. this.isUntitled = false; } /** * Handle a change to the context model state. */ _onModelStateChanged(sender, args) { var _a; if (args.name === 'dirty') { this._handleDirtyState(); } if (!this.context.model.dirty) { if (!this.context.model.collaborative) { if (!((_a = this.context.contentsModel) === null || _a === void 0 ? void 0 : _a.writable)) { const readOnlyIndicator = createReadonlyLabel(this); let roi = this.toolbar.insertBefore('kernelName', 'read-only-indicator', readOnlyIndicator); if (!roi) { this.toolbar.addItem('read-only-indicator', readOnlyIndicator); } } } } } /** * Handle the dirty state of the context model. */ _handleDirtyState() { if (this.context.model.dirty && !this.title.className.includes(DIRTY_CLASS)) { this.title.className += ` ${DIRTY_CLASS}`; } else { this.title.className = this.title.className.replace(DIRTY_CLASS, ''); } } } //# sourceMappingURL=default.js.map