/*
	This is IBDOM, Copyright 2007, Internet Brands, Inc.

	Documentation, Download and Open-Source Licensing available at:
	http://ibdom.sourceforge.net/
	
	Developer blog located at:
	http://ibbydev.blogspot.com/
	
	We *are* hiring skilled developers on many platforms and languages
	Check "careers" at http://www.internetbrands.com/
*/

IBDOM = {
	Config: {
		debug: true,
		ATTRIBUTE_BLANK_VALUE: "",
		ELEMENT_BLANK_VALUE: "N/A",
		USE_FORKED_LOOP_EXECUTION: true
	}//Config
	,
	NodeTypes: {
		ELEMENT_NODE: 1,
		ATTRIBUTE_NODE: 2,
		TEXT_NODE: 3,
		CDATA_NODE: 4
	}//Node Types
	,
	DataTypes: {
		Handlers: {
			defaultHandler: function() {
				return arguments[0];
			}//defaultHandler			
		}//Handlers
		,
		Defs: {
			NUMBER: {
				handler: function() {
					return IBDOM.Utils.getFormattedNumber(arguments[0]);
				}
			}//NUMBER
			,
			NUMBER_ROUNDED_NO_ZEROES: {
				handler: function() {
					return IBDOM.Utils.getFormattedNumber(arguments[0],false,true);
				}
			}//NUMBER_ROUNDED_NO_ZEROES
		}//Data Type Definitions
		,
		getType: function() {
			s = arguments[0];
			if (s) {
				return this.Defs[s.toUpperCase()];
			} else {
				return {
					handler: function() {
						return IBDOM.DataTypes.Handlers.defaultHandler.apply(this,arguments);
					}
				}
			}
		}
	}//DataTypes
	,
	Utils: {
		mapToString: function() {
				filter = arguments[0];
				buff = new IBDOM.Utils.StringBuffer();
				for (k in this) {
					if (typeof(this[k]) == "boolean") {
						if	(
								!filter
								||
								(filter && (k != filter))
							) {
							buff.append(k);
						}
					}
				}//loop thru fields in map
				return buff.toString(this.separator);			
		}
		,
		toStringFilter: function() {
			return this.toString(arguments[0]);
		}
		,
		processTemplate: function() {
			arguments[0].populateFromDataBean({},false,true).cloneNodeAugmented(true).metaCache.processed = true;
		}
		,
		Cache: function() {
			_a=arguments;
			this.collection = {};
			this.keyGetter = _a[0];
			this.valueGetter = _a[1];
			this.returnValueWrapper = _a[2];
			this.get = function() {
				key = this.keyGetter.apply(this,arguments);
				if (key) {
					value = this.collection[key];
					if (!value) {
						value = this.collection[key] = this.valueGetter.apply(this,arguments);
					}
					return ((w = this.returnValueWrapper) ? w(value) : value);
				} else {
					IBDOM.Utils.debug("No key returned");
					return null;
				}
			};
			this.inspect = function() {
				b = new IBDOM.Utils.StringBuffer();
				for (p in this.collection) {
					b.append(p).append(": ").append(this.collection[p].toString()).append("\n");
				}
				return b.toString();
			}
		}//Cache Object constructor
		,
		forkedLoopExecution: function() {
			var config = arguments[0];
			currentCollectionObject = config.collection[config.currentCollectionIndex];
			if (currentCollectionObject) {
				config.actionToPerform.apply(config.objectToActOn, config.actionToPerformStaticParameters.concat([currentCollectionObject,config.currentCollectionIndex]));
				window.setTimeout(
					function() {
						IBDOM.Utils.forkedLoopExecution(
							{
								collection: config.collection,
								currentCollectionIndex: config.currentCollectionIndex + 1,
								actionToPerformStaticParameters: config.actionToPerformStaticParameters,
								actionToPerform: config.actionToPerform,
								objectToActOn: config.objectToActOn,
								postProcessingAction: config.postProcessingAction
							}
						)
					}//recursive call
					,
					0				
				);
			} else if (config.currentCollectionIndex != 0) {
				if (config.postProcessingAction) {
					config.postProcessingAction.apply(config.objectToActOn,config.actionToPerformStaticParameters);
				}
			}//no collection object, and not first object
		}//forkedLoopExecution
		,
		scape: function() {
			return (
				window.encodeURIComponent
				?
				window.encodeURIComponent(arguments[0])
				:
				escape(arguments[0])
			);
		}
		,
		getElement: function() {
			ioo = arguments[0];
			theElement = (typeof(ioo) != "object") ? document.getElementById(ioo) : ioo;
			if (theElement) return IBDOM.IBElement.getAugmentedElement(theElement);
			else return null;
		}//getElement
		,
		isCollection: function() {
			return (
				arguments[0]
				&&
				(
					(l = arguments[0].length)
					||
					(l == 0)
				)
				&&
				(!isNaN(l))
			);
		}//isCollection
		,
		getFormattedNumber: function() {
			_a=arguments;
			p = _a[0];
			numDec = _a[1];
			intRound = _a[2];
			tenPow = intRound ? 1 : (numDec ? Math.pow(10,numDec) : 1);
			p = Math.round(p * tenPow)/tenPow;
			if (numDec) p =  (p + 1/Math.pow(10,(numDec+1)));
			p = p + "";
			decIndex=p.indexOf(".");
			if (decIndex != -1)
				intpart = p.substring(0,decIndex);
			else
				intpart = p;
			if (intpart.length > 3) {
				formattedIntPart = new IBDOM.Utils.StringBuffer();
				ccount = intpart.length - 1;
				poscount = 1;
				while((c=intpart.charAt(ccount))) {
					formattedIntPart.append(c);
					if	( 
							(ccount != 0)
							&&
							((poscount % 3) == 0)
						) {
						formattedIntPart.append(",");
					}
					ccount--;
					poscount++;
				}//loop thru int chars
				formattedIntPart.__strings__.reverse();
				formattedIntPart = formattedIntPart.toString();
			} else {
				formattedIntPart = intpart;
			}
			
			if (decIndex != -1) {
				decpart = p.substring(decIndex + 1,decIndex + (numDec+1));
				return (formattedIntPart + "." + decpart);
			} else {
				return formattedIntPart;
			}
			
		}//getFormattedNumber
		,
		getString: function() {
			return (a=arguments[0])?a.join(""):"";
		}//getString
		,
		StringBuffer: function(arr) {
			if (arr)
				this.__strings__ = arr;
			else
				this.__strings__ = new Array;
			
			this.append = function() {
				str = arguments[0];
				this.__strings__.push(str);
				return this;
			}
			
			this.toString = function() {
				separator = arguments[0];
				if (this.__strings__ && this.__strings__.join ) {
					if (!separator) separator = "";
					return this.__strings__.join(separator);
				} else {
					return "";
				}
			}
		}//StringBuffer Class
		,
		getDataKey: function() {
			dataString = arguments[0];
			if (dataString && (typeof (dataString) == "string")) {
				dataString = dataString.trim();
				dataValue = dataString.split("data:")[1];
				if (dataValue) {
					dv = dataValue.split(":type:");
					return {
						propName: dv[0],
						dataType: IBDOM.DataTypes.getType(dv[1])
					}
				} else return null;
			} else return null;
		}//getDataKey
		,
		getConditionalClassDirective: function() {
			_a=arguments;
			nodeRef = _a[0];
			doNotRemoveClass = _a[1];
			classDirectiveString = IBDOM.IBElement.methods.getFirstPartialClassValue.apply(nodeRef,["setclass"]);
			if (classDirectiveString) {
				if (!doNotRemoveClass) {
					IBDOM.IBElement.methods.unsetClassValue.apply(nodeRef,[classDirectiveString]);
				}
				ccds = classDirectiveString.split("{if}");
				ccd_class = ccds[0].split(":")[1];
				ccd_condition = ccds[1];
				return {
					condition: ccd_condition,
					theClass: ccd_class
				};
			} else { return null }
		}//getConditionalClassDirective
		,
		getAttrDataMap: function() {
			el = arguments[0];
			attrs = el.attributes;
			adm = {};
			for(ac=0;(attrs && (att=attrs[ac]));ac++) {
				if (!adm[att.nodeName]) {
					if (el.getAttribute(att.nodeName) && (dataKey = IBDOM.Utils.getDataKey(el.getAttribute(att.nodeName)))) {
						adm[att.nodeName] = dataKey;
						if (att.nodeName == "name") {
							IBDOM.Utils.debug("There currently are some weird WIN/IE issues with setting the NAME attribute in the templating mechanism. See FAQ");
						}
					}
				}
			}//loop thru attributes of the node
			return adm;
		}//getAttrDataMap
		,
		isInsideUnprocessedTemplate: function() {
			_a=arguments;
			innerNode = _a[0];
			outerNode = _a[1];
			pNode = innerNode;
			answer = false;	
			while(!answer && pNode && (pNode != outerNode) && (pNode != document.body)) {
				if (IBDOM.IBElement.methods.hasClassValue.apply(pNode,["IS_TEMPLATE"]) || IBDOM.IBElement.methods.getFirstPartialClassValue.apply(pNode,["template:"])) {
					answer = true;
				}
				pNode = pNode.parentNode;
			}
			return answer;
		}//isInsideUnprocessedTemplate
		,
		debug: function() {
			if (IBDOM.Config.debug) {
				if (oEl = IBDOM.Utils.getElement("debug")) {
					oEl.setTextData(arguments[0]);
				} else {
					alert("Debug Message: " + arguments[0])
				}
			}
		}//debug
		,
		getMapFromSplit: function() {
			return IBDOM.Utils.StringSplitMapCache.get.apply(IBDOM.Utils.StringSplitMapCache,arguments);
		}//getMapFromSplit
		,
		getWrappedFunction: function () {
			_a=arguments;
			wrapperFunc = _a[0];
			wrappeeFunc = _a[1];
			if (wrapperFunc) {
				return function() {
					return wrapperFunc(wrappeeFunc.apply(this,arguments));
				}
			} else {
				return wrappeeFunc;
			}
		}//getWrappedFunction
	}//Utils Functions
	,
	Templates: {
		getTemplate: function() {
			_a=arguments;
			return IBDOM.Templates.Cache.get(null,null,_a[0],_a[1]);
		}
		,
		Stashes: {
			collection: new Array()
			,
			populate: function() {
				if (window.doc) {
					this.collection = doc.gEBCN("templates");
				} else {
					alert("'doc' variable not found: look for: doc = IBDOM.IBElement.getAugmentedElement(document);");
				}
			}
			,
			getTemplate: function(classMatch, optionalTagName) {
				theTemplate = null;
				if (this.collection.length == 0) {
					this.populate();
				}
				if (this.collection.length == 0) {
					alert("You need AT LEAST ONE ELEMENT whose CLASS attribute value is: 'templates'");
				}
				for(stashCount = 0; !theTemplate && (stash = this.collection[stashCount]); stashCount++) {
					theTemplate = stash.gFEBCN(classMatch,optionalTagName);
				}
				if (!theTemplate) {
					alert("template: " + classMatch + " not found in any stash");
				}
				return theTemplate.cloneNodeAugmented(true);
			}
		}//Stashes: you may have several <div class="templates"></div> in a document.
		//you MIGHT call IBDOM.Templates.Stashes.populate() on page init() to avoid calling it on first usage.
	}//ITBTemplate
	,
	IBElement:  {
		/* methods collection inspired from Element.methods at http://www.prototypejs.org/ */
		/* todo: would like to find a reliable way to augment a node's prototype instead of augmenting each instance */
		/*
			while i've managed to make a wrapper object inherit methods from a node's prototype, this wrapper object isn't 
			a node, so calling node-native methods causes weirdness.
		*/
		methods: {
			set: function() {
				return this.setOnlyChild.apply(this,arguments);
			}//set: a bit vague but convenient: delegates to setOnlyChild.
			,
			setOnlyChild: function() {
				nodeRef = IBDOM.IBElement.getAugmentedElement(arguments[0]);
				if (nodeRef) {
					this.removeAllChildren();
					this.appendChild(nodeRef);
				}
				return this;
			}//setOnlyChild: removes all children, appends passed node
			,
			getFirstPartialClassValue: function() {
				v = arguments[0];
				if (v && this.className) {
					cMap = IBDOM.Utils.getMapFromSplit(this.className," ");
					matchClass = null;
					for (c in cMap) {
						if (c.indexOf(v) != -1) {
							if (!matchClass) {
								matchClass = c;
							}
						}
					}//loop thru classes in classmap
					return matchClass;
				} else {
					return null
				}
			}//getFirstPartialClassValue
			,
			hasClassValue: function() {
				theClass = arguments[0];
				if (theClass && this.className) {
					cMap = IBDOM.Utils.getMapFromSplit(this.className," ");
					return (theClass in cMap);
				} else {
					return false;
				}
			}//hasClassValue
			,
			setClassValue: function() {
				theClass = arguments[0];
				if (!IBDOM.IBElement.methods.hasClassValue.apply(this,[theClass])) {
					if (this.className) {
						this.className = IBDOM.Utils.getString([this.className," ",theClass]);
					} else {
						this.className = theClass;
					}
				}//if class not already in map
				return this;
			}//setClassValue
			,
			unsetClassValue: function() {
				theClass = arguments[0];
				if (theClass && this.className) {
					cMap = IBDOM.Utils.getMapFromSplit(this.className," ");
					if (theClass in cMap) {
						this.className = cMap.toStringFilter(theClass);
					}
				}//if class to UNSET was passed
				return this;
			}//unsetClassValue
			,
			cloneNodeAugmented: function() {
				augNode = IBDOM.IBElement.getAugmentedElement(this.cloneNode(arguments[0]));
				if (this.metaCache) {
					augNode.metaCache = this.metaCache;
				}
				return augNode;
			}//cloneNodeAugmented
			,
			populate: function() {
				if (IBDOM.Utils.isCollection(arguments[0])) {
					return this.populateFromBeanCollection.apply(this,arguments);
				} else {
					return this.populateFromDataBean.apply(this,arguments);
				}
			}//populate-dispatcher method
			,
			populateFromBeanCollection: function() {
				beanCollection = arguments[0];
				templateProcessor = arguments[1];
				
				beanTemplate = IBDOM.Templates.Cache.get(this,"template:repeat");
				emptyCollectionTemplate = IBDOM.Templates.Cache.get(this,"template:empty_collection");
								
				this.removeAllChildren();//after capturing the templates above

				if (beanCollection && beanTemplate) {
					if (beanCollection.length > 0) {
						if (IBDOM.Config.USE_FORKED_LOOP_EXECUTION) {
							IBDOM.Utils.forkedLoopExecution(
								{
									collection: beanCollection,
									currentCollectionIndex: 0,
									actionToPerformStaticParameters: [beanTemplate,templateProcessor],
									actionToPerform: function() {
										beanTemplate = arguments[0];
										templateProcessor = arguments[1];
										theBean = arguments[2];
										if (!IBDOM.Utils.isCollection(beanTemplate)) {
											beanTemplate = [beanTemplate];
										}
										for(btc=0;bt=beanTemplate[btc];btc++) {
											tplClone = bt.cloneNodeAugmented(true);
											if (templateProcessor) {
												tplClone.setDataProcessor(templateProcessor);
											}
											tplClone.populateFromDataBean(theBean,true);
											this.appendChild(tplClone);
										}
									},
									objectToActOn: this,
									postProcessingAction: null
								}
							);
						} else {
							for (bc=0;(bean=beanCollection[bc]);bc++) {
								if (!IBDOM.Utils.isCollection(beanTemplate)) {
									beanTemplate = [beanTemplate];
								}
								for(btc=0;bt=beanTemplate[btc];btc++) {
									tplClone = bt.cloneNodeAugmented(true);
									if (templateProcessor) {
										tplClone.setDataProcessor(templateProcessor);
									}
									tplClone.populateFromDataBean(bean,true);
									this.appendChild(tplClone);
								}
							}//normal loop thru bean collection
						}
					} else if (emptyCollectionTemplate) {
						this.appendChild(emptyCollectionTemplate);
					}
					this.unsetClassValue("IB_POPULATE");
				} else {
					if (emptyCollectionTemplate) {
						this.appendChild(emptyCollectionTemplate);
					}
					IBDOM.Utils.debug("empty bean collection passed or no template found for: " + this.localName);
				}
				return this;
			}//populateFromBeanCollection
			,
			setDataProcessor: function() {
				this.dataProcessor = arguments[0];
				return this;
			}//setDataProcessor
			,
			setCachedProp: function() {
				_a=arguments;
				ec = _a[0];
				el = _a[1];
				prop = _a[2];
				valueGetterFunc = _a[3];
				valueGetterFuncArgs = _a[4];
				if (!this.metaCache.processed && !(prop in this.metaCache[ec])) {
					if (!el[prop] && !(el[prop] = this.metaCache[ec][prop])) {
						el[prop] = this.metaCache[ec][prop] = valueGetterFunc.apply(el,valueGetterFuncArgs);
					}
				} else {
					el[prop] = this.metaCache[ec][prop];
				}
			}//setCachedProp
			,
			populateFromDataBean: function() {
				_a=arguments;
				dataBean = _a[0];
				repeatedProcess = _a[1];
				dummyRun = _a[2];
				if (!this.metaCache) this.metaCache = {};

				if (dataBean) {
					dataBean.test = function() {
						return eval(arguments[0]);
					}

					allKids = this.gEBTN("*");
					if (allKids.length == 0) allKids = [this];
					doRoot = true;

					for(ec=-1; (el = (doRoot ? this : allKids[ec])); ec++) {
						if (!this.metaCache[ec]) this.metaCache[ec] = {};
						if (doRoot) doRoot = false;
						belongsToInnerTemplate = false;
						if (dummyRun) {
							belongsToInnerTemplate = true;
						} else if (!repeatedProcess) {
							belongsToInnerTemplate = IBDOM.Utils.isInsideUnprocessedTemplate(el,this); //todo: optimize
						}
						realRun = !belongsToInnerTemplate || dummyRun || repeatedProcess;
						
						if (realRun) {
							this.setCachedProp(
								ec,el,"dataKey",
								IBDOM.Utils.getDataKey,[el.dataKey?null:IBDOM.IBElement.methods.getTextData.apply(el,[])]
							);
						}						
						
						if (realRun) {
							this.setCachedProp(
								ec,el,"conditionalClass",
								IBDOM.Utils.getConditionalClassDirective,[el,belongsToInnerTemplate]
							);
						}
						
						if (cc = el.conditionalClass) {
							if (dataBean.test(cc.condition)) {
								IBDOM.IBElement.methods.setClassValue.apply(el,[cc.theClass]);
							}
						}

						if ((dataKey = el.dataKey) && (strData = dataBean[dataKey.propName])) {
							IBDOM.IBElement.methods.setTextData.apply(el,[dataKey.dataType.handler(strData)]);
						}//if dataKey found for node, and bean has matching property with value
						else if (el.dataKey) {
							if (!belongsToInnerTemplate) {
								IBDOM.IBElement.methods.setTextData.apply(el,[IBDOM.Config.ELEMENT_BLANK_VALUE]);
							}
						}//dataKey found for node, but no matching property with value

						this.setCachedProp(
							ec,el,"attrDataMap",
							IBDOM.Utils.getAttrDataMap,[el]
						);
												
						if (el.attrDataMap) {
							for (mapItemKey in el.attrDataMap) {
								mapItem = el.attrDataMap[mapItemKey];
								if (mapItem && (dataKey = mapItem) && (strData = dataBean[dataKey.propName])) {
									el.setAttribute(mapItemKey,dataKey.dataType.handler(strData));
								} else if (mapItem && (dataKey = mapItem)) {
									if (!belongsToInnerTemplate) {
										el.setAttribute(mapItemKey,IBDOM.Config.ATTRIBUTE_BLANK_VALUE);
									}
								}//dataKey exists, but no matching bean property was found: blank it out.
							}//loop thru attributes of the node
						}//attrDataMap exists
					}//loop thru all descendant nodes
					if (this.dataProcessor) {
						this.dataProcessor.apply(this,arguments);
					}
					this.unsetClassValue("IB_POPULATE");
				}//if passed dataBean exists/not null
				else {
					IBDOM.Utils.debug("empty data bean passed for: " + this.localName + "\r" + this.innerHTML);
				}
				return this;
			}//populateFromDataBean
			,
			removeAllChildren: function() {
				while(fc = this.firstChild)
					this.removeChild(fc);
				return this;
			}//removeAllChildren
			,
			gEBTN: function() {
				_a=arguments;
				t = _a[0];
				ns = _a[1];//optional namespace
				if (!ns) ns = "";
				ebtns = this.getElementsByTagNameNS ? this.getElementsByTagNameNS(ns,t) : [];
				return ebtns[0] ? ebtns : this.getElementsByTagName(t);
			}//getElementsByTagName
			,
			gFEBTN: function() {
				els = this.gEBTN.apply(this,arguments);
				return els[0] ? IBDOM.IBElement.getAugmentedElement(els[0]) : null;
			}//getFirstElementByTagName
			,
			gEBCN: function() {
				cName = arguments[0];
				tagName = arguments[1];
				dels = tagName ? this.gEBTN(tagName) : this.gEBTN("*");
				els = new Array(); j = 0;
				for (t = 0; del=dels[t]; t++) {
					if (
							(c = del.className)
								&&
								(
									cName in IBDOM.Utils.getMapFromSplit(c," ")
								)
						) {
						els[j] = IBDOM.IBElement.getAugmentedElement(del);
						j++;
					}
				}
				return els;
			}//getElementsByClassName
			,
			gFEBCN: function() {
				if (col = this.gEBCN(arguments[0],arguments[1])) {
					return col[0];
				} else return null;				
			}//getFirstElementByClassName
			,
			gEBANV: function() {
				_a=arguments;
				attName = _a[0];
				attValue = _a[1];
				tagName = _a[2];
				nodeResults = new Array();
				rcount = 0;
				els = tagName ? this.gEBTN(tagName) : this.gEBTN("*");
				if (attName && attValue) {
					for (i=0;(el=els[i]);i++) {
						el = IBDOM.IBElement.getAugmentedElement(el);
						if(el.getAttribute && (el.getAttribute(attName) == attValue)) {
							nodeResults[rcount] = el;
							rcount++;
						}//if element has passed attribute, add it to result set
					}//loop thru retrieved elements
				}//attribute Name and Value passed
				return nodeResults;
			}//getElementsByAttributeNameValue
			,
			gFEBANV: function() {
				_a=arguments;
				if (col = this.gEBANV(_a[0], _a[1], _a[2])) {
					return col[0];
				} else return null;
			}//getFirstElementByAttributeNameValue
			,
			getTextData: function() {
				s = new IBDOM.Utils.StringBuffer();
				c = this.firstChild;
				while (c) {
					if ((c.nodeType == IBDOM.NodeTypes.TEXT_NODE) || (c.nodeType == IBDOM.NodeTypes.CDATA_NODE)) {
						s.append(c.data);
					}
					c = c.nextSibling;
				}
				return s.toString();
			}//getTextData
			,
			setTextData: function() {
				if (this.removeAllChildren)
					this.removeAllChildren();
				else
					IBDOM.IBElement.methods.removeAllChildren.apply(this,arguments);
					
				this.appendChild(doc.createTextNode(arguments[0]));
				return this;
			}
			,
			setEvent: function() {
				_a=arguments;
				eventType = _a[0];
				func = _a[1];
				funcArgs = _a[2];
				fPtr = func.apply ? func : window[func];
				this["on"+eventType] = function() {
					fPtr.apply(this,funcArgs);
				}
				return this;
			}//setEvent
			,
			setClick: function() {
				_a=arguments;
				func = _a[0];
				funcArgs = _a[1];
				if (this.href) {
					this.href = "javascript:" + func + "("+funcArgs.join(",")+")";
					return this;
				} else {
					return this.setEvent(
						"click",
						_a[0],
						_a[1]
					);
				}
			}//setClick
			,
			replaceWith: function() {
				newElement = arguments[0];
				if (this.id) {
					transferId = this.id;
					this.removeAttribute("id");
					newElement.setAttribute("id",transferId);
				}
				this.parentNode.replaceChild(newElement,this);
				return newElement;
			}//replaceWith
			,
			replaceWithTemplate: function() {
				if (this.id && this.tagName) {
					return this.replaceWith(IBDOM.Templates.getTemplate(this.id + "_template",this.tagName.toLowerCase()));
				} else {
					return null;
				}
			}//replaceWithTemplate
		}//methods
		,
		getAugmentedElement: function() {
			/* inspired by http://www.prototypejs.org/ */
			node = arguments[0];
			if (node && !node._IB_AUGMENTED) {
				methods = this.methods;
				for (property in methods) {
					value = methods[property];
					if (typeof value == 'function' && !(property in node))
					node[property] = value;
				}
				node._IB_AUGMENTED = {};
			}
			return node;
		}//getAugmentedElement
	}//IB Element object
}//IBDOM Library

IBDOM.Utils.StringSplitMapCache = new IBDOM.Utils.Cache(
	function() {
		return arguments[0];
	}//keyGetter
	,
	function() {
		str = arguments[0];
		separator = arguments[1];
		if (str) {
			arr = str.split(separator);
			map = {};
			for(i = 0;a = arr[i];i++) {
				map[a] = true;
				map.separator = separator;
			}
			map.toString = IBDOM.Utils.mapToString;
			map.toStringFilter = IBDOM.Utils.toStringFilter;
			return map;
		} else {
			return {};
		}
	}//valueGetter
);

IBDOM.Templates.Cache = new IBDOM.Utils.Cache(
	function() {
		_a=arguments;
		nodeRef = _a[0];
		templateType = _a[1];
		passedTplClass = _a[2];
		passedTplTagName = _a[3];
		key = null;
		if (nodeRef && templateType) {
			key = IBDOM.Utils.getString([templateType,"_",(nodeRef.parentNode ? nodeRef.parentNode.nodeName: "NOPARENT"),"/",nodeRef.nodeName,"_",nodeRef.id]);
		} else if (passedTplClass) {
			key = passedTplClass + "_" + passedTplTagName;
		} else {
			IBDOM.Utils.debug("Could not identify template");
		}
		return key;
	}//keyGetter
	,
	function() {
		_a=arguments;
		nodeRef = _a[0];
		templateType = _a[1];
		passedTplClass = _a[2];
		passedTplTagName = _a[3];
		tpl = null;
		if (passedTplClass) {
			tpl = IBDOM.Templates.Stashes.getTemplate(passedTplClass,passedTplTagName);
		} else {
			tpl = nodeRef.gEBCN(templateType);
			if (tpl.length == 1) {
				tpl = tpl[0];
				tpl.unsetClassValue(templateType);
				tpl.setClassValue("IS_TEMPLATE");
			} else if (tpl.length == 0) {
				useTemplateDirective = nodeRef.getFirstPartialClassValue(templateType);
				if (useTemplateDirective) {
					tplClass = useTemplateDirective.split("|")[1];
					if (tplClass && (tplClass == ((tSplit = tplClass.split(","))[0]))) {
						tpl = IBDOM.Templates.Stashes.getTemplate(tplClass);
					}//tplClass was found in directive, look for it in Tpl Stash
					else {
						tpl = [];
						for(tc=0;cName=tSplit[tc];tc++) {
							tpl[tc] = IBDOM.Templates.Stashes.getTemplate(cName);
						}
					}
				}//tpl directive for templateType was found
			}//if template isn't contained in reference element, look in Tpl Stash
		}//template class was passed vs computed from node reference
		if (tpl && tpl._IB_AUGMENTED) {
			IBDOM.Utils.processTemplate(tpl);
		} else if (IBDOM.Utils.isCollection(tpl)) {
			for (t=0;tp=tpl[t];t++) {
				IBDOM.Utils.processTemplate(tp);
			}
		}
		return tpl;
	}//valueGetter
	,
	function() {
		tp=arguments[0];
		if (!IBDOM.Utils.isCollection(tp)) {
			return tp.cloneNodeAugmented(true);
		} else {
			tpa = [];
			for(c=0;t=tp[c];c++) {
				tpa[c] = t.cloneNodeAugmented(true);
			}
			return tpa;
		}
	}//returnValueWrapper
);

$e = IBDOM.Utils.getElement;
$t = IBDOM.Templates.getTemplate;
$ = IBDOM.Utils.getWrappedFunction(window.$,IBDOM.Utils.getElement);
doc = IBDOM.IBElement.getAugmentedElement(document);

String.prototype.trim = function() {
	return (this.replace(/^[ \r\n\t\f\s]+/,"").replace(/[ \r\n\t\f\s]+$/,""));
}
