"use strict"; (self["webpackChunk_JUPYTERLAB_CORE_OUTPUT"] = self["webpackChunk_JUPYTERLAB_CORE_OUTPUT"] || []).push([[35],{ /***/ 50035: /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { // ESM COMPAT FLAG __webpack_require__.r(__webpack_exports__); // EXPORTS __webpack_require__.d(__webpack_exports__, { YBaseCell: () => (/* reexport */ YBaseCell), YCodeCell: () => (/* reexport */ YCodeCell), YDocument: () => (/* reexport */ YDocument), YFile: () => (/* reexport */ YFile), YMarkdownCell: () => (/* reexport */ YMarkdownCell), YNotebook: () => (/* reexport */ YNotebook), YRawCell: () => (/* reexport */ YRawCell), convertYMapEventToMapChange: () => (/* reexport */ convertYMapEventToMapChange), createMutex: () => (/* reexport */ createMutex), createStandaloneCell: () => (/* reexport */ createStandaloneCell) }); ;// CONCATENATED MODULE: ../node_modules/@jupyter/ydoc/lib/utils.js /* ----------------------------------------------------------------------------- | Copyright (c) Jupyter Development Team. | Distributed under the terms of the Modified BSD License. |----------------------------------------------------------------------------*/ function convertYMapEventToMapChange(event) { let changes = new Map(); event.changes.keys.forEach((event, key) => { changes.set(key, { action: event.action, oldValue: event.oldValue, newValue: this.ymeta.get(key) }); }); return changes; } /** * Creates a mutual exclude function with the following property: * * ```js * const mutex = createMutex() * mutex(() => { * // This function is immediately executed * mutex(() => { * // This function is not executed, as the mutex is already active. * }) * }) * ``` */ const createMutex = () => { let token = true; return (f) => { if (token) { token = false; try { f(); } finally { token = true; } } }; }; //# sourceMappingURL=utils.js.map // EXTERNAL MODULE: consume shared module (default) @lumino/coreutils@~2.2.1 (singleton) (fallback: ../node_modules/@lumino/coreutils/dist/index.js) var index_js_ = __webpack_require__(5406); // EXTERNAL MODULE: consume shared module (default) @lumino/signaling@~2.1.4 (singleton) (fallback: ../node_modules/@lumino/signaling/dist/index.es6.js) var index_es6_js_ = __webpack_require__(2536); // EXTERNAL MODULE: ../node_modules/lib0/time.js var lib0_time = __webpack_require__(2431); // EXTERNAL MODULE: ../node_modules/lib0/math.js var math = __webpack_require__(11182); // EXTERNAL MODULE: ../node_modules/lib0/observable.js var observable = __webpack_require__(12330); // EXTERNAL MODULE: ../node_modules/lib0/function.js var lib0_function = __webpack_require__(38828); // EXTERNAL MODULE: consume shared module (default) yjs@~13.6.8 (singleton) (fallback: ../node_modules/yjs/dist/yjs.mjs) var yjs_mjs_ = __webpack_require__(17843); ;// CONCATENATED MODULE: ../node_modules/y-protocols/awareness.js /** * @module awareness-protocol */ // eslint-disable-line const outdatedTimeout = 30000 /** * @typedef {Object} MetaClientState * @property {number} MetaClientState.clock * @property {number} MetaClientState.lastUpdated unix timestamp */ /** * The Awareness class implements a simple shared state protocol that can be used for non-persistent data like awareness information * (cursor, username, status, ..). Each client can update its own local state and listen to state changes of * remote clients. Every client may set a state of a remote peer to `null` to mark the client as offline. * * Each client is identified by a unique client id (something we borrow from `doc.clientID`). A client can override * its own state by propagating a message with an increasing timestamp (`clock`). If such a message is received, it is * applied if the known state of that client is older than the new state (`clock < newClock`). If a client thinks that * a remote client is offline, it may propagate a message with * `{ clock: currentClientClock, state: null, client: remoteClient }`. If such a * message is received, and the known clock of that client equals the received clock, it will override the state with `null`. * * Before a client disconnects, it should propagate a `null` state with an updated clock. * * Awareness states must be updated every 30 seconds. Otherwise the Awareness instance will delete the client state. * * @extends {Observable} */ class Awareness extends observable/* Observable */.y { /** * @param {Y.Doc} doc */ constructor (doc) { super() this.doc = doc /** * @type {number} */ this.clientID = doc.clientID /** * Maps from client id to client state * @type {Map>} */ this.states = new Map() /** * @type {Map} */ this.meta = new Map() this._checkInterval = /** @type {any} */ (setInterval(() => { const now = lib0_time/* getUnixTime */.ZG() if (this.getLocalState() !== null && (outdatedTimeout / 2 <= now - /** @type {{lastUpdated:number}} */ (this.meta.get(this.clientID)).lastUpdated)) { // renew local clock this.setLocalState(this.getLocalState()) } /** * @type {Array} */ const remove = [] this.meta.forEach((meta, clientid) => { if (clientid !== this.clientID && outdatedTimeout <= now - meta.lastUpdated && this.states.has(clientid)) { remove.push(clientid) } }) if (remove.length > 0) { removeAwarenessStates(this, remove, 'timeout') } }, math/* floor */.GW(outdatedTimeout / 10))) doc.on('destroy', () => { this.destroy() }) this.setLocalState({}) } destroy () { this.emit('destroy', [this]) this.setLocalState(null) super.destroy() clearInterval(this._checkInterval) } /** * @return {Object|null} */ getLocalState () { return this.states.get(this.clientID) || null } /** * @param {Object|null} state */ setLocalState (state) { const clientID = this.clientID const currLocalMeta = this.meta.get(clientID) const clock = currLocalMeta === undefined ? 0 : currLocalMeta.clock + 1 const prevState = this.states.get(clientID) if (state === null) { this.states.delete(clientID) } else { this.states.set(clientID, state) } this.meta.set(clientID, { clock, lastUpdated: lib0_time/* getUnixTime */.ZG() }) const added = [] const updated = [] const filteredUpdated = [] const removed = [] if (state === null) { removed.push(clientID) } else if (prevState == null) { if (state != null) { added.push(clientID) } } else { updated.push(clientID) if (!lib0_function/* equalityDeep */.Hi(prevState, state)) { filteredUpdated.push(clientID) } } if (added.length > 0 || filteredUpdated.length > 0 || removed.length > 0) { this.emit('change', [{ added, updated: filteredUpdated, removed }, 'local']) } this.emit('update', [{ added, updated, removed }, 'local']) } /** * @param {string} field * @param {any} value */ setLocalStateField (field, value) { const state = this.getLocalState() if (state !== null) { this.setLocalState({ ...state, [field]: value }) } } /** * @return {Map>} */ getStates () { return this.states } } /** * Mark (remote) clients as inactive and remove them from the list of active peers. * This change will be propagated to remote clients. * * @param {Awareness} awareness * @param {Array} clients * @param {any} origin */ const removeAwarenessStates = (awareness, clients, origin) => { const removed = [] for (let i = 0; i < clients.length; i++) { const clientID = clients[i] if (awareness.states.has(clientID)) { awareness.states.delete(clientID) if (clientID === awareness.clientID) { const curMeta = /** @type {MetaClientState} */ (awareness.meta.get(clientID)) awareness.meta.set(clientID, { clock: curMeta.clock + 1, lastUpdated: lib0_time/* getUnixTime */.ZG() }) } removed.push(clientID) } } if (removed.length > 0) { awareness.emit('change', [{ added: [], updated: [], removed }, origin]) awareness.emit('update', [{ added: [], updated: [], removed }, origin]) } } /** * @param {Awareness} awareness * @param {Array} clients * @return {Uint8Array} */ const encodeAwarenessUpdate = (awareness, clients, states = awareness.states) => { const len = clients.length const encoder = encoding.createEncoder() encoding.writeVarUint(encoder, len) for (let i = 0; i < len; i++) { const clientID = clients[i] const state = states.get(clientID) || null const clock = /** @type {MetaClientState} */ (awareness.meta.get(clientID)).clock encoding.writeVarUint(encoder, clientID) encoding.writeVarUint(encoder, clock) encoding.writeVarString(encoder, JSON.stringify(state)) } return encoding.toUint8Array(encoder) } /** * Modify the content of an awareness update before re-encoding it to an awareness update. * * This might be useful when you have a central server that wants to ensure that clients * cant hijack somebody elses identity. * * @param {Uint8Array} update * @param {function(any):any} modify * @return {Uint8Array} */ const modifyAwarenessUpdate = (update, modify) => { const decoder = decoding.createDecoder(update) const encoder = encoding.createEncoder() const len = decoding.readVarUint(decoder) encoding.writeVarUint(encoder, len) for (let i = 0; i < len; i++) { const clientID = decoding.readVarUint(decoder) const clock = decoding.readVarUint(decoder) const state = JSON.parse(decoding.readVarString(decoder)) const modifiedState = modify(state) encoding.writeVarUint(encoder, clientID) encoding.writeVarUint(encoder, clock) encoding.writeVarString(encoder, JSON.stringify(modifiedState)) } return encoding.toUint8Array(encoder) } /** * @param {Awareness} awareness * @param {Uint8Array} update * @param {any} origin This will be added to the emitted change event */ const applyAwarenessUpdate = (awareness, update, origin) => { const decoder = decoding.createDecoder(update) const timestamp = time.getUnixTime() const added = [] const updated = [] const filteredUpdated = [] const removed = [] const len = decoding.readVarUint(decoder) for (let i = 0; i < len; i++) { const clientID = decoding.readVarUint(decoder) let clock = decoding.readVarUint(decoder) const state = JSON.parse(decoding.readVarString(decoder)) const clientMeta = awareness.meta.get(clientID) const prevState = awareness.states.get(clientID) const currClock = clientMeta === undefined ? 0 : clientMeta.clock if (currClock < clock || (currClock === clock && state === null && awareness.states.has(clientID))) { if (state === null) { // never let a remote client remove this local state if (clientID === awareness.clientID && awareness.getLocalState() != null) { // remote client removed the local state. Do not remote state. Broadcast a message indicating // that this client still exists by increasing the clock clock++ } else { awareness.states.delete(clientID) } } else { awareness.states.set(clientID, state) } awareness.meta.set(clientID, { clock, lastUpdated: timestamp }) if (clientMeta === undefined && state !== null) { added.push(clientID) } else if (clientMeta !== undefined && state === null) { removed.push(clientID) } else if (state !== null) { if (!f.equalityDeep(state, prevState)) { filteredUpdated.push(clientID) } updated.push(clientID) } } } if (added.length > 0 || filteredUpdated.length > 0 || removed.length > 0) { awareness.emit('change', [{ added, updated: filteredUpdated, removed }, origin]) } if (added.length > 0 || updated.length > 0 || removed.length > 0) { awareness.emit('update', [{ added, updated, removed }, origin]) } } ;// CONCATENATED MODULE: ../node_modules/@jupyter/ydoc/lib/ydocument.js /* ----------------------------------------------------------------------------- | Copyright (c) Jupyter Development Team. | Distributed under the terms of the Modified BSD License. |----------------------------------------------------------------------------*/ /** * Generic shareable document. */ class YDocument { constructor(options) { var _a; /** * Handle a change to the ystate. */ this.onStateChanged = (event) => { const stateChange = new Array(); event.keysChanged.forEach(key => { const change = event.changes.keys.get(key); if (change) { stateChange.push({ name: key, oldValue: change.oldValue, newValue: this.ystate.get(key) }); } }); this._changed.emit({ stateChange }); }; this._changed = new index_es6_js_.Signal(this); this._isDisposed = false; this._disposed = new index_es6_js_.Signal(this); this._ydoc = (_a = options === null || options === void 0 ? void 0 : options.ydoc) !== null && _a !== void 0 ? _a : new yjs_mjs_.Doc(); this._ystate = this._ydoc.getMap('state'); this._undoManager = new yjs_mjs_.UndoManager([], { trackedOrigins: new Set([this]), doc: this._ydoc }); this._awareness = new Awareness(this._ydoc); this._ystate.observe(this.onStateChanged); } /** * YJS document. */ get ydoc() { return this._ydoc; } /** * Shared state */ get ystate() { return this._ystate; } /** * YJS document undo manager */ get undoManager() { return this._undoManager; } /** * Shared awareness */ get awareness() { return this._awareness; } /** * The changed signal. */ get changed() { return this._changed; } /** * A signal emitted when the document is disposed. */ get disposed() { return this._disposed; } /** * Whether the document is disposed or not. */ get isDisposed() { return this._isDisposed; } /** * Document state */ get state() { return index_js_.JSONExt.deepCopy(this.ystate.toJSON()); } /** * Whether the object can undo changes. */ canUndo() { return this.undoManager.undoStack.length > 0; } /** * Whether the object can redo changes. */ canRedo() { return this.undoManager.redoStack.length > 0; } /** * Dispose of the resources. */ dispose() { if (this._isDisposed) { return; } this._isDisposed = true; this.ystate.unobserve(this.onStateChanged); this.awareness.destroy(); this.undoManager.destroy(); this.ydoc.destroy(); this._disposed.emit(); index_es6_js_.Signal.clearData(this); } /** * Get the value for a state attribute * * @param key Key to get */ getState(key) { const value = this.ystate.get(key); return typeof value === 'undefined' ? value : index_js_.JSONExt.deepCopy(value); } /** * Set the value of a state attribute * * @param key Key to set * @param value New attribute value */ setState(key, value) { if (!index_js_.JSONExt.deepEqual(this.ystate.get(key), value)) { this.ystate.set(key, value); } } /** * Get the document source * * @returns The source */ get source() { return this.getSource(); } /** * Set the document source * * @param value The source to set */ set source(value) { this.setSource(value); } /** * Undo an operation. */ undo() { this.undoManager.undo(); } /** * Redo an operation. */ redo() { this.undoManager.redo(); } /** * Clear the change stack. */ clearUndoHistory() { this.undoManager.clear(); } /** * Perform a transaction. While the function f is called, all changes to the shared * document are bundled into a single event. */ transact(f, undoable = true, origin = null) { this.ydoc.transact(f, undoable ? this : origin); } } //# sourceMappingURL=ydocument.js.map ;// CONCATENATED MODULE: ../node_modules/@jupyter/ydoc/lib/yfile.js /* ----------------------------------------------------------------------------- | Copyright (c) Jupyter Development Team. | Distributed under the terms of the Modified BSD License. |----------------------------------------------------------------------------*/ /** * Shareable text file. */ class YFile extends YDocument { /** * Create a new file * * #### Notes * The document is empty and must be populated */ constructor() { super(); /** * Document version */ this.version = '1.0.0'; /** * YJS file text. */ this.ysource = this.ydoc.getText('source'); /** * Handle a change to the ymodel. */ this._modelObserver = (event) => { this._changed.emit({ sourceChange: event.changes.delta }); }; this.undoManager.addToScope(this.ysource); this.ysource.observe(this._modelObserver); } /** * Creates a standalone YFile */ static create() { return new YFile(); } /** * File text */ get source() { return this.getSource(); } set source(v) { this.setSource(v); } /** * Dispose of the resources. */ dispose() { if (this.isDisposed) { return; } this.ysource.unobserve(this._modelObserver); super.dispose(); } /** * Get the file text. * * @returns File text. */ getSource() { return this.ysource.toString(); } /** * Set the file text. * * @param value New text */ setSource(value) { this.transact(() => { const ytext = this.ysource; ytext.delete(0, ytext.length); ytext.insert(0, value); }); } /** * Replace content from `start' to `end` with `value`. * * @param start: The start index of the range to replace (inclusive). * @param end: The end index of the range to replace (exclusive). * @param value: New source (optional). */ updateSource(start, end, value = '') { this.transact(() => { const ysource = this.ysource; // insert and then delete. // This ensures that the cursor position is adjusted after the replaced content. ysource.insert(start, value); ysource.delete(start + value.length, end - start); }); } } //# sourceMappingURL=yfile.js.map ;// CONCATENATED MODULE: ../node_modules/@jupyter/ydoc/lib/ycell.js /* ----------------------------------------------------------------------------- | Copyright (c) Jupyter Development Team. | Distributed under the terms of the Modified BSD License. |----------------------------------------------------------------------------*/ /** * Create a new shared cell model given the YJS shared type. */ const createCellModelFromSharedType = (type, options = {}) => { switch (type.get('cell_type')) { case 'code': return new YCodeCell(type, type.get('source'), type.get('outputs'), options); case 'markdown': return new YMarkdownCell(type, type.get('source'), options); case 'raw': return new YRawCell(type, type.get('source'), options); default: throw new Error('Found unknown cell type'); } }; /** * Create a new cell that can be inserted in an existing shared model. * * If no notebook is specified the cell will be standalone. * * @param cell Cell JSON representation * @param notebook Notebook to which the cell will be added */ const createCell = (cell, notebook) => { var _a, _b; const ymodel = new yjs_mjs_.Map(); const ysource = new yjs_mjs_.Text(); const ymetadata = new yjs_mjs_.Map(); ymodel.set('source', ysource); ymodel.set('metadata', ymetadata); ymodel.set('cell_type', cell.cell_type); ymodel.set('id', (_a = cell.id) !== null && _a !== void 0 ? _a : index_js_.UUID.uuid4()); let ycell; switch (cell.cell_type) { case 'markdown': { ycell = new YMarkdownCell(ymodel, ysource, { notebook }, ymetadata); if (cell.attachments != null) { ycell.setAttachments(cell.attachments); } break; } case 'code': { const youtputs = new yjs_mjs_.Array(); ymodel.set('outputs', youtputs); ycell = new YCodeCell(ymodel, ysource, youtputs, { notebook }, ymetadata); const cCell = cell; ycell.execution_count = (_b = cCell.execution_count) !== null && _b !== void 0 ? _b : null; if (cCell.outputs) { ycell.setOutputs(cCell.outputs); } break; } default: { // raw ycell = new YRawCell(ymodel, ysource, { notebook }, ymetadata); if (cell.attachments) { ycell.setAttachments(cell.attachments); } break; } } if (cell.metadata != null) { ycell.setMetadata(cell.metadata); } if (cell.source != null) { ycell.setSource(typeof cell.source === 'string' ? cell.source : cell.source.join('')); } return ycell; }; /** * Create a new cell that cannot be inserted in an existing shared model. * * @param cell Cell JSON representation */ const createStandaloneCell = (cell) => createCell(cell); class YBaseCell { /** * Create a new YCell that works standalone. It cannot be * inserted into a YNotebook because the Yjs model is already * attached to an anonymous Y.Doc instance. */ static create(id) { return createCell({ id, cell_type: this.prototype.cell_type }); } /** * Base cell constructor * * ### Notes * Don't use the constructor directly - prefer using ``YNotebook.insertCell`` * * The ``ysource`` is needed because ``ymodel.get('source')`` will * not return the real source if the model is not yet attached to * a document. Requesting it explicitly allows to introspect a non-empty * source before the cell is attached to the document. * * @param ymodel Cell map * @param ysource Cell source * @param options \{ notebook?: The notebook the cell is attached to \} * @param ymetadata Cell metadata */ constructor(ymodel, ysource, options = {}, ymetadata) { /** * Handle a change to the ymodel. */ this._modelObserver = (events, transaction) => { if (transaction.origin !== 'silent-change') { this._changed.emit(this.getChanges(events)); } }; this._metadataChanged = new index_es6_js_.Signal(this); /** * The notebook that this cell belongs to. */ this._notebook = null; this._changed = new index_es6_js_.Signal(this); this._disposed = new index_es6_js_.Signal(this); this._isDisposed = false; this._undoManager = null; this.ymodel = ymodel; this._ysource = ysource; this._ymetadata = ymetadata !== null && ymetadata !== void 0 ? ymetadata : this.ymodel.get('metadata'); this._prevSourceLength = ysource ? ysource.length : 0; this._notebook = null; this._awareness = null; this._undoManager = null; if (options.notebook) { this._notebook = options.notebook; if (this._notebook.disableDocumentWideUndoRedo) { this._undoManager = new yjs_mjs_.UndoManager([this.ymodel], { trackedOrigins: new Set([this]), doc: this._notebook.ydoc }); } } else { // Standalone cell const doc = new yjs_mjs_.Doc(); doc.getArray().insert(0, [this.ymodel]); this._awareness = new Awareness(doc); this._undoManager = new yjs_mjs_.UndoManager([this.ymodel], { trackedOrigins: new Set([this]) }); } this.ymodel.observeDeep(this._modelObserver); } /** * Cell notebook awareness or null. */ get awareness() { var _a, _b, _c; return (_c = (_a = this._awareness) !== null && _a !== void 0 ? _a : (_b = this.notebook) === null || _b === void 0 ? void 0 : _b.awareness) !== null && _c !== void 0 ? _c : null; } /** * The type of the cell. */ get cell_type() { throw new Error('A YBaseCell must not be constructed'); } /** * The changed signal. */ get changed() { return this._changed; } /** * Signal emitted when the cell is disposed. */ get disposed() { return this._disposed; } /** * Cell id */ get id() { return this.getId(); } /** * Whether the model has been disposed or not. */ get isDisposed() { return this._isDisposed; } /** * Whether the cell is standalone or not. * * If the cell is standalone. It cannot be * inserted into a YNotebook because the Yjs model is already * attached to an anonymous Y.Doc instance. */ get isStandalone() { return this._notebook !== null; } /** * Cell metadata. * * #### Notes * You should prefer to access and modify the specific key of interest. */ get metadata() { return this.getMetadata(); } set metadata(v) { this.setMetadata(v); } /** * Signal triggered when the cell metadata changes. */ get metadataChanged() { return this._metadataChanged; } /** * The notebook that this cell belongs to. */ get notebook() { return this._notebook; } /** * Cell input content. */ get source() { return this.getSource(); } set source(v) { this.setSource(v); } /** * The cell undo manager. */ get undoManager() { var _a; if (!this.notebook) { return this._undoManager; } return ((_a = this.notebook) === null || _a === void 0 ? void 0 : _a.disableDocumentWideUndoRedo) ? this._undoManager : this.notebook.undoManager; } get ysource() { return this._ysource; } /** * Whether the object can undo changes. */ canUndo() { return !!this.undoManager && this.undoManager.undoStack.length > 0; } /** * Whether the object can redo changes. */ canRedo() { return !!this.undoManager && this.undoManager.redoStack.length > 0; } /** * Clear the change stack. */ clearUndoHistory() { var _a; (_a = this.undoManager) === null || _a === void 0 ? void 0 : _a.clear(); } /** * Undo an operation. */ undo() { var _a; (_a = this.undoManager) === null || _a === void 0 ? void 0 : _a.undo(); } /** * Redo an operation. */ redo() { var _a; (_a = this.undoManager) === null || _a === void 0 ? void 0 : _a.redo(); } /** * Dispose of the resources. */ dispose() { var _a; if (this._isDisposed) return; this._isDisposed = true; this.ymodel.unobserveDeep(this._modelObserver); if (this._awareness) { // A new document is created for standalone cell. const doc = this._awareness.doc; this._awareness.destroy(); doc.destroy(); } if (this._undoManager) { // Be sure to not destroy the document undo manager. if (this._undoManager === ((_a = this.notebook) === null || _a === void 0 ? void 0 : _a.undoManager)) { this._undoManager = null; } else { this._undoManager.destroy(); } } this._disposed.emit(); index_es6_js_.Signal.clearData(this); } /** * Get cell id. * * @returns Cell id */ getId() { return this.ymodel.get('id'); } /** * Gets cell's source. * * @returns Cell's source. */ getSource() { return this.ysource.toString(); } /** * Sets cell's source. * * @param value: New source. */ setSource(value) { this.transact(() => { this.ysource.delete(0, this.ysource.length); this.ysource.insert(0, value); }); // @todo Do we need proper replace semantic? This leads to issues in editor bindings because they don't switch source. // this.ymodel.set('source', new Y.Text(value)); } /** * Replace content from `start' to `end` with `value`. * * @param start: The start index of the range to replace (inclusive). * * @param end: The end index of the range to replace (exclusive). * * @param value: New source (optional). */ updateSource(start, end, value = '') { this.transact(() => { const ysource = this.ysource; // insert and then delete. // This ensures that the cursor position is adjusted after the replaced content. ysource.insert(start, value); ysource.delete(start + value.length, end - start); }); } /** * Delete a metadata cell. * * @param key The key to delete */ deleteMetadata(key) { if (typeof this.getMetadata(key) === 'undefined') { return; } this.transact(() => { this._ymetadata.delete(key); const jupyter = this.getMetadata('jupyter'); if (key === 'collapsed' && jupyter) { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { outputs_hidden, ...others } = jupyter; if (Object.keys(others).length === 0) { this._ymetadata.delete('jupyter'); } else { this._ymetadata.set('jupyter', others); } } else if (key === 'jupyter') { this._ymetadata.delete('collapsed'); } }, false); } getMetadata(key) { const metadata = this._ymetadata; // Transiently the metadata can be missing - like during destruction if (metadata === undefined) { return undefined; } if (typeof key === 'string') { const value = metadata.get(key); return typeof value === 'undefined' ? undefined // undefined is converted to `{}` by `JSONExt.deepCopy` : index_js_.JSONExt.deepCopy(metadata.get(key)); } else { return index_js_.JSONExt.deepCopy(metadata.toJSON()); } } setMetadata(metadata, value) { var _a, _b; if (typeof metadata === 'string') { if (typeof value === 'undefined') { throw new TypeError(`Metadata value for ${metadata} cannot be 'undefined'; use deleteMetadata.`); } const key = metadata; // Only set metadata if we change something to avoid infinite // loop of signal changes. if (index_js_.JSONExt.deepEqual((_a = this.getMetadata(key)) !== null && _a !== void 0 ? _a : null, value)) { return; } this.transact(() => { var _a; this._ymetadata.set(key, value); if (key === 'collapsed') { const jupyter = ((_a = this.getMetadata('jupyter')) !== null && _a !== void 0 ? _a : {}); if (jupyter.outputs_hidden !== value) { this.setMetadata('jupyter', { ...jupyter, outputs_hidden: value }); } } else if (key === 'jupyter') { const isHidden = value['outputs_hidden']; if (typeof isHidden !== 'undefined') { if (this.getMetadata('collapsed') !== isHidden) { this.setMetadata('collapsed', isHidden); } } else { this.deleteMetadata('collapsed'); } } }, false); } else { const clone = index_js_.JSONExt.deepCopy(metadata); if (clone.collapsed != null) { clone.jupyter = clone.jupyter || {}; clone.jupyter.outputs_hidden = clone.collapsed; } else if (((_b = clone === null || clone === void 0 ? void 0 : clone.jupyter) === null || _b === void 0 ? void 0 : _b.outputs_hidden) != null) { clone.collapsed = clone.jupyter.outputs_hidden; } if (!index_js_.JSONExt.deepEqual(clone, this.getMetadata())) { this.transact(() => { for (const [key, value] of Object.entries(clone)) { this._ymetadata.set(key, value); } }, false); } } } /** * Serialize the model to JSON. */ toJSON() { return { id: this.getId(), cell_type: this.cell_type, source: this.getSource(), metadata: this.getMetadata() }; } /** * Perform a transaction. While the function f is called, all changes to the shared * document are bundled into a single event. * * @param f Transaction to execute * @param undoable Whether to track the change in the action history or not (default `true`) */ transact(f, undoable = true, origin = null) { !this.notebook || this.notebook.disableDocumentWideUndoRedo ? this.ymodel.doc == null ? f() : this.ymodel.doc.transact(f, undoable ? this : origin) : this.notebook.transact(f, undoable); } /** * Extract changes from YJS events * * @param events YJS events * @returns Cell changes */ getChanges(events) { const changes = {}; const sourceEvent = events.find(event => event.target === this.ymodel.get('source')); if (sourceEvent) { changes.sourceChange = sourceEvent.changes.delta; } const metadataEvents = events.find(event => event.target === this._ymetadata); if (metadataEvents) { changes.metadataChange = metadataEvents.changes.keys; metadataEvents.changes.keys.forEach((change, key) => { switch (change.action) { case 'add': this._metadataChanged.emit({ key, newValue: this._ymetadata.get(key), type: 'add' }); break; case 'delete': this._metadataChanged.emit({ key, oldValue: change.oldValue, type: 'remove' }); break; case 'update': { const newValue = this._ymetadata.get(key); const oldValue = change.oldValue; let equal = true; if (typeof oldValue == 'object' && typeof newValue == 'object') { equal = index_js_.JSONExt.deepEqual(oldValue, newValue); } else { equal = oldValue === newValue; } if (!equal) { this._metadataChanged.emit({ key, type: 'change', oldValue, newValue }); } } break; } }); } const modelEvent = events.find(event => event.target === this.ymodel); // The model allows us to replace the complete source with a new string. We express this in the Delta format // as a replace of the complete string. const ysource = this.ymodel.get('source'); if (modelEvent && modelEvent.keysChanged.has('source')) { changes.sourceChange = [ { delete: this._prevSourceLength }, { insert: ysource.toString() } ]; } this._prevSourceLength = ysource.length; return changes; } } /** * Shareable code cell. */ class YCodeCell extends YBaseCell { /** * Create a new YCodeCell that works standalone. It cannot be * inserted into a YNotebook because the Yjs model is already * attached to an anonymous Y.Doc instance. */ static create(id) { return super.create(id); } /** * Code cell constructor * * ### Notes * Don't use the constructor directly - prefer using ``YNotebook.insertCell`` * * The ``ysource`` is needed because ``ymodel.get('source')`` will * not return the real source if the model is not yet attached to * a document. Requesting it explicitly allows to introspect a non-empty * source before the cell is attached to the document. * * @param ymodel Cell map * @param ysource Cell source * @param youtputs Code cell outputs * @param options \{ notebook?: The notebook the cell is attached to \} * @param ymetadata Cell metadata */ constructor(ymodel, ysource, youtputs, options = {}, ymetadata) { super(ymodel, ysource, options, ymetadata); this._youtputs = youtputs; } /** * The type of the cell. */ get cell_type() { return 'code'; } /** * The code cell's prompt number. Will be null if the cell has not been run. */ get execution_count() { return this.ymodel.get('execution_count') || null; } set execution_count(count) { // Do not use `this.execution_count`. When initializing the // cell, we need to set execution_count to `null` if we compare // using `this.execution_count` it will return `null` and we will // never initialize it if (this.ymodel.get('execution_count') !== count) { this.transact(() => { this.ymodel.set('execution_count', count); }, false); } } /** * The code cell's execution state. */ get executionState() { var _a; return (_a = this.ymodel.get('execution_state')) !== null && _a !== void 0 ? _a : 'idle'; } set executionState(state) { if (this.ymodel.get('execution_state') !== state) { this.transact(() => { this.ymodel.set('execution_state', state); }, false); } } /** * Cell outputs. */ get outputs() { return this.getOutputs(); } set outputs(v) { this.setOutputs(v); } get youtputs() { return this._youtputs; } /** * Execution, display, or stream outputs. */ getOutputs() { return index_js_.JSONExt.deepCopy(this._youtputs.toJSON()); } createOutputs(outputs) { const newOutputs = []; for (const output of index_js_.JSONExt.deepCopy(outputs)) { let _newOutput1; if (output.output_type === 'stream') { // Set the text field as a Y.Text const { text, ...outputWithoutText } = output; _newOutput1 = outputWithoutText; const newText = new yjs_mjs_.Text(); let _text = text instanceof Array ? text.join() : text; newText.insert(0, _text); _newOutput1['text'] = newText; } else { _newOutput1 = output; } const _newOutput2 = []; for (const [key, value] of Object.entries(_newOutput1)) { _newOutput2.push([key, value]); } const newOutput = new yjs_mjs_.Map(_newOutput2); newOutputs.push(newOutput); } return newOutputs; } /** * Replace all outputs. */ setOutputs(outputs) { this.transact(() => { this._youtputs.delete(0, this._youtputs.length); const newOutputs = this.createOutputs(outputs); this._youtputs.insert(0, newOutputs); }, false); } /** * Remove text from a stream output. */ removeStreamOutput(index, start, origin = null) { this.transact(() => { const output = this._youtputs.get(index); const prevText = output.get('text'); const length = prevText.length - start; prevText.delete(start, length); }, false, origin); } /** * Append text to a stream output. */ appendStreamOutput(index, text, origin = null) { this.transact(() => { const output = this._youtputs.get(index); const prevText = output.get('text'); prevText.insert(prevText.length, text); }, false, origin); } /** * Replace content from `start' to `end` with `outputs`. * * @param start: The start index of the range to replace (inclusive). * * @param end: The end index of the range to replace (exclusive). * * @param outputs: New outputs (optional). */ updateOutputs(start, end, outputs = [], origin = null) { const fin = end < this._youtputs.length ? end - start : this._youtputs.length - start; this.transact(() => { this._youtputs.delete(start, fin); const newOutputs = this.createOutputs(outputs); this._youtputs.insert(start, newOutputs); }, false, origin); } /** * Clear all outputs from the cell. */ clearOutputs(origin = null) { this.transact(() => { this._youtputs.delete(0, this._youtputs.length); }, false, origin); } /** * Serialize the model to JSON. */ toJSON() { return { ...super.toJSON(), outputs: this.getOutputs(), execution_count: this.execution_count }; } /** * Extract changes from YJS events * * @param events YJS events * @returns Cell changes */ getChanges(events) { const changes = super.getChanges(events); const streamOutputEvent = events.find( // Changes to the 'text' of a cell's stream output can be accessed like so: // ycell['outputs'][output_idx]['text'] // This translates to an event path of: ['outputs', output_idx, 'text] event => event.path.length === 3 && event.path[0] === 'outputs' && event.path[2] === 'text'); if (streamOutputEvent) { changes.streamOutputChange = streamOutputEvent.changes.delta; } const outputEvent = events.find(event => event.target === this.ymodel.get('outputs')); if (outputEvent) { changes.outputsChange = outputEvent.changes.delta; } const modelEvent = events.find(event => event.target === this.ymodel); if (modelEvent && modelEvent.keysChanged.has('execution_count')) { const change = modelEvent.changes.keys.get('execution_count'); changes.executionCountChange = { oldValue: change.oldValue, newValue: this.ymodel.get('execution_count') }; } if (modelEvent && modelEvent.keysChanged.has('execution_state')) { const change = modelEvent.changes.keys.get('execution_state'); changes.executionStateChange = { oldValue: change.oldValue, newValue: this.ymodel.get('execution_state') }; } return changes; } } class YAttachmentCell extends YBaseCell { /** * Cell attachments */ get attachments() { return this.getAttachments(); } set attachments(v) { this.setAttachments(v); } /** * Gets the cell attachments. * * @returns The cell attachments. */ getAttachments() { return this.ymodel.get('attachments'); } /** * Sets the cell attachments * * @param attachments: The cell attachments. */ setAttachments(attachments) { this.transact(() => { if (attachments == null) { this.ymodel.delete('attachments'); } else { this.ymodel.set('attachments', attachments); } }, false); } /** * Extract changes from YJS events * * @param events YJS events * @returns Cell changes */ getChanges(events) { const changes = super.getChanges(events); const modelEvent = events.find(event => event.target === this.ymodel); if (modelEvent && modelEvent.keysChanged.has('attachments')) { const change = modelEvent.changes.keys.get('attachments'); changes.attachmentsChange = { oldValue: change.oldValue, newValue: this.ymodel.get('attachments') }; } return changes; } } /** * Shareable raw cell. */ class YRawCell extends YAttachmentCell { /** * Create a new YRawCell that works standalone. It cannot be * inserted into a YNotebook because the Yjs model is already * attached to an anonymous Y.Doc instance. */ static create(id) { return super.create(id); } /** * String identifying the type of cell. */ get cell_type() { return 'raw'; } /** * Serialize the model to JSON. */ toJSON() { return { id: this.getId(), cell_type: 'raw', source: this.getSource(), metadata: this.getMetadata(), attachments: this.getAttachments() }; } } /** * Shareable markdown cell. */ class YMarkdownCell extends YAttachmentCell { /** * Create a new YMarkdownCell that works standalone. It cannot be * inserted into a YNotebook because the Yjs model is already * attached to an anonymous Y.Doc instance. */ static create(id) { return super.create(id); } /** * String identifying the type of cell. */ get cell_type() { return 'markdown'; } /** * Serialize the model to JSON. */ toJSON() { return { id: this.getId(), cell_type: 'markdown', source: this.getSource(), metadata: this.getMetadata(), attachments: this.getAttachments() }; } } //# sourceMappingURL=ycell.js.map ;// CONCATENATED MODULE: ../node_modules/@jupyter/ydoc/lib/ynotebook.js /* ----------------------------------------------------------------------------- | Copyright (c) Jupyter Development Team. | Distributed under the terms of the Modified BSD License. |----------------------------------------------------------------------------*/ /** * Shared implementation of the Shared Document types. * * Shared cells can be inserted into a SharedNotebook. * Shared cells only start emitting events when they are connected to a SharedNotebook. * * "Standalone" cells must not be inserted into a (Shared)Notebook. * Standalone cells emit events immediately after they have been created, but they must not * be included into a (Shared)Notebook. */ class YNotebook extends YDocument { /** * Create a new notebook * * #### Notes * The document is empty and must be populated * * @param options */ constructor(options = {}) { var _a; super(); /** * Document version */ this.version = '2.0.0'; /** * YJS map for the notebook metadata */ this.ymeta = this.ydoc.getMap('meta'); /** * Handle a change to the ystate. */ this._onMetaChanged = (events) => { const metadataEvents = events.find(event => event.target === this.ymeta.get('metadata')); if (metadataEvents) { const metadataChange = metadataEvents.changes.keys; const ymetadata = this.ymeta.get('metadata'); metadataEvents.changes.keys.forEach((change, key) => { switch (change.action) { case 'add': this._metadataChanged.emit({ key, type: 'add', newValue: ymetadata.get(key) }); break; case 'delete': this._metadataChanged.emit({ key, type: 'remove', oldValue: change.oldValue }); break; case 'update': { const newValue = ymetadata.get(key); const oldValue = change.oldValue; let equal = true; if (typeof oldValue == 'object' && typeof newValue == 'object') { equal = index_js_.JSONExt.deepEqual(oldValue, newValue); } else { equal = oldValue === newValue; } if (!equal) { this._metadataChanged.emit({ key, type: 'change', oldValue, newValue }); } } break; } }); this._changed.emit({ metadataChange }); } const metaEvent = events.find(event => event.target === this.ymeta); if (!metaEvent) { return; } if (metaEvent.keysChanged.has('metadata')) { // Handle metadata change when adding/removing the YMap const change = metaEvent.changes.keys.get('metadata'); if ((change === null || change === void 0 ? void 0 : change.action) === 'add' && !change.oldValue) { const metadataChange = new Map(); for (const key of Object.keys(this.metadata)) { metadataChange.set(key, { action: 'add', oldValue: undefined }); this._metadataChanged.emit({ key, type: 'add', newValue: this.getMetadata(key) }); } this._changed.emit({ metadataChange }); } } if (metaEvent.keysChanged.has('nbformat')) { const change = metaEvent.changes.keys.get('nbformat'); const nbformatChanged = { key: 'nbformat', oldValue: (change === null || change === void 0 ? void 0 : change.oldValue) ? change.oldValue : undefined, newValue: this.nbformat }; this._changed.emit({ nbformatChanged }); } if (metaEvent.keysChanged.has('nbformat_minor')) { const change = metaEvent.changes.keys.get('nbformat_minor'); const nbformatChanged = { key: 'nbformat_minor', oldValue: (change === null || change === void 0 ? void 0 : change.oldValue) ? change.oldValue : undefined, newValue: this.nbformat_minor }; this._changed.emit({ nbformatChanged }); } }; /** * Handle a change to the list of cells. */ this._onYCellsChanged = (event) => { // update the type cell mapping by iterating through the added/removed types event.changes.added.forEach(item => { const type = item.content.type; if (!this._ycellMapping.has(type)) { const c = createCellModelFromSharedType(type, { notebook: this }); this._ycellMapping.set(type, c); } }); event.changes.deleted.forEach(item => { const type = item.content.type; const model = this._ycellMapping.get(type); if (model) { model.dispose(); this._ycellMapping.delete(type); } }); let index = 0; // this reflects the event.changes.delta, but replaces the content of delta.insert with ycells const cellsChange = []; event.changes.delta.forEach((d) => { if (d.insert != null) { const insertedCells = d.insert.map((ycell) => this._ycellMapping.get(ycell)); cellsChange.push({ insert: insertedCells }); this.cells.splice(index, 0, ...insertedCells); index += d.insert.length; } else if (d.delete != null) { cellsChange.push(d); this.cells.splice(index, d.delete); } else if (d.retain != null) { cellsChange.push(d); index += d.retain; } }); this._changed.emit({ cellsChange: cellsChange }); }; this._metadataChanged = new index_es6_js_.Signal(this); /** * Internal Yjs cells list */ this._ycells = this.ydoc.getArray('cells'); this._ycellMapping = new WeakMap(); this._disableDocumentWideUndoRedo = (_a = options.disableDocumentWideUndoRedo) !== null && _a !== void 0 ? _a : false; this.cells = this._ycells.toArray().map(ycell => { if (!this._ycellMapping.has(ycell)) { this._ycellMapping.set(ycell, createCellModelFromSharedType(ycell, { notebook: this })); } return this._ycellMapping.get(ycell); }); this.undoManager.addToScope(this._ycells); this._ycells.observe(this._onYCellsChanged); this.ymeta.observeDeep(this._onMetaChanged); } /** * Creates a standalone YNotebook * * Note: This method is useful when we need to initialize * the YNotebook from the JavaScript side. */ static create(options = {}) { var _a, _b, _c, _d, _e, _f, _g, _h, _j; const ynotebook = new YNotebook({ disableDocumentWideUndoRedo: (_a = options.disableDocumentWideUndoRedo) !== null && _a !== void 0 ? _a : false }); const data = { cells: (_c = (_b = options.data) === null || _b === void 0 ? void 0 : _b.cells) !== null && _c !== void 0 ? _c : [], nbformat: (_e = (_d = options.data) === null || _d === void 0 ? void 0 : _d.nbformat) !== null && _e !== void 0 ? _e : 4, nbformat_minor: (_g = (_f = options.data) === null || _f === void 0 ? void 0 : _f.nbformat_minor) !== null && _g !== void 0 ? _g : 5, metadata: (_j = (_h = options.data) === null || _h === void 0 ? void 0 : _h.metadata) !== null && _j !== void 0 ? _j : {} }; ynotebook.fromJSON(data); return ynotebook; } /** * Wether the undo/redo logic should be * considered on the full document across all cells. * * Default: false */ get disableDocumentWideUndoRedo() { return this._disableDocumentWideUndoRedo; } /** * Notebook metadata */ get metadata() { return this.getMetadata(); } set metadata(v) { this.setMetadata(v); } /** * Signal triggered when a metadata changes. */ get metadataChanged() { return this._metadataChanged; } /** * nbformat major version */ get nbformat() { return this.ymeta.get('nbformat'); } set nbformat(value) { this.transact(() => { this.ymeta.set('nbformat', value); }, false); } /** * nbformat minor version */ get nbformat_minor() { return this.ymeta.get('nbformat_minor'); } set nbformat_minor(value) { this.transact(() => { this.ymeta.set('nbformat_minor', value); }, false); } /** * Dispose of the resources. */ dispose() { if (this.isDisposed) { return; } this._ycells.unobserve(this._onYCellsChanged); this.ymeta.unobserveDeep(this._onMetaChanged); super.dispose(); } /** * Get a shared cell by index. * * @param index: Cell's position. * * @returns The requested shared cell. */ getCell(index) { return this.cells[index]; } /** * Add a shared cell at the notebook bottom. * * @param cell Cell to add. * * @returns The added cell. */ addCell(cell) { return this.insertCell(this._ycells.length, cell); } /** * Insert a shared cell into a specific position. * * @param index: Cell's position. * @param cell: Cell to insert. * * @returns The inserted cell. */ insertCell(index, cell) { return this.insertCells(index, [cell])[0]; } /** * Insert a list of shared cells into a specific position. * * @param index: Position to insert the cells. * @param cells: Array of shared cells to insert. * * @returns The inserted cells. */ insertCells(index, cells) { const yCells = cells.map(c => { const cell = createCell(c, this); this._ycellMapping.set(cell.ymodel, cell); return cell; }); this.transact(() => { this._ycells.insert(index, yCells.map(cell => cell.ymodel)); }); return yCells; } /** * Move a cell. * * @param fromIndex: Index of the cell to move. * @param toIndex: New position of the cell. */ moveCell(fromIndex, toIndex) { this.moveCells(fromIndex, toIndex); } /** * Move cells. * * @param fromIndex: Index of the first cells to move. * @param toIndex: New position of the first cell (in the current array). * @param n: Number of cells to move (default 1) */ moveCells(fromIndex, toIndex, n = 1) { // FIXME we need to use yjs move feature to preserve undo history const clones = new Array(n) .fill(true) .map((_, idx) => this.getCell(fromIndex + idx).toJSON()); this.transact(() => { this._ycells.delete(fromIndex, n); this._ycells.insert(fromIndex > toIndex ? toIndex : toIndex - n + 1, clones.map(clone => createCell(clone, this).ymodel)); }); } /** * Remove a cell. * * @param index: Index of the cell to remove. */ deleteCell(index) { this.deleteCellRange(index, index + 1); } /** * Remove a range of cells. * * @param from: The start index of the range to remove (inclusive). * @param to: The end index of the range to remove (exclusive). */ deleteCellRange(from, to) { // Cells will be removed from the mapping in the model event listener. this.transact(() => { this._ycells.delete(from, to - from); }); } /** * Delete a metadata notebook. * * @param key The key to delete */ deleteMetadata(key) { if (typeof this.getMetadata(key) === 'undefined') { return; } const allMetadata = this.metadata; delete allMetadata[key]; this.setMetadata(allMetadata); } getMetadata(key) { const ymetadata = this.ymeta.get('metadata'); // Transiently the metadata can be missing - like during destruction if (ymetadata === undefined) { return undefined; } if (typeof key === 'string') { const value = ymetadata.get(key); return typeof value === 'undefined' ? undefined // undefined is converted to `{}` by `JSONExt.deepCopy` : index_js_.JSONExt.deepCopy(value); } else { return index_js_.JSONExt.deepCopy(ymetadata.toJSON()); } } setMetadata(metadata, value) { var _a; if (typeof metadata === 'string') { if (typeof value === 'undefined') { throw new TypeError(`Metadata value for ${metadata} cannot be 'undefined'; use deleteMetadata.`); } if (index_js_.JSONExt.deepEqual((_a = this.getMetadata(metadata)) !== null && _a !== void 0 ? _a : null, value)) { return; } const update = {}; update[metadata] = value; this.updateMetadata(update); } else { if (!this.metadata || !index_js_.JSONExt.deepEqual(this.metadata, metadata)) { const clone = index_js_.JSONExt.deepCopy(metadata); const ymetadata = this.ymeta.get('metadata'); // Transiently the metadata can be missing - like during destruction if (ymetadata === undefined) { return undefined; } this.transact(() => { ymetadata.clear(); for (const [key, value] of Object.entries(clone)) { ymetadata.set(key, value); } }); } } } /** * Updates the metadata associated with the notebook. * * @param value: Metadata's attribute to update. */ updateMetadata(value) { // TODO: Maybe modify only attributes instead of replacing the whole metadata? const clone = index_js_.JSONExt.deepCopy(value); const ymetadata = this.ymeta.get('metadata'); // Transiently the metadata can be missing - like during destruction if (ymetadata === undefined) { return undefined; } this.transact(() => { for (const [key, value] of Object.entries(clone)) { ymetadata.set(key, value); } }); } /** * Get the notebook source * * @returns The notebook */ getSource() { return this.toJSON(); } /** * Set the notebook source * * @param value The notebook */ setSource(value) { this.fromJSON(value); } /** * Override the notebook with a JSON-serialized document. * * @param value The notebook */ fromJSON(value) { this.transact(() => { this.nbformat = value.nbformat; this.nbformat_minor = value.nbformat_minor; const metadata = value.metadata; if (metadata['orig_nbformat'] !== undefined) { delete metadata['orig_nbformat']; } if (!this.metadata) { const ymetadata = new yjs_mjs_.Map(); for (const [key, value] of Object.entries(metadata)) { ymetadata.set(key, value); } this.ymeta.set('metadata', ymetadata); } else { this.metadata = metadata; } const useId = value.nbformat === 4 && value.nbformat_minor >= 5; const ycells = value.cells.map(cell => { if (!useId) { delete cell.id; } return cell; }); this.insertCells(this.cells.length, ycells); this.deleteCellRange(0, this.cells.length); }); } /** * Serialize the model to JSON. */ toJSON() { // strip cell ids if we have notebook format 4.0-4.4 const pruneCellId = this.nbformat === 4 && this.nbformat_minor <= 4; return { metadata: this.metadata, nbformat_minor: this.nbformat_minor, nbformat: this.nbformat, cells: this.cells.map(c => { const raw = c.toJSON(); if (pruneCellId) { delete raw.id; } return raw; }) }; } } //# sourceMappingURL=ynotebook.js.map ;// CONCATENATED MODULE: ../node_modules/@jupyter/ydoc/lib/index.js /* ----------------------------------------------------------------------------- | Copyright (c) Jupyter Development Team. | Distributed under the terms of the Modified BSD License. |----------------------------------------------------------------------------*/ /** * @packageDocumentation * @module ydoc */ //# sourceMappingURL=index.js.map /***/ }), /***/ 79504: /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ Dp: () => (/* binding */ from), /* harmony export */ G: () => (/* binding */ some), /* harmony export */ JJ: () => (/* binding */ unfold), /* harmony export */ Z$: () => (/* binding */ last), /* harmony export */ kJ: () => (/* binding */ isArray), /* harmony export */ s7: () => (/* binding */ appendTo) /* harmony export */ }); /* unused harmony exports create, copy, every, equalFlat, flatten, fold, unique, uniqueBy, map */ /** * Utility module to work with Arrays. * * @module array */ /** * Return the last element of an array. The element must exist * * @template L * @param {ArrayLike} arr * @return {L} */ const last = arr => arr[arr.length - 1] /** * @template C * @return {Array} */ const create = () => /** @type {Array} */ ([]) /** * @template D * @param {Array} a * @return {Array} */ const copy = a => /** @type {Array} */ (a.slice()) /** * Append elements from src to dest * * @template M * @param {Array} dest * @param {Array} src */ const appendTo = (dest, src) => { for (let i = 0; i < src.length; i++) { dest.push(src[i]) } } /** * Transforms something array-like to an actual Array. * * @function * @template T * @param {ArrayLike|Iterable} arraylike * @return {T} */ const from = Array.from /** * True iff condition holds on every element in the Array. * * @function * @template ITEM * @template {ArrayLike} ARR * * @param {ARR} arr * @param {function(ITEM, number, ARR):boolean} f * @return {boolean} */ const every = (arr, f) => { for (let i = 0; i < arr.length; i++) { if (!f(arr[i], i, arr)) { return false } } return true } /** * True iff condition holds on some element in the Array. * * @function * @template S * @template {ArrayLike} ARR * @param {ARR} arr * @param {function(S, number, ARR):boolean} f * @return {boolean} */ const some = (arr, f) => { for (let i = 0; i < arr.length; i++) { if (f(arr[i], i, arr)) { return true } } return false } /** * @template ELEM * * @param {ArrayLike} a * @param {ArrayLike} b * @return {boolean} */ const equalFlat = (a, b) => a.length === b.length && every(a, (item, index) => item === b[index]) /** * @template ELEM * @param {Array>} arr * @return {Array} */ const flatten = arr => fold(arr, /** @type {Array} */ ([]), (acc, val) => acc.concat(val)) /** * @template T * @param {number} len * @param {function(number, Array):T} f * @return {Array} */ const unfold = (len, f) => { const array = new Array(len) for (let i = 0; i < len; i++) { array[i] = f(i, array) } return array } /** * @template T * @template RESULT * @param {Array} arr * @param {RESULT} seed * @param {function(RESULT, T, number):RESULT} folder */ const fold = (arr, seed, folder) => arr.reduce(folder, seed) const isArray = Array.isArray /** * @template T * @param {Array} arr * @return {Array} */ const unique = arr => from(set.from(arr)) /** * @template T * @template M * @param {ArrayLike} arr * @param {function(T):M} mapper * @return {Array} */ const uniqueBy = (arr, mapper) => { /** * @type {Set} */ const happened = set.create() /** * @type {Array} */ const result = [] for (let i = 0; i < arr.length; i++) { const el = arr[i] const mapped = mapper(el) if (!happened.has(mapped)) { happened.add(mapped) result.push(el) } } return result } /** * @template {ArrayLike} ARR * @template {function(ARR extends ArrayLike ? T : never, number, ARR):any} MAPPER * @param {ARR} arr * @param {MAPPER} mapper * @return {Array} */ const map = (arr, mapper) => { /** * @type {Array} */ const res = Array(arr.length) for (let i = 0; i < arr.length; i++) { res[i] = mapper(/** @type {any} */ (arr[i]), i, /** @type {any} */ (arr)) } return /** @type {any} */ (res) } /***/ }), /***/ 38828: /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ Hi: () => (/* binding */ equalityDeep), /* harmony export */ PP: () => (/* binding */ callAll), /* harmony export */ gB: () => (/* binding */ isOneOf), /* harmony export */ id: () => (/* binding */ id) /* harmony export */ }); /* unused harmony exports nop, apply, equalityStrict, equalityFlat, isArray, isString, isNumber, is, isTemplate */ /* harmony import */ var _array_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(79504); /* harmony import */ var _object_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(36498); /** * Common functions and function call helpers. * * @module function */ /** * Calls all functions in `fs` with args. Only throws after all functions were called. * * @param {Array} fs * @param {Array} args */ const callAll = (fs, args, i = 0) => { try { for (; i < fs.length; i++) { fs[i](...args) } } finally { if (i < fs.length) { callAll(fs, args, i + 1) } } } const nop = () => {} /** * @template T * @param {function():T} f * @return {T} */ const apply = f => f() /** * @template A * * @param {A} a * @return {A} */ const id = a => a /** * @template T * * @param {T} a * @param {T} b * @return {boolean} */ const equalityStrict = (a, b) => a === b /** * @template T * * @param {Array|object} a * @param {Array|object} b * @return {boolean} */ const equalityFlat = (a, b) => a === b || (a != null && b != null && a.constructor === b.constructor && ((array.isArray(a) && array.equalFlat(a, /** @type {Array} */ (b))) || (typeof a === 'object' && object.equalFlat(a, b)))) /* c8 ignore start */ /** * @param {any} a * @param {any} b * @return {boolean} */ const equalityDeep = (a, b) => { if (a == null || b == null) { return equalityStrict(a, b) } if (a.constructor !== b.constructor) { return false } if (a === b) { return true } switch (a.constructor) { case ArrayBuffer: a = new Uint8Array(a) b = new Uint8Array(b) // eslint-disable-next-line no-fallthrough case Uint8Array: { if (a.byteLength !== b.byteLength) { return false } for (let i = 0; i < a.length; i++) { if (a[i] !== b[i]) { return false } } break } case Set: { if (a.size !== b.size) { return false } for (const value of a) { if (!b.has(value)) { return false } } break } case Map: { if (a.size !== b.size) { return false } for (const key of a.keys()) { if (!b.has(key) || !equalityDeep(a.get(key), b.get(key))) { return false } } break } case Object: if (_object_js__WEBPACK_IMPORTED_MODULE_0__/* .length */ .kE(a) !== _object_js__WEBPACK_IMPORTED_MODULE_0__/* .length */ .kE(b)) { return false } for (const key in a) { if (!_object_js__WEBPACK_IMPORTED_MODULE_0__/* .hasProperty */ .l$(a, key) || !equalityDeep(a[key], b[key])) { return false } } break case Array: if (a.length !== b.length) { return false } for (let i = 0; i < a.length; i++) { if (!equalityDeep(a[i], b[i])) { return false } } break default: return false } return true } /** * @template V * @template {V} OPTS * * @param {V} value * @param {Array} options */ // @ts-ignore const isOneOf = (value, options) => options.includes(value) /* c8 ignore stop */ const isArray = _array_js__WEBPACK_IMPORTED_MODULE_1__/* .isArray */ .kJ /** * @param {any} s * @return {s is String} */ const isString = (s) => s && s.constructor === String /** * @param {any} n * @return {n is Number} */ const isNumber = n => n != null && n.constructor === Number /** * @template {abstract new (...args: any) => any} TYPE * @param {any} n * @param {TYPE} T * @return {n is InstanceType} */ const is = (n, T) => n && n.constructor === T /** * @template {abstract new (...args: any) => any} TYPE * @param {TYPE} T */ const isTemplate = (T) => /** * @param {any} n * @return {n is InstanceType} **/ n => n && n.constructor === T /***/ }), /***/ 22592: /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ JG: () => (/* binding */ copy), /* harmony export */ UI: () => (/* binding */ map), /* harmony export */ Ue: () => (/* binding */ create), /* harmony export */ Yj: () => (/* binding */ any), /* harmony export */ Yu: () => (/* binding */ setIfUndefined) /* harmony export */ }); /* unused harmony export all */ /** * Utility module to work with key-value stores. * * @module map */ /** * Creates a new Map instance. * * @function * @return {Map} * * @function */ const create = () => new Map() /** * Copy a Map object into a fresh Map object. * * @function * @template X,Y * @param {Map} m * @return {Map} */ const copy = m => { const r = create() m.forEach((v, k) => { r.set(k, v) }) return r } /** * Get map property. Create T if property is undefined and set T on map. * * ```js * const listeners = map.setIfUndefined(events, 'eventName', set.create) * listeners.add(listener) * ``` * * @function * @template V,K * @template {Map} MAP * @param {MAP} map * @param {K} key * @param {function():V} createT * @return {V} */ const setIfUndefined = (map, key, createT) => { let set = map.get(key) if (set === undefined) { map.set(key, set = createT()) } return set } /** * Creates an Array and populates it with the content of all key-value pairs using the `f(value, key)` function. * * @function * @template K * @template V * @template R * @param {Map} m * @param {function(V,K):R} f * @return {Array} */ const map = (m, f) => { const res = [] for (const [key, value] of m) { res.push(f(value, key)) } return res } /** * Tests whether any key-value pairs pass the test implemented by `f(value, key)`. * * @todo should rename to some - similarly to Array.some * * @function * @template K * @template V * @param {Map} m * @param {function(V,K):boolean} f * @return {boolean} */ const any = (m, f) => { for (const [key, value] of m) { if (f(value, key)) { return true } } return false } /** * Tests whether all key-value pairs pass the test implemented by `f(value, key)`. * * @function * @template K * @template V * @param {Map} m * @param {function(V,K):boolean} f * @return {boolean} */ const all = (m, f) => { for (const [key, value] of m) { if (!f(value, key)) { return false } } return true } /***/ }), /***/ 11182: /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ Fp: () => (/* binding */ max), /* harmony export */ GR: () => (/* binding */ isNegativeZero), /* harmony export */ GW: () => (/* binding */ floor), /* harmony export */ VV: () => (/* binding */ min), /* harmony export */ Wn: () => (/* binding */ abs) /* harmony export */ }); /* unused harmony exports ceil, imul, round, log10, log2, log, sqrt, add, isNaN, pow, exp10, sign */ /** * Common Math expressions. * * @module math */ const floor = Math.floor const ceil = Math.ceil const abs = Math.abs const imul = Math.imul const round = Math.round const log10 = Math.log10 const log2 = Math.log2 const log = Math.log const sqrt = Math.sqrt /** * @function * @param {number} a * @param {number} b * @return {number} The sum of a and b */ const add = (a, b) => a + b /** * @function * @param {number} a * @param {number} b * @return {number} The smaller element of a and b */ const min = (a, b) => a < b ? a : b /** * @function * @param {number} a * @param {number} b * @return {number} The bigger element of a and b */ const max = (a, b) => a > b ? a : b const isNaN = Number.isNaN const pow = Math.pow /** * Base 10 exponential function. Returns the value of 10 raised to the power of pow. * * @param {number} exp * @return {number} */ const exp10 = exp => Math.pow(10, exp) const sign = Math.sign /** * @param {number} n * @return {boolean} Wether n is negative. This function also differentiates between -0 and +0 */ const isNegativeZero = n => n !== 0 ? n < 0 : 1 / n < 0 /***/ }), /***/ 36498: /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ $m: () => (/* binding */ equalFlat), /* harmony export */ Ed: () => (/* binding */ forEach), /* harmony export */ f0: () => (/* binding */ assign), /* harmony export */ kE: () => (/* binding */ length), /* harmony export */ l$: () => (/* binding */ hasProperty), /* harmony export */ xb: () => (/* binding */ isEmpty) /* harmony export */ }); /* unused harmony exports create, keys, map, some, every */ /** * Utility functions for working with EcmaScript objects. * * @module object */ /** * @return {Object} obj */ const create = () => Object.create(null) /** * Object.assign */ const assign = Object.assign /** * @param {Object} obj */ const keys = Object.keys /** * @template V * @param {{[k:string]:V}} obj * @param {function(V,string):any} f */ const forEach = (obj, f) => { for (const key in obj) { f(obj[key], key) } } /** * @todo implement mapToArray & map * * @template R * @param {Object} obj * @param {function(any,string):R} f * @return {Array} */ const map = (obj, f) => { const results = [] for (const key in obj) { results.push(f(obj[key], key)) } return results } /** * @param {Object} obj * @return {number} */ const length = obj => keys(obj).length /** * @param {Object} obj * @param {function(any,string):boolean} f * @return {boolean} */ const some = (obj, f) => { for (const key in obj) { if (f(obj[key], key)) { return true } } return false } /** * @param {Object|undefined} obj */ const isEmpty = obj => { // eslint-disable-next-line for (const _k in obj) { return false } return true } /** * @param {Object} obj * @param {function(any,string):boolean} f * @return {boolean} */ const every = (obj, f) => { for (const key in obj) { if (!f(obj[key], key)) { return false } } return true } /** * Calls `Object.prototype.hasOwnProperty`. * * @param {any} obj * @param {string|symbol} key * @return {boolean} */ const hasProperty = (obj, key) => Object.prototype.hasOwnProperty.call(obj, key) /** * @param {Object} a * @param {Object} b * @return {boolean} */ const equalFlat = (a, b) => a === b || (length(a) === length(b) && every(a, (val, key) => (val !== undefined || hasProperty(b, key)) && b[key] === val)) /***/ }), /***/ 12330: /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ y: () => (/* binding */ Observable) /* harmony export */ }); /* unused harmony export ObservableV2 */ /* harmony import */ var _map_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(22592); /* harmony import */ var _set_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(79287); /* harmony import */ var _array_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(79504); /** * Observable class prototype. * * @module observable */ /** * Handles named events. * @experimental * * This is basically a (better typed) duplicate of Observable, which will replace Observable in the * next release. * * @template {{[key: string]: function(...any):void}} EVENTS */ class ObservableV2 { constructor () { /** * Some desc. * @type {Map>} */ this._observers = map.create() } /** * @template {string} NAME * @param {NAME} name * @param {EVENTS[NAME]} f */ on (name, f) { map.setIfUndefined(this._observers, /** @type {string} */ (name), set.create).add(f) return f } /** * @template {string} NAME * @param {NAME} name * @param {EVENTS[NAME]} f */ once (name, f) { /** * @param {...any} args */ const _f = (...args) => { this.off(name, /** @type {any} */ (_f)) f(...args) } this.on(name, /** @type {any} */ (_f)) } /** * @template {string} NAME * @param {NAME} name * @param {EVENTS[NAME]} f */ off (name, f) { const observers = this._observers.get(name) if (observers !== undefined) { observers.delete(f) if (observers.size === 0) { this._observers.delete(name) } } } /** * Emit a named event. All registered event listeners that listen to the * specified name will receive the event. * * @todo This should catch exceptions * * @template {string} NAME * @param {NAME} name The event name. * @param {Parameters} args The arguments that are applied to the event listener. */ emit (name, args) { // copy all listeners to an array first to make sure that no event is emitted to listeners that are subscribed while the event handler is called. return array.from((this._observers.get(name) || map.create()).values()).forEach(f => f(...args)) } destroy () { this._observers = map.create() } } /* c8 ignore start */ /** * Handles named events. * * @deprecated * @template N */ class Observable { constructor () { /** * Some desc. * @type {Map} */ this._observers = _map_js__WEBPACK_IMPORTED_MODULE_0__/* .create */ .Ue() } /** * @param {N} name * @param {function} f */ on (name, f) { _map_js__WEBPACK_IMPORTED_MODULE_0__/* .setIfUndefined */ .Yu(this._observers, name, _set_js__WEBPACK_IMPORTED_MODULE_1__/* .create */ .Ue).add(f) } /** * @param {N} name * @param {function} f */ once (name, f) { /** * @param {...any} args */ const _f = (...args) => { this.off(name, _f) f(...args) } this.on(name, _f) } /** * @param {N} name * @param {function} f */ off (name, f) { const observers = this._observers.get(name) if (observers !== undefined) { observers.delete(f) if (observers.size === 0) { this._observers.delete(name) } } } /** * Emit a named event. All registered event listeners that listen to the * specified name will receive the event. * * @todo This should catch exceptions * * @param {N} name The event name. * @param {Array} args The arguments that are applied to the event listener. */ emit (name, args) { // copy all listeners to an array first to make sure that no event is emitted to listeners that are subscribed while the event handler is called. return _array_js__WEBPACK_IMPORTED_MODULE_2__/* .from */ .Dp((this._observers.get(name) || _map_js__WEBPACK_IMPORTED_MODULE_0__/* .create */ .Ue()).values()).forEach(f => f(...args)) } destroy () { this._observers = _map_js__WEBPACK_IMPORTED_MODULE_0__/* .create */ .Ue() } } /* c8 ignore end */ /***/ }), /***/ 79287: /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ Ue: () => (/* binding */ create) /* harmony export */ }); /* unused harmony exports toArray, first, from */ /** * Utility module to work with sets. * * @module set */ const create = () => new Set() /** * @template T * @param {Set} set * @return {Array} */ const toArray = set => Array.from(set) /** * @template T * @param {Set} set * @return {T} */ const first = set => set.values().next().value || undefined /** * @template T * @param {Iterable} entries * @return {Set} */ const from = entries => new Set(entries) /***/ }), /***/ 2431: /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ ZG: () => (/* binding */ getUnixTime) /* harmony export */ }); /* unused harmony exports getDate, humanizeDuration */ /** * Utility module to work with time. * * @module time */ /** * Return current time. * * @return {Date} */ const getDate = () => new Date() /** * Return current unix time. * * @return {number} */ const getUnixTime = Date.now /** * Transform time (in ms) to a human readable format. E.g. 1100 => 1.1s. 60s => 1min. .001 => 10μs. * * @param {number} d duration in milliseconds * @return {string} humanized approximation of time */ const humanizeDuration = d => { if (d < 60000) { const p = metric.prefix(d, -1) return math.round(p.n * 100) / 100 + p.prefix + 's' } d = math.floor(d / 1000) const seconds = d % 60 const minutes = math.floor(d / 60) % 60 const hours = math.floor(d / 3600) % 24 const days = math.floor(d / 86400) if (days > 0) { return days + 'd' + ((hours > 0 || minutes > 30) ? ' ' + (minutes > 30 ? hours + 1 : hours) + 'h' : '') } if (hours > 0) { /* c8 ignore next */ return hours + 'h' + ((minutes > 0 || seconds > 30) ? ' ' + (seconds > 30 ? minutes + 1 : minutes) + 'min' : '') } return minutes + 'min' + (seconds > 0 ? ' ' + seconds + 's' : '') } /***/ }) }]); //# sourceMappingURL=35.20ba31d4f65b5da8ab98.js.map?v=20ba31d4f65b5da8ab98