/****************************************************
 * 												    *		
 * 			Sociax HTML 标签关联模型  			*
 *                                                  *
 ****************************************************/

/**
 * HTML 标签关联模型
 * @model-node 模型节点的标签属性标记
 * @event-node 模型下事件节点的标签属性标记
 * @author _慢节奏
 */
(function(window) {

var document = window.document;

/**
 * 激活模型
 * 
 * @param node 元素节点
 * @param node 父模型节点，若为空则将node 作为父模型节点
 * @param fns  挂载到标签上的事件方法，格式说明如下：
 * {
 *     model: {
 *         method1 : {
 *         	   click     : function(){},
 *         	   mouseover : function(){},
 *        	   mouseout  : function(){},
 *         	   load      : function(){}
 *     	   },
 *     	   method2 : {
 *             blur   : function(){},
 *             focus  : function(){},
 *             submit : function(){}
 *     	   }
 *     },
 *     event: {
 *         method1 : {
 *         	   click     : function(){},
 *         	   mouseover : function(){},
 *        	   mouseout  : function(){},
 *         	   load      : function(){}
 *     	   },
 *     	   method2 : {
 *             blur   : function(){},
 *             focus  : function(){},
 *             submit : function(){}
 *     	   }
 *     }
 * }
 */
var module = function( node, fns ) {
    module.addFns(fns);
	if ( node ) {
		// 预清除，防止重复模型化引起的双重缓存
		module.nodes.init( node );
	}
};

/**
 * 保存事件的方法
 * 
 * @param fns  挂载到标签上的事件方法，格式说明同module 函数的fns 参数
 */
module.addFns = function( fns ) {
	if ( !fns ) return module;
	if ( fns.model ) {
		module.addModelFns( fns.model );
	}
	if ( fns.event ) {
		module.addEventFns( fns.event );
	}
	return module;
};

/**
 * 保存模型事件的方法
 * 
 * @param fns  挂载到模型上的事件方法，格式说明如下：
 * {
 *     method1 : {
 *         click     : function(){},
 *         mouseover : function(){},
 *         mouseout  : function(){},
 *         load      : function(){}
 *     },
 *     method2 : {
 *         blur   : function(){},
 *         focus  : function(){},
 *         submit : function(){}
 *     }
 * } 
 */
module.addModelFns = function( fns ) {
	if ( "object" != typeof fns ) return module;
	var name;
	for ( name in fns ) {
		module.nodes.models.fns[name] = fns[name];
	}
	return module;
};

/**
 * 保存模型下事件节点的方法
 * 
 * @param fns  挂载到模型上的事件方法，属性说明同module.addModelFns 的fns 参数
 */
module.addEventFns = function( fns ) {
	if ( "object" != typeof fns ) return module;
	var name;
	for ( name in fns ) {
		module.nodes.events.fns[name] = fns[name];
	}
	return module;
};

/**
 * 获取节点的参数
 *
 * @param node 模型/事件节点
 */
module.getArgs = function( node ) {
	return node.getAttribute( "model-node" ) ? module.getModelArgs( node ) : module.getEventArgs( node );
};

/**
 * 设置节点的参数
 *
 * @param node 模型/事件节点
 * @param uri URI 格式的参数
 */
module.setArgs = function( node, uri ) {
	return node.getAttribute( "model-node" ) ? module.setModelArgs( node, uri ) : module.setEventArgs( node, uri );
};

/**
 * 获取模型节点的参数
 *
 * @param node 模型节点
 */
module.getModelArgs = function( node, uri ) {
    node.args || ( node.args = module.URI2Obj( node.getAttribute( "model-args" ) ) );
    return node.args;
};

/**
 * 设置模型节点的参数
 *
 * @param node 模型节点
 */
module.setModelArgs = function( node, uri ) {
    node.args = undefined;
    node.setAttribute( "model-args", uri );
    return module;
};

/**
 * 获取事件节点的参数
 *
 * @param node 事件节点
 */
module.getEventArgs = function( node ) {
    node.args || ( node.args = module.URI2Obj( node.getAttribute( "event-args" ) ) );
    return node.args;
};

/**
 * 设置事件节点的参数
 *
 * @param node 事件节点
 */
module.setEventArgs = function( node, uri ) {
    node.args = undefined;
    node.setAttribute( "event-args", uri );
    return module;
};

/**
 * 将uri转换为对象格式
 *
 * @param uri URI 格式的数据
 */
module.URI2Obj = function( uri ) {
	if ( ! uri ) return {};
    var obj = {},
    	args = uri.split( "&" ),
    	l, arg;
    l = args.length;
    while ( l -- > 0 ) {
        arg = args[l];
        if ( ! arg ) {
            continue;
        }
        arg = arg.split( "=" );
        obj[arg[0]] = arg[1];
    }
    return obj;
};

/**
 * 获取全局内指定的模型节点
 *
 * @param name 模型节点的命名
 */
module.getModels = function( name ) {
	return module.nodes.models[name];
};

/**
 * 获取全局内指定的事件节点
 *
 * @param name 事件节点的命名
 */
module.getEvents = function( name ) {
	return module.nodes.events[name];
}

/**
 * 删除节点上的监听
 *
 * @param object node 节点对象
 */
module.removeListener = function( node ) {
	module.nodes.removeListener( node );
	return module;
};

/**
 * 为节点添加监听
 *
 * @param object node 节点对象
 * @param object events 监听的事件
 * {
 *     click     : function(){},
 *     mouseover : function(){},
 *     mouseout  : function(){}
 * }
 */
module.addListener = function( node, events ) {
	module.nodes.addListener( node, events );
	return module;
};

module.getPreviousModel = function( node, siblingName ) {
	return module.nodes.getPreviousModel( node, siblingName );
};

module.getNextModel = function( node, siblingName ) {
	return module.nodes.getNextModel( node, siblingName );
};

module.getPreviousEvent = function( node, siblingName ) {
	return module.nodes.getPreviousEvent( node, siblingName );
};

module.getNextEvent = function( node, siblingName ) {
	return module.nodes.getNextEvent( node, siblingName );
};

/**
 * 模型化节点对象
 * 
 * @property function init 初始化模型
 * @property function _init 逐级扫描指定节点下的各级子元素的模型结构，并缓存模型和事件的DOM对象
 * @property function clear 清楚元素节点的子模型节点和子事件节点合集对象
 * @property function getParentModel
 * @property function addListener 为模型和事件节点附加事件方法
 * @property object _onload 自定义onload 事件
 * @property object _onload.execute 执行onload 事件队列
 * @property object _onload.queue onload 事件队列
 * @property object models 罗列并缓存模型节点
 * @property object events 罗列并缓存事件节点
 * @property object models.fns 存放模型节点的事件方法
 * @property object events.fns 存放事件节点的事件方法
 */
module.nodes = {
	init: function( node ) {
		// 初始化模型
		this._init( node );
		// 执行onload 事件
		this._onload.execute();
		return this;
	},
	_init: function( node, parentModel ) {
		var childNode = node.firstChild,
			childParentModel,
		    model_name,
		    event_name;

		! parentModel && ( parentModel = this.getParentModel( node ) );

		switch ( node.nodeName ) {
			case "DIV": case "UL":case "DL": 
			case "FORM":case "LI":case "DD": 
				model_name = node.getAttribute( "model-node" );
				if ( model_name ) {
					this._clearModel( node );

					node.modelName = model_name;

					this.addListener( node, this.models.fns[model_name] );

					node.parentModel = parentModel;

					( parentModel.childModels[model_name] = parentModel.childModels[model_name] || [] ).push( node );

					( this.models[model_name] = this.models[model_name] || [] ).push( node );

					childParentModel = node;
				}
				break;
			case "A": case "SPAN": case "LABEL":
			case "STRONG": case "INPUT": case "SELECT":
			case "BUTTON": case "IMG": case "TEXTAREA": 
			case "H1": case "H2": case "H3": case "H4":case "I":			
				event_name = node.getAttribute( "event-node" );
				if ( event_name ) {
					this._clearEvent(node);

					node.eventName = event_name;

					this.addListener( node, this.events.fns[event_name] );

					node.parentModel = parentModel;
					( parentModel.childEvents[event_name] = parentModel.childEvents[event_name] || [] ).push( node );

					( this.events[event_name] = this.events[event_name] || [] ).push( node );
				}
				break;
			case "HEAD": case "BODY":
				this[node.nodeName.toLowerCase()] = node;
				break;
			case "#document":
				this._clearModel(node);
				break;
		}

		! childParentModel && ( childParentModel = parentModel );
		while ( childNode ) {
			(1 == childNode.nodeType ) && this._init( childNode, childParentModel );
			childNode = childNode.nextSibling;
		}
	},
	_clearModel: function( node ) {
		node.modelName   = undefined;
		node.parentModel = undefined;
		node.childModels = {};
		node.childEvents = {};
		node.args = undefined;
		return this;
	},
	_clearEvent: function( node ) {
		node.eventName   = undefined;
		node.parentModel = undefined;
		node.args = undefined;
		return this;
	},
	getParentModel: function( node ) {
		var parentNode = node.parentNode,
			parentModel;
		if ( parentNode && 1 === parentNode.nodeType ) {
			parentModel = parentNode.getAttribute('model-node') ? parentNode : this.getParentModel( parentNode );
		}
		return parentModel || document;
	},
	getPreviousModel: function( node, siblingName ) {
		return this._getSiblingNode( node, { siblingType: "model", siblingName: siblingName }, "previous" );
	},
	getNextModel: function( node, siblingName ) {
		return this._getSiblingNode( node, { siblingType: "model", siblingName: siblingName }, "next" );
	},
	getPreviousEvent: function( node, siblingName ) {
		return this._getSiblingNode( node, { siblingType: "event", siblingName: siblingName }, "previous" );
	},
	getNextEvent: function( node, siblingName ) {
		return this._getSiblingNode( node, { siblingType: "event", siblingName: siblingName }, "next" );
	},
	_getSiblingNode: function( node, siblingArgs, direction ) {
		var sibling;
		if ( !node ) return null;
		sibling = node[ [ direction, "Sibling" ].join("") ];
		return ( sibling && ( siblingArgs.siblingName === sibling[ [ siblingArgs.siblingType, "Name" ].join("") ] ) ) ? sibling : this._getSiblingNode( sibling, siblingArgs, direction );
	},
	addListener: function( node, events ) {
        if ( "object" == typeof events ) {
        	var event;
            for ( event in events ) {
            	switch ( event ) {
            		case "load":
            			node[event] = events[event];
                        // 添加到队列
                        this._onload.queue.push( node );
           			    break;
            		case "callback":
            			node[event] = events[event];
            			break;
           			case "mouseenter": case "mouseleave":
           				// 兼容非IE
           				if ( document.addEventListener ) {
           					var refer = {mouseenter: "mouseover", mouseleave: "mouseout"};
           					node["on" + refer[event]] = (function( event, fn ){
           						return function( e ) {
	           						// 上一响应mouseover/mouseout 事件的元素
	           						var parent = e.relatedTarget;
	           						// 假如存在这个元素并且这个元素不等于目标元素
									while( parent && parent != this ){
										try {
											//上一响应的元素开始往上寻找目标元素
											parent = parent.parentNode;
										} catch( e ) {
											break;
										}
							 
									}
									// 假如找不到，表明当前事件触发点不在目标元素内
									if ( parent != this ) {
										//运行目标方法，否则不运行
										node[event] = fn;
										node[event]();
									}
								};
           					})( event, events[event] );
           				} else {
                        	node["on" + event] = events[event];
           				}
           				break;
            		default :
                        node["on" + event] = events[event];
            	}
            }
        }
	},
	removeListener: function( node ) {
		node.onclick = node.onfocus = node.onblur = node.onmouseout
		= node.onmouseover = node.onmouseenter = node.onmouserleave
		= node.onchange = null;
		return this;
	},
	_onload: {
		execute: function() {
			var l = this.queue.length,
				i;
			for ( i = 0; i < l; i ++ ) {
				
				this.queue[i]["load"]();
				this.queue[i]["load"] = undefined;	
				
			}
			// 重置队列
			this.queue = [];
		},
		queue: []
	},
	models: {
		fns: {

		}
	},
	events: {
		fns: {
		}
	},
	getHead: function() {
		this.head || (this.head = document.getElementsByTagName("head")[0]);
		return this.head;
	},
	getBody: function() {
		this.body || (this.body = document.getElementsByTagName("body")[0]);
		return this.body;
	}
};

/**
 * 加载CSS 文件
 *
 * @param string url CSS 文件URL
 */
module.getCSS = (function() {
	var temp = [];
	//返回内部包函数,供外部调用并可以更改temp的值
	return function( url ){
		var	head = module.nodes.getHead(),
			flag = 0,
			link,
			i = temp.length - 1;
		// 第二次调用的时候就不=0了
		for ( ; i >= 0; i -- ) {
			flag = ( temp[i] == url ) ? 1 : 0;
		}

		if ( flag == 0 ) {
			// 未载入过
		    link  = document.createElement( "link" );
			link.setAttribute( "rel", "stylesheet" );
			link.setAttribute( "type", "text/css" );
			link.setAttribute( "href", url );
			head.appendChild( link );
			temp.push( url );
		}
	};
})();

/**
 * 加载js 文件
 *
 * @param string url js 文件URL
 * @param function fn 执行函数
 */
module.getJS = (function() {
	var temp = [];
	//返回内部包函数,供外部调用并可以更改temp的值
	return function( url, fn ){
		// 第二次调用的时候就不=0了
		var	head,
			script,
			onload,
			flag = 0,
			i = temp.length - 1;
		// 第二次调用的时候就不=0了
		for ( ; i >= 0; i -- ) {
			flag = ( temp[i] == url ) ? 1 : 0;
		}

		if ( flag == 0 ) {
			// 未载入过
			// 记录url
			temp.push( url );
			// 载入
			head = module.nodes.getHead();
			script = document.createElement( "script" );
			script.setAttribute( "src", url );

			if ( "function" == typeof fn ) {
				script.onload = script.onreadystatechange = function() {
					// FF 下没有readyState 值，IE 有readyState 值，需加以判断
					if( ! this.readyState || "loaded" == this.readyState || "complete" == this.readyState ) {
						this.onload = this.onreadystatechange = null;

						fn();

						fn = undefined;

						script = undefined;
					}
				};
			}

			head.appendChild( script );
		} else {
			if("function" == typeof fn){
				fn();
				fn = undefined;
			}	
		}
	};
})();

/**
 * Execute functions when the DOM is ready 
 *
 * @param function fn 格式的数据
 */
module.ready = function( fn ) {
	if ( "function" !== typeof fn ) {
		return;
	}

	if ( document.addEventListener ) {
		// Use the handy event callback
		document.addEventListener( "DOMContentLoaded", fn, false );
	// If IE event model is used
	} else if ( document.attachEvent ) {
		// maybe late but safe also for iframes
		document.attachEvent( "onreadystatechange", fn );
	}
};

window.M = module;

M.ready(function() {
	M(document);
});

})(window);