/**
 * Provides custom elements and functionality used with widgets
 * 
 * @version 0.4.0 2009-06-23
 * @author Gregor Kofler
 *
 * currently providing
 *
 * vxJS.widget.shared.hiLite(text, node, className)
 * vxJS.widget.shared.shadeTableRows(parameterObject)
 * vxJS.widget.shared.list(parameterObject)
 * vxJS.widget.accordion()
 * vxJS.widget.simpleTabs()
 * 
 * @todo list: Safari doesn't follow keystrokes
 */
/*global vxJS*/

if(!vxJS) { throw Error("widget.shared: vxJS core missing."); }

vxJS.widget.shared = {

	/**
	 * hilight text beneath a given node
	 */
	hiLite: function(text, node, cName) {

		var	rex = new RegExp(text, "gi"),
			chunks, hits, frag = document.createDocumentFragment(), i;

		if(!cName)	{ cName = "hiLite"; }
		if(!node)	{ node = document; }

		vxJS.dom.walk(node, function(n) {
			if(n.nodeType !== 3) {
				return;
			}

			hits = n.nodeValue.match(rex);

			if(hits && hits.length) {

				chunks = n.nodeValue.split(rex);

				for(i = 0; i < chunks.length-1; ++i) {
					frag.appendChild(document.createTextNode(chunks[i]));
					frag.appendChild("span".setProp("class", cName).create(hits.shift()));
				}

				frag.appendChild(document.createTextNode(chunks[i]));

				n.parentNode.replaceChild(frag, n);
			}
		});
	},

	/**
	 * shade table rows by assigning alternating class names
	 */
	shadeTableRows: function(param) {
		var t, r, c, i, l;
		if(!param || !(t = param.element)) {
			return;
		}

		if (t.nodeName !== "TBODY") {
			if (!t.tBodies || !(t = t.tBodies[0])) {
				return;
			}
		}
		if(!(r = t.rows)) {
			return;
		}
		c = param.classNames && param.classNames.length && param.classNames.length > 1 ? param.classNames : ["row0", "row1"];

		for(i = 0, l = r.length; i < l; ++i) {
			r[i].className = c[i % c.length];
		}
	},

	/**
	 * a list widget, used with vxJS.widget.autoSuggest(), vxJS.widget.queryPopup(), etc.
	 *
	 * served events: "select", "toggle", "cancel"
	 */
	list: function(param) {
		param = param || {};

		var	ul = "ul".setProp("class", param.className || "vxJS_list").create(),
			entries, multiple = param.multiple || false, selected = [], current, that = {},
			i, kc, kdFlag;
		
		ul.style.position = "relative";
		
		var setCurrent = function(n) {
			if(current === n) {
				return;
			}
			if(current) {
				current.className = current.className.replace(/([ ]?current)/, "");
			}
			current = n;
			n.className += n.className ? " current" : "current";

			if(!multiple) {
				selected[0] = n;
			}
		};
		
		var focus = function() {
			ul.tabIndex = -1;
			ul.focus();
		};
		
		var toggleSelected = function(n) {
			var i;
			if(!multiple) {
				return;
			}
			for(i = selected.length; i--;) {
				if(selected[i] === n) {
					selected.splice(i, 1);
					n.className = n.className.replace(/([ ]?selected)/, "");
					vxJS.serve(that, "toggle");
					return;
				}
			}
			n.className += n.className ? " selected" : "selected";
			selected.push(n);
			vxJS.serve(that, "toggle");
		};

		var scrollIn = function() {
			var	d = vxJS.dom.getElementOffset(current, ul).y,
				liH = d + vxJS.dom.getElementSize(current).y,
				ulH = vxJS.dom.getElementSize(ul).y;

			if(d < ul.scrollTop) {
				ul.scrollTop = d;
			}
			
			else if(liH > ulH + ul.scrollTop) {
				ul.scrollTop = liH - ulH;
			}
		};

		var hiLite = function(n) {
			if(n !== ul) {
				setCurrent(n.nodeName !== "LI" ? vxJS.dom.getParentElement(n, "li") : n);
			}
		};

		var findEntry = function(n) {
			var i;
			for(i = entries.length; --i >= 0;) {
				if(entries[i].elem === n) {
					return entries[i];
				}
			}
		};

		var fill = function(newEntries) {
			var i;
			entries = newEntries || [];
			
			if(!entries.length) {
				ul.style.display = "none";
				return;
			}

			vxJS.dom.deleteChildNodes(ul);
			for(i = 0; i < entries.length; ++i) {
				entries[i].elem = "li".create(entries[i].elements ? vxJS.dom.parse(entries[i].elements) : entries[i].text);
				entries[i].elem.style.position = "relative";
				ul.appendChild(entries[i].elem);
			}

			ul.style.display = "";
			setCurrent(ul.firstChild);
			scrollIn();
		};

		if(param.hoverSelect) {
			vxJS.event.addListener(ul, "mouseover", function() {
					hiLite(this);
				}
			);
		}

		vxJS.event.addListener(ul, "keypress", function(e) {
			vxJS.event.preventDefault(e);

			if(!kdFlag) {
				switch (kc) {
					case 38:
						that.up();
						break;
					case 40:
						that.down();
						break;
				}
			}
			kdFlag = false;
		});

		var mousedown = function() {
			if(!param.hoverSelect) {
				hiLite(this);
			}
			if(this !== ul) {
				vxJS.event.serve(that, "select");
			}
		};

		var click = function() {
			if(this !== ul) {
				toggleSelected(this.nodeName !== "LI" ? vxJS.dom.getParentElement(this, "li") : this);
			}
		};

		var keydown = function(e) {

			kdFlag = true;
			kc = e.keyCode;

			switch (kc) {
				case 13:
					vxJS.event.serve(that, "select"); break;
				case 27:
					vxJS.event.serve(that, "cancel"); break;
				case 38:
					that.up(); break;
				case 40:
					that.down(); break;
				case 36:
					that.first(); break;
				case 35:
					that.last(); break;
				case 32:
					toggleSelected(current); break;
			}
			vxJS.event.preventDefault(e);
		};

		vxJS.event.addListener(ul, "mousedown", mousedown);
		vxJS.event.addListener(ul, "click", click);
		vxJS.event.addListener(ul, "keydown", keydown);

		fill(param.entries);

		/**
		 * exposed properties and functions
		 */
		that.getSelected = function() {
			var s = [], i;
			if(selected.length) {
				for(i = 0; i < selected.length; ++i) {
					s.push(findEntry(selected[i]));
				}
				return s;
			}
		};

		that.up = function() {
			if(current === ul.firstChild) {
				return;
			}
			if(!current && ul.lastChild) {
				setCurrent(ul.lastChild);
			}
			else {
				setCurrent(current.previousSibling);
			}
			scrollIn();
		};

		that.down = function() {
			if(current === ul.lastChild) {
				return;
			}
			if(!current && ul.firstChild) {
				setCurrent(ul.firstChild);
			}
			else {
				setCurrent(current.nextSibling);
			}
			scrollIn();
		};
		
		that.first = function() {
			if(current === ul.firstChild) {
				return;
			}
			setCurrent(ul.firstChild);
			scrollIn();
		};

		that.last = function() {
			if(current === ul.lastChild) {
				return;
			}
			setCurrent(ul.lastChild);
			scrollIn();
		};

		that.getRows	= function() { return entries ? entries.length : 0; };
		that.focus		= focus;
		that.fill		= fill;
		that.element	= ul;

		return that;
	}
};

vxJS.widget.accordion = function() {
	var folded = vxJS.dom.getElementsByClassName("vxJS_foldThis"), i;

	if((i = folded.length)) {
		for(;i--;) {
			(function(container) {
				var i = 0, div, bar, v, f, ctrl = [], c = container.childNodes;

				var push = function() {
					v = false;
					if(!f && bar.className.indexOf("default") !== -1) {
						f = true;
						v = true;
					}
					ctrl.push({
						bar: bar,
						div: div,
						visibility: v
					});
					div.style.display = v ? "" : "none"; 
				};

				while(i < c.length && c[i].tagName !== "H2") { i++; }
				
				while(c[i]) {
					if(c[i].tagName === "H2") {
						if(bar) {
							push();
						}
						bar = c[i++];
						div = "div".create();
					}
					else {
						div.appendChild(c[i]);
					}
				}

				push();

				for(i = ctrl.length; i--;) {
					container.insertBefore(ctrl[i].div, ctrl[i].bar.nextSibling);
				}

				vxJS.event.addListener(container, "click", function(group) {
					var last = function() {
						for(var i = group.length; i--;) {
							if(group[i].visibility) {
								return group[i];
							}
						}
					}();

					return function() {
						var i, t, n = this.nodeName !== "H2" ? vxJS.dom.getParentElement(this, "h2") : this;

						if(!n) {
							return;
						}

						if(last) {
							last.visibility = false;
							last.bar.className = last.bar.className.replace(/ ?shown/, "");
							if(vxJS.fx && vxJS.fx.roll) {
								vxJS.element.register(last.div).fx("roll", { direction: "up", transition: "easeInOut", duration: 0.3 });
							}
							else {
								last.div.style.display = "none";
							}
						}

						for(i = group.length; i--;) {
							t = group[i];
							if(t.bar === n) {
								if(t == last) {
									last = null;
									return;
								}
								t.visibility = true;
								t.bar.className = t.bar.className+" shown";
								if(vxJS.fx && vxJS.fx.roll) {
									vxJS.element.register(t.div).fx("roll", { direction: "down", transition: "easeInOut", duration: 0.3 });
								}
								else {
									t.div.style.display = "";
								}
								last = t;
								break;
							}
						}
					};
				}(ctrl));
			})(folded[i]);
		}
	}
};

vxJS.widget.simpleTabs = function(textWrap) {
	var tabbed = vxJS.dom.getElementsByClassName("vxJS_tabThis"), i;
	if((i = tabbed.length)) {
		for(;i--;) {
			(function(container) {
				var secs = vxJS.dom.getElementsByClassName("section", container),
					ctrl = [], h2, ul, f, v, i, t;

				for(i = secs.length; i--;) {
					h2 = secs[i].getElementsByTagName("h2");

					if(h2.length > 0) {
						if(!f && ((i > 0 && secs[i].className.indexOf("default") !== -1) || i === 0)) {
							f = true;
							v = true;
						}
						else {
							v = false;
						}
						secs[i].style.display = v ? "" : "none";
						t = vxJS.dom.concatText(h2[0]);
						h2[0].parentNode.removeChild(h2[0]);

						ctrl.push({
							page: secs[i],
							tab: "li".setProp("class", v ? "shown" : "").create(textWrap ? textWrap.create(t) : t),
							visibility: v
						});
					}
				}
		
				if(!ctrl.length) {
					return;
				}

				ul = "ul".setProp("class", "vxJS_tabBar").create();

				for(i = ctrl.length; i--;) {
					ul.appendChild(ctrl[i].tab);
				}

				container.insertBefore(ul, container.firstChild);
			
				vxJS.event.addListener(ul, "click", function(group) {
					var last = function() {
						for(var i = group.length; i--;) {
							if(group[i].visibility) {
								return group[i];
							}
						}
					}();

					return function() {
						var i, t, n = this.nodeName !== "LI" ? vxJS.dom.getParentElement(this, "li") : this;

						if(last.tab === n) {
							return;
						}

						last.visibility = false;
						last.tab.className = last.tab.className.replace(/ ?shown/, ""); 
						last.page.style.display = "none";

						for(i = group.length; i--;) {
							t = group[i];
							if(t.tab === n) {
								t.visibility = true;
								t.tab.className = t.tab.className+" shown"; 
								t.page.style.display = "";
								last = t;
								break;
							}
						}
					};
				}(ctrl));
			})(tabbed[i]);
		}
	}
};