/*
 * 	exFixed 1.2.0 - jQuery plugin
 *	written by Cyokodog	
 *
 *	Copyright (c) 2009 Cyokodog (http://d.hatena.ne.jp/cyokodog/)
 *	Dual licensed under the MIT (MIT-LICENSE.txt)
 *	and GPL (GPL-LICENSE.txt) licenses.
 *
 *	Built for jQuery library
 *	http://jquery.com
 *
 */

(function($j){

	$j.ex = $j.ex || {};
	var ex = $j.extend({},$j.ex);

	/*
	 * 	ex.defineExPlugin 2.0
	 */
	ex.defineExPlugin = function( pluginName , constr , param){
		$j.fn[ pluginName ]=
			function( config , callback ){
				var o = this,arr = [];
				p = param ||{
					eachTarget : true
				};
				if(p.eachTarget)
					o.each(function( idx ){
						arr.push(new constr( o.eq(idx) , config ));
					});
				else
					arr.push(new constr( o , config ));
				var obj = $j(arr);
				for(var i in constr.prototype)( function(i){
					if(i.slice(0,1)!= '_'){
						obj[i] = function(){
							return obj[0][i].apply( obj[0] , arguments );
						};
					}
				})(i);
				obj.target = function(){ return o };
				o['get'+pluginName.substr(0,1).toUpperCase()+pluginName.substr(1)] = function(){
					return obj;
				};
				if(typeof callback == 'function')obj.each(callback);				
				return this;
			};
	};
	ex.scrollEvent = function( target , config ){
		var o = this;
		if( typeof config == 'function') config = {
			callback : config
		}
		var c = o.config = $j.extend({},ex.scrollEvent.defaults,config,{
			target : target
		});
		c.status = 0;
		c.scroll = o.getPos();
		c.target.scroll(function( evt ){
			if (o.isMove()) {
				c.status = (c.status == 0 ? 1 : (c.status == 1 ? 2 : c.status) );
				c.callback( evt , c );
			}
			if(c.tm) clearTimeout(c.tm);
			c.tm = setTimeout(function(){
				o.isMove();
				c.status = 0;
				c.callback( evt , c );
			},c.delay);
		});
	}
	$j.extend(ex.scrollEvent.prototype,{
		isMove : function(){
			var o = this, c = o.config;
			var pos = o.getPos();
			var scrollY = (pos.top != c.scroll.top);
			var scrollX = (pos.left != c.scroll.left);
			if(scrollY || scrollX){
				c.scrollY = scrollY;
				c.scrollX = scrollX;
				c.prevScroll = c.scroll;
				c.scroll = pos;
				return true;
			}
			return false;
		},
		getPos : function(){
			var o = this, c = o.config;
			return {
				top : c.target.scrollTop(),
				left : c.target.scrollLeft()
			}		
		}
	});
	ex.scrollEvent.defaults = {
		delay : 100
	}

	$j.ex.fixed = function(target, config){
		var o = this;
		var c = o.config = $j.extend({},$j.ex.fixed.defaults,config,{
			target : target,
			logicSize : {},
			rowSize : {},
			currentStyle : '',
			style : '',
			window : $j(window),
			staticFixed : false,		
			oldBrowser : $j.browser.msie && ($j.browser.version < 7.0 || !$j.boxModel)
		});

		if(c.baseNode) c.baseNode = $(c.baseNode);

		var size = o._cleanSize(c);

		//static ?
		o._eachSizeSet(function(idx , at1 , cm1){
			c.staticFixed = c.staticFixed || 
				(size[at1.pos1] == undefined && size[at1.pos2] == undefined);
		});

		if( c.oldBrowser ) o._padPos( size , o._cleanSize(c.target[0].currentStyle) );
		else if(c.staticFixed) return;

		c.container = $j.boxModel ? $j('html') : $j('body');
		c.container.height(); //for IE Bug

		c.target.css('position',c.oldBrowser ? 'absolute' : 'fixed');
		if(c.oldBrowser && !/hidden|scroll/i.test(c.target.css('overflow'))){
			c.target.css('overflow','hidden');
		}

		o._smoothPatch();

		o._fixed(size);
		c.window.resize( function(){
			if(c.oldBrowser || c.baseNode){
				o._fixed();
			}
		});

		if(!(c.fixedX && c.fixedY)){
			if (c.oldBrowser) {
				var tm;
				c.window.scroll(function(){
					if(tm) clearTimeout(tm);
					tm = setTimeout(function(){
						o._fixed();
					},0);
				});
			}
			else{
				new ex.scrollEvent(c.window,function( evt , pa ){
					if((pa.scrollX && !c.fixedX) || (pa.scrollY && !c.fixedY)){
						if(pa.status == 1){
							o._fixed(c.logicSize,{
								unfixed:true
							});
						}
						else
						if (pa.status == 0) {
							o._fixed();
						}
					}
				})
			}
		}
	}
	$j.ex.fixed.config = {
		smoothPatched : false
	};
	$j.ex.fixed.defaults = {
	//	top : ?,
	//	right : ?,
	//	bottom : ?,
	//	left : ?,
	//	width : ?,
	//	height : ?,
		baseNode : '',
		baseX : true,
		baseY : true,
		fixedX : true,
		fixedY : true
	};
	$j.extend($j.ex.fixed.prototype,{
		_attn :[
			{size:'height',pos1:'top',pos2:'bottom'},
			{size:'width',pos1:'left',pos2:'right'}
		],
		_camel :[
			{size:'Height',pos1:'Top',pos2:'Bottom'},
			{size:'Width',pos1:'Left',pos2:'Right'}
		],
		_moveFixedFront : function(){
			var o = this , c = o.config;
			var parents = c.target.parents();
			var containers = parents.filter(function(idx){
				var el = parents.eq(idx);
				return !(/HTML|BODY/i.test(el[0].tagName)) && parents.eq(idx).css('position')!='static';
			});
			if(containers.size())
				containers.eq(containers.size()-1).after(c.target)
			return o;
		},
		_smoothPatch : function(){
			var o = this , c = o.config;
			o._moveFixedFront();
			if( !c.oldBrowser ) return o;
			$j.ex.fixed.config.smoothPatched = true;
			var html = $j('html');
			if(html.css('background-image') == 'none'){
				html.css({'background-image':'url(null)'});
			}
			html.css({'background-attachment':'fixed'});
			return o;
		},
		_eachSize : function( f ){
			var o = this , c = o.config;
			for (var i = 0; i < o._attn.length; i++) {
				var attn = o._attn[i];
				for (var j in attn) {
					var name = attn[j];
					f({
						idx : i,
						name : name,
						camel : name.slice(0,1).toUpperCase() + name.slice(1)
					});
				}
			}
		},
		_eachSizeSet : function( f ){
			var o = this , c = o.config;
			for (var i = 0; i < o._attn.length; i++) {
				f( i , o._attn[i] , o._camel[i] , o._attn[1-i] , o._camel[1-i]);
			}
		},
		_parseSize : function( val , xFlg ){
			var o = this , c = o.config;
			if( val == 'auto' ) return undefined;
			if((val + '').indexOf('%') < 0) return parseInt(val) || 0;
			var cSize = c.container.attr(xFlg ? 'clientWidth' : 'clientHeight');
			return Math.round(cSize * parseInt(val) / 100);
		},
		_parseIntSize : function( val , xFlg ){
			var o = this , c = o.config;
			return parseInt( o._parseSize( val , xFlg ) ) || 0;
		},
		_cleanSize : function(size){
			var o = this , c = o.config;
			var ret = {};
			o._eachSize( function( pa ){
				if(/undefined|auto/i.test(size[pa.name])){
					try{
						delete size[pa.name];
					}
					catch(e){}
				}
				else{
					ret[pa.name] = size[pa.name];
				}
			});
			return ret;	
		},
		_padPos : function( size , pad ){
			var o = this , c = o.config;
			var pos;
			o._eachSizeSet(function(idx , at1 , cm1){
				if (size[at1.pos1] == undefined && size[at1.pos2] == undefined) {
					if( (pos = pad[at1.pos1]) != undefined ) size[at1.pos1] = pos;
					else if( (pos = pad[at1.pos2]) != undefined ) size[at1.pos2] = pos;
				}
				if(size[at1.size] == undefined) size[at1.size] = pad[at1.size];
			});
			return size;
		},
		_calcRowSize : function( size , opt ){
			var o = this , c = o.config;
			var opt = $j.extend({
				abs : false,
				base : c.baseNode,
				unfixed : false
			},opt);
			var ret = {};

			o._eachSize( function( pa ){
				var val = size[pa.name];
				if(!(/undefined/i.test( val ))){
					ret[pa.name] = o._parseIntSize(val,/width|left|right/i.test(pa.name));
					if (opt.abs && /top|left/i.test(pa.name)){
						ret[pa.name] += c.window['scroll'+pa.camel]();
					}
				}
			});

			if(opt.base){
				var basePos = c.baseNode.offset();
				o._eachSizeSet( function(idx , pa , cm ){
					basePos[pa.pos2] = c.container.attr('client'+cm.size)
						- (basePos[pa.pos1] + c.baseNode['outer'+cm.size]());
				});
				o._eachSize( function( pa ){
					if(!(/height|width/i.test(pa.name)) && ret[pa.name] == undefined
						&& ((!pa.idx && c.baseY) || (pa.idx && c.baseX)) ){
						var name = pa.name == 'top' ? 'bottom' : pa.name == 'bottom' ? 'top' : pa.name == 'left' ? 'right' : 'left';
						ret[name] += basePos[name];
					}
				});
			}

			var fg = opt.unfixed && !c.fixedX ? -1 : 1;
			if(fg == -1|| (!opt.unfixed && !c.fixedY)){
				if(ret.top != undefined)ret.top -= (c.window.scrollTop()*fg);
				if(ret.bottom != undefined)ret.bottom += (c.window.scrollTop()*fg);
			}
			var fg = !opt.unfixed && !c.fixedX ? -1 : 1;
			if(fg == -1|| (opt.unfixed && !c.fixedY) ){
				if(ret.left != undefined )ret.left += (c.window.scrollLeft()*fg);
				if(ret.right != undefined )ret.right -= (c.window.scrollLeft()*fg);
			}
			return ret;
		},
		_fixed : function( size , opt ){
			var o = this , c = o.config;
			var opt = $j.extend({
				unfixed : false
			},opt);
			if(size) c.logicSize = o._padPos(o._cleanSize(size),c.logicSize);
			if(!c.oldBrowser){
				c.target.css( 
					$.extend(
						c.baseNode || !(c.fixedX && c.fixedY) ? o._calcRowSize( c.logicSize , opt ) : c.logicSize,
						{position:opt.unfixed?'absolute':'fixed'}
					)
				);
			}
			else{
				var rowSize = o._calcRowSize( c.logicSize );
				if(c.target.is(':hidden')) c.target.show();
				var hide = false;
				o._eachSizeSet( function( idx , pa , cm ){
					c.target.css( pa.size , rowSize[ pa.size ] );
					var pos1 = rowSize[ pa.pos1 ];
					if( pos1 == undefined ){ //right,bottom based
						pos1 = c.container.attr( 'client' + cm.size ) - rowSize[ pa.pos2 ] - c.target[ 'outer' + cm.size ]();
					}
					var over = (pos1 + c.target['outer'+cm.size]()) - c.container.attr('client'+cm.size);
					if (over > 0) {
						over = c.target[pa.size]() - over;
						if (over > 0) 
							c.target[pa.size](over);
						else hide = true;
					}
					if(!hide){
						c.target[0].style.setExpression( pa.pos1 ,
							pos1 +
							(	(!idx && !c.fixedY) || (idx && !c.fixedX) ? 
								c.window['scroll'+cm.pos1]() : 
								'+eval(document.body.scroll'+cm.pos1+'||document.documentElement.scroll'+cm.pos1+')'
							)
						);
					}
				});
				if(hide) c.target.hide();
			}
		},
		target : function(){
			return this.config.target;
		},
		fixedOpen : function( f ){
			var o = this , c = o.config;
			if(c.staticFixed) return;
		
			if (c.oldBrowser) {
				c.target[0].style.removeExpression('top');
				c.target[0].style.removeExpression('left');
			}
			if( f ) setTimeout(function(){ // for window.scrollTop()
				if (c.oldBrowser) {
					c.target.css({top:'auto',left:'auto'});
					c.target.css(o._calcRowSize(c.logicSize , {
						abs : true
					}));
				}
				f();
			},100);
			return o;
		},
		fixedClose : function( size ){
			var o = this , c = o.config;
			if(c.staticFixed) return;
			o._fixed( size );
			return o;
		},
		fixedSize : function( size ){
			var o = this , c = o.config;
			return o._calcRowSize(o._padPos(size,c.logicSize),{
				abs : c.oldBrowser
			});
		},
		resize : function( size ){
			var o = this , c = o.config;
			o.fixedOpen(function(){
				o.fixedClose( size );
			})	
			return o;
		}
	});
	ex.defineExPlugin('exFixed',$j.ex.fixed);

})(jQuery);


