// JavaScript Document

var $$ = function (id) {
    return "string" == typeof id ? document.getElementById(id) : id;
};
var isIE8 = navigator.userAgent.indexOf('MSIE 8.0') != -1;
var isChrome = navigator.userAgent.indexOf('Chrome') != -1;
var isSafari = navigator.userAgent.indexOf('AppleWebKit') != -1;
function addEvent(element, type, handler) {
	if (element.addEventListener) {
		element.addEventListener(type, handler, false);
	} else {
		if (!handler.$$guid) handler.$$guid = addEvent.guid++;
		if (!element.events) element.events = {};
		var handlers = element.events[type];
		if (!handlers) {
			handlers = element.events[type] = {};
			if (element["on" + type]) {
				handlers[0] = element["on" + type];
			}
		}
		handlers[handler.$$guid] = handler;
		element["on" + type] = handleEvent;
	}
};
addEvent.guid = 1;
function removeEvent(element, type, handler) {
	if (element.removeEventListener) {
		element.removeEventListener(type, handler, false);
	} else {
		if (element.events && element.events[type]) {
			delete element.events[type][handler.$$guid];
		}
	}
};
function handleEvent(event) {
	var returnValue = true;
	event = fixEvent(event);
	var handlers = this.events[event.type];
	for (var i in handlers) {
		this.$$handleEvent = handlers[i];
		if (this.$$handleEvent(event) === false) {
			returnValue = false;
		}
	}
	return returnValue;
};
function fixEvent(event) {
	if (event) return event;
	event = ((this.ownerDocument || this.document || this).parentWindow || window).event;
	var scrolldoc = isChrome || isSafari ? document.body : document.documentElement;
	event.pageX = event.clientX + scrolldoc.scrollLeft;
	event.pageY = event.clientY + scrolldoc.scrollTop;
	event.target = event.srcElement;
	event.stopPropagation = fixEvent.stopPropagation;
	event.preventDefault = fixEvent.preventDefault;
	if(event.type == "mouseout") {
		event.relatedTarget = event.toElement;
	}else if(event.type == "mouseover") {
		event.relatedTarget = event.fromElement;
	}
	return event;
};
fixEvent.preventDefault = function() {
	this.returnValue = false;
};
fixEvent.stopPropagation = function() {
	this.cancelBubble = true;
};

var forEach = function(array, callback, thisObject){
	if(array.forEach){
		array.forEach(callback, thisObject);
	}else{
		for (var i = 0, len = array.length; i < len; i++) { callback.call(thisObject, array[i], i, array); }
	}
}

var Bind = function(object, fun) {
	var args = Array.prototype.slice.call(arguments, 2);
	return function() {
		return fun.apply(object, args.concat(Array.prototype.slice.call(arguments)));
	}
}

var BindAsEventListener = function(object, fun) {
	var args = Array.prototype.slice.call(arguments, 2);
	return function(event) {
		return fun.apply(object, [event].concat(args));
	}
}

var Extend = function(destination, source) {
	for (var property in source) {
		destination[property] = source[property];
	}
	return destination;
}

var DeepExtend = function(destination, source){
	for (var property in source) {
		var copy = source[property];
		if ( destination === copy ) continue;
		if ( typeof copy === "object" ){
			destination[property] = DeepExtend(destination[property] || {}, copy);
		}else{
			destination[property] = copy;
		}
	}
	return destination;
}

var Filter = function(array, callback, thisObject){
	if(array.filter){
		return array.filter(callback, thisObject);
	}else{
		var res = [];
		for (var i = 0, len = array.length; i < len; i++) { callback.call(thisObject, array[i], i, array) && res.push(array[i]); }
		return res;
	}
}

var Every = function(array, callback, thisObject){
	if(array.every){
		return array.every(callback, thisObject);
	}else{
		var res = [];
		for (var i = 0, len = array.length; i < len; i++) {
			if (!callback.call(thisObject, array[i], i, array)) return false;
		};
		return true;
	}
}

var IndexOf = function(array, elt){
	if(array.indexOf){
		return array.indexOf(elt);
	}else{
		var res = [];
		for (var i = 0, len = array.length; i < len; i++) {
			if (array[i] === elt) return i;
		};
		return -1;
	}
}

var Contains = function(a, b){
	return a.contains ? a != b && a.contains(b) : !!(a.compareDocumentPosition(b) & 16);
}

var isArray = function( obj ) {
	return Object.prototype.toString.call(obj) === "[object Array]";
}

function GetRelative(relElem, fixedElem, options){
	var doc = relElem.ownerDocument, docElem = doc.documentElement
		,scrolldoc = isChrome || isSafari ? doc.body : docElem,
		rect = relElem.getBoundingClientRect();
	//默认值
	options = Extend({
		Align:			"clientleft",//水平方向定位
		vAlign:			"clienttop",//垂直方向定位
		CustomLeft:		0,//自定义left定位
		CustomTop:		0,//自定义top定位
		PercentLeft:	0,//自定义left百分比定位
		PercentTop:		0,//自定义top百分比定位
		Adaptive:		false,//是否自适应定位
		Reset:			false//自适应定位时是否重新定位
	}, options || {});
	//ie8的getBoundingClientRect获取不准确
	if ( isIE8 ) {
		var elem = relElem, left = - scrolldoc.scrollLeft, top = - scrolldoc.scrollTop;
		while (elem) { left += elem.offsetLeft, top += elem.offsetTop; elem = elem.offsetParent; };
		rect.left = left; rect.right = left + relElem.offsetWidth;
		rect.top = top; rect.bottom = top + relElem.offsetHeight;
	};
	var iLeft = GetRelative.Left(options.Align, rect, fixedElem) + options.CustomLeft,iTop = GetRelative.Top(options.vAlign, rect, fixedElem) + options.CustomTop;
	if (options.PercentLeft) { iLeft += .01 * options.PercentLeft * relElem.offsetWidth; };
	if (options.PercentTop) { iTop += .01 * options.PercentTop * relElem.offsetHeight; };
	if (options.Adaptive) {
		var maxLeft = docElem.clientWidth - fixedElem.offsetWidth,
			maxTop = docElem.clientHeight - fixedElem.offsetHeight;
		if (options.Reset) {
			if (iLeft > maxLeft || iLeft < 0) {
				iLeft = GetRelative.Left(2 * iLeft > maxLeft ? "left" : "right", rect, fixedElem) + options.CustomLeft;
			};	
			if (iTop > maxTop || iTop < 0) {
				iTop = GetRelative.Top(2 * iTop > maxTop ? "top" : "bottom", rect, fixedElem) + options.CustomTop;
			};
		} else {
			//修正到适合位置
			iLeft = Math.max(Math.min(iLeft, maxLeft), 0);
			iTop = Math.max(Math.min(iTop, maxTop), 0);
		};
	};
	iLeft += scrolldoc.scrollLeft; iTop += scrolldoc.scrollTop;
	return { Left: iLeft, Top: iTop };
};
GetRelative.Left = function(align, rect, fixedElem){
	var iLeft = 0;
	switch (align.toLowerCase()) {
		case "left" :
			return rect.left - fixedElem.offsetWidth;
		case "clientleft" :
			return rect.left;
		case "center" :
			return (rect.left + rect.right - fixedElem.offsetWidth)/2;
		case "clientright" :
			return rect.right - fixedElem.offsetWidth;
		case "right" :
		default :
			return rect.right;
	};
};
GetRelative.Top = function(valign, rect, fixedElem){
	var iTop = 0;
	switch (valign.toLowerCase()) {
		case "top" :
			return rect.top - fixedElem.offsetHeight;
		case "clienttop" :
			return rect.top;
		case "center" :
			return (rect.top + rect.bottom - fixedElem.offsetHeight)/2;
		case "clientbottom" :
			return rect.bottom - fixedElem.offsetHeight;
		case "bottom" :
		default :
			return rect.bottom;
	};
};
var FixedMenu = function(containers, options) {
	this._timerContainer = null;//容器定时器
	this._timerMenu = null;//菜单定时器
	this._frag = document.createDocumentFragment();//碎片对象，保存菜单元素
	this._menus = {};//菜单对象
	
	this._containers = [];//容器集合
	
	this.SetOptions(options);
	
	this._custommenu = this.options.Menu;
	
	this.Css = this.options.Css;
	this.Hover = this.options.Hover;
	this.Active = this.options.Active;
	this.Tag = this.options.Tag;
	this.Txt = this.options.Txt;
	this.FixedMenu = this.options.FixedMenu;
	this.Fixed = this.options.Fixed;
	this.Attribute = this.options.Attribute;
	this.Property = this.options.Property;
	this.onBeforeShow = this.options.onBeforeShow;
	this.Delay = parseInt(this.options.Delay) || 0;
	forEach(isArray(containers) ? containers : [containers], function(o, i){
		var pos, menu;
		if ( o.id ) {
			pos = o.id; menu = o.menu ? o.menu : pos;
		} else {
			pos = menu = o;
		};
		pos = $$(pos); menu = $$(menu);
		//容器对象 Pos:定位元素 Menu:插入菜单元素
		pos && menu && this.IniContainer( i, { Pos: pos, Menu: menu } );
	}, this);
	//初始化程序
	this.Ini();
};
FixedMenu.prototype = {
  //设置默认属性
  SetOptions: function(options) {
	this.options = {//默认值
		Menu:[],//自定义菜单集合
		Delay:200,//延迟值(微秒)
		Tag:"div",//默认生成标签
		Css:undefined,//默认样式
		Hover:undefined,//触发菜单样式
		Active:undefined,//显示下级菜单时显示样式
		Txt:"",//菜单内容
		FixedMenu:	true,//是否相对菜单定位（否则相对容器）
		Fixed:{ Align: "clientleft", vAlign: "bottom" },//定位对象
		Attribute:{},//自定义Attribute属性
		Property:{},//自定义Property属性
		onBeforeShow:function(){}//菜单显示时执行
	};
	Extend( this.options, options || {} );
  },
  Ini: function() {
	this.Hide();//隐藏菜单
	this.BuildMenu();//生成菜单对象
	this.forEachContainer(this.ResetContainer);//重置容器属性
	this.InsertMenu(0, 0);//显示菜单
  },
  BuildMenu: function() {
	//清除旧菜单dom(包括自定义的)
	this.forEachMenu(function(o){
		var elem = o._elem;
		if ( elem ) {
			//防止dom内存泄漏
			removeEvent( elem, "mouseover", o._event );
			elem.parentNode.removeChild(elem);
			o._elem = o.elem = null;
		};
	});
	//设置菜单默认值
	var options = {
		id:			0,//id
		rank:		0,//排序
		elem:		"",//自定义元素
		tag:		this.Tag,
		css:		this.Css,
		hover:		this.Hover,
		active:		this.Active,
		txt:		this.Txt,
		fixedmenu:	!!this.FixedMenu,
		fixed:		this.Fixed,
		attribute:	this.Attribute,
		property:	this.Property
	};
	//先定义"0"顶级菜单
	this._menus = { "0": { "_children": [] } };
	//整理自定义菜单并插入到程序菜单对象
	forEach(this._custommenu, function(o) {
		//生成菜单对象(由于包含对象，要用深度扩展)
		var menu = DeepExtend( DeepExtend( {}, options ), o || {} );
		//去掉相同id菜单，同时排除了id为"0"的菜单
		if ( !!this._menus[ menu.id ] ) { return; };
		//重置属性
		menu._children = []; menu._index = -1;
		this._menus[menu.id] = menu;
	}, this);
	//建立树形结构
	this.forEachMenu(function( o, id, menus ) {
		if ( "0" === id ) { return; };//顶级没有父级菜单
		var parent = this._menus[o.parent];
		//父级菜单不存在或者父级是自己的话，当成一级菜单
		if ( !parent || parent === o ) { parent = menus[o.parent = "0"]; };
		//插入到父级菜单对象的_children中
		parent._children.push(o);
	});
	//整理菜单对象
	this.forEachMenu(function(o) {
		//如果有自定义元素的话先放到碎片文档中
		!!o.elem && ( o.elem = $$(o.elem) ) && this._frag.appendChild(o.elem);
		//修正样式,优先使用自定义元素的class
		if ( !!o.elem && o.elem.className ) {
			o.css = o.elem.className;
		} else if ( o.css === undefined ) { o.css = ""; };
		if ( o.hover === undefined ) { o.hover = o.css; };
		if ( o.active === undefined ) { o.active = o.hover; };
		//对菜单对象的_children集合排序(先按rank再按id排序)
		o._children.sort(function( x, y ) { return x.rank - y.rank || x.id - y.id; });
	});
  },
  //插入菜单
  InsertMenu: function(index, parent) {
	var container = this._containers[index];
	//如果是同一个父级菜单不用重复插入
	if ( container._parent === parent ) { return; };
	container._parent = parent;
	//把原有容器内菜单移到碎片对象中
	forEach( container._menus, function(o) { o._elem && this._frag.appendChild(o._elem); }, this );
	//重置子菜单对象集合
	container._menus = [];
	//把从父级菜单元素的子菜单对象集合获取的元素插入到容器
	forEach(this._menus[parent]._children, function( menu, i ){
		this.CheckMenu( menu, index );//检查菜单
		container._menus.push(menu);//加入到容器的子菜单集合，方便调用
		container.Menu.appendChild(menu._elem);//菜单元素插入到容器
	}, this);
  },
  //检查菜单
  CheckMenu: function(menu, index) {
	//索引保存到菜单对象属性中，方便调用
	menu._index = index;
	//如果菜单对象没有元素
	if ( !menu._elem ) {
		var elem = menu.elem;
		//如果没有自定义元素的话创建一个
		if ( !elem ) { elem = document.createElement(menu.tag); elem.innerHTML = menu.txt; };
		//设置property
		Extend( elem, menu.property );
		//设置attribute
		var attribute = menu.attribute;
		for (var att in attribute) { elem.setAttribute( att, attribute[att] ); };
		//设置样式
		elem.className = menu.css;
		//设置事件
		menu._event = BindAsEventListener( this, this.HoverMenu, menu );//用于清除事件
		addEvent( elem, "mouseover", menu._event );
		//保存到菜单对象
		menu._elem = elem;
	};
  },
  //触发菜单
  HoverMenu: function(e, menu) {
	var elem = menu._elem;
	//如果是内部元素触发直接返回
	if ( Contains( elem, e.relatedTarget ) || elem === e.relatedTarget ) { return; };
	clearTimeout(this._timerMenu);
	//可能在多个容器间移动，所以全部容器都重新设置样式
	this.forEachContainer(function(o, i){
		if ( o.Pos.visibility === "hidden" ) { return; };
		this.ResetCss(o);
		//设置当前菜单为active样式
		var menu = o._active;
		if ( menu ) { menu._elem.className = menu.active; };
	});
	//设置当前菜单为触发样式
	if ( this._containers[menu._index]._active !== menu ) { elem.className = menu.hover; };
	//触发显示菜单
	this._timerMenu = setTimeout( Bind( this, this.ShowMenu, menu ), this.Delay );
  },
  //显示菜单
  ShowMenu: function(menu) {
	var index = menu._index, container = this._containers[index], child = !!menu._children.length;
	//隐藏不需要的容器
	this.forEachContainer( function(o, i) { i > index && this.HideContainer(o); } );
	//重置当前容器_active
	container._active = null;	
	//如果有子级菜单
	if ( child ) {
		//设置当前容器_active
		container._active = menu;
		//显示下一级容器
		index++;//设置索引
		this.CheckContainer(index);//检查容器
		this.InsertMenu(index, menu.id);//插入菜单
		this.ShowContainer(menu);//显示容器
	};
	//重置当前容器的css
	this.ResetCss(container);
	//设置当前菜单样式
	menu._elem.className = child ? menu.active : menu.hover;
  },
  //初始化容器(索引, 容器元素)
  IniContainer: function(index, container) {
	var oContainer = container.Pos;
	//重置属性
	this.ResetContainer(container);
	//添加事件
	addEvent( oContainer, "mouseover", Bind( this, function(){ clearTimeout(this._timerContainer); } ) );
	addEvent( oContainer, "mouseout", BindAsEventListener( this, function(e){
		//先判断是否移出到所有容器之外
		var elem = e.relatedTarget,
			isOut = Every( this._containers, function(o){ return !(Contains(o.Pos, elem) || o.Pos == elem); } );
		if ( isOut ) {
			//清除定时器并隐藏
			clearTimeout(this._timerContainer); clearTimeout(this._timerMenu);
			this._timerContainer = setTimeout( Bind( this, this.Hide ), this.Delay );
		};
	}));
	//除了第一个容器外设置浮动样式
	if ( index ) {
		var css = container.Pos.style;
		css.position = "absolute"; css.display = "block"; css.margin = 0;
		//要后面的覆盖前面的
		css.zIndex = this._containers[index - 1].Pos.style.zIndex + 1;
	};
	//记录索引，方便调用
	container._index = index;
	//插入到容器集合
	this._containers[index] = container;
  },
  //检查容器
  CheckContainer: function(index) {
	if ( index > 0 && !this._containers[index] ) {
		//如果容器不存在，根据前一个容器复制成新容器，第一个容器必须自定义
		var pre = this._containers[index - 1].Pos
		//用了新的添加事件方式，没有ie的cloneNode的bug
			,container = pre.parentNode.insertBefore( pre.cloneNode(false), pre );
		//清除id防止冲突
		container.id = "";
		//初始化容器
		this.IniContainer( index, { Pos: container, Menu: container } );
	};
  },
  //显示容器
  ShowContainer: function(menu) {
	var index = menu._index,
		container = this._containers[index + 1].Pos,
		elem = menu.fixedmenu ? menu._elem : this._containers[index].Pos,
		pos = GetRelative( elem, container, menu.fixed );
	//定位并显示容器
	container.style.left = pos.Left + "px";
	container.style.top = pos.Top + "px";
	//执行显示前事件
	this.onBeforeShow(container, menu);
	container.style.visibility = "visible";
  },
  //隐藏容器
  HideContainer: function(container) {
	//设置隐藏
	var css = container.Pos.style;
	css.visibility = "hidden"; css.left = css.top = "-9999px";
	//重置上一个菜单的触发菜单对象
	this._containers[container._index - 1]._active = null;
  },
  //重置容器对象属性
  ResetContainer: function(container) {
	container._active = null;//重置触发菜单
	container._menus = [];//重置子菜单对象集合
	container._parent = -1;//重置父级菜单id
  },
  //隐藏菜单
  Hide: function() {
	this.forEachContainer(function(o, i){
		if ( i === 0 ) {
			//如果是第一个重设样式和_active
			this.ResetCss(o);
		} else {//隐藏容器
			this.HideContainer(o);
		};
	});
  },
  //重设容器菜单样式
  ResetCss: function(container) {
	forEach( container._menus, function(o, i){ o._elem.className = o.css; }, this );
  },
  //历遍菜单对象集合
  forEachMenu: function(callback) {
	for ( var id in this._menus ) { callback.call( this, this._menus[id], id, this._menus ); };
  },
  //历遍容器对象集合
  forEachContainer: function(callback) {
	forEach( this._containers, callback, this );
  },
  //添加自定义菜单
  Add: function(menu) {
	this._custommenu = this._custommenu.concat(menu);
	this.Ini();
  },
  //修改自定义菜单
  Edit: function(menu) {
	forEach( isArray( menu ) ? menu : [ menu ], function(o){
		//如果对应id的菜单存在
		if ( o.id && this._menus[o.id] ) {
			//从自定义菜单中找出对应菜单,并修改
			Every( this._custommenu, function(m, i){
				if ( m.id === o.id ) {
					this._custommenu[i] = DeepExtend( m, o ); return false;
				};
				return true;
				//用Every可以跳出循环
			}, this );
		};
	}, this );
	this.Ini();
  },
  //删除自定义菜单
  Delete: function() {
	var ids = Array.prototype.slice.call(arguments);
	this._custommenu = Filter( this._custommenu, function(o){
		return IndexOf(ids, o.id) === -1;
	});
	this.Ini();
  }
};



