/*
	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: {
				id: 2,
				handler: function() {
					return IBDOM.Utils.getFormattedNumber(arguments[0]);
				}
			}//NUMBER
			,
			NUMBER_ROUNDED_NO_ZEROES: {
				id: 3,
				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 {
					id: -1,
					handler: function() {
						return IBDOM.DataTypes.Handlers.defaultHandler.apply(this,arguments);
					}
				}
			}
		}
	}//DataTypes
	,
	Utils: {
		forkedLoopExecution: function() {
			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() {
			return IBDOM.IBElement.getAugmentedElement(document.getElementById(arguments[0]));
		}//getElement
		,
		isCollection: function() {
			return (
				arguments[0]
				&&
				(
					(l = arguments[0].length)
					||
					(l == 0)
				)
				&&
				(!isNaN(l))
			);
		}
		,
		getFormattedNumber: function() {
			/*
				todo: replace doTrailingZeroes boolean with
				decimal points specification
			*/
			p = arguments[0];
			doTrailingZeroes = arguments[1];
			useMathDotRound = arguments[2];

			if (useMathDotRound) {
				p = Math.round(p);
			}
			
			if (doTrailingZeroes) p = p + 0.001;
			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 + 3);
				return (formattedIntPart + "." + decpart);
			} else {
				return formattedIntPart;
			}
			
		}//getFormattedNumber
		,
		getString: function() {
			arr = arguments[0];
			if (arr) {
				return (new IBDOM.Utils.StringBuffer(arr)).toString();
			} else {
				return "";
			}
		}//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
		,
		setConditionalClassDirective: function() {
			nodeRef = arguments[0];
			doNotRemoveClass = arguments[1];
			classDirectiveString = IBDOM.IBElement.methods.getFirstPartialClassValue.apply(nodeRef,["setclass"]);
			if (classDirectiveString) {
				if (!doNotRemoveClass) {
					IBDOM.IBElement.methods.unsetClassValue.apply(nodeRef,[classDirectiveString]);
				}
				nodeRef.conditionalClassDirective = {
					string: classDirectiveString
				};
			}
		}//setConditionalClassDirective
		,
		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() {
			str = arguments[0];
			if (map = this.StringSplitMapCache.get(str)) {
				return map;
			} else {
				separator = arguments[1];
				arr = str.split(separator);
				map = new Array();
				for (i=0;a=arr[i];i++) {
					map[a] = true;
					map.separator = separator;
				}
				return this.StringSplitMapCache.add(str,map);
			}
		}//getMapFromSplit
		,
		StringSplitMapCache: {
		/*
			WARNING/ TODO:
			the add method below should be modified to put some bounds
			on the size of collection ... to avert big ol' memory leaks.
		*/
			collection: new Array()
			,
			inspect: function() {
				b = new IBDOM.Utils.StringBuffer();
				for (p in this.collection) {
					if (typeof(this.collection[p]) != "function") {
						b.append(this.collection[p].toString());
						b.append("\n");
					}
				}
				return b.toString();
			}
			,
			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);			
			}
			,
			add: function() {
				l = arguments[0];
				o = arguments[1];
				map = this.collection[l] = o;
				map.toString = IBDOM.Utils.StringSplitMapCache.mapToString;
				map.toStringFilter = function() {
					return this.toString(arguments[0]);
				}
				return map;
			}
			,
			get: function() {
				l = arguments[0];
				return this.collection[l];
			}
		}//StringSplitMapCache
	}//Utils Functions
	,
	Templates: {
		getTemplate: function() {
			className = arguments[0];
			optionalTagName = arguments[1];
			return IBDOM.Templates.Cache.findOrStore(null,null,className,optionalTagName);
		}
		,
		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);
				}
				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.
		,
		Cache: {
			collection: new Array()
			,
			findOrStore: function() {
				nodeRef = arguments[0];
				templateType = arguments[1];
				passedTplClass = arguments[2];
				passedTplTagName = arguments[3];
				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");
				}
				tpl = this.collection[key];
				if (!tpl) {
					if (passedTplClass) {
						tpl = IBDOM.Templates.Stashes.getTemplate(passedTplClass,passedTplTagName);
					} else {
						tpl = nodeRef.gFEBCN(templateType);
						if (tpl) {
							tpl.unsetClassValue(templateType);
							tpl.setClassValue("IS_TEMPLATE");
						} else {
							useTemplateDirective = nodeRef.getFirstPartialClassValue(templateType);
							if (useTemplateDirective) {
								tplClass = useTemplateDirective.split("|")[1];
								if (tplClass) {
									tpl = IBDOM.Templates.Stashes.getTemplate(tplClass);
								}//tplClass was found in directive, look for it in $("templates")
							}//tpl directive for templateType was found
						}//if template isn't contained in reference element, look in $("templates")
					}//template class was passed vs computed from node reference
					if (tpl) {
						tpl = tpl.populateFromDataBean({},false,true).cloneNodeAugmented(true);
						this.collection[key] = tpl;
					}
				}//if template not already in cache, find it, then cache it.
				if (tpl) {
					return tpl.cloneNodeAugmented(true);
				} else {
					return null;
				}
			}//findOrStore
			,
			inspect: function() {
				buff = new IBDOM.Utils.StringBuffer();
				for (p in this.collection) {
					buff.append(p);
					buff.append(": ");
					buff.append(this.collection[p]);
					buff.append("\n");
				}
				return buff.toString();
			}
		}//cache
	}//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.findOrStore(this,"template:repeat");
				emptyCollectionTemplate = IBDOM.Templates.Cache.findOrStore(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];
										tplClone = beanTemplate.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]) && (tplClone = beanTemplate.cloneNodeAugmented(true));bc++) {
								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
			,
			isInsideUnprocessedTemplate: function() {
				populateStartNode = arguments[0];
				pNode = this;
				answer = false;

				while(!answer && pNode && (pNode != populateStartNode) && (pNode != document.body)) {
					//if (pNode.hasClassValue("IS_TEMPLATE") || pNode.getFirstPartialClassValue("template:")) {
					if (IBDOM.IBElement.methods.hasClassValue.apply(pNode,["IS_TEMPLATE"]) || IBDOM.IBElement.methods.getFirstPartialClassValue.apply(pNode,["template:"])) {
						answer = true;
					}
					pNode = IBDOM.IBElement.getAugmentedElement(pNode.parentNode);
				}
				return answer;
			}//isInsideUnprocessedTemplate
			,
			populateFromDataBean: function() {
				dataBean = arguments[0];
				repeatedProcess = arguments[1];
				dummyRun = arguments[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;
					//IBDOM.IBElement.getAugmentedElement()
					for(ec=-1; (el = (doRoot ? this : allKids[ec])); ec++) {
						if (doRoot) doRoot = false;
						
						belongsToInnerTemplate = false;
						if (dummyRun) {
							belongsToInnerTemplate = true;
						} else if (!repeatedProcess) {
							belongsToInnerTemplate = IBDOM.IBElement.methods.isInsideUnprocessedTemplate.apply(el,[this]); //todo: optimize
						}
						
						/*
							todo: refactor repeated usage of this.metaCache[ec]
						*/
						
						if (!this.metaCache[ec]) this.metaCache[ec] = {};
						
						if (!el.dataKey) {
							if (
									this.metaCache[ec].dataKey
								) {
								el.dataKey = this.metaCache[ec].dataKey;
							} else {
								this.metaCache[ec].dataKey = el.dataKey = IBDOM.Utils.getDataKey(IBDOM.IBElement.methods.getTextData.apply(el,[]));
							}						
						}//no dataKey cached from previous usage of this specific node instance


						if (!el.conditionalClassDirective) {
							if (
									(ccd = this.metaCache[ec].conditionalClassDirective)
							) {
								el.conditionalClassDirective = ccd;
							} else {
								IBDOM.Utils.setConditionalClassDirective(el,belongsToInnerTemplate);
								this.metaCache[ec].conditionalClassDirective = el.conditionalClassDirective;
							}
						}

						if (ccd = el.conditionalClassDirective) {
							ccds = ccd.string.split("{if}");
							ccd_class = ccds[0].split(":")[1];
							ccd_condition = ccds[1];
							if (dataBean.test(ccd_condition)) {
								IBDOM.IBElement.methods.setClassValue.apply(el,[ccd_class]);
							}
						}

						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
						
						attrs = el.attributes;
						if (!el.attrDataMap) {
							if ((adm = this.metaCache[ec].attrDataMap)) {
								el.attrDataMap = adm;
							} else {
								this.metaCache[ec].attrDataMap = el.attrDataMap = {};
								for(ac=0;(attrs && (att=attrs[ac]));ac++) {
									if (!el.attrDataMap[att.nodeName]) {
										if (el.getAttribute(att.nodeName) && (dataKey = IBDOM.Utils.getDataKey(el.getAttribute(att.nodeName)))) {
											el.attrDataMap[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
							}//dataMap wasn't populated, create it and populate it
						}//node dataMap cached from previous usage of this specific node instance
						
						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() {
				t = arguments[0];
				ns = arguments[1];//optional namespace
				if (!ns) ns = "";
				results = null;
				if (this.getElementsByTagNameNS) {
					results = this.getElementsByTagNameNS(ns,t);
				}
				if ((results == null) || (results.length == 0))
				results = this.getElementsByTagName(t);
				return results;
			}//getElementsByTagName
			,
			gFEBTN: function() {
				els = this.gEBTN.apply(this,arguments);
				if (els)
					return IBDOM.IBElement.getAugmentedElement(els[0]);
				else
					return null;
			}//getFirstElementByTagName
			,
			gEBCN: function() {
			/*todo: we *could* possibly cache this output, not sure it's worth the trouble. */
				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() {
			/*todo: see gEBCN todo */
				if (col = this.gEBCN(arguments[0],arguments[1])) {
					return col[0];
				} else return null;				
			}//getFirstElementByClassName
			,
			gEBANV: function() {
				attName = arguments[0];
				attValue = arguments[1];
				tagName = arguments[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() {
				if (col = this.gEBANV(arguments[0], arguments[1], arguments[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() {
				eventType = arguments[0];
				func = arguments[1];
				funcArgs = arguments[2];
				rFalse = "";
				this.setAttribute(
					"on"+eventType,
					func + "(" + funcArgs.join(",") + ");" + rFalse
				);
				return this;
			}//setEvent
			,
			setClick: function() {
				func = arguments[0];
				funcArgs = arguments[1];
				if (this.href) {
					this.href = "javascript:" + func + "("+funcArgs.join(",")+")";
					return this;
				} else {
					return this.setEvent(
						"click",
						arguments[0],
						arguments[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

function $e(idOrObject) {
	theElement = (typeof(idOrObject) == "string") ? document.getElementById(idOrObject) : idOrObject;
	if (theElement)
		return IBDOM.IBElement.getAugmentedElement(theElement);
	else
		return null;
}//getElementById

function $t(classMatch, optionalTagName) {
	return IBDOM.Templates.getTemplate(classMatch, optionalTagName);
}//getTemplate from id="templates"

function getDollarFunction(passedOldDollar) {
	var olddollar = passedOldDollar;
	if (olddollar) {
		return function() {
			val = $e(arguments[0]);
			return olddollar(val);  
		};
	} else {
		return $e;
	}
}//getDollarFunction

$ = getDollarFunction(window.$);

doc = IBDOM.IBElement.getAugmentedElement(document);

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