/**
 * Basic Library from Advanced DOM Scripting
 * http://advanceddomscripting.com
 *
 * This library is not compressed and is not recommended for production use in
 * its current state. The code is excessively verbose and heavily commented
 * as it was written as a teaching tool. It is recommended you edit the code for
 * better performance and smaller file size.

 * @projectDescription Basic 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: Basic-final-verbose.js 183 2007-07-17 20:23:30Z jsambells $
 * @see http://advanceddomscripting.com/source/documentation
 * @namespace Basic
 */

/**
* Missing getElementById.
* Example of creating a DOM replacement this isn't necessary because the
* library assume browsers that support it.
*/
if(document.all && !document.getElementById) {
    document.getElementById = function(id) {
         return document.all[id];
    }
}


/**
* 重复一个字符串
* 使用原型的方式扩充核心对象
**/
if (!String.repeat) {
    String.prototype.repeat = function(l){
        return new Array(l+1).join(this);
    }
}

/** 
* 清除字符串首尾空白符
* 使用原型的方式扩充核心对象
**/
if (!String.trim) {
    String.prototype.trim = function() {
        return this.replace(/^\s+|\s+$/g,'');
    }
}

/**
 * Basic Namespace
 * This anonymous function acts as a namespace wrapper for the rest
 * of the methods. Methods are then assigned to the window object
 * using: window['UED']['methodName'] = methodReference;
 * @alias Basic
 */
(function(){
	
	/**
	 * 定义命名空间
	 */
	if(!window['UED']) {
		window['UED'] = {};
	}
	
	/**
	 * 注册命名空间
	 */
	function registerNamespace(){
		var objectTmp = null, stringSplit, sringNamespace;
		
		for(var i = 0; i < arguments.length; i++){
			sringNamespace = '';
			//将参数字符串分割开来
			stringSplit = arguments[i].split('.');
			for(var j = 0; j < stringSplit.length; j++){
				if(j > 0){
					sringNamespace += '.';
				}
				sringNamespace += stringSplit[j];
				
				eval('if(typeof ' + sringNamespace + '== "undefined"){' + sringNamespace + ' = {};}');
			}
		}
	}
	window['UED']['namespace'] = registerNamespace;
	
	
	/********************************
	* Chapter 1
	*********************************/
	
	/**
	 * 确定浏览器是否可以使用库中所有方法
	 */
	function isCompatible(other) {
		// 使用能力检测检查必要条件
		if( other===false 
			|| !Array.prototype.push
			|| !Object.hasOwnProperty
			|| !document.createElement
			|| !document.getElementsByTagName
			) {
			alert('TR- if you see this message isCompatible is failing incorrectly.');
			return false;
		}
		return true;
	}
	window['UED']['isCompatible'] = isCompatible;
	
	/**
	 * 根据DOM对象id获取单个对象或对象数组
	 */
	function $() {
		var elements = new Array();
		
		// 查找作为参数提供的所有元素
		for (var i = 0; i < arguments.length; i++) {
			var element = arguments[i];
			
			// 如果参数为一个字符串则假定其为一个ID
			if (typeof element == 'string') {
				element = document.getElementById(element);
			}
			
			//如果只有一个参数，则返回这个元素，否则将该元素压入elements数组
			if (arguments.length == 1) {
				return element;
			}
			
			// 否则压入数组
			elements.push(element);
		}
		
		// Return the array of multiple requested elements
		return elements;
	};
	window['UED']['$'] = $;
	
	
	/**
	 * 向指定节点node为事件type绑定执行函数listener
	 */
	function addEvent( node, type, listener ) {
		// Check compatibility using the 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['UED']['addEvent'] = addEvent;
	
	/**
	 * 移除指定节点node为事件type绑定的执行函数listener
	 */
	function removeEvent(node, type, listener ) {
		if(!(node = $(node))) return false;
		if (node.removeEventListener) {
			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['UED']['removeEvent'] = removeEvent;
	
	/**
	 * 获取指定节点parent下指定标记tag且class属性为className的所有节点
	 */
	function getElementsByClassName(className, tag, parent){
		parent = parent || document;
		if(!(parent = $(parent))) return false;
		
	   //查找所有匹配的标记
		var allTags = (tag == "*" && parent.all) ? parent.all : parent.getElementsByTagName(tag);
		var matchingElements = new Array();
		
		//创建正则表达式来判断className是否正确
		className = className.replace(/\-/g, "\\-");
		var regex = new RegExp("(^|\\s)" + className + "(\\s|$)");
		
		var element;
		//检查每一个元素
		for(var i=0; i<allTags.length; i++){
			element = allTags[i];
			if(regex.test(element.className)){
				matchingElements.push(element);
			}
		}
		
		//返回匹配的元素数组
		return matchingElements;
	};
	window['UED']['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['UED']['toggleDisplay'] = toggleDisplay;
	
	/**
	 * 向指定节点referenceNode之后插入DOM节点node
	 */
	function insertAfter(node, referenceNode) {
		if(!(node = $(node))) return false;
		if(!(referenceNode = $(referenceNode))) return false;
		
		return referenceNode.parentNode.insertBefore(node, referenceNode.nextSibling);
	};
	window['UED']['insertAfter'] = insertAfter;
	
	/**
	 * 删除指定节点下的所有子节点
	 */
	function removeChildren(parent) {
		if(!(parent = $(parent))) return false;
		
		//存在子节点时删除子节点
		while (parent.firstChild) {
			 parent.firstChild.parentNode.removeChild(parent.firstChild);
		}
		// Return the parent again so you can stack the methods
		return parent;
	};
	window['UED']['removeChildren'] = removeChildren;
	
	/**
	 * 向指定节点parent第一个子节点之前添加新节点newChild
	 */
	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 again so you can stack the methods
		return parent;
	} 
	window['UED']['prependChild'] = prependChild;
	
	/*
	*	向指定节点parent添加指定标记tagName的新节点,并添加类样式css
	*/
	function addChild(parent,tagName,css){
		var newElement;
		if(!(parent = $(parent))) return false;
		
		newElement = document.createElement(tagName);
		if(css){
			addClassName(newElement,css);
		}
		parent.appendChild(newElement);
		
		return newElement;
	}
	window['UED']['addChild'] = addChild;
	
	/**
	* 判断一个对象是否为数组
	*/
	function isArray(object) {  
		return Object.prototype.toString.call(object) === '[object Array]';   
	} 
	window['UED']['isArray'] = isArray;
	
	
	/**
	* 判断是否为对象
	*/
	
	function isObject(object){
		return (typeof object == 'object')&& object.constructor == Object;
	}
	window['UED']['isObject'] = isObject;
	
	/********************************
	* Chapter 2
	*********************************/
	
	/**
	 * 改变函数的执行环境(在指定对象上执行指定函数)
	 */
	function bindFunction(obj, func) {
		return function() {
			func.apply(obj,arguments);    
		};
	};
	window['UED']['bindFunction'] = bindFunction;
	
	
	/**
	 * 获取浏览器可见区域尺寸
	 */
	function getBrowserWindowSize() {
		var de = document.documentElement;
		
		// window.innerWidth for most browsers
		// document.documentElement.clientWidth for MSIE in strict mode
		// document.body.clientWidth for MSIE in quirks mode
		
		return {
			'width':(
				window.innerWidth 
				|| (de && de.clientWidth ) 
				|| document.body.clientWidth),
			'height':(
				window.innerHeight 
				|| (de && de.clientHeight ) 
				|| document.body.clientHeight)
		}
	};
	window['UED']['getBrowserWindowSize'] = getBrowserWindowSize;
	
	function getElementPositionAndSizeInDocument(node){
		if(!(node = $(node))){
			return false;
		}

		return {
			'positionX'	:	node.offsetLeft,
			'positionY'	:	node.offsetTop,
			'width'		:	node.offsetWidth,
			'height'	:	node.offsetHeight
		}
	}
	window['UED']['getElementPositionAndSizeInDocument'] = getElementPositionAndSizeInDocument;
	
	/********************************
	* Chapter 3
	*********************************/
	
	/**
	 * Constants for note type comparison
	 */
	window['UED']['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 walkElementsLinear(func,node) {
		var root = node || window.document;
		var nodes = root.getElementsByTagName("*");
		for(var i = 0 ; i < nodes.length ; i++) {
			func.call(nodes[i]);
		}
	};
	window['UED']['walkElementsLinear'] = walkElementsLinear;
	
	/**
	 * 递归遍历DOM树.
	 */
	function walkTheDOMRecursive(func,node,depth,returnedFromParent) {
		var root = node || window.document;
		returnedFromParent = func.call(root,depth++,returnedFromParent);
		node = root.firstChild;
		while(node) {
			walkTheDOMRecursive(func,node,depth,returnedFromParent);
			node = node.nextSibling;
		}
	};
	window['UED']['walkTheDOMRecursive'] = walkTheDOMRecursive;
	
	/**
	 * 递归遍历DOM树并查找每个节点的属性.
	 */
	function walkTheDOMWithAttributes(node,func,depth,returnedFromParent) {
		var root = node || window.document;
		returnedFromParent = func(root,depth++,returnedFromParent);
		if (root.attributes) {
			for(var i=0; i < root.attributes.length; i++) {
				walkTheDOMWithAttributes(root.attributes[i],func,depth-1,returnedFromParent);
			}
		}
		if(root.nodeType != Basic.node.ATTRIBUTE_NODE) {
			node = root.firstChild;
			while(node) {
				walkTheDOMWithAttributes(node,func,depth,returnedFromParent);
				node = node.nextSibling;
			}
		}
	};
	window['UED']['walkTheDOMWithAttributes'] = walkTheDOMWithAttributes;
	
	/**
	 * Walk the DOM recursively using a callback function
	 */
	function walkTheDOM(node, func) {
		func(node);
		node = node.firstChild;
		while (node) {
			 walkTheDOM(node, func);
			 node = node.nextSibling;
		}
	}
	window['UED']['walkTheDOM'] = walkTheDOM;
	
	/**
	 * 将连字符格式(word-word)的字符串转换为驼峰格式(wordWord)字符串.
	 */
	function camelize(s) {
		return s.replace(/-(\w)/g, function (strMatch, p1){
			return p1.toUpperCase();
		});
	}
	window['UED']['camelize'] = camelize;
	
	/********************************
	* Chapter 4
	*********************************/
	
	/**
	 * 将驼峰格式(wordWord)的字符串转换为连字符格式(word-word)字符串.
	 */
	function uncamelize(s, sep) {
		sep = sep || '-';
		return s.replace(/([a-z])([A-Z])/g, function (strMatch, p1, p2){
			return p1 + sep + p2.toLowerCase();
		});
	}
	window['UED']['camelize'] = camelize;
	
	
	/**
	 * 页面加载完成(除图像载入完成以外).
	 */
	function addLoadEvent(loadEvent,waitForImages) {
		if(!isCompatible()) return false;
		
		// 如果等待标记为true则使用常规的添加事件的方法
		if(waitForImages) {
			return addEvent(window, 'load', loadEvent);
		}
		
		// 否则使用一些不同的方式包装的loadEvent()方法
		
		// 以便为this关键字指定正确的内容,同时确保事件不会被执行两次
		var init = function() {
	
			if (arguments.callee.done) return;
			// 如果这个函数已经被调用过了则返回
	
			// 标记这个函数以便检验它是否被执行
			arguments.callee.done = true;
	
			// 在document的环境中运行载入事件
			loadEvent.apply(document,arguments);
		};
		
		// 为DOMContentLoaded 事件注册事件侦听器
		if (document.addEventListener) {
			document.addEventListener("DOMContentLoaded", init, false);
		}
		
		// 对于 Safari, 使用setInterval()函数检测 
		if (/WebKit/i.test(navigator.userAgent)) {
			var _timer = setInterval(function() {
				if (/loaded|complete/.test(document.readyState)) {
					clearInterval(_timer);
					init();
				}
			},10);
		}
		// 对于IE, (使用条件注释) 
		// 附加一个在载入过程最后执行的脚本，并检测该脚本是否载入完成
		/*@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['UED']['addLoadEvent'] = addLoadEvent;
	
	/**
	 * 停止事件传播
	 */
	function stopPropagation(eventObject) {
		eventObject = eventObject || getEventObject(eventObject);
		if(eventObject.stopPropagation) {
			eventObject.stopPropagation();
		} else {
			eventObject.cancelBubble = true;
		}
	}
	window['UED']['stopPropagation'] = stopPropagation;
	
	/**
	 * 取消默认动作.
	 */
	function preventDefault(eventObject) {
		eventObject = eventObject || getEventObject(eventObject);
		if(eventObject.preventDefault) {
			eventObject.preventDefault();
		} else {
			eventObject.returnValue = false;
		}
	}
	window['UED']['preventDefault'] = preventDefault;
	
	/**
	 * 获取适当的事件对象
	 */
	function getEventObject(W3CEvent) {
		return W3CEvent || window.event;
	}
	window['UED']['getEventObject'] = getEventObject;
	
	/**
	 * 访问事件的目标元素.
	 */
	function getTarget(eventObject) {
		eventObject = eventObject || getEventObject(eventObject);
		// 检查target是W3C还是MSIE
		var target = eventObject.target ? eventObject.target : eventObject.srcElement;
		// Reassign the target to the parent
		// if it is a text node like in Safari
		if(target.nodeType == UED.node.TEXT_NODE) {
			target = node.parentNode;
		}
		return target;
	
	}
	window['UED']['getTarget'] = getTarget;
	
	/**
	 * 确定哪个鼠标按钮被单击
	 */
	function getMouseButton(eventObject) {
		eventObject = eventObject || getEventObject(eventObject);
		// 使用适当的属性初始化一个对象变量
		var buttons = {
			'left':false,
			'middle':false,
			'right':false
		};
		// 检查eventObject对象的方法toString()的值
		// W3C Dom 对象有一个toString()并且此时该方法的返回值应该是MouseEvent
		if(eventObject.toString && eventObject.toString().indexOf('MouseEvent') != -1) {
			// W3C 方法
			switch(eventObject.button) {
				case 0: buttons.left = true; break;
				case 1: buttons.middle = true; break;
				case 2: buttons.right = true; break;
				default: break;
			}
		} else if(eventObject.button) {
			// MSIE 方法
			switch(eventObject.button) {
				case 1: buttons.left = true; break;
				case 2: buttons.right = true; break;
				case 3:
					buttons.left = true;
					buttons.right = true;
				break;
				case 4: buttons.middle = true; break;
				case 5:
					buttons.left = true;
					buttons.middle = true;
				break;
				case 6:
					buttons.middle = true;
					buttons.right = true;
				break;
				case 7:
					buttons.left = true;
					buttons.middle = true;
					buttons.right = true;
				break;
				default: break;
			}
		} else {
			return false;
		}
		return buttons;
	
	}
	window['UED']['getMouseButton'] = getMouseButton;
	
	/**
	 * 获取鼠标指针在文档中的位置. 
	 */
	function getPointerPositionInDocument(eventObject) {
		eventObject = eventObject || getEventObject(eventObject);
		var x = eventObject.pageX || (eventObject.clientX +
			(document.documentElement.scrollLeft || document.body.scrollLeft));
		var y= eventObject.pageY || (eventObject.clientY +
			(document.documentElement.scrollTop || document.body.scrollTop));
		//x and y now contain the coordinates of the mouse relative to the document origin
		return {'x':x,'y':y};
	}
	window['UED']['getPointerPositionInDocument'] = getPointerPositionInDocument;
		
	/**
	 * 获取按键代码及ASCII码值
	 */
	function getKeyPressed(eventObject) {
		eventObject = eventObject || getEventObject(eventObject);
		var code = eventObject.keyCode;
		var value = String.fromCharCode(code);
		return {'code':code,'value':value};
	}
	window['UED']['getKeyPressed'] = getKeyPressed;
	
	
	/********************************
	* Chapter 5
	*********************************/
	
	/**
	 * 通过对象的ID值引用对象并修改其CSS样式
	 */
	function setStyleById(element, styles) {
		// 取得对象的引用
		if(!(element = $(element))) return false;
		// 遍历styles对象并应用每个属性
		for (property in styles) {
			if(!styles.hasOwnProperty(property)) continue;
		
			if(element.style.setProperty) {
				//DOM2 样式方法
				element.style.setProperty(
				uncamelize(property,'-'),styles[property],null);
			} else {
				//备用方法
				element.style[camelize(property)] = styles[property];
			}
		}
		return true;
	}
	window['UED']['setStyle'] = setStyleById;
	window['UED']['setStyleById'] = setStyleById;
	
	/**
	 * 通过对象的class名称来修改多个元素的样式
	 */
	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['UED']['setStylesByClassName'] = setStylesByClassName;
	
	/**
	 * 通过对象的tagName来修改多个元素的样式
	 */
	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['UED']['setStylesByTagName'] = setStylesByTagName;
	
	/**
	 * 获得元素应用的类名数组
	 */
	function getClassNames(element) {
		if(!(element = $(element))) return false;
		// 用一个空格替换多个空格，然后基于空格来分割类名
		return element.className.replace(/\s+/,' ').split(' ');
	};
	window['UED']['getClassNames'] = getClassNames;
	
	/**
	 * 检查元素是否应用了指定的类
	 */
	function hasClassName(element, className) {
		if(!(element = $(element))) return false;
		var classes = getClassNames(element);
		for (var i = 0; i < classes.length; i++) {
			// 检查className是否匹配，如果是则返回true
			if (classes[i] === className) { return true; }
		}
		return false;
	};
	window['UED']['hasClassName'] = hasClassName;
	
	/**
	 * 为元素添加类
	 */
	function addClassName(element, className) {
		if(!(element = $(element))) return false;
		// 将要添加的类名添加到当前className的末尾，如果没有className，则不包含空格
		element.className += (element.className ? ' ' : '') + className;
		return true;
	};
	window['UED']['addClassName'] = addClassName;
	
	/**
	 * 从元素引用的类中删除指定类
	 */
	function removeClassName(element, className) {
		if(!(element = $(element))) return false;
		var classes = getClassNames(element);
		var length = classes.length
		//遍历数组删除匹配的项，因为从数组中删除项会使得数组变短，所以要反向循环
		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['UED']['removeClassName'] = removeClassName;
	
	/**
	* 给文档新添加一张样式表
	*/
	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['UED']['addStyleSheet'] = addStyleSheet;
	
	/** 
	 * 移除文档中指定的样式表
	 */
	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;
			// 禁用样式表
			styles[i].disabled = true;
			// 移除节点
			node.parentNode.removeChild(node);
		}
	}
	window['UED']['removeStyleSheet'] = removeStyleSheet;
	
	/**
	 * 通过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) {
				// 规范化的media字符串
				media = media.replace(/,\s*/,',');
				var sheetMedia;
					
				if(document.styleSheets[i].media.mediaText) {
					// DOM 方法
					sheetMedia = document.styleSheets[i].media.mediaText.replace(/,\s*/,',');
					// Safari 人添加额外的逗号和空格
					sheetMedia = sheetMedia.replace(/,\s*$/,'');
				} else {
					// MSIE
					sheetMedia = document.styleSheets[i].media.replace(/,\s*/,',');
				}
				// 如果media不匹配则跳过
				if (media != sheetMedia) { continue; }
			}
			sheets.push(document.styleSheets[i]);
		}
		return sheets;
	}
	window['UED']['getStyleSheets'] = getStyleSheets;
	
	/**
	 * 编辑一条样式规则
	 */
	function editCSSRule(selector,styles,url,media) {
		var styleSheets = (typeof url == 'array' ? url : getStyleSheets(url,media));
	
		for ( i = 0; i < styleSheets.length; i++ ) {
	
			// 取得规则列表
			// DOM2样式规范方法是styleSheets[i].cssRules
			// MSIE的方法是styleSheets[i].rules
			var rules = styleSheets[i].cssRules || styleSheets[i].rules;
			if (!rules) { continue; }
				   
			// 由于MSIE默认使用大写故转换为大写形式
			// 如果你使用的是区分大小写的ID，则可能会导致冲突
			selector = selector.toUpperCase();
			
			for(var j = 0; j < rules.length; j++) {
				// 检查是否匹配
				if(rules[j].selectorText.toUpperCase() == selector) {
					for (property in styles) {
						if(!styles.hasOwnProperty(property)) { continue; }
						// 设置新的样式属性
						rules[j].style[camelize(property)] = styles[property];
					}
				}
			}
		}
	}
	window['UED']['editCSSRule'] = editCSSRule;
	
	/**
	 * 添加一条新的CSS样式规则
	 */
	function addCSSRule(selector, styles, index, url, media) {
		var declaration = '';
	
		// 根据styles参数(样式对象)构建声明字符串
		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++) {
			// 添加样式规则       
			if(styleSheets[i].insertRule) {
				// DOM2 样式规范方法
				// index = length 是列表末尾
				newIndex = (index >= 0 ? index : styleSheets[i].cssRules.length);
				styleSheets[i].insertRule(selector + ' { ' + declaration + ' } ', 
					newIndex);
			} else if(styleSheets[i].addRule) {
				// Microsoft 的方法
				// index = -1 是列表的末尾
				newIndex = (index >= 0 ? index : -1);
				styleSheets[i].addRule(selector, declaration, newIndex);
			}
		}
	}
	window['UED']['addCSSRule'] = addCSSRule;
	
	/**
	 * 取得元素的计算样式
	 */
	function getStyle(element,property) {
		if(!(element = $(element)) || !property) return false;
		// 检查元素的style属性的值
		var value = element.style[camelize(property)];
		if (!value) {
			// 取得计算的样式值
			if (document.defaultView && document.defaultView.getComputedStyle) {
				// DOM方法
				var css = document.defaultView.getComputedStyle(element, null);
				value = css ? css.getPropertyValue(property) : null;
			} else if (element.currentStyle) {
				// MSIE方法
				value = element.currentStyle[camelize(property)];
			}
		}
		// 返加空字符串而不是auto，这样就不必检查auto值了
		return value == 'auto' ? '' : value;
	}
	window['UED']['getStyle'] = getStyle;
	window['UED']['getStyleById'] = getStyle;
	
	
	/********************************
	* Chapter 7: Case Study
	*********************************/
	
	// no code added in Chapter 6
	
	
	/********************************
	* Chapter 7
	*********************************/
	
	/*
	parseJSON(string,filter)
	这是一个在公共域方法 http://www.json.org/json.js 基础上进行了少量修改的版本。
	该方法解析JSON文本以生成一个对象或数组。它可能抛出SyntaxError exception异常。
	
	The optional filter parameter is a function which can filter and
	transform the results. It receives each of the keys and values, and
	its return value is used instead of the original value. If it
	returns what it received, then structure is not modified. If it
	returns undefined then the member is deleted.
	
	Example:
	
	// Parse the text. If a key contains the string 'date' then
	// convert the value to a date.
	
	myData = parseJSON(string,function (key, value) {
		return key.indexOf('date') >= 0 ? new Date(value) : value;
	});
	
	*/
	function parseJSON(s,filter) {
		var j;
	
		function walk(k, v) {
			var i;
			if (v && typeof v === 'object') {
				for (i in v) {
					if (v.hasOwnProperty(i)) {
						v[i] = walk(i, v[i]);
					}
				}
			}
			return filter(k, v);
		}
	
	
	// 解析通过三个阶段进行。
	// 第一阶段，通过正则表达式检测JSON文本，查找非JSON字符。其中，特别注意'()' 和'new'，因为它会导致变量的值发生改变。
	// 不过，为完全起见这里会拒绝所有不希望出现的字符。
	
	 if (/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/.
				test(s)) {
	
	// 第二阶段，使用eval函数将JSON文本编译为Javascript结构。
	// 其中的'{' 操作符具有语法上的二义性：即它可以定义一个语句块，也可以表示对象字面量。
	// 这里将JSON文本用括号括起来是为了消除这种二义性
	
			try {
				j = eval('(' + s + ')');
			} catch (e) {
				throw new SyntaxError("parseJSON");
			}
		} else {
			throw new SyntaxError("parseJSON");
		}
	
	// 在可选的第三个阶段，代码递归地遍历了新生成的结构而且将每个名/值对传递给一个过滤函数，以便进行可能的转换
	
		if (typeof filter === 'function') {
			j = walk('', j);
		}
		return j;
	};
		
			
	/**
	 * 设置XMLHttpRequest对象的各个部分 
	 */
	function getRequestObject(url,options) {
		
		// 初始化请求对象
		var req = false;
		if(window.XMLHttpRequest) {
			var req = new window.XMLHttpRequest();
		} else if (window.ActiveXObject) {
			var req = new window.ActiveXObject('Microsoft.XMLHTTP');
		}
		if(!req) return false;
		
		// 定义默认选项
		options = options || {};
		options.method = options.method || 'GET';
		options.send = options.send || null;
	
		// 为请求的每个阶段定义不同的侦听器
		req.onreadystatechange = function() {
			switch (req.readyState) {
				case 1:
					// 载入中
					if(options.loadListener) {
						options.loadListener.apply(req,arguments);
					}
					break;
				case 2:
					// 载入完成
					if(options.loadedListener) {
						options.loadedListener.apply(req,arguments);
					}
					break;
				case 3:
					// 交互
					if(options.ineractiveListener) {
						options.ineractiveListener.apply(req,arguments);
					}
					break;
				case 4:
					// 完成
					// 如果失败则抛出错误
					try { 
					if (req.status && req.status == 200) {
						
						// 针对content-type的特殊侦听器
						// 由于 Content-Type头部中可能包含字符集，如:Content-Type: text/html; charset=ISO-8859-4
						// 因此通过正则表达式提取出所需的部分.
						var contentType = req.getResponseHeader('Content-Type');
						var mimeType = contentType.match(/\s*([^;]+)\s*(;|$)/i)[1];
											
						switch(mimeType) {
							case 'text/javascript':
							case 'application/javascript':
								// 响应是 JavaScript 因此以req.responseText作为回调函数的参数
								if(options.jsResponseListener) {
									options.jsResponseListener.call(
										req,
										req.responseText
									);
								}
								break;
							case 'application/json':
								// 响应是JSON，因此需要用匿名函数对req.responseText进行解析以返回作为回调参数的JSON对象
								if(options.jsonResponseListener) {
									try {
										var json = parseJSON(
											req.responseText
										);
									} catch(e) {
										var json = false;
									}
									options.jsonResponseListener.call(
										req,
										json
									);
								}
								break;
							case 'text/xml':
							case 'application/xml':
							case 'application/xhtml+xml':
								// 响应是 XML，因此以 req.responseXML 作为回调的参数，此时是一个Document对象
								if(options.xmlResponseListener) {
									options.xmlResponseListener.call(
										req,
										req.responseXML
									);
								}
								break;
							case 'text/html':
								// 响应是HTML，因此以 req.responseText 作为回调函数的参数
								if(options.htmlResponseListener) {
									options.htmlResponseListener.call(
										req,
										req.responseText
									);
								}
								break;
						}
					
						// 针对响应成功完成的侦听器
						if(options.completeListener) {
							options.completeListener.apply(req,arguments);
						}
	
					} else {
						// 响应完成但存在错误
						if(options.errorListener) {
							options.errorListener.apply(req,arguments);
						}
					}
					
	
					} catch(e) {
						//忽略错误
						//alert('Response Error: ' + e);
					}
					break;
			}
		};
		// 打开请求
		req.open(options.method, url, true);
		// 添加特殊的头部信息以标识请求
		req.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
		return req;
	}
	window['UED']['getRequestObject'] = getRequestObject;
	
	/**
	 * 通过简单的包装getRequestObject()和send()方法发送 XMLHttpRequest 对象的请求
	 */
	function ajaxRequest(url,options) {
		var req = getRequestObject(url,options);
		return req.send(options.send);
	}
	window['UED']['ajaxRequest'] = ajaxRequest;
	
	
	
	/**
	 * A counter for the XssHttpRequest objects
	 */
	var XssHttpRequestCount=0;
	
	/**
	 * An cross-site <script> tag implementation of the XMLHttpReqest object 
	 */
	var XssHttpRequest = function(){
		this.requestID = 'XSS_HTTP_REQUEST_' + (++XssHttpRequestCount);
	}
	XssHttpRequest.prototype = {
		url:null,
		scriptObject:null,
		responseJSON:null,
		status:0,
		readyState:0,
		timeout:30000,
		onreadystatechange:function() { },
		
		setReadyState: function(newReadyState) {
			// Only update the ready state if it's newer than the current state
			if(this.readyState < newReadyState || newReadyState==0) {
				this.readyState = newReadyState;
				this.onreadystatechange();
			}
		},
		
		open: function(url,timeout){
			this.timeout = timeout || 30000;
			// Append a special variable to the URL called XSS_HTTP_REQUEST_CALLBACK
			// that contains the name of the callback function for this request
			this.url = url 
				+ ((url.indexOf('?')!=-1) ? '&' : '?' ) 
				+ 'XSS_HTTP_REQUEST_CALLBACK=' 
				+ this.requestID 
				+ '_CALLBACK';    
			this.setReadyState(0);        
		},
		
		send: function(){
			var requestObject = this;
			
			// Create a new script object to load the external data
			this.scriptObject = document.createElement('script');
			this.scriptObject.setAttribute('id',this.requestID);
			this.scriptObject.setAttribute('type','text/javascript');
			// Don't set the src or append to the document yet...
			
			
			// Create a setTimeout() method that will trigger after a given 
			// number of milliseconds. If the script hasn't loaded by the given
			// time it will be cancelled
			var timeoutWatcher = setTimeout(function() {
				// Re-populate the window method with an empty method incase the 
				// script loBasic later on after we've assumed it stalled
				window[requestObject.requestID + '_CALLBACK'] = function() { };
				
				// Remove the script to prevent it from loading further
				requestObject.scriptObject.parentNode.removeChild(
					requestObject.scriptObject
				);
	
				// Set the status to error
				requestObject.status = 2;
				requestObject.statusText = 'Timeout after ' 
					+ requestObject.timeout 
					+ ' milliseconds.'            
				
				// Update the state
				requestObject.setReadyState(2);
				requestObject.setReadyState(3);
				requestObject.setReadyState(4);
						
			},this.timeout);
			
			
			// Create a method in the window object that matches the callback
			// in the request. When called it will processing the rest of 
			// the request
			window[this.requestID + '_CALLBACK'] = function(JSON) {
				// When the script loBasic this method will execute, passing in
				// the desired JSON object.
			
				// Clear the timeoutWatcher method as the request 
				// loaded successfully
				clearTimeout(timeoutWatcher);
	
				// Update the state
				requestObject.setReadyState(2);
				requestObject.setReadyState(3);
				
				// Set the status to success 
				requestObject.responseJSON = JSON; 
				requestObject.status=1;
				requestObject.statusText = 'Loaded.' 
			
				// Update the state
				requestObject.setReadyState(4);
			}
	
			// Set the initial state
			this.setReadyState(1);
			
			// Now set the src property and append to the document's 
			// head. This will load the script
			this.scriptObject.setAttribute('src',this.url);                    
			var head = document.getElementsByTagName('head')[0];
			head.appendChild(this.scriptObject);
			
		}
	}
	window['UED']['XssHttpRequest'] = XssHttpRequest;
	
	/**
	 * Setup the various parts of the new XssHttpRequest Object 
	 */
	function getXssRequestObject(url,options) {
		var req = new  XssHttpRequest();
	 
		options = options || {};
		// Default timeout of 30 sec
		options.timeout = options.timeout || 30000;
	
		req.onreadystatechange = function() {
			switch (req.readyState) {
				case 1:
					// Loading
					if(options.loadListener) {
						options.loadListener.apply(req,arguments);
					}
					break;
				case 2:
					// Loaded
					if(options.loadedListener) {
						options.loadedListener.apply(req,arguments);
					}
					break;
				case 3:
					// Interactive
					if(options.ineractiveListener) {
						options.ineractiveListener.apply(req,arguments);
					}
					break;
				case 4:
					// Complete
					if (req.status == 1) {
						// The request was successful
						if(options.completeListener) {
							options.completeListener.apply(req,arguments);
						}
					} else {
						// There was an error
						if(options.errorListener) {
							options.errorListener.apply(req,arguments);
						}
					}
					break;
			}
		};
		req.open(url,options.timeout);
		
		return req;
	}
	window['UED']['getXssRequestObject'] = getXssRequestObject;
	
	/**
	 * send an XssHttpRequest 
	 */
	function xssRequest(url,options) {
		var req = getXssRequestObject(url,options);
		return req.send(null);
	}
	window['UED']['xssRequest'] = xssRequest;
	
	/**
	 * a helper method to make callbacks 
	 */
	function makeCallback(method, target) {
		return function() { method.apply(target,arguments); }
	}
	
	/**
	 * A URL hash listener used to trigger 
	 * registered methods based on hashes 
	 */
	var actionPager =  {
		// The previous hash
		lastHash : '',
		// A list of the methods registered for the hash patterns
		callbacks: [],
		// The safari history list
		safariHistory : false,
		// A reference to the iframe for Internet Explorer
		msieHistory: false,
		// The class name of the links that should be converted
		ajaxifyClassName: '',
		// The root URL of the application. This will be stripped off the URLS
		// when creating the hashes
		ajaxifyRoot: '',
		
		
		init: function(ajaxifyClass,ajaxifyRoot,startingHash) {
	
			this.ajaxifyClassName = ajaxifyClass || 'BasicActionLink';
			this.ajaxifyRoot = ajaxifyRoot || '';
	
			if (/Safari/i.test(navigator.userAgent)) {
				this.safariHistory = [];
			} else if (/MSIE/i.test(navigator.userAgent)) {
				// In the case of MSIE, add a iframe to track override the back button
				this.msieHistory = document.createElement("iframe");
				this.msieHistory.setAttribute("id", "msieHistory");
				this.msieHistory.setAttribute("name", "msieHistory");
				setStyleById(this.msieHistory,{
					'width':'100px',
					'height':'100px',
					'border':'1px solid black',
					'visibility':'visible',
					'zIndex':'-1'
				});
				document.body.appendChild(this.msieHistory);
				this.msieHistory = frames['msieHistory'];
				
			}
	
			// Convert the links to AJAX links
			this.ajaxifyLinks();
	
			// Get the current location
			var location = this.getLocation();
	
			// Check if the location has a hash (from a bookmark)
			// or if a hash has bee provided
			if(!location.hash && !startingHash) { startingHash = 'start'; }
	
			// Store the hash as necessary
			ajaxHash = this.getHashFromURL(location.hash) || startingHash;
			this.addBackButtonHash(ajaxHash);
	
			// Add a watching event to look for changes in the location bar
			var watcherCallback = makeCallback(this.watchLocationForChange,this);
			window.setInterval(watcherCallback,200);
		},
		ajaxifyLinks: function() {
			// Convert the links to anchors for ajax handling
			links = getElementsByClassName(this.ajaxifyClassName, 'a', document);
			for(var i=0 ; i < links.length ; i++) {
				if(hasClassName(links[i],'BasicActionPagerModified')) { continue; }
			
				// Convert the herf attribute to #value
				links[i].setAttribute(
					'href',
					this.convertURLToHash(links[i].getAttribute('href'))
				);
				addClassName(links[i],'BasicActionPagerModified');
	
				// Attach a click event to add history as necessary
				addEvent(links[i],'click',function() {
					 if (this.href && this.href.indexOf('#') > -1) {
						 actionPager.addBackButtonHash(
							actionPager.getHashFromURL(this.href)
						);
					 }
				});
			}
		},
		addBackButtonHash: function(ajaxHash) {
			// Store the hash
			if (!ajaxHash) return false;
			if (this.safariHistory !== false) {
				// Using a special array for Safari
				if (this.safariHistory.length == 0) {
					this.safariHistory[window.history.length] = ajaxHash;
				} else {
					this.safariHistory[window.history.length+1] = ajaxHash;
				}
				return true;
			} else if (this.msieHistory !== false) {
				// By navigating the iframe in MSIE
				this.msieHistory.document.execCommand('Stop');
				this.msieHistory.location.href = '/fakepage?hash='
					+ ajaxHash
					+ '&title='+document.title;
				return true;
			} else {
				// By changing the location value
				// The function is wrapped using makeCallback so that this 
				// will refer to the actionPager from within the timeout method
				var timeoutCallback = makeCallback(function() {
					if (this.getHashFromURL(window.location.href) != ajaxHash) {
						window.location.replace(location.href+'#'+ajaxHash);
					}
				},this);
				setTimeout(timeoutCallback, 200);
				return true;
			}
			return false;
		},
		watchLocationForChange: function() {
			
			var newHash;
			// Retrieve the value for the new hash
			if (this.safariHistory !== false) {
				// From the history array for safari
				if (this.safariHistory[history.length]) {
					newHash = this.safariHistory[history.length];
				}
			} else if (this.msieHistory !== false) {
				// From the location of the iframe in MSIE
				newHash = this.msieHistory.location.href.split('&')[0].split('=')[1];
			} else if (location.hash != '') {
				// From the window.location otherwise
				newHash = this.getHashFromURL(window.location.href);
	
			}
	
			// Update the page if the new hash doesn't equal the last hash
			if (newHash && this.lastHash != newHash) {
				if (this.msieHistory !== false 
				&& this.getHashFromURL(window.location.href) != newHash) {
					// Fix the location bar in MSIE so it bookmarks properly
					location.hash = newHash;
				}
				
				// Try executing any registered listeners
				// using try/catch incase of an exception
				try {
					this.executeListeners(newHash);
					// Update the links again incase any new
					// ones were added with the handler
					this.ajaxifyLinks();
				} catch(e) {
					// This will catch any bad JS in the callbacks.
					alert(e);
				}
				
				// Save this as the last hash
				this.lastHash = newHash;
			}
		},
		register: function(regex,method,context){
			var obj = {'regex':regex};
			if(context) {
				// A context has been specified
				obj.callback = function(matches) { method.apply(context,matches); };
			} else {
				// Use the window as the context
				obj.callback = function(matches) { method.apply(window,matches); };
			}
			
			// Add listeners to the callback array
			this.callbacks.push(obj)
		},
		convertURLToHash: function(url) {
			if (!url) {
				// No url so return a pound
				return '#';
			} else if(url.indexOf("#") != -1) {
				// Has a hash so return it
				return url.split("#")[1];
			} else {
				// If the URL includes the domain name (MSIE) strip it off.
				if(url.indexOf("://") != -1) {
					url = url.match(/:\/\/[^\/]+(.*)/)[1];
				}
				// Strip off the root as specified in init()
				return '#' + url.substr(this.ajaxifyRoot.length)
			}
		},
		getHashFromURL: function(url) {
			if (!url || url.indexOf("#") == -1) { return ''; }
			return url.split("#")[1];
		},
		getLocation: function() {
			// Check for a hash
			if(!window.location.hash) {
				// Not one so make it
				var url = {host:null,hash:null}
				if (window.location.href.indexOf("#") > -1) {
					parts = window.location.href.split("#")[1];
					url.domain = parts[0];
					url.hash = parts[1];
				} else {
					url.domain = window.location;
				}
				return url;
			}
			return window.location;
		},
		executeListeners: function(hash){
			// Execute any listeners that match the hash
			for(var i in this.callbacks) {
				if((matches = hash.match(this.callbacks[i].regex))) {
					this.callbacks[i].callback(matches);
				}
			}
		}
	}
	window['UED']['actionPager'] = actionPager;
	
	/**
	 * 一个复制Javascript对象的辅助方法
	 */
	function clone(myObj) {
		if(typeof(myObj) != 'object') return myObj;
		if(myObj == null) return myObj;
		var myNewObj = new Object();
		for(var i in myObj) {
			myNewObj[i] = clone(myObj[i]);
		}
		return myNewObj;
	}
	
	/**
	 * An array to hold the queues 
	 */
	var requestQueue = [];
	
	/**
	 * Wrapper for the Basic.ajaxRequest method the enables a queue 
	 */
	function ajaxRequestQueue(url,options,queue) {
		queue = queue || 'default';
		
		// This object will wrap the option listeners in another function
		// so the option object needs to be unique. If a shared options object 
		// is used when the method is call it will get into a recursive mess.
		options = clone(options) || {};
		if(!requestQueue[queue]) requestQueue[queue] = [];
		 
		// The queue needs to invoke the next request using the completeListener
		// when the previous request is complete. If the complete listener is 
		// already defined then you need to invoke it first.
		
		// Grab the old listener
		var userCompleteListener = options.completeListener;
	
		// Add a new listener
		options.completeListener = function() {
			
			// If there was an old one invoke it first
			if(userCompleteListener) {
				// this will refer to the request object
				userCompleteListener.apply(this,arguments);        
			};
	
			// Remove this request from the queue
			requestQueue[queue].shift();
			
			// Invoke the next item in the queue
			if(requestQueue[queue][0]) {
				// The request is in the req property but you alos need to include
				// the send option incase it's a POST request
				var q = requestQueue[queue][0].req.send(
					requestQueue[queue][0].send
				);
			}
		}
		
		// If there's an error the rest of the queue should be cancelled
		// by calling their error methods
		
		// Grab the old listener
		var userErrorListener = options.errorListener;
	
		// Add a new listener
		options.errorListener = function() {
		
			if(userErrorListener) {
				userErrorListener.apply(this,arguments);        
			};
			
			// Remove this request from the queue as the error 
			// was already invoked
			requestQueue[queue].shift();
			
			// Kill the rest of the queue as there was an error but call the
			// errorListener on each first. By invoking the error listener on
			// the next item in the queue it will clear all queued requests as
			// as each will invoke the next in a chain
			
			// Check if there is still anything in the queue
			if(requestQueue[queue].length) {
				
				// Grab the next one
				var q = requestQueue[queue].shift();
	
				// Abort the request.
				q.req.abort();
				
				// Fake a request object so that the errorListener thinks it
				// completed and runs accordingly
	
				var fakeRequest = new Object();
				
				// Set the status to 0 and readyState to 4 (as if 
				// the request completed but failed
				fakeRequest.status = 0;
				fakeRequest.readyState = 4
	
				fakeRequest.responseText = null;
				fakeRequest.responseXML = null;
	
				// Set an error so you can show a message if you wish.
				fakeRequest.statusText = 'A request in the queue received an error';
	
				// Invoke the state change. If readyState is 4 and 
				// status is not 200 then errorListener will be invoked.
				q.error.apply(fakeRequest);
			}
		   
		}
		
		// Add this requests to the queue
		requestQueue[queue].push({
			req:getRequestObject(url,options),
			send:options.send,
			error:options.errorListener
		});
	
	
		// If the length of the queue is only one 
		// item (the first) invoke the request    
		if(requestQueue[queue].length == 1) {
			ajaxRequest(url,options);
		}
	}
	window['UED']['ajaxRequestQueue'] = ajaxRequestQueue;
	
	/********************************
	* Chapter 7 thru 12
	*********************************/
	//nothing added to the library

})();



