// ==UserScript==
// @name            gmpage_scroller
// @namespace       http://blog.setunai.net/
// @include         http://*
// @version         0.92
// ==/UserScript==

if (document.contentType != 'text/html') return;
if (window.frameElement != null) return;

var g = {
	'debug' : false,
	'log' : function(msg) {
		if (!this.debug) {
			return;
		}
		GM_log(msg, 0);
	},
	'page' : null
}

function Page() {
	this._target = '';

	g.log(document.compatMode);
	switch (document.compatMode) {
	case 'BackCompat':
	case 'QuirksMode':
		this._target = 'body';
		break;
	case 'CSS1Compat':
		this._target = 'documentElement';
		break;
	default:
		break;
	}
}
Page.prototype = {
	'_h' : null,
	'_speed' : 30,
	'_rate' : 94,

	'setH' : function () {
		this._h = this._getPageH();

		for (var i=0; i<this._h.length; i++) {
			this._h[i].realTop = this._getTopPosition(this._h[i]);
			g.log(this._h[i].textContent + ':' + this._h[i].realTop);
		}

		this._h.sort(this._cmpTop);
	},
		'_getPageH' : function () {
			var headers = document.evaluate(
				'//h1|//h2|//h3|//h4|//h5|//h6', document.body, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null
			);

			var minWidth = Math.floor(document.width / 3);

			var index = 0;
			var h = [];
			for(var i=0; i<headers.snapshotLength; i++) {
				var width = headers.snapshotItem(i).offsetWidth;

				if (minWidth < width) {
					h[index++] = headers.snapshotItem(i);
				}
			}

			return h;
		},
		'_cmpTop' : function(a, b) {
			return a.realTop - b.realTop;
		},
		'_getTopPosition' : function(ele) {
			var total = ele.offsetTop;

			var pNode = ele;
			while (pNode = pNode.offsetParent) {
				total += pNode.offsetTop;
			}
			return total;
		},
	'ready' : function() {
		if (this._target == '') {
			return false;
		}
		return true;
	},
	'nextH' : function () {
		var top = document[this._target].scrollTop;
		var defaultPos = top + (Math.floor(window.innerHeight / 100) * this._rate);

		var nowIndex = null;
		for(var i=0; i<this._h.length; i++) {
			if (top == this._h[i].realTop) {
				nowIndex = i;
				break;
			}
		}
		var through = top;
		if (nowIndex != null) {
			through = top + Math.floor(window.innerHeight / 5);
		} else {
			nowIndex = 0;
		}

		for(var i=nowIndex; i<this._h.length; i++) {
			if (through < this._h[i].realTop) {
				if (this._h[i].realTop < defaultPos) {
					g.log('nextH :' + this._h[i].textContent);
					this._moveNext(this._h[i].realTop);
					return;
				}
				break;
			}
		}
		g.log('nextH default:' + defaultPos);
		this._dispLine(top + window.innerHeight);
		this._moveNext(defaultPos);
	},
		'_moveNext' : function(target) {
			var label = this._target;
			var top = document[label].scrollTop;

			var val = this._getDiff(top, target);
			if (val == 0) {
				return;
			}

			var count = 0;
			var timeId = setInterval(function () {
				var pos = top + (val * ++count);

				if (target > pos) {
					document[label].scrollTop = pos;
				}else{
					document[label].scrollTop = target;
					clearInterval(timeId);
				}
			}, this._speed);
		}, 
	'prevH' : function() {
		var top = document[this._target].scrollTop;
		var defaultPos = top - (Math.floor(window.innerHeight / 100) * this._rate); 
		if (defaultPos < 0) {
			defaultPos = 0;
		}

		for(var i=this._h.length-1; i>=0; i--) {
			if (top > this._h[i].realTop) {
				if (this._h[i].realTop > defaultPos) {
					g.log('prevH :' + this._h[i].textContent);
					this._movePrev(this._h[i].realTop);
					return;
				}
				break;
			}
		}
		g.log('prevH default:' + defaultPos);
		this._dispLine(top);
		this._movePrev(defaultPos);
	},
		'_movePrev' : function(target) {
			var label = this._target;
			var top = document[label].scrollTop;

			var val = this._getDiff(top, target);
			if (val == 0) {
				return;
			}

			var count = 0;
			var timeId = setInterval(function () {
				var pos = top - (val * ++count);

				if (target < pos) {
					document[label].scrollTop = pos;
				}else{
					document[label].scrollTop = target;
					clearInterval(timeId);
				}
			}, this._speed);
		},
	'_getDiff' : function (top, target) {
		var s = 0;
		if (top > target) {
			s = top - target;
		}else {
			s = target - top;
		}
		if (s == 0) {
			return 0;
		}

		var diff = Math.floor(s / 5);
		if (s > 200) {
			diff = Math.floor(s / 10);
		}

		return diff;
	}, 

	'_dispLine' : function (pos) {
		var line = document.createElement('hr');

		with(line.style) {
			top = pos + 'px';
			left = '0px';
			width = '99%';

			padding = '0xp';
			margin = '0px';

			position = 'absolute';

			border = '1px solid red';
			display = 'block';
			visibility = 'visible';

			zIndex = 999;
		};

		document.body.appendChild(line);
		var timeId = setTimeout(function () {
			var line = document.body.lastChild;
			document.body.removeChild(line);
			clearTimeout(timeId);
		}, 1200);
	}
}

window.addEventListener('load', function() { 
	g.page = new Page();
	if (!g.page.ready()) {
		return;
	}

	g.page.setH();

	window.addEventListener('keydown', function(e) {
		if (new RegExp(/input|textarea/i).test(e.target.tagName)) {
			return;
		}
		if (e.shiftKey || e.ctrlKey) {
			return;
		}

		switch (e.keyCode) {
		case 74:		// j
			g.page.nextH();
			break;
		case 75:		// k
			g.page.prevH();
			break;
		default:
			return;
		}

		e.preventDefault();
	}, false);
}, false);




