
//-----------------------------------------------------------------------------
// implements smooth scroll

var smoothscroll = {
	
	// default scoll data
	WHEEL_STEP 				: 70,
	WHEEL_DUMPING 		: 0.41,
	WHEEL_BDUMPING 		: 1/890,
	WHEEL_ACCEL 			: 70,
	KEYBOARD_STEP 		: 100,
	KEYBOARD_DUMPING 	: 0.50,
	KEYBOARD_BDUMPING : 0.0,
	KEYBOARD_ACCEL 		: 100,
	PAGE_MARGIN 			: 40,

	m_sobj: null,
	m_jumpto: 0,
	m_wheelactive: false,
	m_lastfunctime: 0,
	m_lasteventtime: 0,
	m_speed: 300,
	m_enable: true,			// enable smooth scroll
	m_floatd: 0,

	m_lasttarget: null,
	m_keyscrolltarget: null,

	range: function(t,min,max) { return (t<min)?min:((t>max)?max:t); },

	//-----------------------------------------------------------------------------
	// operator pref
	
	getBoolPref: function(pref_name, default_value) 
	{
	   var result = default_value;
	   var prefservice = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService);
	   var prefs = prefservice.getBranch("");
	   if (prefs.getPrefType(pref_name) == prefs.PREF_BOOL) 
	       try { result = prefs.getBoolPref(pref_name); }catch(e){}
	
	   return(result);
	},

	//-------------------------------------------------------------------------------
	// smooth scroll implements
	
	toggleKeyHook: function(b)
	{
		var keyset = window.document.getElementById("SmoothScroll");
		if (keyset)
		{
			var keys = keyset.childNodes;
			for (var i=0; i<keys.length; i++)
				keys[i].setAttribute("disabled", !b);
		}
	},
     
	refreshTarget: function(target,detail)
	{
		var lastbody = (this.m_sobj) ? this.m_sobj.body : null;
		this.m_sobj = this.findNodeToScroll(target,detail);

		if(this.m_sobj == null || this.m_sobj.body != lastbody) 
		{
			this.m_wheelactive = false;
		}
		
		if(this.m_sobj) 
			dump("cometbird-----------------------" + this.m_sobj.log+"\n");
		else
			dump("cometbird----------------------- this obj is null\n");
	},

	checkDesignMode: function(ev)
	{
		if(ev.originalTarget == this.m_lasttarget) return;
		this.m_lasttarget = ev.originalTarget;

		var b = true;
		var mode = ev.originalTarget.ownerDocument.designMode;
		if(mode && mode.toLowerCase() == "on") b = false;
		this.toggleKeyHook(b);
	},

	handleEvent: function(ev)
	{
		if (!this.m_enable)
			return;	

		if(ev.altKey || ev.shiftKey || ev.ctrlKey) return;

		var fromkeyboard = false;

		switch(ev.type)
		{
		case "DOMMouseScroll":
			if(this.m_wheelactive == false) 
				this.refreshTarget(ev.originalTarget,ev.detail);
			break;

		case "shortcut":
			// sometimes happens on ff3 and this event must be ignored.
			if(this.m_sobj && 
				this.m_sobj.target.tagName.toLowerCase() == "input" &&
				this.m_sobj.target.getAttribute("type").toLowerCase() == "text") return;
				
			fromkeyboard = true;
			break;

		case "TabSelect":
			this.m_lasttarget = null;
			this.m_keyscrolltarget = null;
			this.refreshTarget(ev.originalTarget,ev.detail);
			this.checkDesignMode(ev);
			return;

		case "keydown":
		{	
			this.checkDesignMode(ev);
			if(this.m_keyscrolltarget != null) 
			{
				this.refreshTarget(this.m_keyscrolltarget,ev.detail);
				if(this.m_sobj == null) 
				{
					this.m_keyscrolltarget = null;
					this.refreshTarget(ev.originalTarget,ev.detail);
				}
			} 
			else 
			{
				this.refreshTarget(ev.originalTarget,ev.detail);
			}
			return;			
		}

		case "focus":
			this.m_lasttarget = null;
			this.m_keyscrolltarget = null;
			return;

		case "mousedown":
			this.m_keyscrolltarget = ev.target;
			this.m_wheelactive = false;
			
			// if mousedown on link, reset m_keyscrolltarget as null, for load a new page
			var node = ev.target;
			while ( node.nodeName.toLowerCase() != "html" )
			{
				if (node.nodeName.toLowerCase() == "a")
				{
					this.m_keyscrolltarget = null;
					break;
				}
				node = node.parentNode;
			}
			
			return;

		default: 
			return;
		}

		if(!this.m_sobj) { return; }

		ev.preventDefault();

		if(ev.detail == 0) return; // maybe this fixes microsoft smooth wheel problem

		var ctm = (new Date()).getTime();

		var pagescroll = (ev.detail*ev.detail != 1);

		// branch for step
		var step = (fromkeyboard) ?
			((pagescroll) ? Math.max(0, this.m_sobj.body.clientHeight - this.PAGE_MARGIN) : this.KEYBOARD_STEP) : this.WHEEL_STEP;

		this.m_speed = (this.m_speed + Math.min(300,ctm - this.m_lasteventtime)) / 2;

		var edgemargin = (0.64 / (fromkeyboard ? this.KEYBOARD_DUMPING : this.WHEEL_DUMPING)) + 3 ;

		if(!this.m_wheelactive)
		{
			this.m_lasteventtime = ctm;
			var delta =  step * ((ev.detail<0)?-1:1);
			this.m_sobj.body.scrollTop += (delta<0)?-1:1;
			this.m_jumpto = this.m_sobj.body.scrollTop + delta;
			// smooth edge
			this.m_jumpto = this.range(this.m_jumpto,-edgemargin,this.m_sobj.maxscroll+edgemargin);
			this.m_wheelactive = true;
			this.m_lastscrolltop = this.m_sobj.body.scrollTop;
			this.m_lastfunctime = (new Date()).getTime(); - 17;
			this.m_vpos = this.m_sobj.body.scrollTop;
			this.m_lastd = 0;
			this.funcwheel(fromkeyboard,delta);
		}
		else 
		{
			if((ev.detail<0 && this.m_jumpto == -edgemargin) || (ev.detail>0 && this.m_jumpto == this.m_sobj.maxscroll+edgemargin)) 
					return;

			this.m_lasteventtime = ctm;
			var accel = 1.0;
			if (fromkeyboard) 
			{
				if(!pagescroll) 
					accel = this.KEYBOARD_ACCEL/100;
			} 
			else 
			{
				var remain = this.m_jumpto - this.m_sobj.body.scrollTop;
				if((remain * ev.detail) < 0) 
				{
					this.m_jumpto = this.m_sobj.body.scrollTop; 
					return; 
				}
				accel = this.range(this.WHEEL_ACCEL/this.m_speed, 1.0, (400/step));
			}
			
			this.m_jumpto += Math.round(step*((ev.detail<0)?-1:1) * accel);
			// smooth edge
			this.m_jumpto = this.range(this.m_jumpto,-edgemargin,this.m_sobj.maxscroll+edgemargin);
		}
	},

	funcwheel: function(kbd,idelta)
	{
		if (this.m_wheelactive == false || !this.m_sobj) 
		{
			this.m_wheelactive = false;
			return; 
		}

		var tm = (new Date()).getTime();
		var frametime = (tm - this.m_lastfunctime);

		var fframetime = frametime/17;

		if (frametime<=0) 
		{ 
			setTimeout("smoothscroll.funcwheel("+kbd+","+idelta+");",1); 
			return; 
		}

		this.m_lastfunctime = tm;

		var f = kbd ? this.KEYBOARD_DUMPING : this.WHEEL_DUMPING;
		var bdump = kbd ? this.KEYBOARD_BDUMPING : this.WHEEL_BDUMPING;

		if (bdump>0)
		{
			var timefromev = tm - this.m_lasteventtime + 17;
			var bd = 1.0;
			 bd = this.range(timefromev/(bdump*2000) + ((this.m_lastd!=0)?0.20:0.0), 0.05, 1.0);
			this.m_lastd = bd;
			f *= bd;
		}
		
		f *= this.range(fframetime,0.6,1.0);
		if (fframetime > 1.0) f += (1.0-f) * f * Math.min((fframetime-1.0), 1.0);
		var fordest = (this.m_jumpto - this.m_vpos);
		var d = fordest * f;

		var lenfordest = fordest*fordest;

		if (lenfordest<=1.0 || (lenfordest<100.0 && d*d<0.4) || (!kbd && d*idelta<0) || this.m_sobj.body.scrollTop != this.m_lastscrolltop ) 
		{ 
			this.m_wheelactive = false; 
			return; 
		}

		this.m_vpos += d;		
		this.m_sobj.body.scrollTop = this.m_vpos; //Math.round(this.m_vpos); someone said that a bug the stride always tend to 1px farther to upward but we dont care.

		// collide to an edge of scroll area
		if (this.m_sobj.body.scrollTop == 0 || this.m_sobj.body.scrollTop == this.m_sobj.maxscroll) 
		{ 
			this.m_wheelactive = false; 
			return; 
		}
	
		this.m_lastscrolltop = this.m_sobj.body.scrollTop;

		setTimeout("smoothscroll.funcwheel("+kbd+","+idelta+");",10);
	},

	// based on the code in All-in-One Gestures
	findNodeToScroll: function(orig,hint) 
	{
		// scroll type
		function getstyle(e, pname) 
		{
			var p = e.ownerDocument.defaultView.getComputedStyle(e, "").getPropertyValue(pname);
			var val = parseFloat(p);
			if(!isNaN(val)) return Math.ceil(val);
			if(p == "thin") return 1;
			if(p == "medium") return 3;
			if(p == "thick") return 5;
			return 0;
		}

		// 1 vertical only  2 horizontal only
		function getscrolltype(wscroll, wclient, hscroll, hclient) 
		{
			if(hclient < 50) return 3;
			if(wscroll <= wclient && hscroll <= hclient) return 3;
			if(wscroll > wclient && hscroll > hclient) return 0;
			if(wscroll > wclient) return ((hscroll <= hclient) - 0) << 1;  // 0 or 2
			return (wscroll <= wclient) - 0; 
		}

		var scrollovercheck = 
			(!hint)?
				(function(){return true;}):(
				(hint<0)?
					(function(a,b){return (a>0);}):
					(function(a,b){return (a<b)&&(b>1)/* <- magic code | there is an area unmovable really but looks like 1px scrollable */;})
		);

		var doc = orig.ownerDocument.documentElement;
		if (doc && doc.nodeName.toLowerCase() != "html") 
			return null;

		var bodies = doc.getElementsByTagName("body");
		if(!bodies || bodies.length == 0)
			return null;

		var body = bodies[0];

		var node = (orig == doc) ? body : orig;

		var retobj = 
		{
			scrollXBy: function(d){retobj.body.scrollLeft += d;},
			scrollYBy: function(d){retobj.body.scrollTop += d;},
			target: orig
		};

		var log = "";

		upperbody = false;

		do{
			var nodename = node.nodeName.toLowerCase();// a->div->body->html

			/*log*/log += nodename;

			/***/try{
			if(/^(option|optgroup)$/.test(nodename)) { return null; }

			var overflowprop = node.ownerDocument.defaultView.getComputedStyle(node, "").getPropertyValue("overflow");

			if(node.clientWidth && node.clientHeight &&
				(overflowprop != "hidden") &&
				(node == doc || node == body || overflowprop != "visible") 
			  )
			{
				var realwidth = node.clientWidth + getstyle(node,"border-left-width") + getstyle(node,"border-right-width");
				var realheight = node.clientHeight + getstyle(node,"border-top-width") + getstyle(node,"border-bottom-width");

				var scrolltype = getscrolltype(node.scrollWidth, realwidth, node.scrollHeight, realheight);
				/*log*/log += "(" + node.scrollTop + " " + (node.scrollHeight-realheight) + ")";

				if((scrolltype == 0 || scrolltype == 1) && 
					((/^(html|body)$/.test(nodename) == false) || scrollovercheck(node.scrollTop,node.scrollHeight-realheight))
				)
				{
					retobj.scrolltype = scrolltype;
					retobj.body = node;
					retobj.width = realwidth;
					retobj.height = realheight;
					retobj.maxscroll = node.scrollHeight - node.clientHeight;
					retobj.log = log;
					return retobj;
				}
			}

			if(node == doc) break;

			/***/}catch(e){}

			/*log*/log += "->";

			node = node.parentNode;

		}while(node);
		if(orig.ownerDocument.defaultView.frameElement) 
			return this.findNodeToScroll(orig.ownerDocument.defaultView.frameElement.ownerDocument.documentElement,hint);

		log += "default";
		// no scrollable area found in content ( mainly for image only page to handle )
		// return body
		//return null;
		retobj.scrolltype = 0;
		retobj.body = body;
		retobj.width = body.clientWidth;
		retobj.height = body.clientHeight;
		retobj.maxscroll = body.scrollHeight - body.clientHeight;
		retobj.log = log;
		return retobj;
	},

};

//-----------------------------------------------------------------------------

function smooth_scroll(val)
{
	var ev = { 
		preventDefault: function(){}, 
		stopPropagation: function(){}, 
		detail: val, 
		type: "shortcut" 
		};
	smoothscroll.handleEvent(ev);
}

function refresh_preferences(ev)
{
	if (ev)
	{
		if(ev.eventPhase!=2)
			return;	
	}
	
	smoothscroll.m_enable = smoothscroll.getBoolPref("general.smoothScroll", true);
	dump("options smooth scroll checkbox value is---------" + smoothscroll.m_enable + "\n");
	
	// enable hotkey
	smoothscroll.toggleKeyHook(smoothscroll.m_enable);
}

//-----------------------------------------------------------------------------
// init smoothscroll

function smoothscroll_unload()
{
	smoothscroll.m_sobj = null;
	
	window.removeEventListener("onload",					smoothscroll_onload,			false);
	window.removeEventListener("focus",						refresh_preferences,			false);
	window.removeEventListener("focus",						smoothscroll,							false);
	window.removeEventListener("DOMMouseScroll", 	smoothscroll, 						false);
	window.removeEventListener("keydown",					smoothscroll,							true);
	window.removeEventListener("unload",					smoothscroll_unload,			false);
	getBrowser().tabContainer.removeEventListener("TabSelect", smoothscroll,false);	
	window.removeEventListener("mousedown",				smoothscroll,							false);
}

function smoothscroll_onload()
{
	try{smoothscroll_unload();}catch(e){}
	
	window.addEventListener("focus", 					refresh_preferences,					false);
	window.addEventListener("focus", 					smoothscroll,									false);
	window.addEventListener("DOMMouseScroll", smoothscroll, 								false);
	window.addEventListener("keydown",				smoothscroll,									true);
	window.addEventListener("unload",					smoothscroll_unload,					false);
	getBrowser().tabContainer.addEventListener("TabSelect", smoothscroll,		false);		
	window.addEventListener("mousedown",			smoothscroll,									false);

	// print preview issue
	smoothscroll.onEnterPrintPreview_last = onEnterPrintPreview;
	smoothscroll.onExitPrintPreview_last = onExitPrintPreview;
	
	onEnterPrintPreview = function() { smoothscroll.onEnterPrintPreview_last(); smoothscroll.toggleKeyHook(false);  };
	onExitPrintPreview = function() { smoothscroll.onExitPrintPreview_last(); smoothscroll.toggleKeyHook(true); };

	window.setTimeout(refresh_preferences, 10);
}

window.addEventListener("load", 	smoothscroll_onload, false);
