molstar
Version:
A comprehensive macromolecular library.
273 lines (272 loc) • 11.8 kB
JavaScript
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { __assign } from "tslib";
import { StateTree } from '../tree/immutable';
import { StateObjectCell, StateObjectSelector, StateObjectRef } from '../object';
import { StateTransform } from '../transform';
import { produce } from 'immer';
export { StateBuilder };
var StateBuilder;
(function (StateBuilder) {
function buildTree(state) {
if (!state.state || state.state.tree === state.editInfo.sourceTree) {
return state.tree.asImmutable();
}
// The tree has changed in the meantime, we need to reapply the changes!
var tree = state.state.tree.asTransient();
for (var _i = 0, _a = state.actions; _i < _a.length; _i++) {
var a = _a[_i];
switch (a.kind) {
case 'add':
tree.add(a.transform);
break;
case 'update':
tree.setParams(a.ref, a.params);
break;
case 'delete':
tree.remove(a.ref);
break;
case 'insert': {
var children = tree.children.get(a.ref).toArray();
tree.add(a.transform);
for (var _b = 0, children_1 = children; _b < children_1.length; _b++) {
var c = children_1[_b];
tree.changeParent(c, a.transform.ref);
}
break;
}
}
}
state.editInfo.sourceTree = state.tree;
return tree.asImmutable();
}
function is(obj) {
return !!obj && typeof obj.getTree === 'function';
}
StateBuilder.is = is;
function isTo(obj) {
return !!obj && typeof obj.getTree === 'function' && typeof obj.ref === 'string';
}
StateBuilder.isTo = isTo;
// type ToFromCell<C extends StateObjectCell> = C extends StateObjectCell<infer A, StateTransform<infer T extends StateTransformer>> ? To<A, any>: never
var Root = /** @class */ (function () {
function Root(tree, state) {
this.state = { state: state, tree: tree.asTransient(), actions: [], editInfo: { applied: false, sourceTree: tree, count: 0, lastUpdate: void 0 } };
}
Object.defineProperty(Root.prototype, "editInfo", {
get: function () { return this.state.editInfo; },
enumerable: false,
configurable: true
});
Object.defineProperty(Root.prototype, "currentTree", {
get: function () { return this.state.tree; },
enumerable: false,
configurable: true
});
Root.prototype.to = function (refOrCellOrSelector) {
var ref = typeof refOrCellOrSelector === 'string'
? refOrCellOrSelector
: StateObjectCell.is(refOrCellOrSelector)
? refOrCellOrSelector.transform.ref
: refOrCellOrSelector.ref;
return new To(this.state, ref, this);
};
Root.prototype.toRoot = function () { return new To(this.state, this.state.tree.root.ref, this); };
Root.prototype.delete = function (obj) {
var ref = StateObjectRef.resolveRef(obj);
if (!ref || !this.state.tree.transforms.has(ref))
return this;
this.editInfo.count++;
this.state.tree.remove(ref);
this.state.actions.push({ kind: 'delete', ref: ref });
return this;
};
Root.prototype.getTree = function () { return buildTree(this.state); };
Root.prototype.commit = function (options) {
if (!this.state.state)
throw new Error('Cannot commit template tree');
return this.state.state.runTask(this.state.state.updateTree(this, options));
};
return Root;
}());
StateBuilder.Root = Root;
var To = /** @class */ (function () {
function To(state, ref, root) {
this.state = state;
this.root = root;
this.ref = ref;
if (!this.state.tree.transforms.has(ref)) {
throw new Error("Could not find node '".concat(ref, "'."));
}
}
Object.defineProperty(To.prototype, "editInfo", {
get: function () { return this.state.editInfo; },
enumerable: false,
configurable: true
});
Object.defineProperty(To.prototype, "selector", {
get: function () { return new StateObjectSelector(this.ref, this.state.state); },
enumerable: false,
configurable: true
});
To.prototype.getApplyRoot = function () {
return StateTree.getDecoratorRoot(this.state.tree, this.ref);
};
/**
* Apply the transformed to the parent node
* If no params are specified (params <- undefined), default params are lazily resolved.
*/
To.prototype.apply = function (tr, params, options) {
if (tr.definition.isDecorator) {
return this.insert(tr, params, options);
}
var applyRoot = this.getApplyRoot();
var t = tr.apply(applyRoot, params, options);
this.state.tree.add(t);
this.editInfo.count++;
this.editInfo.lastUpdate = t.ref;
this.state.actions.push({ kind: 'add', transform: t });
return new To(this.state, t.ref, this.root);
};
/**
* If the ref is present, the transform is applied.
* Otherwise a transform with the specifed ref is created.
*/
To.prototype.applyOrUpdate = function (ref, tr, params, options) {
if (this.state.tree.transforms.has(ref)) {
var to = this.to(ref);
if (params)
to.update(params);
return to;
}
else {
return this.apply(tr, params, __assign(__assign({}, options), { ref: ref }));
}
};
/**
* Apply the transformed to the parent node
* If no params are specified (params <- undefined), default params are lazily resolved.
* The transformer cannot be a decorator to be able to use this.
*/
To.prototype.applyOrUpdateTagged = function (tags, tr, params, options) {
if (tr.definition.isDecorator) {
throw new Error("Can't use applyOrUpdateTagged on decorator transformers.");
}
var applyRoot = this.getApplyRoot();
var children = this.state.tree.children.get(applyRoot).values();
while (true) {
var child = children.next();
if (child.done)
break;
var tr_1 = this.state.tree.transforms.get(child.value);
if (tr_1 && StateTransform.hasTags(tr_1, tags)) {
var to = this.to(child.value);
to.updateTagged(params, tagsUnion(tr_1.tags, tags, options && options.tags));
return to;
}
}
var t = tr.apply(applyRoot, params, __assign(__assign({}, options), { tags: tagsUnion(tags, options && options.tags) }));
this.state.tree.add(t);
this.editInfo.count++;
this.editInfo.lastUpdate = t.ref;
this.state.actions.push({ kind: 'add', transform: t });
return new To(this.state, t.ref, this.root);
};
/**
* A helper to greate a group-like state object and keep the current type.
*/
To.prototype.group = function (tr, params, options) {
return this.apply(tr, params, options);
};
/**
* Inserts a new transform that does not change the object type and move the original children to it.
*/
To.prototype.insert = function (tr, params, options) {
// cache the children
var children = this.state.tree.children.get(this.ref).toArray();
// add the new node
var t = tr.apply(this.ref, params, options);
this.state.tree.add(t);
// move the original children to the new node
for (var _i = 0, children_2 = children; _i < children_2.length; _i++) {
var c = children_2[_i];
this.state.tree.changeParent(c, t.ref);
}
this.editInfo.count++;
this.editInfo.lastUpdate = t.ref;
this.state.actions.push({ kind: 'insert', ref: this.ref, transform: t });
return new To(this.state, t.ref, this.root);
};
To.prototype.updateTagged = function (params, tags) {
if (this.state.tree.setParams(this.ref, params) || this.state.tree.setTags(this.ref, tags)) {
this.editInfo.count++;
this.editInfo.lastUpdate = this.ref;
this.state.actions.push({ kind: 'update', ref: this.ref, params: params });
}
};
To.prototype.update = function (paramsOrTransformer, provider) {
var params;
if (provider) {
var old = this.state.tree.transforms.get(this.ref);
params = produce(old.params, provider);
}
else {
params = typeof paramsOrTransformer === 'function'
? produce(this.state.tree.transforms.get(this.ref).params, paramsOrTransformer)
: paramsOrTransformer;
}
if (this.state.tree.setParams(this.ref, params)) {
this.editInfo.count++;
this.editInfo.lastUpdate = this.ref;
this.state.actions.push({ kind: 'update', ref: this.ref, params: params });
}
return this.root;
};
To.prototype.to = function (ref) { return this.root.to(ref); };
To.prototype.toRoot = function () { return this.root.toRoot(); };
To.prototype.delete = function (ref) { return this.root.delete(ref); };
To.prototype.getTree = function () { return buildTree(this.state); };
/** Returns selector to this node. */
To.prototype.commit = function (options) {
if (!this.state.state)
throw new Error('Cannot commit template tree');
return this.state.state.runTask(this.state.state.updateTree(this, options));
};
return To;
}());
StateBuilder.To = To;
})(StateBuilder || (StateBuilder = {}));
function tagsUnion() {
var arrays = [];
for (var _i = 0; _i < arguments.length; _i++) {
arrays[_i] = arguments[_i];
}
var set = void 0;
var ret = [];
for (var _a = 0, arrays_1 = arrays; _a < arrays_1.length; _a++) {
var xs = arrays_1[_a];
if (!xs)
continue;
if (!set)
set = new Set();
if (typeof xs === 'string') {
if (set.has(xs))
continue;
set.add(xs);
ret.push(xs);
}
else {
for (var _b = 0, xs_1 = xs; _b < xs_1.length; _b++) {
var x = xs_1[_b];
if (set.has(x))
continue;
set.add(x);
ret.push(x);
}
}
}
return ret;
}