handsontable
Version:
Handsontable is a JavaScript Data Grid available for React, Angular and Vue.
214 lines (203 loc) • 6.78 kB
JavaScript
;
exports.__esModule = true;
exports.depthFirstPreOrder = depthFirstPreOrder;
require("core-js/modules/es.error.cause.js");
require("core-js/modules/es.array.push.js");
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
/**
* Depth-first pre-order strategy (https://en.wikipedia.org/wiki/Tree_traversal#Pre-order_(NLR)).
*
* @type {string}
*/
const TRAVERSAL_DF_PRE = exports.TRAVERSAL_DF_PRE = 'DF-pre-order';
/**
* @param {Function} callback A callback which will be called on each visited node.
* @param {*} context A context to pass through.
* @returns {boolean}
*/
function depthFirstPreOrder(callback, context) {
let continueTraverse = callback.call(context, this);
for (let i = 0; i < this.childs.length; i++) {
if (continueTraverse === false) {
return false;
}
continueTraverse = depthFirstPreOrder.call(this.childs[i], callback, context);
}
return continueTraverse;
}
/**
* Depth-first post-order strategy (https://en.wikipedia.org/wiki/Tree_traversal#Post-order_(NLR)).
*
* @type {string}
*/
const TRAVERSAL_DF_POST = exports.TRAVERSAL_DF_POST = 'DF-post-order';
/**
* @param {Function} callback A callback which will be called on each visited node.
* @param {*} context A context to pass through.
* @returns {boolean}
*/
function depthFirstPostOrder(callback, context) {
for (let i = 0; i < this.childs.length; i++) {
const continueTraverse = depthFirstPostOrder.call(this.childs[i], callback, context);
if (continueTraverse === false) {
return false;
}
}
return callback.call(context, this);
}
/**
* Breadth-first traversal strategy (https://en.wikipedia.org/wiki/Tree_traversal#Breadth-first_search_/_level_order).
*
* @type {string}
*/
const TRAVERSAL_BF = exports.TRAVERSAL_BF = 'BF';
/**
* @param {Function} callback A callback which will be called on each visited node.
* @param {*} context A context to pass through.
*/
function breadthFirst(callback, context) {
const queue = [this];
/**
* Internal processor.
*/
function process() {
if (queue.length === 0) {
return;
}
const node = queue.shift();
queue.push(...node.childs);
if (callback.call(context, node) !== false) {
process();
}
}
process();
}
/**
* Default strategy for tree traversal.
*
* @type {string}
*/
const DEFAULT_TRAVERSAL_STRATEGY = TRAVERSAL_BF;
/**
* Collection of all available tree traversal strategies.
*
* @type {Map<string, Function>}
*/
const TRAVERSAL_STRATEGIES = new Map([[TRAVERSAL_DF_PRE, depthFirstPreOrder], [TRAVERSAL_DF_POST, depthFirstPostOrder], [TRAVERSAL_BF, breadthFirst]]);
/**
*
*/
class TreeNode {
constructor(data) {
/**
* A tree data.
*
* @type {object}
*/
_defineProperty(this, "data", {});
/**
* A parent node.
*
* @type {TreeNode}
*/
_defineProperty(this, "parent", null);
/**
* A tree leaves.
*
* @type {TreeNode[]}
*/
_defineProperty(this, "childs", []);
this.data = data;
}
/**
* Adds a node to tree leaves. Added node is linked with the parent node through "parent" property.
*
* @param {TreeNode} node A TreeNode to add.
*/
addChild(node) {
node.parent = this;
this.childs.push(node);
}
/* eslint-disable jsdoc/require-description-complete-sentence */
/**
* @memberof TreeNode#
* @function cloneTree
*
* Clones a tree structure deeply.
*
* For example, for giving a tree structure:
* .--(B1)--.
* .-(C1) .-(C2)-.----.
* (D1) (D2) (D3) (D4)
*
* Cloning a tree starting from C2 node creates a mirrored tree structure.
* .-(C2')-.-----.
* (D2') (D3') (D4')
*
* The cloned tree can be safely modified without affecting the original structure.
* After modification, the clone can be merged with a tree using the "replaceTreeWith" method.
*
* @param {TreeNode} [nodeTree=this] A TreeNode to clone.
* @returns {TreeNode}
*/
/* eslint-enable jsdoc/require-description-complete-sentence */
cloneTree() {
let nodeTree = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this;
const clonedNode = new TreeNode({
...nodeTree.data
});
for (let i = 0; i < nodeTree.childs.length; i++) {
clonedNode.addChild(this.cloneTree(nodeTree.childs[i]));
}
return clonedNode;
}
/**
* Replaces the current node with a passed tree structure.
*
* @param {TreeNode} nodeTree A TreeNode to replace with.
*/
replaceTreeWith(nodeTree) {
this.data = {
...nodeTree.data
};
this.childs = [];
for (let i = 0; i < nodeTree.childs.length; i++) {
this.addChild(nodeTree.childs[i]);
}
}
/**
* Traverses the tree structure through node childs. The walk down traversing supports
* a three different strategies.
* - Depth-first pre-order strategy (https://en.wikipedia.org/wiki/Tree_traversal#Pre-order_(NLR));
* - Depth-first post-order strategy (https://en.wikipedia.org/wiki/Tree_traversal#Post-order_(NLR));
* - Breadth-first traversal strategy (https://en.wikipedia.org/wiki/Tree_traversal#Breadth-first_search_/_level_order).
*
* @param {Function} callback The callback function which will be called for each node.
* @param {string} [traversalStrategy=DEFAULT_TRAVERSAL_STRATEGY] Traversing strategy.
*/
walkDown(callback) {
let traversalStrategy = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_TRAVERSAL_STRATEGY;
if (!TRAVERSAL_STRATEGIES.has(traversalStrategy)) {
throw new Error(`Traversal strategy "${traversalStrategy}" does not exist`);
}
TRAVERSAL_STRATEGIES.get(traversalStrategy).call(this, callback, this);
}
/**
* Traverses the tree structure through node parents.
*
* @param {Function} callback The callback function which will be called for each node.
*/
walkUp(callback) {
const context = this;
const process = node => {
const continueTraverse = callback.call(context, node);
if (continueTraverse !== false && node.parent !== null) {
process(node.parent);
}
};
process(this);
}
}
exports.default = TreeNode;