1 /**
  2 * @constructor
  3 */
  4 var YNode = function(value){
  5 	this.value	= typeof(value!='undefined')?value:null;
  6 	this.parent	= null;
  7 	this.childs	= [];
  8 };
  9 YNode.prototype = {
 10 	constructor		: YNode,
 11 	constructorName : "YNode",
 12 	// -------------------------- basic use
 13 	getRoot	:function(){
 14 		if(this.parent)	return this.parent.getRoot();
 15 		else 			return this;
 16 	},
 17 	getValue	:function(){
 18 		//alert(this instanceof this.constructor);
 19 		return this.value;
 20 	},
 21 	setValue	:function(value){
 22 		this.value = value;
 23 	},
 24 	hasIndex	:function(index){
 25 		return index>=0 && index<this.childs.length && index%1==0;
 26 	},
 27 	getDepth	:function(){
 28 		var node = this;
 29 		var depth=0;
 30 		while(node.parent){	depth++;	node=node.parent;	}
 31 		return depth;
 32 	},
 33 	getIndex	:function(){
 34 		if(this.parent)	return this.parent.getChildIndex(this);
 35 		else			return -1;
 36 	},
 37 	getChildIndex	:function(ynode){
 38 		if(ynode instanceof this.constructor){
 39 			for(var i=0;i<this.childs.length;i++){
 40 				if(ynode==this.childs[i])return i;
 41 			}
 42 			return -1;
 43 		}else throw("\n"+this.constructorName+".getChildIndex Error :\n ynode must be an instance of '"+this.constructorName+"'");
 44 	},
 45 	addChilds	:function(ynodeList){
 46 		for(var i in ynodeList){
 47 			this.addChild(ynodeList[i]);
 48 		}
 49 	},
 50 	addChild	:function(ynode){
 51 		if(ynode instanceof this.constructor){
 52 			ynode.parent = this;
 53 			this.childs.push(ynode);
 54 			return this.childs.length-1;
 55 		}else throw("\n"+this.constructorName+".addChild Error :\n ynode must be an instance of '"+this.constructorName+"'.");
 56 	},
 57 	addChildAt	:function(ynode,index){
 58 		if(ynode instanceof this.constructor){
 59 			if(index>=0 && index<this.childs.length){
 60 				this.childs.splice(index,0,ynode);
 61 			}else this.childs.push(ynode);
 62 			ynode.parent = this;
 63 		}else throw("\n"+this.constructorName+".addChildAt Error :\n ynode must be an instance of '"+this.constructorName+"'.");
 64 	},
 65 	/**
 66 	add ynode before this one in his parent list.
 67 	@param {YNode} ynode the new and previous node.
 68 	*/
 69 	addPrev	:function(ynode){
 70 		if(this.parent){
 71 			this.parent.addChildAt(ynode,this.getIndex());
 72 		}else throw("\n"+this.constructorName+".addNext Error:\nynode have no parent.");
 73 	},
 74 	/**
 75 	add ynode after this one in his parent list.
 76 	@param {YNode} ynode the new and next node.
 77 	*/
 78 	addNext	:function(ynode){
 79 		if(this.parent){
 80 			this.parent.addChildAt(ynode,this.getIndex()+1);
 81 		}else throw("\n"+this.constructorName+".addNext Error:\nynode have no parent.");
 82 	},
 83 	/**
 84 	remove child at 'index'.
 85 	@param {int} index removed node index.
 86 	@return {YNode} the removed node and childs.
 87 	*/
 88 	removeChildAt	:function(index){
 89 		if(this.hasIndex(index)){
 90 			var node = this.childs[index];
 91 			this.childs[index].parent = null;
 92 			this.childs.splice(index,1);
 93 			return node;
 94 		}else throw("\n"+this.constructorName+".removeChildAt Error:\nindex="+index+" out of range[0->"+(this.childs.length-1)+"].");
 95 	},
 96 	/**
 97 	remove every node childs.
 98 	@return {YNode[]} removed nodes list.
 99 	*/
100 	removeAll	:function(){
101 		var list = this.chids;
102 		this.chids = [];
103 		for(var i in list){	list[i].parent = null;}
104 		return list;
105 	},
106 	/**
107 	exchange node but keep replaced node childs.
108 	@param {YNode} ynode new node.
109 	@param {int} index replaced node index.
110 	@return {YNode} the replaced node with the new node childs.
111 	*/
112 	substitute	:function(ynode,index){
113 		if(this.hasIndex(index)){
114 			var tmp = this.exchange(ynode,index);
115 			var list = tmp.removeAll();
116 			var list2 = ynode.removeAll();
117 			ynode.addChilds(list);
118 			tmp.addChilds(list2);
119 		}else throw("\n"+this.constructorName+".substitute Error:\nindex="+index+" out of range[0->"+(this.childs.length-1)+"].");
120 	},
121 	/**
122 	exchange node and childs.
123 	@param {YNode} ynode new node.
124 	@param {int} index replaced node index.
125 	@return {YNode} the replaced node and childs.
126 	*/
127 	exchange	:function(ynode,index){
128 		if(this.hasIndex(index)){
129 			var tmp = this.removeChildAt(index);
130 			this.addChildAt(ynode,index);
131 			return tmp;
132 		}else throw("\n"+this.constructorName+".exchange Error:\nindex="+index+" out of range[0->"+(this.childs.length-1)+"].");
133 	},
134 	removeChild	:function(ynode){
135 		var ci = this.getChildIndex(ynode);
136 		if(ci==-1)throw("\n"+this.constructorName+".removeChild Error:\nynode object not found.");
137 		return this.removeChildAt(ci);
138 	},
139 	removeMe	:function(){
140 		if(this.parent)	return this.parent.removeChild(this);
141 		else			return null;
142 	},
143 	swapChild	:function(indexA,indexB){
144 		if(this.hasIndex(indexA)&&this.hasIndex(indexB)&&indexA==indexB){
145 			var tmp = this.childs[indexA];
146 			this.childs[indexA]=this.childs[indexB];
147 			this.childs[indexB]=tmp;
148 			return true;
149 		}else return false;
150 	},
151 	getPath	:function(){
152 		var puf = function(node,datas){return  node.getIndex();	};
153 		var res = this.parseUp(puf,null);
154 		res.shift();
155 		return res;
156 	},
157 	// -------------------------- parsing
158 	parseUp		:function(Function,datas){
159 		var p_pup = function(node,Function,datas,result){
160 			result.unshift(Function(node,datas));
161 			if(node.parent){	p_pup(node.parent,Function,datas,result);	}
162 			return result;
163 		};
164 		return p_pup(this,Function,datas,[]);
165 	},
166 	parseChilds	:function(Function,datas){
167 		var res = [];
168 		for(var i=0;i<this.childs.length;i++){
169 			res.push(Function(this.childs[i],datas));
170 		}return res;
171 	},
172 	parseBA	:function(FunctionBefore,FunctionAfter,datas){
173 		var pparse = function(node,before,after,datas,parentResult){
174 			if(datas && datas.parseStop)return parentResult;
175 			var res = {childs:[],datas:null};
176 			if(typeof(before)=="function"){
177 				res.before = before(node,datas,parentResult);
178 				res.datas = res.before;
179 			}
180 			for(var i=0;i<node.childs.length;i++){
181 				pparse(node.childs[i],before,after,datas,res);
182 			}
183 			if(typeof(after)=="function"){
184 				res.after = after(node,datas,parentResult);
185 				res.datas = res.before;
186 			}
187 			parentResult.childs.push(res);
188 			return parentResult;
189 		};
190 		return pparse(this,FunctionBefore,FunctionAfter,datas,{childs:[],datas:null}).childs[0];
191 	},
192 	findAll	:function(Function,datas){
193 		var obj = {found:[],funk:Function,dataz:datas};
194 		this.parse(function(node,obj){
195 			if(obj.funk(node,obj.dataz)){	obj.found.push( node);	}
196 		},obj);
197 		return obj.found;
198 	},
199 	find	:function(Function,datas){
200 		var obj = {found:null,funk:Function,dataz:datas};
201 		this.parse(function(node,obj){
202 			if(obj.funk(node,obj.dataz)){	obj.found = node;	obj.parseStop = true;	}
203 		},obj);
204 		return obj.found;
205 	},
206 	parse	:function(Function,datas){
207 		return this.parseBA(Function,null,datas);
208 	},
209 	parseFromLeafs	:function(Function,datas){
210 		return this.parseBA(null,Function,datas);
211 	},
212 	parseResultToNode	:function(presult){
213 		if(presult&&typeof(presult.datas)!='undefined'){
214 			var node = new YNode(presult.datas);
215 			if(presult.childs){
216 				for(var i=0;i<presult.childs.length;i++){
217 					node.addChild(this.parseResultToNode(presult.childs[i]));
218 				}
219 			}return node;
220 		}return null;
221 	}
222 };
223 /*
224 // how to extend :
225 var YNodeEx=function(value){
226 	YNode.call(this);
227 	this.value = value;
228 };
229 YNodeEx.prototype = new YNode();
230 YNodeEx.prototype.constructor		= YNodeEx;
231 YNodeEx.prototype.constructorName	= 'YNodeEx';
232 */