/*!
 * JavaScript Custom Forms v0.5
 * Copyright 2011, Unknown
 */
jcf = {};
jcf.modules = {};
jcf.baseOptions = {
	hiddenClass:'jcf-hidden',
	replaceAll:true
}

// advanced replacer class
jcf.customForms = function(opt) {
	this.options = jcf.baseOptions;
	jcf.lib.extend(this.options, opt);
}
jcf.customForms.prototype.replace = function(obj, opt) {
	for(var k in jcf.modules) {
		var els = jcf.lib.queryBySelector(jcf.modules[k].prototype.selector);
		for(var i = 0; i<els.length; i++) {
			new jcf.modules[k]({
				replaces:els[i]
			});
		}
	}
}
jcf.setBaseModule = function(obj) {
	jcf.customControl = function(opt){
		this.options = {
			labelActiveClass: 'jcf-label-active',
			focusClass:'jcf-focus',
			foo:'bar'
		}
		jcf.lib.extend(this.options, opt);
		this.init();
	}
	for(var p in obj) {
		jcf.customControl.prototype[p] = obj[p];
	}
}
jcf.addModule = function(obj) {
	if(obj.name){
		// create new module proto class
		jcf.modules[obj.name] = function(){
			jcf.modules[obj.name].superclass.constructor.apply(this, arguments);
		}
		jcf.lib.inherit(jcf.modules[obj.name], jcf.customControl);
		for(var p in obj) {
			jcf.modules[obj.name].prototype[p] = obj[p]
		}

		// on create module
		jcf.modules[obj.name].prototype.onCreateModule();

		// make callback for exciting modules
		for(var mod in jcf.modules) {
			if(jcf.modules[mod] != jcf.modules[obj.name]) {
				jcf.modules[mod].prototype.onModuleAdded(jcf.modules[obj.name]);
			}
		}
	}
}

// custom control base class
jcf.setBaseModule({
	init: function(){
		this.initOptions();
		this.realElement = this.options.replaces;
		if(this.realElement) {
			this.replaceObject();
		}
	},
	initOptions: function(){

	},
	createWrapper: function(){
		this.fakeElement = document.createElement('div');
		this.fakeElement.jcf = this;
		this.realElement.jcf = this;
		this.labelFor = jcf.lib.getLabelFor(this.realElement);
		jcf.lib.disableTextSelection(this.fakeElement);
		jcf.lib.addClass(this.realElement, jcf.baseOptions.hiddenClass);
	},
	initEvents: function(){
		jcf.lib.event.add(this.realElement, 'click', this.onClick);
		jcf.lib.event.add(this.realElement, 'focus', this.onFocus);
		jcf.lib.event.add(this.realElement, 'blur', this.onBlur);

		jcf.lib.event.add(this.fakeElement, 'click', this.onFakeClick);
		jcf.lib.event.add(this.fakeElement, 'mousedown', this.onFakeMouseDown);
		jcf.lib.event.add(this.fakeElement, 'mouseup', this.onFakeMouseUp);

		if(this.labelFor) {
			this.labelFor.jcf = this;
			jcf.lib.event.add(this.labelFor, 'click', this.onFakeClick);
			jcf.lib.event.add(this.labelFor, 'mousedown', this.onFakeMouseDown);
			jcf.lib.event.add(this.labelFor, 'mouseup', this.onFakeMouseUp);
		}
	},
	setupWrapper: function(){
		// implement in subclass
	},
	refreshState: function(){
		// implement in subclass
	},
	replaceObject: function(){
		this.createWrapper();
		this.initEvents();
		this.setupWrapper();
	},
	onClick: function(){
		this.jcf.refreshState();
	},
	onFocus: function(){
		clearTimeout(this.jcf.focusTimer);
		this.jcf.fakeElement.mouseDownFlag = false;
		if(!this.jcf.realElement.disabled) {
			jcf.lib.addClass(this.jcf.fakeElement,this.jcf.options.focusClass);
		}
	},
	onBlur: function(){
		var _this = this;
		this.jcf.focusTimer = setTimeout(function(){
			if(!_this.jcf.fakeElement.mouseDownFlag) {
				jcf.lib.removeClass(_this.jcf.fakeElement,_this.jcf.options.focusClass);
			}
		},10);
	},
	onFakeClick: function(){
		this.jcf.realElement.focus();
	},
	onFakeMouseDown: function(){
		this.jcf.fakeElement.mouseDownFlag = true;
		return false;
	},
	onFakeMouseUp: function(){
		this.jcf.fakeElement.mouseDownFlag = false;
	},
	onCreateModule: function(){
		// implement in subclass
	},
	onModuleAdded: function(module) {
		// implement in subclass
	},
	onControlReady: function() {
		// implement in subclass
	}
});










// custom forms util library
jcf.lib = {
	bind: function(func, scope){
		var _function = func;
		return function() {
			return _function.apply(scope, arguments);
		}
	},
	browser: (function() {
		var rwebkit = /(webkit)[ \/]([\w.]+)/,
		ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/,
		rmsie = /(msie) ([\w.]+)/,
		rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/;
		var ua = navigator.userAgent.toLowerCase();
		var match = rwebkit.exec( ua ) || ropera.exec( ua ) || rmsie.exec( ua ) || ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) || [];
		var res = {};
		res[match[1]] = true;
		res.version = match[2] || "0";
		return res;
	})(),
	getOffset: function(element) {
		var el = element;
		var valueT = 0, valueL = 0;
		if (element.offsetParent) {
			do {
				// hello reflow
				valueT += element.offsetTop  || 0;
				valueL += element.offsetLeft || 0;
				if(element != document.body && element != document.body.parentNode) {
					valueT -= element.scrollTop  || 0;
					valueL -= element.scrollLeft  || 0;
				}
				valueT += parseInt(jcf.lib.getStyle(element,'borderTopWidth')) || 0;
				valueL += parseInt(jcf.lib.getStyle(element,'borderLeftWidth')) || 0;
				element = element.offsetParent;
			} while (element);
		}
		return {top:valueT, left:valueL};
	},
	getStyle: function(el, prop) {
		if (document.defaultView && document.defaultView.getComputedStyle) {
			return document.defaultView.getComputedStyle(el, null)[prop];
		} else if (el.currentStyle) {
			return el.currentStyle[prop];
		} else {
			return el.style[prop];
		}
	},
	getParent: function(obj, selector) {
		while(obj.parentNode && obj.parentNode != document.body) {
			if(obj.parentNode.tagName.toLowerCase() == selector.toLowerCase()) {
				return obj.parentNode;
			}
			obj = obj.parentNode;
		}
		return false;
	},
	isParent: function(child, parent) {
		while(child.parentNode) {
			if(child.parentNode === parent) {
				return true;
			}
			child = child.parentNode;
		}
		return false;
	},
	getLabelFor: function(object) {
		if(jcf.lib.getParent(object,'label')) {
			return object.parentNode;
		} else if(object.id && object.id.length) {
			return jcf.lib.queryBySelector('label[for=' + object.id + ']')[0];
		}
	},
	disableSelection: function() {
		document.onselectstart = function(){return false}
	},
	enableSelection: function() {
		document.onselectstart = null
	},
	disableTextSelection: function(el){
		if(el.setProperty) {
			el.setProperty("unselectable","on");
		}
		el.style.MozUserSelect = 'none';
		el.style.KhtmlUserSelect = 'none';
	},
	queryBySelector: function(selector, scope){
		return document.querySelectorAll ? (scope || document).querySelectorAll(selector) : jcf.lib.getElementsBySelector(selector, scope);
	},
	prevSibling: function(node) {
		while(node = node.previousSibling) if(node.nodeType == 1) break;
		return node;
	},
	nextSibling: function(node) {
		while(node = node.nextSibling) if(node.nodeType == 1) break;
		return node;
	},
	fireEvent: function(element,event) {
		if (document.createEventObject){
			var evt = document.createEventObject();
			return element.fireEvent('on'+event,evt)
		}
		else{
			var evt = document.createEvent('HTMLEvents');
			evt.initEvent(event, true, true );
			return !element.dispatchEvent(evt);
		}
	},
	isParent: function(p, c) {
		while(c.parentNode) {
			if(p == c) {
				return true;
			}
			c = c.parentNode;
		}
		return false;
	},
	inherit: function(Child, Parent) {
		var F = function() { }
		F.prototype = Parent.prototype
		Child.prototype = new F()
		Child.prototype.constructor = Child
		Child.superclass = Parent.prototype
	},
	extend: function(obj1,obj2) {
		for(var prop in obj2) {
			obj1[prop] = obj2[prop];
		}
	},
	hasClass: function (obj,cname) {
		return (obj.className ? obj.className.match(new RegExp('(\\s|^)'+cname+'(\\s|$)')) : false);
	},
	addClass: function (obj,cname) {
		if (!this.hasClass(obj,cname)) obj.className += " "+cname;
	},
	removeClass: function (obj,cname) {
		if (this.hasClass(obj,cname)) obj.className=obj.className.replace(new RegExp('(\\s|^)'+cname+'(\\s|$)'),' ');
	},
	getAllClasses: function(cname, prefix, skip) {
		if(!skip) skip = '';
		if(!prefix) prefix = '';
		return cname ? cname.replace(new RegExp('(\\s|^)'+skip+'(\\s|$)'),' ').replace(/[\s]*([\S]+)+[\s]*/gi,prefix+"$1 ") : '';
	},
	getElementsBySelector: function(selector, scope) {
		// Attempt to fail gracefully in lesser browsers
		if (!document.getElementsByTagName) {
			return new Array();
		}
		// Split selector in to tokens
		var tokens = selector.split(' ');
		var currentContext = new Array(scope || document.body);
		for (var i = 0; i < tokens.length; i++) {
			token = tokens[i].replace(/^\s+/,'').replace(/\s+$/,'');
			if (token.indexOf('#') > -1) {
				// Token is an ID selector
				var bits = token.split('#');
				var tagName = bits[0];
				var id = bits[1];
				var element = document.getElementById(id);
				if (tagName && element.nodeName.toLowerCase() != tagName) {
					// tag with that ID not found, return false
					return new Array();
				}
				// Set currentContext to contain just this element
				currentContext = new Array(element);
				continue; // Skip to next token
			}
			if (token.indexOf('.') > -1) {
				// Token contains a class selector
				var bits = token.split('.');
				var tagName = bits[0];
				var className = bits[1];
				if (!tagName) {
					tagName = '*';
				}
				// Get elements matching tag, filter them for class selector
				var found = new Array;
				var foundCount = 0;
				for (var h = 0; h < currentContext.length; h++) {
					var elements;
					if (tagName == '*') {
						elements = currentContext[h].getElementsByTagName('*');
					} else {
						elements = currentContext[h].getElementsByTagName(tagName);
					}
					for (var j = 0; j < elements.length; j++) {
						found[foundCount++] = elements[j];
					}
				}
				currentContext = new Array;
				var currentContextIndex = 0;
				for (var k = 0; k < found.length; k++) {
					if (found[k].className && found[k].className.match(new RegExp('(\\s|^)'+className+'(\\s|$)'))) {
						currentContext[currentContextIndex++] = found[k];
					}
				}
				continue; // Skip to next token
			}
			// Code to deal with attribute selectors
			if (token.match(/^(\w*)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/)) {
				var tagName = RegExp.$1;
				var attrName = RegExp.$2;
				var attrOperator = RegExp.$3;
				var attrValue = RegExp.$4;
				if (!tagName) {
					tagName = '*';
				}
				// Grab all of the tagName elements within current context
				var found = new Array;
				var foundCount = 0;
				for (var h = 0; h < currentContext.length; h++) {
					var elements;
					if (tagName == '*') {
						elements = currentContext[h].getElementsByTagName('*');
					} else {
						elements = currentContext[h].getElementsByTagName(tagName);
					}
					for (var j = 0; j < elements.length; j++) {
						found[foundCount++] = elements[j];
					}
				}
				currentContext = new Array;
				var currentContextIndex = 0;
				var checkFunction; // This function will be used to filter the elements
				if(attrName.toLowerCase() == 'for' && jcf.lib.browser.msie && jcf.lib.browser.version < 8) {
					attrName = 'htmlFor';
				}
				switch (attrOperator) {
					case '=': // Equality
						checkFunction = function(e) { return (e.getAttribute(attrName) == attrValue); };
						break;
					case '~': // Match one of space seperated words
						checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('(\\s|^)'+attrValue+'(\\s|$)'))); };
						break;
					case '|': // Match start with value followed by optional hyphen
						checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('^'+attrValue+'-?'))); };
						break;
					case '^': // Match starts with value
						checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) == 0); };
						break;
					case '$': // Match ends with value
						checkFunction = function(e) { return (e.getAttribute(attrName).lastIndexOf(attrValue) == e.getAttribute(attrName).length - attrValue.length); };
						break;
					case '*': // Match ends with value
						checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) > -1); };
						break;
					default :
						// Just test for existence of attribute
						checkFunction = function(e) { return e.getAttribute(attrName); };
				}
				currentContext = new Array;
				var currentContextIndex = 0;
				for (var k = 0; k < found.length; k++) {
					if (checkFunction(found[k])) {
						currentContext[currentContextIndex++] = found[k];
					}
				}
				continue; // Skip to next token
			}
			// If we get here, token is JUST an element (not a class or ID selector)
			tagName = token;
			var found = new Array;
			var foundCount = 0;
			for (var h = 0; h < currentContext.length; h++) {
				var elements = currentContext[h].getElementsByTagName(tagName);
				for (var j = 0; j < elements.length; j++) {
					found[foundCount++] = elements[j];
				}
			}
			currentContext = found;
		}
		return currentContext;
	},
	domReady: function (handler){
		var called = false
		function ready() {
			if (called) return;
			called = true;
			handler();
		}
		if (document.addEventListener) {
			document.addEventListener( "DOMContentLoaded", ready, false )
		} else if (document.attachEvent) {
			if (document.documentElement.doScroll && window == window.top) {
				function tryScroll(){
					if (called) return
					if (!document.body) return
					try {
						document.documentElement.doScroll("left")
						ready()
					} catch(e) {
						setTimeout(tryScroll, 0)
					}
				}
				tryScroll()
			}
			document.attachEvent("onreadystatechange", function(){
				if ( document.readyState === "complete" ) {
					ready()
				}
			})
		}
		if (window.addEventListener) window.addEventListener('load', ready, false)
		else if (window.attachEvent) window.attachEvent('onload', ready)
	},
	event: (function(){
		var guid = 0;
		function fixEvent(event) {
			event = event || window.event;
			if ( event.isFixed ) {
				return event;
			}
			event.isFixed = true;
			event.preventDefault = event.preventDefault || function(){this.returnValue = false}
			event.stopPropagation = event.stopPropagaton || function(){this.cancelBubble = true}
			if(!event.target) {
				event.target = event.srcElement;
			}
			if(!event.relatedTarget && event.fromElement) {
				event.relatedTarget = event.fromElement == event.target ? event.toElement : event.fromElement;
			}
			if(event.pageX == null && event.clientX != null ) {
				var html = document.documentElement, body = document.body;
				event.pageX = event.clientX + (html && html.scrollLeft || body && body.scrollLeft || 0) - (html.clientLeft || 0);
				event.pageY = event.clientY + (html && html.scrollTop || body && body.scrollTop || 0) - (html.clientTop || 0);
			}
			if(typeof event.layerX == 'undefined') {
				event.layerX = event.offsetX;
				event.layerY = event.offsetY;
			}
			if(!event.which && event.button ) {
				event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
			}
			return event;
		}

		function commonHandle(event) {
			if(!this.events) return;
			event = fixEvent(event)
			var handlers = this.events[event.type]
			for(var g in handlers ) {
				var handler = handlers[g]
				var ret = handler.call(this, event)
				if ( ret === false ) {
					event.preventDefault()
					event.stopPropagation()
				}
			}
		}

		return {
			add: function(elem, type, handler) {
				if (elem.setInterval && (elem != window && !elem.frameElement)) {
					elem = window;
				}
				if (!handler.guid) {
					handler.guid = ++guid;
				}
				if (!elem.events) {
					elem.events = {}
					elem.handle = function(event) {
						if (typeof jcf.lib.event !== "undefined") {
							return commonHandle.call(elem, event)
						}
					}
				}

				if (!elem.events[type]) {
					elem.events[type] = {}
					if (elem.addEventListener) {
						elem.addEventListener(type, elem.handle, true);
					} else {
						if (elem.attachEvent) elem.attachEvent('on' + type, elem.handle);
					}
				}
				elem.events[type][handler.guid] = handler
			},

			remove: function(elem, type, handler) {
				var handlers = elem.events && elem.events[type];
				if (!handlers) return;
				delete handlers[handler.guid];
				for(var any in handlers) return;
				if (elem.removeEventListener) elem.removeEventListener(type, elem.handle, false)
				else if (elem.detachEvent) elem.detachEvent("on" + type, elem.handle)
				delete elem.events[type]

				for (var any in elem.events) return;
				try {
					delete elem.handle
					delete elem.events
				} catch(e) { // IE
					try {
						elem.removeAttribute("handle")
						elem.removeAttribute("events")
					} catch(e) {}
				}
			}
		}
	}())
}
