summaryrefslogtreecommitdiff
path: root/frontend/beta/js/YUI-extensions/data/Tree.js
Side-by-side diff
Diffstat (limited to 'frontend/beta/js/YUI-extensions/data/Tree.js') (more/less context) (ignore whitespace changes)
-rw-r--r--frontend/beta/js/YUI-extensions/data/Tree.js412
1 files changed, 412 insertions, 0 deletions
diff --git a/frontend/beta/js/YUI-extensions/data/Tree.js b/frontend/beta/js/YUI-extensions/data/Tree.js
new file mode 100644
index 0000000..afa5b20
--- a/dev/null
+++ b/frontend/beta/js/YUI-extensions/data/Tree.js
@@ -0,0 +1,412 @@
+YAHOO.namespace('ext.data');
+
+/**
+ * @class YAHOO.ext.data.Tree
+ * @extends YAHOO.ext.util.Observable
+ * The class represents a tree data structure and bubbles all the events for it's nodes. The nodes
+ * in the tree have most standard DOM functionality.
+ * @constructor
+ * @param {Node} root (optional) The root node
+ */
+YAHOO.ext.data.Tree = function(root){
+ this.nodeHash = {};
+ this.root = null;
+ if(root){
+ this.setRootNode(root);
+ }
+ this.events = {
+ 'append' : true,
+ 'remove' : true,
+ 'move' : true,
+ 'insert' : true,
+ 'beforeappend' : true,
+ 'beforeremove' : true,
+ 'beforemove' : true,
+ 'beforeinsert' : true
+ };
+};
+
+YAHOO.extendX(YAHOO.ext.data.Tree, YAHOO.ext.util.Observable, {
+ pathSeparator: '/',
+
+ getRootNode : function(){
+ return this.root;
+ },
+
+ setRootNode : function(node){
+ this.root = node;
+ node.ownerTree = this;
+ node.isRoot = true;
+ return node;
+ },
+
+ getNodeById : function(id){
+ return this.nodeHash[id];
+ },
+
+ registerNode : function(node){
+ this.nodeHash[node.id] = node;
+ },
+
+ unregisterNode : function(node){
+ delete this.nodeHash[node.id];
+ },
+
+ toString : function(){
+ return '[Tree'+(this.id?' '+this.id:'')+']';
+ }
+});
+
+/**
+ * @class YAHOO.ext.tree.Node
+ * @extends YAHOO.ext.util.Observable
+ * @cfg {String} text The text for this node
+ * @cfg {String} id The id for this node
+ * @constructor
+ * @param {Object} attributes The attributes/config for the node
+ */
+YAHOO.ext.data.Node = function(attributes){
+ this.attributes = attributes || {};
+ this.leaf = this.attributes.leaf;
+ this.id = this.attributes.id;
+ if(!this.id){
+ this.id = YAHOO.util.Dom.generateId(null, 'ynode-');
+ this.attributes.id = this.id;
+ }
+
+ this.childNodes = [];
+ if(!this.childNodes.indexOf){ // indexOf is a must
+ this.childNodes.indexOf = function(o){
+ for(var i = 0, len = this.length; i < len; i++){
+ if(this[i] == o) return i;
+ }
+ return -1;
+ };
+ }
+ this.parentNode = null;
+ this.firstChild = null;
+ this.lastChild = null;
+ this.previousSibling = null;
+ this.nextSibling = null;
+
+ this.events = {
+ 'append' : true,
+ 'remove' : true,
+ 'move' : true,
+ 'insert' : true,
+ 'beforeappend' : true,
+ 'beforeremove' : true,
+ 'beforemove' : true,
+ 'beforeinsert' : true
+ };
+};
+
+YAHOO.extendX(YAHOO.ext.data.Node, YAHOO.ext.util.Observable, {
+ fireEvent : function(evtName){
+ // first do standard event for this node
+ if(YAHOO.ext.data.Node.superclass.fireEvent.apply(this, arguments) === false){
+ return false;
+ }
+ // then bubble it up to the tree if the event wasn't cancelled
+ if(this.ownerTree){
+ if(this.ownerTree.fireEvent.apply(this.ownerTree, arguments) === false){
+ return false;
+ }
+ }
+ return true;
+ },
+
+ isLeaf : function(){
+ return this.leaf === true;
+ },
+
+ setFirstChild : function(node){
+ this.firstChild = node;
+ },
+
+ setLastChild : function(node){
+ this.lastChild = node;
+ },
+
+ isLast : function(){
+ return (!this.parentNode ? true : this.parentNode.lastChild == this);
+ },
+
+ isFirst : function(){
+ return (!this.parentNode ? true : this.parentNode.firstChild == this);
+ },
+
+ hasChildNodes : function(){
+ return !this.isLeaf() && this.childNodes.length > 0;
+ },
+
+ appendChild : function(node){
+ var multi = false;
+ if(node instanceof Array){
+ multi = node;
+ }else if(arguments.length > 1){
+ multi = arguments;
+ }
+ // if passed an array or multiple args do them one by one
+ if(multi){
+ for(var i = 0, len = multi.length; i < len; i++) {
+ this.appendChild(multi[i]);
+ }
+ }else{
+ if(this.fireEvent('beforeappend', this.ownerTree, this, node) === false){
+ return false;
+ }
+ var index = this.childNodes.length;
+ var oldParent = node.parentNode;
+ // it's a move, make sure we move it cleanly
+ if(oldParent){
+ if(node.fireEvent('beforemove', node.getOwnerTree(), node, oldParent, this, index) === false){
+ return false;
+ }
+ oldParent.removeChild(node);
+ }
+ var index = this.childNodes.length;
+ if(index == 0){
+ this.setFirstChild(node);
+ }
+ this.childNodes.push(node);
+ node.parentNode = this;
+ var ps = this.childNodes[index-1];
+ if(ps){
+ node.previousSibling = ps;
+ ps.nextSibling = node;
+ }
+ this.setLastChild(node);
+ node.setOwnerTree(this.getOwnerTree());
+ this.fireEvent('append', this.ownerTree, this, node, index);
+ if(oldParent){
+ node.fireEvent('move', this.ownerTree, node, oldParent, this, index);
+ }
+ return node;
+ }
+ },
+
+ removeChild : function(node){
+ var index = this.childNodes.indexOf(node);
+ if(index == -1){
+ return false;
+ }
+ if(this.fireEvent('beforeremove', this.ownerTree, this, node) === false){
+ return false;
+ }
+
+ // remove it from childNodes collection
+ this.childNodes.splice(index, 1);
+
+ // update siblings
+ if(node.previousSibling){
+ node.previousSibling.nextSibling = node.nextSibling;
+ }
+ if(node.nextSibling){
+ node.nextSibling.previousSibling = node.previousSibling;
+ }
+
+ // update child refs
+ if(this.firstChild == node){
+ this.setFirstChild(node.nextSibling);
+ }
+ if(this.lastChild == node){
+ this.setLastChild(node.previousSibling);
+ }
+
+ node.setOwnerTree(null);
+ // clear any references from the node
+ node.parentNode = null;
+ node.previousSibling = null;
+ node.nextSibling = null;
+ this.fireEvent('remove', this.ownerTree, this, node);
+ return node;
+ },
+
+ insertBefore : function(node, refNode){
+ if(!refNode){ // like standard Dom, refNode can be null for append
+ return this.appendChild(node);
+ }
+ // nothing to do
+ if(node == refNode){
+ return false;
+ }
+
+ if(this.fireEvent('beforeinsert', this.ownerTree, this, node, refNode) === false){
+ return false;
+ }
+ var index = this.childNodes.indexOf(refNode);
+ var oldParent = node.parentNode;
+ var refIndex = index;
+
+ // when moving internally, indexes will change after remove
+ if(oldParent == this && this.childNodes.indexOf(node) < index){
+ refIndex--;
+ }
+
+ // it's a move, make sure we move it cleanly
+ if(oldParent){
+ if(node.fireEvent('beforemove', node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
+ return false;
+ }
+ oldParent.removeChild(node);
+ }
+ if(refIndex == 0){
+ this.setFirstChild(node);
+ }
+ this.childNodes.splice(refIndex, 0, node);
+ node.parentNode = this;
+ var ps = this.childNodes[refIndex-1];
+ if(ps){
+ node.previousSibling = ps;
+ ps.nextSibling = node;
+ }
+ node.nextSibling = refNode;
+ node.setOwnerTree(this.getOwnerTree());
+ this.fireEvent('insert', this.ownerTree, this, node, refNode);
+ if(oldParent){
+ node.fireEvent('move', this.ownerTree, node, oldParent, this, refIndex, refNode);
+ }
+ return node;
+ },
+
+ item : function(index){
+ return this.childNodes[index];
+ },
+
+ replaceChild : function(newChild, oldChild){
+ this.insertBefore(newChild, oldChild);
+ this.removeChild(oldChild);
+ return oldChild;
+ },
+
+ indexOf : function(child){
+ return this.childNodes.indexOf(child);
+ },
+
+ getOwnerTree : function(){
+ // if it doesn't have one, look for one
+ if(!this.ownerTree){
+ var p = this;
+ while(p){
+ if(p.ownerTree){
+ this.ownerTree = p.ownerTree;
+ break;
+ }
+ p = p.parentNode;
+ }
+ }
+ return this.ownerTree;
+ },
+
+ setOwnerTree : function(tree){
+ // if it's move, we need to update everyone
+ if(tree != this.ownerTree){
+ if(this.ownerTree){
+ this.ownerTree.unregisterNode(this);
+ }
+ this.ownerTree = tree;
+ var cs = this.childNodes;
+ for(var i = 0, len = cs.length; i < len; i++) {
+ cs[i].setOwnerTree(tree);
+ }
+ if(tree){
+ tree.registerNode(this);
+ }
+ }
+ },
+
+ getPath : function(attr){
+ attr = attr || 'id';
+ var p = this.parentNode;
+ var b = [this.attributes[attr]];
+ while(p){
+ b.unshift(p.attributes[attr]);
+ p = p.parentNode;
+ }
+ var sep = this.getOwnerTree().pathSeparator;
+ return sep + b.join(sep);
+ },
+
+ bubble : function(fn, scope, args){
+ var p = this;
+ while(p){
+ if(fn.call(scope || p, args || p) === false){
+ break;
+ }
+ p = p.parentNode;
+ }
+ },
+
+ cascade : function(fn, scope, args){
+ if(fn.call(scope || this, args || this) !== false){
+ var cs = this.childNodes;
+ for(var i = 0, len = cs.length; i < len; i++) {
+ cs[i].cascade(fn, scope, args);
+ }
+ }
+ },
+
+ eachChild : function(fn, scope, args){
+ var cs = this.childNodes;
+ for(var i = 0, len = cs.length; i < len; i++) {
+ if(fn.call(scope || this, args || cs[i]) === false){
+ break;
+ }
+ }
+ },
+
+ findChild : function(attribute, value){
+ var cs = this.childNodes;
+ for(var i = 0, len = cs.length; i < len; i++) {
+ if(cs[i].attributes[attribute] == value){
+ return cs[i];
+ }
+ }
+ return null;
+ },
+
+ /**
+ * Sorts this nodes children using the supplied sort function
+ * @param {Function} fn
+ * @param {Object} scope
+ */
+ sort : function(fn, scope){
+ var cs = this.childNodes;
+ var len = cs.length;
+ if(len > 0){
+ var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
+ cs.sort(sortFn);
+ for(var i = 0; i < len; i++){
+ var n = cs[i];
+ n.previousSibling = cs[i-1];
+ n.nextSibling = cs[i+1];
+ if(i == 0){
+ this.setFirstChild(n);
+ }
+ if(i == len-1){
+ this.setLastChild(n);
+ }
+ }
+ }
+ },
+
+ contains : function(node){
+ return node.isAncestor(this);
+ },
+
+ isAncestor : function(node){
+ var p = this.parentNode;
+ while(p){
+ if(p == node){
+ return true;
+ }
+ p = p.parentNode;
+ }
+ return false;
+ },
+
+ toString : function(){
+ return '[Node'+(this.id?' '+this.id:'')+']';
+ }
+});