/**
 * ADS Library from Advanced DOM Scripting
 * http://advanceddomscripting.com
 *
 * @projectDescription ADS library from the book "AdvancED DOM Scripting" http://advanceddomscripting.com/
 * @author Jeffrey Sambells jeff@advanceddomscripting.com
 * @copyright Jeffrey Sambells 2007 unless otherwise noted
 * @version $Id: ADS-final-verbose.js 183 2007-07-17 20:23:30Z jsambells $
 * @see http://advanceddomscripting.com/source/documentation
 * @namespace ADS
 */

(function(){
 
	//The ADS namespace
	if(!window.ADS) {window['ADS'] = { } }

function bindFunction(obj, func) {
		return function() {
			func.apply(obj, arguments);
		};
	};
window['ADS']['bindFunction'] = bindFunction;

function getBrowserWindowSize() {
		var de = document.documentElement;
		return {
			'width':(
				window.innerWidth
				|| (de && de.clientWidth)
				|| document.body.clientWidth ),
			'height':(
				window.innerHeight
				|| (de && de.clientHeight)
				|| document.body.clientHeight )
			}
	};
window['ADS']['getBrowserWindowSize'] = getBrowserWindowSize;

window['ADS']['node'] = {
	ELEMENT_NODE				:1,
	ATTRIBUTE_NODE				:2,
	TEXT_NODE					:3,
	CDATA_SECTION_NODE			:4,
	ENTITY_REFERENCE_NODE		:5,
	ENTITY_NODE					:6,
	PROCESSING_INSTRUCTION_NODE	:7,
	COMMENT_NODE				:8,
	DOCUMENT_NODE				:9,
	DOCUMENT_TYPE_NODE			:10,
	DOCUMENT_FRAGMENT_NODE		:11,
	NOTATION_NODE				:12
}

function stopPropagation(eventObject) {
	eventObject = eventObject || getEventObject(eventObject);
	if(eventObject.stopPropagation) {
		eventObject.stopPropagation();
	} else {
		eventObject.cancelBubble = true;
	}
}
window['ADS']['stopProgagation'] = stopPropagation;

function preventDefault(eventObject) {
	eventObject = eventObject || getEventObject(eventObject);
	if (eventObject.preventDefault) {
		eventObject.preventDefault();
	} else {
		eventObject.returnValue = false;
	}
}
window['ADS']['preventDefault'] = preventDefault;

/**
 * Convert hyphenated word-word strings to camel case wordWord strings.
 */
function camelize(s) {
    return s.replace(/-(\w)/g, function (strMatch, p1){
        return p1.toUpperCase();
    });
}
window['ADS']['camelize'] = camelize;

/**
 * Convert camel case wordWord strings to hyphenated word-word strings.
 */
function uncamelize(s, sep) {
    sep = sep || '-';
    return s.replace(/([a-z])([A-Z])/g, function (strMatch, p1, p2){
        return p1 + sep + p2.toLowerCase();
    });
}
window['ADS']['camelize'] = camelize;

function addLoadEvent(loadEvent, waitForImages) {
	if(!isCompatible ()) return false;
	
	//If the wait flag is true, use the regular add event method
	if(waitForImages) {
		return addEvent(window,'load',loadEvent);
	}
	//Otherwise use a number of different methods
	
	//Wrap the loadEvent method to assign the correct content for the this keyword and ensure that the event
	//doesn't execute twice
	var init = function () {
		//Return if this function has already been called
		if(arguments.callee.done) return;
		
		//Mark this function so you can verify it was already run
		arguments.callee.done = true;
		
		//Run the load event in the context of the document
		loadEvent.apply(document.arguments);
	};
	//Register the event using the DOMContentLoaded event
	if (document.addEventListener) {
		document.addEventListener("DOMContentLoaded", init, false);
	}
	
	//For Safari, use a setInterval() to see if the document has loaded
	if(/WebKit/i.test(navigator.userAgent)) {
		var _timer = setInterval(function() {
			if (/loaded|complete/.test(document.readystate)) {
				clearInterval(_timer);
				init();
			}
		}, 10);
	}
	
	//For Internet Explorer (using conditional comments)
	//Attach a script that is deffered to the end of the load process and then check to see if it has loaded
	/*@cc_on @*/
	/*@if (@_win32)
	document.write("<script id=__ie_onload defer src=javascript:void(0)><\/script>");
	var script = document.getElementById("__ie_onload");
	script.onreadystatechange = function() {
		if (this.readyState == "complete") {
			init();
		}
	};
	/*@end @*/
	return true;
}
window['ADS']['addLoadEvent'] = addLoadEvent;


function getEventObject(W3CEvent) {
	return W3CEvent || window.event;
}
window['ADS']['getEventObject'] = getEventObject;

//Access an event's target element
function getTarget(eventObject){
	eventObject = eventObject || getEventObject(eventObject);
	
	//check if target is W3C or MSIE
	var target = eventObject.target || eventObject.srcElement;
	
	//reassign the target to the parent if it is a text node as in Safari
	if (target.nodeType == ADS.node.TEXT_NODE) {
		target = node.parentNode;
	}
	return target;
}
window['ADS']['getTarget'] = getTarget;


//Determine which keys are pressed for key events
function getKeyPressed(eventObject) {
	eventObject = eventObject || getEventObject(eventObject);
	
	var code = eventObject.keyCode;
	var value = String.fromCharCode(code);
	return {'code':code,'value':value};
}
window['ADS']['getKeyPressed'] = getKeyPressed;

//changes the style of a single element by id
function setStyleById(element, styles) {
	//retrieve an object reference
	if(!(element = $(element))) return false;
	
	//Loop through the styles object and apply each property
	for (property in styles) {
		if(!styles.hasOwnProperty(property)) continue
		
		if(element.style.setProperty) {
			//DOM style method
			element.style.setProperty(
			uncamelize(property,'-'),styles[property],null);
		} else {
			//Alternative method
			element.style[camelize(property)] = styles[property];
		}
	}
	return true;
}
window['ADS']['setStyle'] = setStyleById;
window['ADS']['setStyleById'] = setStyleById;

//changes the style of multiple elements by class name
function setStylesByClassName(parent, tag, className, styles) {
	if(!(parent = $(parent))) return false;
	
	var elements = getElementsByClassName(className, tag, parent);
	for (var e=0; e < elements.length; e++) {
			setStyleById(elements[e], styles);
	}
	return true;
}
window['ADS']['setStylesByClassName']= setStylesByClassName;

//Changes the style of multiple elements based on tag name
function setStylesByTagName(tagname, styles, parent) {
	parent = $(parent) || document;
	
	var elements = parent.getElementsByTagName(tagname);
	for (var e=0; e < elements.length; e++) {
		setStyleById(elements[e], styles);
	}
}
window['ADS']['setStylesByTagName'] = setStylesByTagName;

//Retrieve the classes as an array
function getClassNames(element) {
	if (!(element = $(element))) return false;
	
	//Replace multiple spaces with one space and then split the classname spaces
	return element.className.replace(/\s+/,' ').split(' ');
};
window['ADS']['getClassNames'] = getClassNames;


//Check if a class exists on an element
function hasClassName(element,className) {
	if(!(element = $(element))) return false;
	
	var classes = getClassNames(element);
	for (var i=0; i < classes.length; i++) {
		//check if the className matches and return true if it does
		if (classes[i] === className) { return true; }
	}
	return false;
}
window['ADS']['hasClassName'] = hasClassName;


//Add a class to an element
function addClassName(element, className) {
	if(!(element = $(element))) return false;
	
	//Append the classmane to the end of the current className
	//If there is no className, don't include the space
	element.className += (element.className ? ' ' : '') + className;
	return true;
};
window['ADS']['addClassName'] = addClassName;


//Remove a class from an element
function removeClassName(element, className) {
	if(!(element = $(element))) return false;
	
	var classes = getClassNames(element);
	var length = classes.length;
	
	//Loop through the array deleting matching items 
	//You loop in reverse as you're deleting items from
	
	for (var i=length-1; i >= 0; i--) {
		if(classes[i] === className) { delete(classes[i]);}
	}
	element.className = classes.join(' ');
	return (length == classes.length ? false : true);
};
window['ADS']['removeClassName'] = removeClassName;


//Add a new style sheet
function addStyleSheet(url, media) {
	media = media || 'screen';
	var link = document.createElement('LINK');
	link.setAttribute('rel','stylesheet');
	link.setAttribute('type','text/css');
	link.setAttribute('href',url);
	link.setAttribute('media',media);
	document.getElementsByTagName('head')[0].appendChild(link);
}
window['ADS']['addStyleSheet'] = addStyleSheet;

//Remove a style sheet
function removeStyleSheet(url, media) {
	var styles = getStyleSheets(url, media);
	for (var i=0; i < styles.length; i++) {
		var node = styles[i].ownerNode || styles[i].owningElement;
		//disable the style sheet
		styles[i].disabled = true;
		//remove the node
		node.parentNode.removeChild(node);
	}
}
window['ADS']['removeStyleSheet'] = removeStyleSheet;

//Retrieve an array of style sheets by URL
function getStyleSheets(url, media) {
	var sheets = [];
	for (var i = 0; i < document.styleSheets.length; i ++) {
		if (url && document.styleSheets[i].href.indexOf(url) == -1) {
			continue;
		}
		if (media) {
			//normalize the media strings
			media = media.replace(/,\s*/,',');
			var sheetMedia;
			
			if (document.styleSheets[i].media.mediaText) {
				//DOM Method
				sheetMedia = document.styleSheets[i].media.mediaText.replace(/,\s*/,',');
				//Safari adds an extra comma and space
				sheetMedia = sheetMedia.replace(/,\s*$/,'');
			} else {
				//MSIE Method
				sheetMedia = document.styleSheets[i].media.replace(/,\s*/,',');
			}
			//Skip it if the media do not match
			if (media != sheetMedia) { continue; }
		}
		sheets.push(document.styleSheets[i]);
	}
	return sheets;
}
window['ADS']['getStyleSheets'] = getStyleSheets;

//Edit a css rule
function editCSSRule(selector,styles,url,media) {
	var styleSheets = (typeof url == 'array' ? url : getStyleSheets(url,media)) ;
	for (i = 0; i < styleSheets.length; i++) {
		//Retreive the list of rules
		//The DOM2 style method is styleSheets[i].cssRules
	 	//The MSIE method is styleSheets[i].rules
		var rules = styleSheets[i].cssRules || styleSheets[i].rules;
		if (!rules) { continue; }
		
		//convert to uppercase as MSIE defaults to uppercase tags.
		//Could cause conflicts if you are using case sensitive ids
		selector = selector.toUpperCase();
		
		for (var j=0; j < rules.length; j++) {
			//Check if it matches
			if(rules[j].selectorText.toUpperCase() == selector) {
				for (property in styles) {
					if(!styles.hasOwnProperty(property)) { continue; }
					//Set the new style property
					rules[i].style[camelize(property)] = styles[property];
				}
			}
		}
	}
}
window['ADS']['editCSSRule'] = editCSSRule;

//Add a CSS rule
function addCSSRule(selector, styles, index, url, media) {
	var declaration = '';
	//Build the declaration string from the style object
	for (property in styles) {
		if(!styles.hasOwnProperty(property)) { continue; }
			declaration += property + ':' + styles[property] + '; ';
	}
	var styleSheets = (typeof url == 'array' ? url : getStyleSheets(url,media));
	var newIndex;
	for(var i = 0; i < styleSheets.length; i ++) {
		//Add the rule
		if (styleSheets[i].insertRule) {
			//the DOM2 style method
			//index = length is the end of the list
			newIndex = ( index >= 0 ? index : styleSheets[i].cssRules.length);
			styleSheets[i].insertRule(selector + ' { ' + declaration + ' } ', newIndex);
		} else if (styleSheets[i].addRule) {
			//MSIE Method
			// index = -1 is the end of the list
			newIndex = (index >= 0 ? index : -1);
			styleSheets[i].addRule(selector, declaration,newIndex);
		}
	}
}
window['ADS']['addCSSRule'] = addCSSRule;


//retrieve the computed style of an element
function getStyle(element,property) {
	if(!(element = $(element)) || !property) return false;
	//Check for the value in the element's style property
	var value = element.style[camelize(property)];
	if (!value) {
		//retreive the computed style value
		if(document.defaultView && document.defaultView.getComputedStyle) {
			//the DOM method
			var css = document.defaultView.getComputedStyle(element,null);
			value = css ? css.getPropertyValue(property) : null;
		} else if(element.currentStyle) {
			//MSIE method
			value = element.currentStyle[camelize(property)];
		}
	}
	//return an empty string rather than auto so that you don't have to check for auto values
	return value == 'auto' ? '' : value;
}
window['ADS']['getStyle'] = getStyle;
window['ADS']['getStyleById'] = getStyle;

function isCompatible(other) {
	//Use capability detection to check whether browser is compatible
	if(other===false 
	   || !Array.prototype.push
	   || !Object.hasOwnProperty
	   || !document.createElement
	   || !document.getElementsByTagName
	   ) {
		return false;
	}
	return true;
};
window['ADS']['isCompatible'] = isCompatible;

function $(){
	var elements = new Array();
	
	//find all the elements supplied as arguments
	for (var i=0; i <arguments.length; i++) {
		var element = arguments[i];
		
		//If the argument is a string, assume it's an id
		if (typeof element == 'string') {
			element = document.getElementById(element);
		}
		
		//If only one argument was supplied, return the element immediately
		if (arguments.length == 1) {
			return element;
		}
		
		//Otherwise add it to the array
		elements.push(element);
	}
	//Return the array of multiple requested elements
	return elements;
};
window['ADS']['$'] = $;

function addEvent(node, type, listener) {
		//check compatibility using earlier method to ensure graceful degradation
		if(!isCompatible()) { return false;}
		
		if(!(node = $(node))) { return false;}
		
		if (node.addEventListener) {
			//W3C method
			node.addEventListener(type, listener, false);
			return true;
		} else if(node.attachEvent) {
			//MSIE method
			node['e'+type+listener] = listener;
			node[type+listener] = function() {
				node['e'+type+listener](window.event);
			}
			node.attachEvent('on'+type, node[type+listener]);
			return true;
		}
		//didn't have either, so return false
		return false;
};
window['ADS']['addEvent'] = addEvent;

function removeEvent(node, type, listener) {
	if(!(node = $(node))) { return false;}
	
	if (node.removeEventListener) {
		//W3C method
		node.removeEventListener(type, listener, false);
		return true;
	} else if (node.detachEvent) {
		//MSIE method
		node.detachEvent ('on'+type, node[type+listener]);
		node[type+listener] = null;
		return true;
	}
	//didn't have either so return false
	return false;
};
window['ADS']['removeEvent'] = removeEvent;

function getElementsByClassName(className, tag, parent){
	parent = parent || document;
	if(!(parent = $(parent))) { return false; }
	
	//Locate all the matching tags
	var allTags = (tag == "*" && parent.all) ? parent.all : parent.getElementsByTagName(tag);
	var matchingElements = new Array();
	
	//Create a regular expression to determine if the className is correct
	className = className.replace(/\-/g, "\\-");
	var regex = new RegExp("(^|\\s)" + className + "(\\s|$)");
	
	var element;
	// Check each element
	for (var i=0; i < allTags.length; i++) {
		element = allTags[i];
		if (regex.test(element.className)) {
			matchingElements.push(element);
		}
	}
	//Return any matching elements;
	return matchingElements;
};
window['ADS']['getElementsByClassName'] = getElementsByClassName;

function toggleDisplay(node,value) {
	if (!(node = $(node))) { return false; }
	
	if ( node.style.display != 'none') {
			node.style.display = 'none';
	}
	else {
		node.style.display = value || '';
	}
	return true;		  
};
window['ADS']['toggleDisplay'] = toggleDisplay;

function insertAfter(node, referenceNode) {
	if(!(node = $(node))) { return false; }
	if (!(referenceNode = $(referenceNode))) { return false; }
	
	return referenceNoce.parentNode.insertBefore(node, referenceNode.nextSibling);
};
window['ADS']['insertAfter'] = insertAfter;

function removeChildren(parent) {
	if (!(parent = $(parent))) { return false; }
	
	//While there is a child remove it
	while (parent.firstChild) {
		parent.firstChild.parentNode.removeChild(parent.firstChild);
	}
	
	//Return the parent again so you can chain the methods
	return parent;
};
window['ADS']['removeChildren'] = removeChildren;

function prependChild(parent, newChild) {
	if (!(parent = $(parent))) { return false ; }
	if (!(newChild = $(newChild))) { return false; }
	
	if (parent.firstChild) {
		//There is already a child so insert before the first one
		parent.insertBefore(newChild, parent.firstChild);
	}
	else {
		//no children, so just append
		parent.appendChild(newChild);
	}
	//Return the parent so you can chain the methods
	return parent;
};
window['ADS']['prependChild'] = prependChild;

})();
