Commit unstaged changes
Coming back to this project after a while, these changes were still unstaged. In order not to lose them I'm committing them here, even though I don't remember what they're for. They might not even work properly.
This commit is contained in:
parent
a3ed8012b2
commit
3f8057490f
1 changed files with 239 additions and 116 deletions
|
|
@ -4,6 +4,12 @@
|
||||||
* Utility functions
|
* Utility functions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
function removeAllChildren(element) {
|
||||||
|
while (element.firstChild) {
|
||||||
|
element.removeChild(element.lastChild);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Create a new DOM element.
|
// Create a new DOM element.
|
||||||
// 'classes' can either be a string or a list of strings.
|
// 'classes' can either be a string or a list of strings.
|
||||||
// A child can either be a string or a DOM element.
|
// A child can either be a string or a DOM element.
|
||||||
|
|
@ -40,71 +46,132 @@ const RelPos = Object.freeze({
|
||||||
});
|
});
|
||||||
|
|
||||||
class Path {
|
class Path {
|
||||||
constructor(...nodeIds) {
|
constructor(...components) {
|
||||||
this.elements = nodeIds;
|
this._components = components.slice();
|
||||||
|
}
|
||||||
|
|
||||||
|
get components() {
|
||||||
|
return this._components.slice();
|
||||||
}
|
}
|
||||||
|
|
||||||
get length() {
|
get length() {
|
||||||
return this.elements.length;
|
return this._components.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
get last() {
|
get last() {
|
||||||
return this.elements[this.length - 1];
|
return this._components[this.length - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
get parent() {
|
get parent() {
|
||||||
if (this.length === 0) return undefined;
|
if (this.length === 0) return undefined;
|
||||||
return new Path(...this.elements.slice(0, this.length - 1));
|
return new Path(...this._components.slice(0, this.length - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
append(nodeId) {
|
append(nodeId) {
|
||||||
return new Path(...this.elements.concat([nodeId]));
|
return new Path(...this._components.concat([nodeId]));
|
||||||
}
|
}
|
||||||
|
|
||||||
concat(otherPath) {
|
concat(otherPath) {
|
||||||
return new Path(...this.elements.concat(otherPath.elements));
|
return new Path(...this._components.concat(otherPath._components));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class NodeElements {
|
class NodeElements {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.text = newElement("span", "node-text");
|
this._elText = newElement("span", "node-text");
|
||||||
this.permissions = newElement("span", "node-permissions");
|
this._elPermissions = newElement("span", "node-permissions");
|
||||||
this.children = newElement("div", "node-children");
|
this._elChildren = newElement("div", "node-children");
|
||||||
|
|
||||||
let line = newElement("div", "node-line", this.text, this.permissions);
|
let line = newElement("div", "node-line", this._elText, this._elPermissions);
|
||||||
this.main = newElement("div", ["node", "is-folded"], line, this.children);
|
this._elMain = newElement("div", ["node", "is-folded"], line, this._elChildren);
|
||||||
|
}
|
||||||
|
|
||||||
|
get text() {
|
||||||
|
return this._elText.textContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
set text(text) {
|
||||||
|
this._elText.textContent = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
set permissions(perms) {
|
||||||
|
this._elPermissions.textContent = perms.asText;
|
||||||
|
}
|
||||||
|
|
||||||
|
get hasChildren() {
|
||||||
|
return this._elMain.classList.contains("has-children");
|
||||||
|
}
|
||||||
|
|
||||||
|
set hasChildren(flag) {
|
||||||
|
return this._elMain.classList.toggle("has-children", flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
removeAllChildren() {
|
removeAllChildren() {
|
||||||
while (this.children.firstChild) {
|
removeAllChildren(this._elChildren);
|
||||||
this.children.removeChild(this.children.lastChild);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addChild(child) {
|
||||||
|
this._elChildren.appendChild(child._elMain);
|
||||||
|
}
|
||||||
|
|
||||||
|
appendTo(element) {
|
||||||
|
element.appendChild(this._elMain);
|
||||||
|
}
|
||||||
|
|
||||||
|
get folded() {
|
||||||
|
return this._elMain.classList.contains("is-folded");
|
||||||
|
}
|
||||||
|
|
||||||
|
set folded(flag) {
|
||||||
|
this._elMain.classList.toggle("is-folded", flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleFolded() {
|
||||||
|
this.folded = !this.folded;
|
||||||
|
}
|
||||||
|
|
||||||
|
get hasCursor() {
|
||||||
|
return this._elMain.classList.contains("has-cursor");
|
||||||
|
}
|
||||||
|
|
||||||
|
set hasCursor(flag) {
|
||||||
|
return this._elMain.classList.toggle("has-cursor", flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
get hasEditor() {
|
||||||
|
return this._elMain.classList.contains("has-editor");
|
||||||
|
}
|
||||||
|
|
||||||
|
set hasEditor(flag) {
|
||||||
|
return this._elMain.classList.toggle("has-editor", flag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Node {
|
class NodePermissions {
|
||||||
constructor(nodeJson) {
|
constructor(edit, delete_, reply, act) {
|
||||||
this.elements = undefined;
|
this._edit = edit;
|
||||||
|
this._delete = delete_;
|
||||||
this.text = nodeJson.text;
|
this._reply = reply;
|
||||||
|
this._act = act;
|
||||||
// Permissions
|
|
||||||
this.edit = nodeJson.edit;
|
|
||||||
this.delete = nodeJson.delete;
|
|
||||||
this.reply = nodeJson.reply;
|
|
||||||
this.act = nodeJson.act;
|
|
||||||
|
|
||||||
this.children = new Map();
|
|
||||||
this.order = nodeJson.order;
|
|
||||||
this.order.forEach(childId => {
|
|
||||||
let childJson = nodeJson.children[childId];
|
|
||||||
let childNode = new Node(childJson);
|
|
||||||
this.children.set(childId, childNode);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getPermissionText() {
|
get edit() {
|
||||||
|
return this._edit;
|
||||||
|
}
|
||||||
|
|
||||||
|
get delete() {
|
||||||
|
return this._delete;
|
||||||
|
}
|
||||||
|
|
||||||
|
get reply() {
|
||||||
|
return this._reply;
|
||||||
|
}
|
||||||
|
|
||||||
|
get act() {
|
||||||
|
return this._act;
|
||||||
|
}
|
||||||
|
|
||||||
|
get asText() {
|
||||||
return [
|
return [
|
||||||
"(",
|
"(",
|
||||||
this.edit ? "e" : "-",
|
this.edit ? "e" : "-",
|
||||||
|
|
@ -114,23 +181,44 @@ class Node {
|
||||||
")"
|
")"
|
||||||
].join("");
|
].join("");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
hasChildren() {
|
class Node {
|
||||||
return this.order.length > 0;
|
constructor(nodeJson) {
|
||||||
|
this._el = undefined;
|
||||||
|
|
||||||
|
this._text = nodeJson.text;
|
||||||
|
|
||||||
|
this._permissions = new NodePermissions(
|
||||||
|
nodeJson.edit,
|
||||||
|
nodeJson.delete,
|
||||||
|
nodeJson.reply,
|
||||||
|
nodeJson.act,
|
||||||
|
);
|
||||||
|
|
||||||
|
this._children = new Map();
|
||||||
|
this._order = nodeJson.order;
|
||||||
|
this._order.forEach(childId => {
|
||||||
|
let childJson = nodeJson.children[childId];
|
||||||
|
let childNode = new Node(childJson);
|
||||||
|
this._children.set(childId, childNode);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
isFolded() {
|
child(childId) {
|
||||||
if (this.elements === undefined) return undefined;
|
return this._children.get(childId);
|
||||||
return this.elements.main.classList.contains("is-folded");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setFolded(folded) {
|
get order() {
|
||||||
if (this.elements === undefined) return;
|
return this._order.slice();
|
||||||
this.elements.main.classList.toggle("is-folded", folded);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleFolded() {
|
// Only replaces existing children. Does not add new children.
|
||||||
this.setFolded(!this.isFolded());
|
replaceChild(childId, newChild) {
|
||||||
|
let oldChild = this.child(childId);
|
||||||
|
if (oldChild === undefined) return;
|
||||||
|
newChild.obtainElements(oldChild);
|
||||||
|
this._children.set(childId, newChild);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Obtain and update this node's DOM elements. After this call, this.el
|
// Obtain and update this node's DOM elements. After this call, this.el
|
||||||
|
|
@ -140,49 +228,87 @@ class Node {
|
||||||
// its children already has existing DOM elements, they are repurposed.
|
// its children already has existing DOM elements, they are repurposed.
|
||||||
// Otherwise, new DOM elements are created.
|
// Otherwise, new DOM elements are created.
|
||||||
obtainElements(oldNode) {
|
obtainElements(oldNode) {
|
||||||
if (this.elements === undefined) {
|
if (this._el === undefined) {
|
||||||
// Obtain DOM elements because we don't yet have any
|
// Obtain DOM elements because we don't yet have any
|
||||||
if (oldNode === undefined || oldNode.elements === undefined) {
|
if (oldNode === undefined || oldNode._el === undefined) {
|
||||||
this.elements = new NodeElements();
|
this._el = new NodeElements();
|
||||||
} else {
|
} else {
|
||||||
this.elements = oldNode.elements;
|
this._el = oldNode._el;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.elements.text.textContent = this.text;
|
this._el.text = this._text;
|
||||||
this.elements.permissions.textContent = this.getPermissionText();
|
this._el.permissions = this._permissions;
|
||||||
this.elements.main.classList.toggle("has-children", this.hasChildren());
|
this._el.hasChildren = this.order.length > 0;
|
||||||
|
|
||||||
let oldChildren = (oldNode === undefined) ?
|
this._el.removeAllChildren();
|
||||||
new Map() : oldNode.children;
|
|
||||||
|
|
||||||
this.elements.removeAllChildren();
|
let oldChildren = (oldNode === undefined) ? new Map() : oldNode._children;
|
||||||
this.order.forEach(childId => {
|
this._order.forEach(childId => {
|
||||||
let oldChild = oldChildren.get(childId); // May be undefined
|
let oldChild = oldChildren.get(childId); // May be undefined
|
||||||
let child = this.children.get(childId);
|
let child = this._children.get(childId); // Not undefined
|
||||||
child.obtainElements(oldChild);
|
child.obtainElements(oldChild);
|
||||||
this.elements.children.appendChild(child.elements.main);
|
this._el.addChild(child._el);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wrapper functions for this._el
|
||||||
|
|
||||||
|
appendTo(element) {
|
||||||
|
if (this._el === undefined) this.obtainElements();
|
||||||
|
this._el.appendTo(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
get folded() {
|
||||||
|
if (this._el === undefined) return undefined;
|
||||||
|
return this._el.folded;
|
||||||
|
}
|
||||||
|
|
||||||
|
set folded(flag) {
|
||||||
|
if (this._el === undefined) return;
|
||||||
|
this._el.folded = flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleFolded() {
|
||||||
|
if (this._el === undefined) return;
|
||||||
|
this._el.toggleFolded();
|
||||||
|
}
|
||||||
|
|
||||||
|
get hasCursor() {
|
||||||
|
if (this._el === undefined) return undefined;
|
||||||
|
return this._el.hasCursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
set hasCursor(flag) {
|
||||||
|
if (this._el === undefined) return;
|
||||||
|
this._el.hasCursor = flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
get hasEditor() {
|
||||||
|
if (this._el === undefined) return undefined;
|
||||||
|
return this._el.hasEditor;
|
||||||
|
}
|
||||||
|
|
||||||
|
set hasEditor(flag) {
|
||||||
|
if (this._el === undefined) return;
|
||||||
|
this._el.hasEditor = flag;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class NodeTree {
|
class NodeTree {
|
||||||
constructor(rootNodeContainer, rootNode) {
|
constructor(rootNodeContainer, rootNode) {
|
||||||
this.rootNodeContainer = rootNodeContainer;
|
this._rootNodeContainer = rootNodeContainer;
|
||||||
this.rootNode = rootNode;
|
this._rootNode = rootNode;
|
||||||
|
|
||||||
// Prepare root node container
|
// Prepare root node container
|
||||||
rootNode.obtainElements();
|
removeAllChildren(this._rootNodeContainer);
|
||||||
while (rootNodeContainer.firstChild) {
|
this._rootNode.appendTo(this._rootNodeContainer);
|
||||||
rootNodeContainer.removeChild(rootNodeContainer.lastChild);
|
|
||||||
}
|
|
||||||
rootNodeContainer.appendChild(rootNode.elements.main);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
at(path) {
|
at(path) {
|
||||||
let node = this.rootNode;
|
let node = this._rootNode;
|
||||||
for (let childId of path.elements) {
|
for (let childId of path.components) {
|
||||||
node = node.children.get(childId);
|
node = node.child(childId);
|
||||||
if (node === undefined) break;
|
if (node === undefined) break;
|
||||||
}
|
}
|
||||||
return node;
|
return node;
|
||||||
|
|
@ -190,14 +316,11 @@ class NodeTree {
|
||||||
|
|
||||||
updateAt(path, newNode) {
|
updateAt(path, newNode) {
|
||||||
if (path.length === 0) {
|
if (path.length === 0) {
|
||||||
newNode.obtainElements(this.rootNode);
|
newNode.obtainElements(this._rootNode);
|
||||||
this.rootNode = newNode;
|
this._rootNode = newNode;
|
||||||
} else {
|
} else {
|
||||||
let parentNode = this.at(path.parent);
|
let parentNode = this.at(path.parent);
|
||||||
let oldNode = parentNode.children.get(path.last);
|
parentNode.replaceChild(path.last, newNode);
|
||||||
if (oldNode === undefined) return;
|
|
||||||
newNode.obtainElements(oldNode);
|
|
||||||
parentNode.children.set(path.last, newNode);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -249,7 +372,7 @@ class NodeTree {
|
||||||
// Get last child of previous path
|
// Get last child of previous path
|
||||||
while (true) {
|
while (true) {
|
||||||
let prevNode = this.at(prevPath);
|
let prevNode = this.at(prevPath);
|
||||||
if (prevNode.isFolded()) return prevPath;
|
if (prevNode.folded) return prevPath;
|
||||||
|
|
||||||
let childPath = this.getLastChild(prevPath);
|
let childPath = this.getLastChild(prevPath);
|
||||||
if (childPath === undefined) return prevPath;
|
if (childPath === undefined) return prevPath;
|
||||||
|
|
@ -260,7 +383,7 @@ class NodeTree {
|
||||||
|
|
||||||
getNodeBelow(path) {
|
getNodeBelow(path) {
|
||||||
let node = this.at(path);
|
let node = this.at(path);
|
||||||
if (!node.isFolded()) {
|
if (!node.folded) {
|
||||||
let childPath = this.getFirstChild(path);
|
let childPath = this.getFirstChild(path);
|
||||||
if (childPath !== undefined) return childPath;
|
if (childPath !== undefined) return childPath;
|
||||||
}
|
}
|
||||||
|
|
@ -277,49 +400,49 @@ class NodeTree {
|
||||||
|
|
||||||
class Cursor {
|
class Cursor {
|
||||||
constructor(nodeTree) {
|
constructor(nodeTree) {
|
||||||
this.nodeTree = nodeTree;
|
this._nodeTree = nodeTree;
|
||||||
|
|
||||||
this.path = new Path();
|
this._path = new Path();
|
||||||
this.relPos = null; // Either null or a RelPos value
|
this._relPos = null; // Either null or a RelPos value
|
||||||
|
|
||||||
this.restore();
|
this.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
getSelectedNode() {
|
getSelectedNode() {
|
||||||
return this.nodeTree.at(this.path);
|
return this._nodeTree.at(this._path);
|
||||||
}
|
}
|
||||||
|
|
||||||
_applyRelPos() {
|
_applyRelPos() {
|
||||||
if (this.relPos === null) return;
|
if (this._relPos === null) return;
|
||||||
|
|
||||||
let newPath;
|
let newPath;
|
||||||
if (this.relPos === RelPos.FIRST_CHILD) {
|
if (this._relPos === RelPos.FIRST_CHILD) {
|
||||||
newPath = this.nodeTree.getFirstChild(this.path);
|
newPath = this._nodeTree.getFirstChild(this._path);
|
||||||
} else if (this.relPos === RelPos.NEXT_SIBLING) {
|
} else if (this._relPos === RelPos.NEXT_SIBLING) {
|
||||||
newPath = this.nodeTree.getNextSibling(this.path);
|
newPath = this._nodeTree.getNextSibling(this._path);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newPath !== undefined) {
|
if (newPath !== undefined) {
|
||||||
this.path = newPath;
|
this._path = newPath;
|
||||||
this.relPos = null;
|
this._relPos = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_moveToNearestValidNode() {
|
_moveToNearestValidNode() {
|
||||||
// TODO Maybe select a sibling instead of going to nearest visible parent
|
// TODO Maybe select a sibling instead of going to nearest visible parent
|
||||||
let path = new Path();
|
let path = new Path();
|
||||||
for (let element of this.path.elements) {
|
for (let component of this._path.components) {
|
||||||
let newPath = path.append(element);
|
let newPath = path.append(component);
|
||||||
let newNode = this.nodeTree.at(newPath);
|
let newNode = this._nodeTree.at(newPath);
|
||||||
if (newNode === undefined) break;
|
if (newNode === undefined) break;
|
||||||
if (newNode.isFolded()) break;
|
if (newNode.folded) break;
|
||||||
path = newPath;
|
path = newPath;
|
||||||
}
|
}
|
||||||
this.path = path;
|
this._path = path;
|
||||||
}
|
}
|
||||||
|
|
||||||
_set(visible) {
|
_set(visible) {
|
||||||
this.getSelectedNode().elements.main.classList.toggle("has-cursor", visible);
|
this.getSelectedNode().hasCursor = visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
restore() {
|
restore() {
|
||||||
|
|
@ -331,56 +454,56 @@ class Cursor {
|
||||||
moveTo(path) {
|
moveTo(path) {
|
||||||
if (path === undefined) return;
|
if (path === undefined) return;
|
||||||
this._set(false);
|
this._set(false);
|
||||||
this.path = path;
|
this._path = path;
|
||||||
this._set(true);
|
this._set(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
moveUp() {
|
moveUp() {
|
||||||
this.moveTo(this.nodeTree.getNodeAbove(this.path));
|
this.moveTo(this._nodeTree.getNodeAbove(this._path));
|
||||||
}
|
}
|
||||||
|
|
||||||
moveDown() {
|
moveDown() {
|
||||||
this.moveTo(this.nodeTree.getNodeBelow(this.path));
|
this.moveTo(this._nodeTree.getNodeBelow(this._path));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Editor {
|
class Editor {
|
||||||
constructor(nodeTree) {
|
constructor(nodeTree) {
|
||||||
this.nodeTree = nodeTree;
|
this._nodeTree = nodeTree;
|
||||||
|
|
||||||
this.textarea = newElement("textarea");
|
this._elTextarea = newElement("textarea");
|
||||||
this.element = newElement("div", "node-editor", this.textarea);
|
this._elTextarea.addEventListener("input", event => this._updateTextAreaHeight());
|
||||||
this.textarea.addEventListener("input", event => this._updateTextAreaHeight());
|
this._elMain = newElement("div", "node-editor", this.textarea);
|
||||||
|
|
||||||
this.path = undefined;
|
this._path = undefined;
|
||||||
this.asChild = false;
|
this._asChild = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_updateTextAreaHeight() {
|
_updateTextAreaHeight() {
|
||||||
this.textarea.style.height = 0;
|
this._elTextarea.style.height = 0;
|
||||||
this.textarea.style.height = this.textarea.scrollHeight + "px";
|
this._elTextarea.style.height = this._elTextarea.scrollHeight + "px";
|
||||||
}
|
}
|
||||||
|
|
||||||
_getAttachedNode() {
|
_getAttachedNode() {
|
||||||
if (this.path === undefined) return undefined;
|
if (this._path === undefined) return undefined;
|
||||||
return this.nodeTree.at(this.path);
|
return this._nodeTree.at(this._path);
|
||||||
}
|
}
|
||||||
|
|
||||||
_detach(node, asChild) {
|
_detach(node, asChild) {
|
||||||
if (!asChild) {
|
if (!asChild) {
|
||||||
node.elements.main.classList.remove("has-editor");
|
node.hasEditor = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.element.parentNode.removeChild(this.element);
|
this._elMain.parentNode.removeChild(this._elMain);
|
||||||
}
|
}
|
||||||
|
|
||||||
_attachTo(node, asChild) {
|
_attachTo(node, asChild) {
|
||||||
if (asChild) {
|
if (asChild) {
|
||||||
node.elements.children.appendChild(this.element);
|
node._el._elChildren.appendChild(this.element);
|
||||||
node.setFolded(false);
|
node.folded = false;
|
||||||
} else {
|
} else {
|
||||||
node.elements.main.classList.add("has-editor");
|
node._el._elMain.classList.add("has-editor");
|
||||||
node.elements.main.insertBefore(this.element, node.elements.children);
|
node._el._elMain.insertBefore(this.element, node._el._elChildren);
|
||||||
}
|
}
|
||||||
this._updateTextAreaHeight();
|
this._updateTextAreaHeight();
|
||||||
}
|
}
|
||||||
|
|
@ -461,19 +584,19 @@ class Connection {
|
||||||
}
|
}
|
||||||
|
|
||||||
sendEdit(path, text) {
|
sendEdit(path, text) {
|
||||||
this._send({type: "edit", path: path.elements, text: text});
|
this._send({type: "edit", path: path.components, text: text});
|
||||||
}
|
}
|
||||||
|
|
||||||
sendDelete(path) {
|
sendDelete(path) {
|
||||||
this._send({type: "delete", path: path.elements});
|
this._send({type: "delete", path: path.components});
|
||||||
}
|
}
|
||||||
|
|
||||||
sendReply(path, text) {
|
sendReply(path, text) {
|
||||||
this._send({type: "reply", path: path.elements, text: text});
|
this._send({type: "reply", path: path.components, text: text});
|
||||||
}
|
}
|
||||||
|
|
||||||
sendAct(path) {
|
sendAct(path) {
|
||||||
this._send({type: "act", path: path.elements});
|
this._send({type: "act", path: path.components});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue