UNPKG

molstar

Version:

A comprehensive macromolecular library.

990 lines 46.2 kB
"use strict"; /** * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author David Sehnal <david.sehnal@gmail.com> */ Object.defineProperty(exports, "__esModule", { value: true }); exports.State = void 0; var tslib_1 = require("tslib"); var object_1 = require("./object"); var tree_1 = require("./tree"); var transform_1 = require("./transform"); var transformer_1 = require("./transformer"); var mol_task_1 = require("../mol-task"); var selection_1 = require("./state/selection"); var rx_event_helper_1 = require("../mol-util/rx-event-helper"); var builder_1 = require("./state/builder"); var manager_1 = require("./action/manager"); var log_entry_1 = require("../mol-util/log-entry"); var now_1 = require("../mol-util/now"); var param_definition_1 = require("../mol-util/param-definition"); var spine_1 = require("./tree/spine"); var async_queue_1 = require("../mol-util/async-queue"); var debug_1 = require("../mol-util/debug"); var array_1 = require("../mol-util/array"); var generic_1 = require("../mol-data/generic"); var object_2 = require("../mol-util/object"); var State = /** @class */ (function () { function State(rootObject, params) { var _this = this; this.errorFree = true; this.ev = rx_event_helper_1.RxEventHelper.create(); this.globalContext = void 0; this.events = { cell: { stateUpdated: this.ev(), created: this.ev(), removed: this.ev(), }, object: { updated: this.ev(), created: this.ev(), removed: this.ev() }, log: this.ev(), changed: this.ev(), historyUpdated: this.ev() }; this.behaviors = { currentObject: this.ev.behavior({ state: this, ref: transform_1.StateTransform.RootRef }), isUpdating: this.ev.behavior(false), }; this.actions = new manager_1.StateActionManager(); this.cells = new Map(); this.spine = new spine_1.StateTreeSpine.Impl(this.cells); this.tryGetCellData = function (ref) { var _a, _b; var ret = (_b = (_a = _this.cells.get(ref)) === null || _a === void 0 ? void 0 : _a.obj) === null || _b === void 0 ? void 0 : _b.data; if (!ref) throw new Error("Cell '" + ref + "' data undefined."); return ret; }; this.historyCapacity = 5; this.history = []; this.undoingHistory = false; this.inTransaction = false; this.inTransactionError = false; this._inUpdate = false; this.reverted = false; this.updateQueue = new async_queue_1.AsyncQueue(); this._tree = tree_1.StateTree.createEmpty(transform_1.StateTransform.createRoot(params && params.rootState)).asTransient(); var tree = this._tree; var root = tree.root; this.runTask = params.runTask; if ((params === null || params === void 0 ? void 0 : params.historyCapacity) !== void 0) this.historyCapacity = params.historyCapacity; this.cells.set(root.ref, { parent: this, transform: root, sourceRef: void 0, obj: rootObject, status: 'ok', state: (0, tslib_1.__assign)({}, root.state), errorText: void 0, params: { definition: {}, values: {} }, paramsNormalizedVersion: root.version, dependencies: { dependentBy: [], dependsOn: [] }, cache: {} }); this.globalContext = params && params.globalContext; } Object.defineProperty(State.prototype, "tree", { get: function () { return this._tree; }, enumerable: false, configurable: true }); Object.defineProperty(State.prototype, "transforms", { get: function () { return this._tree.transforms; }, enumerable: false, configurable: true }); Object.defineProperty(State.prototype, "current", { get: function () { return this.behaviors.currentObject.value.ref; }, enumerable: false, configurable: true }); Object.defineProperty(State.prototype, "root", { get: function () { return this.cells.get(this._tree.root.ref); }, enumerable: false, configurable: true }); State.prototype.build = function () { return new builder_1.StateBuilder.Root(this.tree, this); }; State.prototype.addHistory = function (tree, label) { if (this.historyCapacity === 0) return; this.history.unshift([tree, label || 'Update']); if (this.history.length > this.historyCapacity) this.history.pop(); this.events.historyUpdated.next({ state: this }); }; State.prototype.clearHistory = function () { if (this.history.length === 0) return; this.history = []; this.events.historyUpdated.next({ state: this }); }; Object.defineProperty(State.prototype, "latestUndoLabel", { get: function () { return this.history.length > 0 ? this.history[0][1] : void 0; }, enumerable: false, configurable: true }); Object.defineProperty(State.prototype, "canUndo", { get: function () { return this.history.length > 0; }, enumerable: false, configurable: true }); State.prototype.undo = function () { var _this = this; return mol_task_1.Task.create('Undo', function (ctx) { return (0, tslib_1.__awaiter)(_this, void 0, void 0, function () { var e; return (0, tslib_1.__generator)(this, function (_a) { switch (_a.label) { case 0: e = this.history.shift(); if (!e) return [2 /*return*/]; this.events.historyUpdated.next({ state: this }); this.undoingHistory = true; _a.label = 1; case 1: _a.trys.push([1, , 3, 4]); return [4 /*yield*/, this.updateTree(e[0], { canUndo: false }).runInContext(ctx)]; case 2: _a.sent(); return [3 /*break*/, 4]; case 3: this.undoingHistory = false; return [7 /*endfinally*/]; case 4: return [2 /*return*/]; } }); }); }); }; State.prototype.getSnapshot = function () { return { tree: tree_1.StateTree.toJSON(this._tree) }; }; State.prototype.setSnapshot = function (snapshot) { var tree = tree_1.StateTree.fromJSON(snapshot.tree); return this.updateTree(tree); }; State.prototype.setCurrent = function (ref) { this.behaviors.currentObject.next({ state: this, ref: ref }); }; State.prototype.updateCellState = function (ref, stateOrProvider) { var cell = this.cells.get(ref); if (!cell) return; var update = typeof stateOrProvider === 'function' ? stateOrProvider(cell.state) : stateOrProvider; if (transform_1.StateTransform.assignState(cell.state, update)) { cell.transform = this._tree.assignState(cell.transform.ref, update); this.events.cell.stateUpdated.next({ state: this, ref: ref, cell: cell }); } }; State.prototype.dispose = function () { this.ev.dispose(); this.actions.dispose(); }; /** * Select Cells using the provided selector. * @example state.query(StateSelection.Generators.byRef('test').ancestorOfType(type)) * @example state.query('test') */ State.prototype.select = function (selector) { return selection_1.StateSelection.select(selector, this); }; /** * Select Cells by building a query generated on the fly. * @example state.select(q => q.byRef('test').subtree()) */ State.prototype.selectQ = function (selector) { if (typeof selector === 'string') return selection_1.StateSelection.select(selector, this); return selection_1.StateSelection.select(selector(selection_1.StateSelection.Generators), this); }; /** * Creates a Task that applies the specified StateAction (i.e. must use run* on the result) * If no ref is specified, apply to root. */ State.prototype.applyAction = function (action, params, ref) { var _this = this; if (ref === void 0) { ref = transform_1.StateTransform.RootRef; } return mol_task_1.Task.create('Apply Action', function (ctx) { var cell = _this.cells.get(ref); if (!cell) throw new Error("'" + ref + "' does not exist."); if (cell.status !== 'ok') throw new Error("Action cannot be applied to a cell with status '" + cell.status + "'"); return runTask(action.definition.run({ ref: ref, cell: cell, a: cell.obj, params: params, state: _this }, _this.globalContext), ctx); }); }; /** Apply series of updates to the state. If any of them fail, revert to the original state. */ State.prototype.transaction = function (edits, options) { var _this = this; return mol_task_1.Task.create('State Transaction', function (ctx) { return (0, tslib_1.__awaiter)(_this, void 0, void 0, function () { var isNested, snapshot, restored, e_1; return (0, tslib_1.__generator)(this, function (_a) { switch (_a.label) { case 0: isNested = this.inTransaction; snapshot = this._tree.asImmutable(); restored = false; _a.label = 1; case 1: _a.trys.push([1, 5, 8, 9]); if (!isNested) this.behaviors.isUpdating.next(true); this.inTransaction = true; this.inTransactionError = false; return [4 /*yield*/, edits(ctx)]; case 2: _a.sent(); if (!this.inTransactionError) return [3 /*break*/, 4]; restored = true; return [4 /*yield*/, this.updateTree(snapshot).runInContext(ctx)]; case 3: _a.sent(); _a.label = 4; case 4: return [3 /*break*/, 9]; case 5: e_1 = _a.sent(); if (!!restored) return [3 /*break*/, 7]; restored = true; return [4 /*yield*/, this.updateTree(snapshot).runInContext(ctx)]; case 6: _a.sent(); this.events.log.next(log_entry_1.LogEntry.error('' + e_1)); _a.label = 7; case 7: if (isNested) { this.inTransactionError = true; throw e_1; } if (options === null || options === void 0 ? void 0 : options.rethrowErrors) throw e_1; return [3 /*break*/, 9]; case 8: if (!isNested) { this.inTransaction = false; this.events.changed.next({ state: this, inTransaction: false }); this.behaviors.isUpdating.next(false); if (!restored) { if (options === null || options === void 0 ? void 0 : options.canUndo) this.addHistory(snapshot, typeof options.canUndo === 'string' ? options.canUndo : void 0); else this.clearHistory(); } } return [7 /*endfinally*/]; case 9: return [2 /*return*/]; } }); }); }); }; Object.defineProperty(State.prototype, "inUpdate", { /** * Determines whether the state is currently "inside" updateTree function. * This is different from "isUpdating" which wraps entire transactions. */ get: function () { return this._inUpdate; }, enumerable: false, configurable: true }); State.prototype.updateTree = function (tree, options) { var _this = this; var params = { tree: tree, options: options }; return mol_task_1.Task.create('Update Tree', function (taskCtx) { return (0, tslib_1.__awaiter)(_this, void 0, void 0, function () { var removed, snapshot, reverted, ret, _a; return (0, tslib_1.__generator)(this, function (_b) { switch (_b.label) { case 0: return [4 /*yield*/, this.updateQueue.enqueue(params)]; case 1: removed = _b.sent(); if (!removed) return [2 /*return*/]; this._inUpdate = true; snapshot = (options === null || options === void 0 ? void 0 : options.canUndo) ? this._tree.asImmutable() : void 0; reverted = false; if (!this.inTransaction) this.behaviors.isUpdating.next(true); _b.label = 2; case 2: _b.trys.push([2, , 7, 8]); if (builder_1.StateBuilder.is(tree)) { if (tree.editInfo.applied) throw new Error('This builder has already been applied. Create a new builder for further state updates'); tree.editInfo.applied = true; } this.reverted = false; if (!(options && (options.revertIfAborted || options.revertOnError))) return [3 /*break*/, 4]; return [4 /*yield*/, this._revertibleTreeUpdate(taskCtx, params, options)]; case 3: _a = _b.sent(); return [3 /*break*/, 6]; case 4: return [4 /*yield*/, this._updateTree(taskCtx, params)]; case 5: _a = _b.sent(); _b.label = 6; case 6: ret = _a; reverted = this.reverted; if (ret.ctx.hadError) this.inTransactionError = true; if (!ret.cell) return [2 /*return*/]; return [2 /*return*/, new object_1.StateObjectSelector(ret.cell.transform.ref, this)]; case 7: this._inUpdate = false; this.updateQueue.handled(params); if (!this.inTransaction) { this.behaviors.isUpdating.next(false); if (!(options === null || options === void 0 ? void 0 : options.canUndo)) { if (!this.undoingHistory) this.clearHistory(); } else if (!reverted) { this.addHistory(snapshot, typeof options.canUndo === 'string' ? options.canUndo : void 0); } } return [7 /*endfinally*/]; case 8: return [2 /*return*/]; } }); }); }, function () { _this.updateQueue.remove(params); }); }; State.prototype._revertibleTreeUpdate = function (taskCtx, params, options) { return (0, tslib_1.__awaiter)(this, void 0, void 0, function () { var old, ret, revert; return (0, tslib_1.__generator)(this, function (_a) { switch (_a.label) { case 0: old = this.tree; return [4 /*yield*/, this._updateTree(taskCtx, params)]; case 1: ret = _a.sent(); revert = ((ret.ctx.hadError || ret.ctx.wasAborted) && options.revertOnError) || (ret.ctx.wasAborted && options.revertIfAborted); if (!revert) return [3 /*break*/, 3]; this.reverted = true; return [4 /*yield*/, this._updateTree(taskCtx, { tree: old, options: params.options })]; case 2: return [2 /*return*/, _a.sent()]; case 3: return [2 /*return*/, ret]; } }); }); }; State.prototype._updateTree = function (taskCtx, params) { return (0, tslib_1.__awaiter)(this, void 0, void 0, function () { var updated, ctx, cell; return (0, tslib_1.__generator)(this, function (_a) { switch (_a.label) { case 0: updated = false; ctx = this.updateTreeAndCreateCtx(params.tree, taskCtx, params.options); _a.label = 1; case 1: _a.trys.push([1, , 3, 4]); return [4 /*yield*/, update(ctx)]; case 2: updated = _a.sent(); if (builder_1.StateBuilder.isTo(params.tree)) { cell = this.select(params.tree.ref)[0]; return [2 /*return*/, { ctx: ctx, cell: cell }]; } return [2 /*return*/, { ctx: ctx }]; case 3: this.spine.current = undefined; if (updated) this.events.changed.next({ state: this, inTransaction: this.inTransaction }); return [7 /*endfinally*/]; case 4: return [2 /*return*/]; } }); }); }; State.prototype.updateTreeAndCreateCtx = function (tree, taskCtx, options) { var _tree = (builder_1.StateBuilder.is(tree) ? tree.getTree() : tree).asTransient(); var oldTree = this._tree; this._tree = _tree; var cells = this.cells; var ctx = { parent: this, editInfo: builder_1.StateBuilder.is(tree) ? tree.editInfo : void 0, errorFree: this.errorFree, taskCtx: taskCtx, oldTree: oldTree, tree: _tree, cells: this.cells, spine: this.spine, results: [], options: (0, tslib_1.__assign)((0, tslib_1.__assign)({}, StateUpdateDefaultOptions), options), changed: false, hadError: false, wasAborted: false, newCurrent: void 0, getCellData: function (ref) { var _a; return (_a = cells.get(ref).obj) === null || _a === void 0 ? void 0 : _a.data; } }; this.errorFree = true; return ctx; }; return State; }()); exports.State = State; (function (State) { function create(rootObject, params) { return new State(rootObject, params); } State.create = create; var ObjectEvent; (function (ObjectEvent) { function isCell(e, cell) { return !!cell && e.ref === cell.transform.ref && e.state === cell.parent; } ObjectEvent.isCell = isCell; })(ObjectEvent = State.ObjectEvent || (State.ObjectEvent = {})); })(State || (State = {})); exports.State = State; var StateUpdateDefaultOptions = { doNotLogTiming: false, doNotUpdateCurrent: true, revertIfAborted: false, revertOnError: false, canUndo: false }; function update(ctx) { return (0, tslib_1.__awaiter)(this, void 0, void 0, function () { var fastTrack, deletes, deletedObjects, roots, current, hasCurrent, _i, deletes_1, d, newCurrent_1, i, cell, _a, deletes_2, d, cell, obj, init, _b, _c, cell, i, d, parent_1, _d, _e, cell, _f, roots_1, root, newCurrent, _g, _h, update_1, transform, current, currentCell; return (0, tslib_1.__generator)(this, function (_j) { switch (_j.label) { case 0: fastTrack = !!(ctx.editInfo && ctx.editInfo.count === 1 && ctx.editInfo.lastUpdate && ctx.editInfo.sourceTree === ctx.oldTree); deletedObjects = []; if (fastTrack) { deletes = []; roots = [ctx.editInfo.lastUpdate]; } else { // find all nodes that will definitely be deleted. // this is done in "post order", meaning that leaves will be deleted first. deletes = findDeletes(ctx); current = ctx.parent.current; hasCurrent = false; for (_i = 0, deletes_1 = deletes; _i < deletes_1.length; _i++) { d = deletes_1[_i]; if (d === current) { hasCurrent = true; break; } } if (hasCurrent) { newCurrent_1 = findNewCurrent(ctx.oldTree, current, deletes, ctx.cells); ctx.parent.setCurrent(newCurrent_1); } for (i = deletes.length - 1; i >= 0; i--) { cell = ctx.cells.get(deletes[i]); if (cell) { dispose(cell.transform, cell.obj, cell === null || cell === void 0 ? void 0 : cell.transform.params, cell.cache, ctx.parent.globalContext); } } for (_a = 0, deletes_2 = deletes; _a < deletes_2.length; _a++) { d = deletes_2[_a]; cell = ctx.cells.get(d); if (cell) { cell.parent = void 0; unlinkCell(cell); } obj = cell && cell.obj; ctx.cells.delete(d); deletedObjects.push(obj); } // Find roots where transform version changed or where nodes will be added. roots = findUpdateRoots(ctx.cells, ctx.tree); } init = initCells(ctx, roots); // Notify additions of new cells. for (_b = 0, _c = init.added; _b < _c.length; _b++) { cell = _c[_b]; ctx.parent.events.cell.created.next({ state: ctx.parent, ref: cell.transform.ref, cell: cell }); } for (i = 0; i < deletes.length; i++) { d = deletes[i]; parent_1 = ctx.oldTree.transforms.get(d).parent; ctx.parent.events.object.removed.next({ state: ctx.parent, ref: d, obj: deletedObjects[i] }); ctx.parent.events.cell.removed.next({ state: ctx.parent, ref: d, parent: parent_1 }); } if (deletedObjects.length) deletedObjects = []; if (init.dependent) { for (_d = 0, _e = init.dependent; _d < _e.length; _d++) { cell = _e[_d]; roots.push(cell.transform.ref); } } // Set status of cells that will be updated to 'pending'. initCellStatus(ctx, roots); _f = 0, roots_1 = roots; _j.label = 1; case 1: if (!(_f < roots_1.length)) return [3 /*break*/, 4]; root = roots_1[_f]; return [4 /*yield*/, updateSubtree(ctx, root)]; case 2: _j.sent(); _j.label = 3; case 3: _f++; return [3 /*break*/, 1]; case 4: // Sync cell states if (!ctx.editInfo) { syncNewStates(ctx); } newCurrent = ctx.newCurrent; // Raise object updated events for (_g = 0, _h = ctx.results; _g < _h.length; _g++) { update_1 = _h[_g]; if (update_1.action === 'created') { ctx.parent.events.object.created.next({ state: ctx.parent, ref: update_1.ref, obj: update_1.obj }); if (!ctx.newCurrent) { transform = ctx.tree.transforms.get(update_1.ref); if (!transform.state.isGhost && update_1.obj !== object_1.StateObject.Null) newCurrent = update_1.ref; } } else if (update_1.action === 'updated') { ctx.parent.events.object.updated.next({ state: ctx.parent, ref: update_1.ref, action: 'in-place', obj: update_1.obj, oldData: update_1.oldData }); } else if (update_1.action === 'replaced') { ctx.parent.events.object.updated.next({ state: ctx.parent, ref: update_1.ref, action: 'recreate', obj: update_1.obj, oldObj: update_1.oldObj }); } } if (newCurrent) { if (!ctx.options.doNotUpdateCurrent) ctx.parent.setCurrent(newCurrent); } else { current = ctx.parent.current; currentCell = ctx.cells.get(current); if (currentCell && (currentCell.obj === object_1.StateObject.Null || (currentCell.status === 'error' && currentCell.errorText === ParentNullErrorText))) { newCurrent = findNewCurrent(ctx.oldTree, current, [], ctx.cells); ctx.parent.setCurrent(newCurrent); } } return [2 /*return*/, deletes.length > 0 || roots.length > 0 || ctx.changed]; } }); }); } function findUpdateRoots(cells, tree) { var findState = { roots: [], cells: cells }; tree_1.StateTree.doPreOrder(tree, tree.root, findState, findUpdateRootsVisitor); return findState.roots; } function findUpdateRootsVisitor(n, _, s) { var cell = s.cells.get(n.ref); if (!cell || cell.transform.version !== n.version) { s.roots.push(n.ref); return false; } if (cell.status === 'error') return false; // nothing below a Null object can be an update root if (cell && cell.obj === object_1.StateObject.Null) return false; return true; } function checkDeleteVisitor(n, _, ctx) { if (!ctx.newTree.transforms.has(n.ref) && ctx.cells.has(n.ref)) ctx.deletes.push(n.ref); } function findDeletes(ctx) { var deleteCtx = { newTree: ctx.tree, cells: ctx.cells, deletes: [] }; tree_1.StateTree.doPostOrder(ctx.oldTree, ctx.oldTree.root, deleteCtx, checkDeleteVisitor); return deleteCtx.deletes; } function syncNewStatesVisitor(n, tree, ctx) { var cell = ctx.cells.get(n.ref); if (!cell || !transform_1.StateTransform.syncState(cell.state, n.state)) return; ctx.parent.events.cell.stateUpdated.next({ state: ctx.parent, ref: n.ref, cell: cell }); } function syncNewStates(ctx) { tree_1.StateTree.doPreOrder(ctx.tree, ctx.tree.root, ctx, syncNewStatesVisitor); } function setCellStatus(ctx, ref, status, errorText) { var cell = ctx.cells.get(ref); var changed = cell.status !== status; cell.status = status; cell.errorText = errorText; if (changed) ctx.parent.events.cell.stateUpdated.next({ state: ctx.parent, ref: ref, cell: cell }); } function initCellStatusVisitor(t, _, ctx) { ctx.cells.get(t.ref).transform = t; setCellStatus(ctx, t.ref, 'pending'); } function initCellStatus(ctx, roots) { for (var _i = 0, roots_2 = roots; _i < roots_2.length; _i++) { var root = roots_2[_i]; tree_1.StateTree.doPreOrder(ctx.tree, ctx.tree.transforms.get(root), ctx, initCellStatusVisitor); } } function unlinkCell(cell) { for (var _i = 0, _a = cell.dependencies.dependsOn; _i < _a.length; _i++) { var other = _a[_i]; (0, array_1.arraySetRemove)(other.dependencies.dependentBy, cell); } } function addCellsVisitor(transform, _, _a) { var ctx = _a.ctx, added = _a.added, visited = _a.visited; visited.add(transform.ref); if (ctx.cells.has(transform.ref)) { return; } var cell = { parent: ctx.parent, transform: transform, sourceRef: void 0, status: 'pending', state: (0, tslib_1.__assign)({}, transform.state), errorText: void 0, params: void 0, paramsNormalizedVersion: '', dependencies: { dependentBy: [], dependsOn: [] }, cache: void 0 }; ctx.cells.set(transform.ref, cell); added.push(cell); } // type LinkCellsCtx = { ctx: UpdateContext, visited: Set<Ref>, dependent: UniqueArray<Ref, StateObjectCell> } function linkCells(target, ctx) { if (!target.transform.dependsOn) return; for (var _i = 0, _a = target.transform.dependsOn; _i < _a.length; _i++) { var ref = _a[_i]; var t = ctx.tree.transforms.get(ref); if (!t) { throw new Error("Cannot depend on a non-existent transform."); } var cell = ctx.cells.get(ref); (0, array_1.arraySetAdd)(target.dependencies.dependsOn, cell); (0, array_1.arraySetAdd)(cell.dependencies.dependentBy, target); } } function initCells(ctx, roots) { var initCtx = { ctx: ctx, visited: new Set(), added: [] }; // Add new cells for (var _i = 0, roots_3 = roots; _i < roots_3.length; _i++) { var root = roots_3[_i]; tree_1.StateTree.doPreOrder(ctx.tree, ctx.tree.transforms.get(root), initCtx, addCellsVisitor); } // Update links for newly added cells for (var _a = 0, _b = initCtx.added; _a < _b.length; _a++) { var cell = _b[_a]; linkCells(cell, ctx); } var dependent; // Find dependent cells initCtx.visited.forEach(function (ref) { var cell = ctx.cells.get(ref); for (var _i = 0, _a = cell.dependencies.dependentBy; _i < _a.length; _i++) { var by = _a[_i]; if (initCtx.visited.has(by.transform.ref)) continue; if (!dependent) dependent = generic_1.UniqueArray.create(); generic_1.UniqueArray.add(dependent, by.transform.ref, by); } }); // TODO: check if dependent cells are all "proper roots" return { added: initCtx.added, dependent: dependent ? dependent.array : void 0 }; } function findNewCurrent(tree, start, deletes, cells) { var deleteSet = new Set(deletes); return _findNewCurrent(tree, start, deleteSet, cells); } function _findNewCurrent(tree, ref, deletes, cells) { if (ref === transform_1.StateTransform.RootRef) return ref; var node = tree.transforms.get(ref); var siblings = tree.children.get(node.parent).values(); var prevCandidate = void 0, seenRef = false; while (true) { var s = siblings.next(); if (s.done) break; if (deletes.has(s.value)) continue; var cell = cells.get(s.value); if (!cell || cell.status === 'error' || cell.obj === object_1.StateObject.Null) { continue; } var t = tree.transforms.get(s.value); if (t.state.isGhost) continue; if (s.value === ref) { seenRef = true; if (!deletes.has(ref)) prevCandidate = ref; continue; } if (seenRef) return t.ref; prevCandidate = t.ref; } if (prevCandidate) return prevCandidate; return _findNewCurrent(tree, node.parent, deletes, cells); } /** Set status and error text of the cell. Remove all existing objects in the subtree. */ function doError(ctx, ref, errorObject, silent) { if (!silent) { ctx.hadError = true; ctx.parent.errorFree = false; } var cell = ctx.cells.get(ref); if (errorObject) { ctx.wasAborted = ctx.wasAborted || mol_task_1.Task.isAbort(errorObject); var message = '' + errorObject; setCellStatus(ctx, ref, 'error', message); if (!silent) ctx.parent.events.log.next({ type: 'error', timestamp: new Date(), message: message }); } else { cell.params = void 0; } if (cell.obj) { var obj = cell.obj; cell.obj = void 0; cell.cache = void 0; ctx.parent.events.object.removed.next({ state: ctx.parent, ref: ref, obj: obj }); } // remove the objects in the child nodes if they exist var children = ctx.tree.children.get(ref).values(); while (true) { var next = children.next(); if (next.done) return; doError(ctx, next.value, void 0, silent); } } var ParentNullErrorText = 'Parent is null'; function updateSubtree(ctx, root) { return (0, tslib_1.__awaiter)(this, void 0, void 0, function () { var isNull, start, update_2, time, e_2, children, next; return (0, tslib_1.__generator)(this, function (_a) { switch (_a.label) { case 0: setCellStatus(ctx, root, 'processing'); isNull = false; _a.label = 1; case 1: _a.trys.push([1, 3, , 4]); start = (0, now_1.now)(); return [4 /*yield*/, updateNode(ctx, root)]; case 2: update_2 = _a.sent(); time = (0, now_1.now)() - start; if (update_2.action !== 'none') ctx.changed = true; setCellStatus(ctx, root, 'ok'); ctx.results.push(update_2); if (update_2.action === 'created') { isNull = update_2.obj === object_1.StateObject.Null; if (!isNull && !ctx.options.doNotLogTiming) ctx.parent.events.log.next(log_entry_1.LogEntry.info("Created " + update_2.obj.label + " in " + (0, now_1.formatTimespan)(time) + ".")); } else if (update_2.action === 'updated') { isNull = update_2.obj === object_1.StateObject.Null; if (!isNull && !ctx.options.doNotLogTiming) ctx.parent.events.log.next(log_entry_1.LogEntry.info("Updated " + update_2.obj.label + " in " + (0, now_1.formatTimespan)(time) + ".")); } else if (update_2.action === 'replaced') { isNull = update_2.obj === object_1.StateObject.Null; if (!isNull && !ctx.options.doNotLogTiming) ctx.parent.events.log.next(log_entry_1.LogEntry.info("Updated " + update_2.obj.label + " in " + (0, now_1.formatTimespan)(time) + ".")); } return [3 /*break*/, 4]; case 3: e_2 = _a.sent(); ctx.changed = true; if (!ctx.hadError) ctx.newCurrent = root; doError(ctx, root, e_2, false); if (!debug_1.isProductionMode) console.error(e_2); return [2 /*return*/]; case 4: children = ctx.tree.children.get(root).values(); _a.label = 5; case 5: if (!true) return [3 /*break*/, 9]; next = children.next(); if (next.done) return [2 /*return*/]; if (!isNull) return [3 /*break*/, 6]; doError(ctx, next.value, void 0, true); return [3 /*break*/, 8]; case 6: return [4 /*yield*/, updateSubtree(ctx, next.value)]; case 7: _a.sent(); _a.label = 8; case 8: return [3 /*break*/, 5]; case 9: return [2 /*return*/]; } }); }); } function resolveParams(ctx, transform, src, cell) { var prms = transform.transformer.definition.params; var definition = prms ? prms(src, ctx.parent.globalContext) : {}; if (cell.paramsNormalizedVersion !== transform.version) { transform.params = param_definition_1.ParamDefinition.normalizeParams(definition, transform.params, 'all'); cell.paramsNormalizedVersion = transform.version; } else { var defaultValues = param_definition_1.ParamDefinition.getDefaultValues(definition); transform.params = transform.params ? (0, object_2.assignIfUndefined)(transform.params, defaultValues) : defaultValues; } param_definition_1.ParamDefinition.resolveRefs(definition, transform.params, ctx.getCellData); return { definition: definition, values: transform.params }; } function updateNode(ctx, currentRef) { var _a; return (0, tslib_1.__awaiter)(this, void 0, void 0, function () { var oldTree, tree, current, transform, parentCell, parent, params, obj, oldParams, oldCache, oldData, newParams, updateKind, _b, _c, oldObj, newObj; return (0, tslib_1.__generator)(this, function (_d) { switch (_d.label) { case 0: oldTree = ctx.oldTree, tree = ctx.tree; current = ctx.cells.get(currentRef); transform = current.transform; // special case for Root if (current.transform.ref === transform_1.StateTransform.RootRef) { return [2 /*return*/, { action: 'none' }]; } parentCell = transform.transformer.definition.from.length === 0 ? ctx.cells.get(current.transform.parent) : selection_1.StateSelection.findAncestorOfType(tree, ctx.cells, currentRef, transform.transformer.definition.from); if (!parentCell) { throw new Error("No suitable parent found for '" + currentRef + "'"); } ctx.spine.current = current; parent = parentCell.obj; current.sourceRef = parentCell.transform.ref; params = resolveParams(ctx, transform, parent, current); if (!(!oldTree.transforms.has(currentRef) || !current.params)) return [3 /*break*/, 2]; current.params = params; return [4 /*yield*/, createObject(ctx, current, transform.transformer, parent, params.values)]; case 1: obj = _d.sent(); updateTag(obj, transform); current.obj = obj; return [2 /*return*/, { ref: currentRef, action: 'created', obj: obj }]; case 2: oldParams = current.params.values; oldCache = current.cache; oldData = (_a = current.obj) === null || _a === void 0 ? void 0 : _a.data; newParams = params.values; current.params = params; if (!(!!current.obj && current.obj !== object_1.StateObject.Null)) return [3 /*break*/, 4]; return [4 /*yield*/, updateObject(ctx, current, transform.transformer, parent, current.obj, oldParams, newParams)]; case 3: _b = _d.sent(); return [3 /*break*/, 5]; case 4: _b = transformer_1.StateTransformer.UpdateResult.Recreate; _d.label = 5; case 5: updateKind = _b; _c = updateKind; switch (_c) { case transformer_1.StateTransformer.UpdateResult.Recreate: return [3 /*break*/, 6]; case transformer_1.StateTransformer.UpdateResult.Updated: return [3 /*break*/, 8]; case transformer_1.StateTransformer.UpdateResult.Null: return [3 /*break*/, 9]; } return [3 /*break*/, 10]; case 6: oldObj = current.obj; dispose(transform, oldObj, oldParams, oldCache, ctx.parent.globalContext); return [4 /*yield*/, createObject(ctx, current, transform.transformer, parent, newParams)]; case 7: newObj = _d.sent(); updateTag(newObj, transform); current.obj = newObj; return [2 /*return*/, { ref: currentRef, action: 'replaced', oldObj: oldObj, obj: newObj }]; case 8: updateTag(current.obj, transform); return [2 /*return*/, { ref: currentRef, action: 'updated', oldData: oldData, obj: current.obj }]; case 9: { dispose(transform, current.obj, oldParams, oldCache, ctx.parent.globalContext); current.obj = object_1.StateObject.Null; return [2 /*return*/, { ref: currentRef, action: 'updated', obj: current.obj }]; } _d.label = 10; case 10: return [2 /*return*/, { action: 'none' }]; } }); }); } function dispose(transform, b, params, cache, globalContext) { var _a, _b; (_b = (_a = transform.transformer.definition).dispose) === null || _b === void 0 ? void 0 : _b.call(_a, { b: b !== object_1.StateObject.Null ? b : void 0, params: params, cache: cache }, globalContext); } function updateTag(obj, transform) { if (!obj || obj === object_1.StateObject.Null) return; obj.tags = transform.tags; } function runTask(t, ctx) { if (typeof t.runInContext === 'function') return t.runInContext(ctx); return t; } function resolveDependencies(cell) { if (cell.dependencies.dependsOn.length === 0) return void 0; var deps = Object.create(null); for (var _i = 0, _a = cell.dependencies.dependsOn; _i < _a.length; _i++) { var dep = _a[_i]; if (!dep.obj) { throw new Error('Unresolved dependency.'); } deps[dep.transform.ref] = dep.obj; } return deps; } function createObject(ctx, cell, transformer, a, params) { if (!cell.cache) cell.cache = Object.create(null); return runTask(transformer.definition.apply({ a: a, params: params, cache: cell.cache, spine: ctx.spine, dependencies: resolveDependencies(cell) }, ctx.parent.globalContext), ctx.taskCtx); } function updateObject(ctx, cell, transformer, a, b, oldParams, newParams) { return (0, tslib_1.__awaiter)(this, void 0, void 0, function () { return (0, tslib_1.__generator)(this, function (_a) { if (!transformer.definition.update) { return [2 /*return*/, transformer_1.StateTransformer.UpdateResult.Recreate]; } if (!cell.cache) cell.cache = Object.create(null); return [2 /*return*/, runTask(transformer.definition.update({ a: a, oldParams: oldParams, b: b, newParams: newParams, cache: cell.cache, spine: ctx.spine, dependencies: resolveDependencies(cell) }, ctx.parent.globalContext), ctx.taskCtx)]; }); }); } //# sourceMappingURL=state.js.map