|
|
|
;
|
|
|
|
(function () {
|
|
|
|
BI.Tree = function () {
|
|
|
|
this.root = new BI.Node(BI.UUID());
|
|
|
|
};
|
|
|
|
|
|
|
|
BI.Tree.prototype = {
|
|
|
|
constructor: BI.Tree,
|
|
|
|
addNode: function (node, newNode, index) {
|
|
|
|
if (BI.isNull(newNode)) {
|
|
|
|
this.root.addChild(node, index);
|
|
|
|
} else if (BI.isNull(node)) {
|
|
|
|
this.root.addChild(newNode, index);
|
|
|
|
} else {
|
|
|
|
node.addChild(newNode, index);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
isRoot: function (node) {
|
|
|
|
return node === this.root || node.id === this.root.id;
|
|
|
|
},
|
|
|
|
|
|
|
|
getRoot: function () {
|
|
|
|
return this.root;
|
|
|
|
},
|
|
|
|
|
|
|
|
clear: function () {
|
|
|
|
this.root.clear();
|
|
|
|
},
|
|
|
|
|
|
|
|
initTree: function (nodes) {
|
|
|
|
var self = this;
|
|
|
|
this.clear();
|
|
|
|
var queue = [];
|
|
|
|
BI.each(nodes, function (i, node) {
|
|
|
|
var n = new BI.Node(node);
|
|
|
|
n.set("data", node);
|
|
|
|
self.addNode(n);
|
|
|
|
queue.push(n);
|
|
|
|
});
|
|
|
|
while (!BI.isEmpty(queue)) {
|
|
|
|
var parent = queue.shift();
|
|
|
|
var node = parent.get("data");
|
|
|
|
BI.each(node.children, function (i, child) {
|
|
|
|
var n = new BI.Node(child);
|
|
|
|
n.set("data", child);
|
|
|
|
queue.push(n);
|
|
|
|
self.addNode(parent, n);
|
|
|
|
})
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
_toJSON: function (node) {
|
|
|
|
var self = this;
|
|
|
|
var children = [];
|
|
|
|
BI.each(node.getChildren(), function (i, child) {
|
|
|
|
children.push(self._toJSON(child));
|
|
|
|
});
|
|
|
|
return BI.extend({
|
|
|
|
id: node.id
|
|
|
|
}, BI.deepClone(node.get("data")), (children.length > 0 ? {
|
|
|
|
children: children
|
|
|
|
} : {}));
|
|
|
|
},
|
|
|
|
|
|
|
|
toJSON: function (node) {
|
|
|
|
var self = this, result = [];
|
|
|
|
BI.each((node || this.root).getChildren(), function (i, child) {
|
|
|
|
result.push(self._toJSON(child));
|
|
|
|
});
|
|
|
|
return result;
|
|
|
|
},
|
|
|
|
|
|
|
|
_toJSONWithNode: function (node) {
|
|
|
|
var self = this;
|
|
|
|
var children = [];
|
|
|
|
BI.each(node.getChildren(), function (i, child) {
|
|
|
|
children.push(self._toJSONWithNode(child));
|
|
|
|
});
|
|
|
|
return BI.extend({
|
|
|
|
id: node.id
|
|
|
|
}, BI.deepClone(node.get("data")), {
|
|
|
|
node: node
|
|
|
|
}, (children.length > 0 ? {
|
|
|
|
children: children
|
|
|
|
} : {}));
|
|
|
|
},
|
|
|
|
|
|
|
|
toJSONWithNode: function (node) {
|
|
|
|
var self = this, result = [];
|
|
|
|
BI.each((node || this.root).getChildren(), function (i, child) {
|
|
|
|
result.push(self._toJSONWithNode(child));
|
|
|
|
});
|
|
|
|
return result;
|
|
|
|
},
|
|
|
|
|
|
|
|
search: function (root, target, param) {
|
|
|
|
if (!(root instanceof BI.Node)) {
|
|
|
|
return arguments.callee.apply(this, [this.root, root, target]);
|
|
|
|
}
|
|
|
|
var self = this, next = null;
|
|
|
|
|
|
|
|
if (BI.isNull(target)) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
if (BI.isEqual(root[param || "id"], target)) {
|
|
|
|
return root;
|
|
|
|
}
|
|
|
|
BI.any(root.getChildren(), function (i, child) {
|
|
|
|
next = self.search(child, target, param);
|
|
|
|
if (null !== next) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return next;
|
|
|
|
},
|
|
|
|
|
|
|
|
_traverse: function (node, callback) {
|
|
|
|
var queue = [];
|
|
|
|
queue.push(node);
|
|
|
|
while (!BI.isEmpty(queue)) {
|
|
|
|
var temp = queue.shift();
|
|
|
|
var b = callback && callback(temp);
|
|
|
|
if (b === false) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (b === true) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (temp != null) {
|
|
|
|
queue = queue.concat(temp.getChildren());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
traverse: function (callback) {
|
|
|
|
this._traverse(this.root, callback);
|
|
|
|
},
|
|
|
|
|
|
|
|
_recursion: function (node, route, callback) {
|
|
|
|
var self = this;
|
|
|
|
return BI.every(node.getChildren(), function (i, child) {
|
|
|
|
var next = BI.clone(route);
|
|
|
|
next.push(child.id);
|
|
|
|
var b = callback && callback(child, next);
|
|
|
|
if (b === false) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (b === true) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return self._recursion(child, next, callback);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
recursion: function (callback) {
|
|
|
|
this._recursion(this.root, [], callback);
|
|
|
|
},
|
|
|
|
|
|
|
|
inOrderTraverse: function (callback) {
|
|
|
|
this._inOrderTraverse(this.root, callback);
|
|
|
|
},
|
|
|
|
|
|
|
|
//中序遍历(递归)
|
|
|
|
_inOrderTraverse: function (node, callback) {
|
|
|
|
if (node != null) {
|
|
|
|
this._inOrderTraverse(node.getLeft());
|
|
|
|
callback && callback(node);
|
|
|
|
this._inOrderTraverse(node.getRight());
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
//中序遍历(非递归)
|
|
|
|
nrInOrderTraverse: function (callback) {
|
|
|
|
|
|
|
|
var stack = [];
|
|
|
|
var node = this.root;
|
|
|
|
while (node != null || !BI.isEmpty(stack)) {
|
|
|
|
while (node != null) {
|
|
|
|
stack.push(node);
|
|
|
|
node = node.getLeft();
|
|
|
|
}
|
|
|
|
node = stack.pop();
|
|
|
|
callback && callback(node);
|
|
|
|
node = node.getRight();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
preOrderTraverse: function (callback) {
|
|
|
|
this._preOrderTraverse(this.root, callback);
|
|
|
|
},
|
|
|
|
|
|
|
|
//先序遍历(递归)
|
|
|
|
_preOrderTraverse: function (node, callback) {
|
|
|
|
if (node != null) {
|
|
|
|
callback && callback(node);
|
|
|
|
this._preOrderTraverse(node.getLeft());
|
|
|
|
this._preOrderTraverse(node.getRight());
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
//先序遍历(非递归)
|
|
|
|
nrPreOrderTraverse: function (callback) {
|
|
|
|
|
|
|
|
var stack = [];
|
|
|
|
var node = this.root;
|
|
|
|
|
|
|
|
while (node != null || !BI.isEmpty(stack)) {
|
|
|
|
|
|
|
|
while (node != null) {
|
|
|
|
callback && callback(node);
|
|
|
|
stack.push(node);
|
|
|
|
node = node.getLeft();
|
|
|
|
}
|
|
|
|
node = stack.pop();
|
|
|
|
node = node.getRight();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
postOrderTraverse: function (callback) {
|
|
|
|
this._postOrderTraverse(this.root, callback);
|
|
|
|
},
|
|
|
|
|
|
|
|
//后序遍历(递归)
|
|
|
|
_postOrderTraverse: function (node, callback) {
|
|
|
|
if (node != null) {
|
|
|
|
this._postOrderTraverse(node.getLeft());
|
|
|
|
this._postOrderTraverse(node.getRight());
|
|
|
|
callback && callback(node);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
//后续遍历(非递归)
|
|
|
|
nrPostOrderTraverse: function (callback) {
|
|
|
|
|
|
|
|
var stack = [];
|
|
|
|
var node = this.root;
|
|
|
|
var preNode = null;//表示最近一次访问的节点
|
|
|
|
|
|
|
|
while (node != null || !BI.isEmpty(stack)) {
|
|
|
|
|
|
|
|
while (node != null) {
|
|
|
|
stack.push(node);
|
|
|
|
node = node.getLeft();
|
|
|
|
}
|
|
|
|
|
|
|
|
node = BI.last(stack);
|
|
|
|
|
|
|
|
if (node.getRight() == null || node.getRight() == preNode) {
|
|
|
|
callback && callback(node);
|
|
|
|
node = stack.pop();
|
|
|
|
preNode = node;
|
|
|
|
node = null;
|
|
|
|
} else {
|
|
|
|
node = node.getRight();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
BI.Node = function (id) {
|
|
|
|
if (BI.isObject(id)) {
|
|
|
|
BI.extend(this, id);
|
|
|
|
} else {
|
|
|
|
this.id = id;
|
|
|
|
}
|
|
|
|
this.clear.apply(this, arguments);
|
|
|
|
};
|
|
|
|
|
|
|
|
BI.Node.prototype = {
|
|
|
|
constructor: BI.Node,
|
|
|
|
|
|
|
|
set: function (key, value) {
|
|
|
|
if (BI.isObject(key)) {
|
|
|
|
BI.extend(this, key);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
this[key] = value;
|
|
|
|
},
|
|
|
|
|
|
|
|
get: function (key) {
|
|
|
|
return this[key];
|
|
|
|
},
|
|
|
|
|
|
|
|
isLeaf: function () {
|
|
|
|
return BI.isEmpty(this.children);
|
|
|
|
},
|
|
|
|
|
|
|
|
getChildren: function () {
|
|
|
|
return this.children;
|
|
|
|
},
|
|
|
|
|
|
|
|
getChildrenLength: function () {
|
|
|
|
return this.children.length;
|
|
|
|
},
|
|
|
|
|
|
|
|
getFirstChild: function () {
|
|
|
|
return BI.first(this.children);
|
|
|
|
},
|
|
|
|
|
|
|
|
getLastChild: function () {
|
|
|
|
return BI.last(this.children);
|
|
|
|
},
|
|
|
|
|
|
|
|
setLeft: function (left) {
|
|
|
|
this.left = left;
|
|
|
|
},
|
|
|
|
|
|
|
|
getLeft: function () {
|
|
|
|
return this.left;
|
|
|
|
},
|
|
|
|
|
|
|
|
setRight: function (right) {
|
|
|
|
this.right = right;
|
|
|
|
},
|
|
|
|
|
|
|
|
getRight: function () {
|
|
|
|
return this.right;
|
|
|
|
},
|
|
|
|
|
|
|
|
setParent: function (parent) {
|
|
|
|
this.parent = parent;
|
|
|
|
},
|
|
|
|
|
|
|
|
getParent: function () {
|
|
|
|
return this.parent;
|
|
|
|
},
|
|
|
|
|
|
|
|
getChild: function (index) {
|
|
|
|
return this.children[index];
|
|
|
|
},
|
|
|
|
|
|
|
|
getChildIndex: function (id) {
|
|
|
|
return BI.findIndex(this.children, function (i, ch) {
|
|
|
|
return ch.get("id") === id;
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
removeChild: function (id) {
|
|
|
|
this.removeChildByIndex(this.getChildIndex(id));
|
|
|
|
},
|
|
|
|
|
|
|
|
removeChildByIndex: function (index) {
|
|
|
|
var before = this.getChild(index - 1);
|
|
|
|
var behind = this.getChild(index + 1);
|
|
|
|
if (before != null) {
|
|
|
|
before.setRight(behind || null);
|
|
|
|
}
|
|
|
|
if (behind != null) {
|
|
|
|
behind.setLeft(before || null);
|
|
|
|
}
|
|
|
|
this.children.splice(index, 1);
|
|
|
|
},
|
|
|
|
|
|
|
|
removeAllChilds: function () {
|
|
|
|
this.children = [];
|
|
|
|
},
|
|
|
|
|
|
|
|
addChild: function (child, index) {
|
|
|
|
var cur = null;
|
|
|
|
if (BI.isUndefined(index)) {
|
|
|
|
cur = this.children.length - 1;
|
|
|
|
} else {
|
|
|
|
cur = index - 1;
|
|
|
|
}
|
|
|
|
child.setParent(this);
|
|
|
|
if (cur >= 0) {
|
|
|
|
this.getChild(cur).setRight(child);
|
|
|
|
child.setLeft(this.getChild(cur));
|
|
|
|
}
|
|
|
|
if (BI.isUndefined(index)) {
|
|
|
|
this.children.push(child);
|
|
|
|
} else {
|
|
|
|
this.children.splice(index, 0, child);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
equals: function (obj) {
|
|
|
|
return this === obj || this.id === obj.id;
|
|
|
|
},
|
|
|
|
|
|
|
|
clear: function () {
|
|
|
|
this.parent = null;
|
|
|
|
this.left = null;
|
|
|
|
this.right = null;
|
|
|
|
this.children = [];
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
BI.extend(BI.Tree, {
|
|
|
|
transformToArrayFormat: function (nodes, pId) {
|
|
|
|
if (!nodes) return [];
|
|
|
|
var r = [];
|
|
|
|
if (BI.isArray(nodes)) {
|
|
|
|
for (var i = 0, l = nodes.length; i < l; i++) {
|
|
|
|
var node = BI.clone(nodes[i]);
|
|
|
|
node.pId = pId;
|
|
|
|
delete node.children;
|
|
|
|
r.push(node);
|
|
|
|
if (nodes[i]["children"]) {
|
|
|
|
r = r.concat(BI.Tree.transformToArrayFormat(nodes[i]["children"], node.id));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
var newNodes = BI.clone(nodes);
|
|
|
|
newNodes.pId = pId;
|
|
|
|
delete newNodes.children;
|
|
|
|
r.push(newNodes);
|
|
|
|
if (nodes["children"]) {
|
|
|
|
r = r.concat(BI.Tree.transformToArrayFormat(nodes["children"], newNodes.id));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return r;
|
|
|
|
},
|
|
|
|
|
|
|
|
arrayFormat: function (nodes, pId) {
|
|
|
|
if (!nodes) return [];
|
|
|
|
var r = [];
|
|
|
|
if (BI.isArray(nodes)) {
|
|
|
|
for (var i = 0, l = nodes.length; i < l; i++) {
|
|
|
|
var node = nodes[i];
|
|
|
|
r.push(node);
|
|
|
|
if (nodes[i]["children"]) {
|
|
|
|
r = r.concat(BI.Tree.transformToArrayFormat(nodes[i]["children"], node.id));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
var newNodes = nodes;
|
|
|
|
r.push(newNodes);
|
|
|
|
if (nodes["children"]) {
|
|
|
|
r = r.concat(BI.Tree.transformToArrayFormat(nodes["children"], newNodes.id));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return r;
|
|
|
|
},
|
|
|
|
|
|
|
|
transformToTreeFormat: function (sNodes) {
|
|
|
|
var i, l;
|
|
|
|
if (!sNodes) {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (BI.isArray(sNodes)) {
|
|
|
|
var r = [];
|
|
|
|
var tmpMap = [];
|
|
|
|
for (i = 0, l = sNodes.length; i < l; i++) {
|
|
|
|
if(BI.isNull(sNodes[i].id)) {
|
|
|
|
return sNodes;
|
|
|
|
}
|
|
|
|
tmpMap[sNodes[i].id] = BI.clone(sNodes[i]);
|
|
|
|
}
|
|
|
|
for (i = 0, l = sNodes.length; i < l; i++) {
|
|
|
|
if (tmpMap[sNodes[i].pId] && sNodes[i].id != sNodes[i].pId) {
|
|
|
|
if (!tmpMap[sNodes[i].pId].children) {
|
|
|
|
tmpMap[sNodes[i].pId].children = [];
|
|
|
|
}
|
|
|
|
tmpMap[sNodes[i].pId].children.push(tmpMap[sNodes[i].id]);
|
|
|
|
} else {
|
|
|
|
r.push(tmpMap[sNodes[i].id]);
|
|
|
|
}
|
|
|
|
delete tmpMap[sNodes[i].id].pId;
|
|
|
|
}
|
|
|
|
return r;
|
|
|
|
} else {
|
|
|
|
return [sNodes];
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
traversal: function (array, callback) {
|
|
|
|
if (BI.isNull(array)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
var self = this;
|
|
|
|
BI.any(array, function (i, item) {
|
|
|
|
if (callback(i, item) === false) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
self.traversal(item.children, callback);
|
|
|
|
})
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})();
|