// Yet Another Smooth Scrolling for IE ver.1.0.1
// Copyright (c) 2008 kataho ( kataho.mail@gmail.com )
//
// ==UserScript==
// @name          YetAnotherSmoothScrolling for IE
// @namespace     http://www.aa.alpha-net.ne.jp/kataho/xpi/yassie.html
// @description	  Make scrolling comfortable smooth
// @include       *
// @run-at	  document_start
// ==/UserScript==

document.yass_samp = 
{
	init: function(_this) 
	{ 
		/* >> Only here you need to edit for changing smooth animation in detail */

		// Pixels scroll with one wheel tick
		_this.m_pref.wheelstep = 100; // (0 - inf) 

		// Latency of rising of speed in beginning of a movement
		var smooth_begin = 30; // (0.0 - 100.0)

		// Latency of fading of speed in ending of a movement
		var smooth_end = 70; // (0.0 - 100.0)

		// Sensitivity for wheel spinning speed that makes wider scale-up to movement
		_this.m_pref.wheelaccel = 200; // (0 - inf) 

		/* << */

		_this.m_pref.wheeldumping = (900-(8.9*smooth_end))/1000;
		_this.m_pref.wheelbdumping = (8.9*smooth_begin)/890;
	},

	m_pref: {},

	m_sobj: null,

	m_jumpto: 0,
	m_wheelactive: false,

	m_lastfunctime: 0,
	m_lasteventtime: 0,
	m_speed: 300,

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

	handleEvent: function(ev)
	{
		if(ev.srcElement.tagName =='TEXTAREA') return true;

		var detail = -ev.wheelDelta;

		_this = document.yass_samp;

		var lastbody = (_this.m_sobj) ? _this.m_sobj.body : null;
		_this.m_sobj = _this.findNodeToScroll(ev.srcElement,detail);

		if(_this.m_sobj == null || _this.m_sobj.body != lastbody) 
		{
			_this.m_wheelactive = false;
		}

		if(!_this.m_sobj) { return true; }
//alert(_this.m_sobj.log);

		if(detail == 0) return false;

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

		// branch for step
		var step = _this.m_pref.wheelstep;

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

		if(!_this.m_wheelactive)
		{
			var delta =  step * ((detail<0)?-1:1);
			_this.m_sobj.body.scrollTop += (delta<0)?-1:1;
			_this.m_jumpto = _this.m_sobj.body.scrollTop + delta;
			_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(_this,ev.fromkeyboard,delta);
		}
		else 
		{
			var accel = _this.m_pref.kbdaccel/100;
			if(!ev.fromkeyboard)
			{
				var remain = _this.m_jumpto - _this.m_sobj.body.scrollTop;
				if((remain * detail) < 0) 
				{
					_this.m_jumpto = _this.m_sobj.body.scrollTop; 
					return false; 
				}
				accel = _this.range(_this.m_pref.wheelaccel/_this.m_speed, 1.0, (400/step));
			}
			_this.m_jumpto += Math.round(step*((detail<0)?-1:1) * accel);
		}
		return false;
	},

	funcwheel: function(_this,kbd,idelta)
	{
		if(_this.m_wheelactive == false) return;
		if(!_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("document.yass_samp.funcwheel(document.yass_samp,"+kbd+","+idelta+");",1); return; }

		_this.m_lastfunctime = tm;

		var f = kbd?_this.m_pref.kbddumping:_this.m_pref.wheeldumping;
		var bdump = kbd?_this.m_pref.kbdbdumping:_this.m_pref.wheelbdumping;

		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 = Math.abs(fordest);

		if(lenfordest<=1 || (lenfordest<10 && d*d<0.4) || 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;

		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("document.yass_samp.funcwheel(document.yass_samp,"+kbd+","+idelta+");",1);
	},

	findNodeToScroll: function(orig,hint) 
	{
		// vertically scrollable?
		function getscrolltype(hscroll, hclient) 
		{
			if(hclient < 50) return false;
			if(hscroll <= hclient) return false;
			return true;
		}

		var scrollovercheck = (!hint)?(function(){return true;}):(function(a,b){return (a>0);});
		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];

		if(doc.scrollTop==0 && body.scrollTop==0 && hint>0)
		{
			doc.scrollTop=1;
			body.scrollTop=1;
		}

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

		var retobj = 
		{
			originalTarget: orig
		};

		var log = "";

		do{
			log += node.nodeName + "("+node.clientHeight+")";

			if(/option|optgroup/.test(node.nodeName.toLowerCase())) { return null; }

			if(node.clientHeight && (node == doc || node == body))
			{
				var realheight = node.clientHeight;
				log += "(" + node.scrollTop + " " + (node.scrollHeight-realheight) + ")";
				if(getscrolltype(node.scrollHeight, realheight) && scrollovercheck(node.scrollTop,node.scrollHeight-realheight))
				{
					retobj.body = node;
					retobj.height = realheight;
					retobj.maxscroll = node.scrollHeight - node.clientHeight;
					retobj.log = log;
					return retobj;
				}
			}

			if(node == doc) break;

			log += "->";
			node = node.parentNode;

		}while(node);
try{
		if(orig.ownerDocument.frames.frameElement) 
			return this.findNodeToScroll(orig.ownerDocument.frames.frameElement.ownerDocument.documentElement,hint);
}catch(e){}
		// exception for ie spcecific weird page
		if(doc.clientHeight == 0 && body.clientHeight > 0 && (doc.scrollHeight - body.clientHeight) > 0)
		{
			return null;
		}


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


document.attachEvent("onmousewheel", document.yass_samp.handleEvent);
document.yass_samp.init(document.yass_samp);
