/*
 *
 * CUSTOM FORM ELEMENTS (CustomFormElements)
 *
 * @author 		Kjell Hählen
 * @version 	1.1
 * @copyright	Copyright (c) 2011, Kjell Hählen
 * 
 * The script of Ryan Fait (www.ryanfait.com) served as a base for CustomFormElements.
 *
 * To use:
 * 
 * 1.	Call the CustomFormElements.init(oConfig).
 * 		Do this when the document or window is loaded. The latter option (window) is preferred, particularly
 * 		when used in combination with dbutton (as I experienced ;) ).
 * 
 * 		The parameter oConfig can override the properties listed below (see oConfig and the inline documentation)
 * 
 * or:
 * 2.	Uncomment (and change if desired) the lines at the end of this file.
 * 
 * Version 1.1:
 * 	- fixed error in oConfig.sReplace (the key 'select' was not considered)
 *  - performance fix in replacement script
 *  - provided a way to prevent replacement of certain inputs (aIgnoreIds, aIgnoreClasses)
 */



var CustomFormElements = {
	oConfig: {
		sStyledClass				: '' /* 'styled' */,		// Elements with this class will be replaced by styled content.
																// Leave empty to replace all applicable elements. In that case, elements
																// that are replaced gain the following class name (sClassToAdd):
		sClassToAdd					: 'styled',					// If ALL form elements are to be styled, this is the class that will be
																// given to the replaced elements.
		bInlineStyle				: true,						// if true, background-position of the replaced elements is handled inline,
																// you do not need to target the css-classes like .pushed and .checked.
																// However, you DO need to provide the next size-variables...
		// size of sprite section
		iCheckboxHeight				: '25',						// Only necessary if bInlineStyle = true. Height of the replacing checkbox.
		iRadioHeight				: '25',						// Only necessary if bInlineStyle = true. Height of the replacing radio button.
		
		// css class names
		sClassDisabled				: 'disabled',				// class for disabled elements
		sClassHover					: 'hover',					// class for hovered select box (note that you can use the a:hover state for
																// radio buttons and checkboxes - for those you do not need the .hover-class)
		sClassPushed				: 'pushed',					// class for pushed elements (when mousebutton is down)
		sClassChecked				: 'checked',				// class for checked elements,
		sReplace					: 'select,checkbox,radio',	// which items to replace.
		aIgnoreIds					: [],						// items with these ids will NOT be replaced,
		aIgnoreClasses				: []						// items with these classes will NOT be replaced				
	},
	
	init: function(oConfig) {
		
		var sClassToAdd = CustomFormElements.oConfig.sStyledClass == '' ? '' : ' ' + CustomFormElements.oConfig.sClassToAdd;

		if (typeof oConfig == 'object')
		{
			for (var i in oConfig)
			{
				if (typeof(CustomFormElements.oConfig[i]) != 'undefined' )
				{
					CustomFormElements.oConfig[i] = oConfig[i];
				}
			}
		}
		
		
		var	bReplaceCheckbox	= (CustomFormElements.oConfig.sReplace.indexOf('checkbox')	!= -1),
			bReplaceRadio		= (CustomFormElements.oConfig.sReplace.indexOf('radio') 	!= -1),
			bReplaceSelect		= (CustomFormElements.oConfig.sReplace.indexOf('select') 	!= -1);
			
		var aInputs = document.getElementsByTagName("input"), aSpan = Array(), oTextnode, aOption, sActive;
		for(a = 0; a < aInputs.length; a++) {
			if(		(
					(aInputs[a].type == "checkbox" && bReplaceCheckbox)
					||
					(aInputs[a].type == "radio" && bReplaceRadio)
					)
					&&
					CustomFormElements.isReplaceable(aInputs[a])
			  )
			{
				aInputs[a].style.display = 'none';
				aSpan[a] = document.createElement("a");
				aSpan[a].href = 'javascript:void(0)';
				aSpan[a].onclick = 'return false;';
				aSpan[a].className = aInputs[a].type;

				aInputs[a].className += sClassToAdd;
				
				if(aInputs[a].checked == true) {
					CustomFormElements.setCheckedState(aSpan[a],true);
				}
				aInputs[a].parentNode.insertBefore(aSpan[a], aInputs[a]);
				aInputs[a].onchange = CustomFormElements.clear;
				if(!aInputs[a].getAttribute("disabled")) {
					aSpan[a].onmousedown = CustomFormElements.pushed;
					aSpan[a].onmouseup = CustomFormElements.check;
				} else {
					aSpan[a].className = aSpan[a].className += " " + CustomFormElements.oConfig.sClassDisabled;
				}
			}
		}
		if (bReplaceSelect) {
			aInputs = document.getElementsByTagName("select");
			for (a = 0; a < aInputs.length; a++) {
				if (CustomFormElements.isReplaceable(aInputs[a])) {
				
					aInputs[a].style.position = 'relative';
					aInputs[a].style.opacity = '0';
					aInputs[a].style.filter = 'alpha(opacity=0)';
					aInputs[a].style.zIndex = '5';
					
					aOption = aInputs[a].getElementsByTagName("option");
					
					if (aOption.length > 0 && typeof aOption[0].childNodes[0] != 'undefined') {
						sActive = aOption[0].childNodes[0].nodeValue;
						oTextnode = document.createTextNode(sActive);
						for (b = 0; b < aOption.length; b++) {
							if (aOption[b].selected == true) {
								oTextnode = document.createTextNode(aOption[b].childNodes[0].nodeValue);
							}
						}
					}
					else {
						oTextnode = document.createTextNode('&nbsp;');
					}
					
					aSpan[a] = document.createElement("span");
					aSpan[a].className = "select";
					
					var sUniqueName = CustomFormElements.getUniqueName(aInputs[a]);
					var sValue = oTextnode.nodeValue;
					
					aSpan[a].id = "select-" + sUniqueName;
					aSpan[a].innerHTML = '<span class="outer"><span class="value" id="value-' + sUniqueName + '">' + sValue + '</span><span class="inner"><!-- EMPTY --></span></span>';
					
					aInputs[a].parentNode.insertBefore(aSpan[a], aInputs[a]);
					
					// make sure you use the right doctype to make this work in IE (no quirks-mode!)
					var iWidth = aSpan[a].offsetWidth;
					if (iWidth) {
						aInputs[a].style.width = iWidth + 'px';
					}
					
					if (!aInputs[a].getAttribute("disabled")) {
						aInputs[a].onchange = CustomFormElements.choose;
						CustomFormElements.addHoverStates(aInputs[a]);
					}
					else {
						aInputs[a].previousSibling.className = aInputs[a].previousSibling.className += " " + CustomFormElements.oConfig.sClassDisabled;
					}
					
					aInputs[a].className += sClassToAdd;
					
					aInputs[a].refresh = function(){
						CustomFormElements.refresh(this);
					}
				}
			}
		}
		document.onmouseup = CustomFormElements.clear;
	},
	pushed: function() {
		CustomFormElements.setPushedState(this, true, 'push');
	},
	check: function() {
		var oElement = this.nextSibling;
		if(oElement.checked == true && oElement.type == "checkbox") {
			CustomFormElements.setCheckedState(oElement, false);
			CustomFormElements.simulateClick(oElement, false);
			//oElement.checked = false;
		} else {
			if(oElement.type == "checkbox") {
				CustomFormElements.setCheckedState(this, true);
			} else {
				CustomFormElements.setCheckedState(this, true);
				group = this.nextSibling.name;
				// added this to make it work with multiple forms
				var oForm = this.nextSibling.form ? this.nextSibling.form : document;
				aInputs = oForm.getElementsByTagName("input");
				for(a = 0; a < aInputs.length; a++) {
					if(aInputs[a].name == group && aInputs[a] != this.nextSibling) {
						CustomFormElements.setCheckedState(aInputs[a].previousSibling, false);
					}
				}
			}
			// lets try to simulate a click on the element
			CustomFormElements.simulateClick(oElement, true);
			//oElement.checked = true;
			
		}
	},
	clear: function() {
		var aInputs = document.getElementsByTagName("input");
		for(var b = 0; b < aInputs.length; b++) {
			if (CustomFormElements.hasClass(aInputs[b], CustomFormElements.oConfig.sClassToAdd))
			{
				CustomFormElements.setCheckedState(aInputs[b].previousSibling, aInputs[b].checked);
			}
		}
	},
	choose: function(oEvent, oElement) {
		var oParent = (typeof oElement != 'undefined') ? oElement : this;
		var aOption = oParent.getElementsByTagName("option");
		var bFound = false;
		var sUniqueName = CustomFormElements.getUniqueName(oParent);
		
		for(d = 0; d < aOption.length; d++) {
			if(aOption[d].selected == true) {
				
				CustomFormElements.showInSelectBox(oParent, aOption[d].innerHTML);
				bFound = true;
			}
		}
		if (!bFound)
		{
			CustomFormElements.showInSelectBox(oParent, aOption.length > 0 ? aOption[0].innerHTML : '');
		}
	},
	getTarget: function(oEvent)
	{
		oEvent = oEvent || window.event;
		return oEvent.target ? oEvent.target : oEvent.srcElement;		
	},
	hasClass: function(oElement, sClassName)
	{
		if (!sClassName) return true;
		var sFullClassName = ' ' + oElement.className + ' ';
		return (sFullClassName.indexOf(' ' + sClassName + ' ') > -1);
	},
	addHoverStates: function(oInput)
	{
		oInput.onmouseover = function(oEvent){
			var oTarget = CustomFormElements.getTarget(oEvent);
			if (oTarget.previousSibling)
			{
				CustomFormElements.getTarget(oEvent).previousSibling.className += ' ' + CustomFormElements.oConfig.sClassHover; 
			}
		};
		oInput.onmouseout = function(oEvent){
			var oTarget = CustomFormElements.getTarget(oEvent);
			if (oTarget.previousSibling)
			{
				var oMySpan = oTarget.previousSibling;
				oMySpan.className = oMySpan.className.replace(' ' + CustomFormElements.oConfig.sClassHover, '');
			}
		};
	},
	removeBackgroundPosition: function(oElement)
	{
		if (typeof oElement.style.removeProperty == 'function')
		{
			oElement.style.removeProperty('background-position');
		}
		else
		{
			// IE
			oElement.style.removeAttribute('backgroundPositionX');
			oElement.style.removeAttribute('backgroundPositionY');
		}
	},
	setCheckedState: function(oElement, bCheck)
	{
		// remove pushed state
		oElement.className = oElement.className.replace(' ' + CustomFormElements.oConfig.sClassPushed, '');
		CustomFormElements.setState(oElement, bCheck, 'check');
	},
	setPushedState: function(oElement, bPush)
	{
		CustomFormElements.setState(oElement, bPush, 'push');
	},
	setState: function(oElement, bNewValue, sStateName)
	{
		var iMultiplier, sClass;
		switch (sStateName)
		{
		case 'check':
			iMultiplier = 2;
			sClass = CustomFormElements.oConfig.sClassChecked;
			break;
		case 'push':
			iMultiplier = oElement.nextSibling.checked == true ? 3 : 1;
			sClass = CustomFormElements.oConfig.sClassPushed;
			break;
		default:
			return;
		}
		
		if (bNewValue)
		{
			if (CustomFormElements.oConfig.bInlineStyle)
			{
				var iHeight = oElement.type == "checkbox" ? CustomFormElements.oConfig.iCheckboxHeight : CustomFormElements.oConfig.iRadioHeight;
				oElement.style.backgroundPosition = "0 -" + iHeight*iMultiplier + "px";
			}
			
			if (!CustomFormElements.hasClass(oElement, sClass))
			{
				oElement.className += ' ' + sClass;
			}
		}
		else
		{
			if (CustomFormElements.oConfig.bInlineStyle)
			{
				CustomFormElements.removeBackgroundPosition(oElement);
			}
			oElement.className = oElement.className.replace(' ' + sClass, '');
		}
	},
	/* deprecated: not needed anymore */
	getUniqueName: function(oInputElement)
	{
		var sUniqueName = ((oInputElement.form && oInputElement.form.name) ? oInputElement.form.name + '-' : '') + oInputElement.name;
		return sUniqueName.replace(/[^a-z0-9\-\_]/gi, '');
	},
	simulateClick: function(oTargetElement, bNewValue)
	{
		var bHasChanged = oTargetElement.type == 'checkbox' ? true : !oTargetElement.checked;
		
		if (bHasChanged)
		{
			if (document.createEvent)
			{
				// Mozilla, etc.
				
				oTargetElement.checked = bNewValue;
				
				CustomFormElements.fireEvent(oTargetElement, 'change');
				CustomFormElements.fireEvent(oTargetElement, 'click');
				
			}
			else
			{
				// IE
				
				// first make the element visible (but outside of the screen), otherwise focus() will not work.
				oTargetElement.style.position = 'absolute';
				oTargetElement.style.top = '-1000px';
				oTargetElement.style.left = '-1000px';
				oTargetElement.style.display = 'block';
				
				// the onchange event will only be fired if the element has focus and is blurred afterwards
				oTargetElement.focus();
				oTargetElement.checked = bNewValue;
				oTargetElement.blur();
				
				// the onchange event will only occur after a click (this will also trigger the click event)
				CustomFormElements.fireEvent(oTargetElement, 'click');
				
				// hide the element again
				oTargetElement.style.display = 'none';
			}	
		}
		else
		{
			// No change, we are just clicking
			CustomFormElements.fireEvent(oTargetElement, 'click');
		}
		
		
	},
	fireEvent: function(oTargetElement, sEventType)
	{
		if (document.createEvent)
		{ 
			// dispatch for firefox + others 
			var oEvent = document.createEvent("HTMLEvents"); 
			oEvent.initEvent(sEventType, true, true); // event type,bubbling,cancelable 
			return !oTargetElement.dispatchEvent(oEvent); 
		}
		else 
		{ 
			// dispatch for IE 
			var oEvent = document.createEventObject();
			return oTargetElement.fireEvent('on'+sEventType, oEvent); 
		}
	},
	showInSelectBox: function(oSelectBox, sHtml)
	{
		var aSpan = oSelectBox.previousSibling.getElementsByTagName('span');
		for (e = 0; e < aSpan.length; e++)
		{
			if (aSpan[e].className == 'value')
			{
				aSpan[e].childNodes[0].nodeValue = sHtml;
			}
		}
	},
	isReplaceable: function(oDomNode)
	{
		// if oConfig.sStyledClass is given, check that the DOM-node has this class
		if (!CustomFormElements.hasClass(oDomNode, CustomFormElements.oConfig.sStyledClass)) return false;
		
		// check if the DOM-node has one of the ingore-classes
		for (i = 0; i < CustomFormElements.oConfig.aIgnoreClasses.length; i++)
		{
			if (CustomFormElements.hasClass(oDomNode, CustomFormElements.oConfig.aIgnoreClasses[i])) return false;
		}
		
		sDomNodeId = typeof oDomNode.id == 'undefined' ? null : oDomNode.id;
		
		if (sDomNodeId)
		{
			// check if the DOM-node has one of the ingore-id's
			for (i = 0; i < CustomFormElements.oConfig.aIgnoreIds.length; i++)
			{
				if (sDomNodeId == CustomFormElements.oConfig.aIgnoreIds[i]) return false;
			}
		}
		
		return true;
			
	},
	/**
	 * refresh() is one of the few PUBLIC methods of CustomFormElements. If the options of a <select>
	 * have changed, you need to refresh it (to make sure the fake selectbox value changes accordingly).
	 * You can either do this by calling CustomFormElements.refresh(oSelectElement) or by calling
	 * refresh() of the element itself: oSelectElement.refresh()
	 */
	refresh: function(oSelect)
	{
		CustomFormElements.choose(null, oSelect);
	}
};


/*
var fnOldOnload = window.onload;
window.onload = function()
{
	if (typeof fnOldOnload == 'function') fnOldOnload();
	CustomFormElements.init();
};
*/
