/* -------------------------------------------------------------------------- */
/** 
 *    @fileoverview
 *       Smooth Scroll w/auto setup
 *
 *    @version rev011.2007-05-15
 *    @requires common.js
 */
/* -------------------------------------------------------------------------- */


var BA_SMOOTHSCROLL;



/* -------------------- Settings for BASmoothScrollAutoSetup -------------------- */

var BA_SMOOTHSCROLL_ENABLED           = true;
var BA_SMOOTHSCROLL_FIND_ATONCE       = false;
var BA_SMOOTHSCROLL_USE_POSTPROCESS   = true;
var BA_SMOOTHSCROLL_OFFSET_X          = 0;
var BA_SMOOTHSCROLL_OFFSET_Y          = 0;
var BA_SMOOTHSCROLL_IGNORE_NODE_CNAME = 'noSmoothScroll'

var BA_SMOOTHSCROLL_NODE_VALIDATOR    = function(node) {
	return !node.hasClassNameBA(BA_SMOOTHSCROLL_IGNORE_NODE_CNAME);
};

var BA_SMOOTHSCROLL_POSTPROCESS_FUNC  = function(curX, curY, destX, destY, SS) {
	if (BA_SMOOTHSCROLL_USE_POSTPROCESS && (BA.ua.isGecko || BA.ua.isWinIE)) {
		location.href = SS.fromNode.getAttributeBA('href');
	}
}



/* -------------------- Constructor : BASmoothScroll -------------------- */

function BASmoothScroll() {
	this.offsetX = 0;
	this.offsetY = 0;
	this.unit    = 5;
	this.wait    = 10;
	this.cuX     = 0;
	this.cuY     = 0;
	this.toX     = 0;
	this.toY     = 0;
	this.timer   = null;
}

BASmoothScroll.prototype = {
	setTargetNode : function(node) {
		if (!node || node.instanceOf != 'BAElement') {
			throw 'BASmoothScroll.setTargetNode: first argument must be a BAElement node';
		} else {
			var nodePos = node.getAbsoluteOffsetBA();
			this.setTargetPos(nodePos.X, nodePos.Y);
		}
	},

	setTargetPos : function(x, y) {
		if (isNaN(x) || isNaN(y)) {
			throw 'BASmoothScroll.setTargetPos: arguments must be a number';
		} else {
			this.stopScroll();
			BAGetGeometry();
			var maxX = (BA.geom.pageW > BA.geom.windowW) ? BA.geom.pageW - BA.geom.windowW : 0;
			var maxY = (BA.geom.pageH > BA.geom.windowH) ? BA.geom.pageH - BA.geom.windowH : 0;
			var zoom = BAGetZoomRatio();
			var posX = parseInt((x + this.offsetX) * zoom, 10);
			var posY = parseInt((y + this.offsetY) * zoom, 10);
			this.toX = (posX < 0) ? 0 : (posX > maxX) ? maxX : posX;
			this.toY = (posY < 0) ? 0 : (posY > maxY) ? maxY : posY;
		}
	},

	startScroll : function() {
		BAGetGeometry();
		if (!this.timer) {
			this.cuX   = BA.geom.scrollX;
			this.cuY   = BA.geom.scrollY;
			this.timer = new BASetInterval(arguments.callee, this.wait, this);
			this.doCallBack(this.onStart, [this.cuX, this.cuY, this.toX, this.toY, this]);
		}

		this.cuX += (this.toX - BA.geom.scrollX) / this.unit; if (this.cuX < 0) this.cuX = 0;
		this.cuY += (this.toY - BA.geom.scrollY) / this.unit; if (this.cuY < 0) this.cuY = 0;
		var newX = Math.floor(this.cuX);
		var newY = Math.floor(this.cuY);
		window.scrollTo(newX, newY);
		this.doCallBack(this.onScroll, [newX, newY, this.toX, this.toY, this]);
		if (newX == this.toX && newY == this.toY) {
			this.stopScroll();
		}
	},

	stopScroll : function() {
		if (this.timer) {
			this.timer.clearTimer();
			this.timer = null;
			this.doCallBack(this.onComplete, [this.cuX, this.cuY, this.toX, this.toY, this]);
		}
	},

	doCallBack : function(func, args) {
		if (func && func.constructor == Function) {
			if (args && args.constructor != Array) {
				args = [args];
			}
			return func.apply(null, args);
		}
		return null;
	},
	
	setCallBack : function(name, func, aThisObject) {
		if (!this[name] || this[name].constructor == Function) {
			this[name] = BACreateDelegate(func, aThisObject);
		}
	},

	onStart : function(curX, curY, destX, destY, SS) { },

	onScroll : function(curX, curY, destX, destY, SS) { },

	onComplete : function(curX, curY, destX, destY, SS) { }
}



/* -------------------- Constructor : BASmoothScrollField inherits BASmoothScroll -------------------- */

function BASmoothScrollField(node) {
	this.node    = node;
	this.offsetX = 0;
	this.offsetY = 0;
	this.unit    = 5;
	this.wait    = 10;
	this.cuX     = 0;
	this.cuY     = 0;
	this.toX     = 0;
	this.toY     = 0;
	this.timer   = null;
}

BASmoothScrollField.prototype = new BASmoothScroll;

BASmoothScrollField.prototype.setTargetNode = function(node) {
	if (!node || node.instanceOf != 'BAElement') {
		throw 'BASmoothScrollField.setTargetNode: first argument must be a BAElement node';
	} else {
		var fieldPos = this.node.getAbsoluteOffsetBA();
		var nodePos  =      node.getAbsoluteOffsetBA();
		this.setTargetPos(nodePos.X - fieldPos.Y, nodePos.Y - fieldPos.Y);
	}
}

BASmoothScrollField.prototype.setTargetPos = function(x, y) {
	if (isNaN(x) || isNaN(y)) {
		throw 'BASmoothScrollField.setTargetPos: arguments must be a number';
	} else {
		this.stopScroll();
		var maxX = this.node.scrollWidth  - this.node.offsetWidth ; if (maxX < 0) maxX = 0;
		var maxY = this.node.scrollHeight - this.node.offsetHeight; if (maxY < 0) maxY = 0;
		var zoom = BAGetZoomRatio();
		var posX = parseInt((x + this.offsetX) * zoom, 10);
		var posY = parseInt((y + this.offsetY) * zoom, 10);
		this.toX = (posX < 0) ? 0 : (posX > maxX) ? maxX : posX;
		this.toY = (posY < 0) ? 0 : (posY > maxY) ? maxY : posY;
	}
}

BASmoothScrollField.prototype.startScroll = function() {
	if (!this.timer) {
		this.cuX   = this.node.scrollLeft;
		this.cuY   = this.node.scrollTop;
		this.timer = new BASetInterval(arguments.callee, this.wait, this);
		this.doCallBack(this.onStart, [this.cuX, this.cuY, this.toX, this.toY, this]);
	}
	var dX = Math.round((this.toX - this.node.scrollLeft) / this.unit);
	var dY = Math.round((this.toY - this.node.scrollTop ) / this.unit);
	if (dX == 0 && this.toX < this.node.scrollLeft) dX = -1;
	if (dX == 0 && this.toX > this.node.scrollLeft) dX =  1;
	if (dY == 0 && this.toY < this.node.scrollTop ) dY = -1;
	if (dY == 0 && this.toY > this.node.scrollTop ) dY =  1;
	this.node.scrollLeft += dX;
	this.node.scrollTop  += dY;
	this.cuX             += dX;
	this.cuY             += dY;
	this.doCallBack(this.onScroll, [this.cuX, this.cuY, this.toX, this.toY, this]);
	if (this.cuX == this.toX && this.cuY == this.toY) {
		this.stopScroll();
	}
}



/* -------------------- Function : BASmoothScrollAutoSetup inherit BASmoothScroll -------------------- */

function BASmoothScrollAutoSetup() {
	this.offsetX  = BA_SMOOTHSCROLL_OFFSET_X;
	this.offsetY  = BA_SMOOTHSCROLL_OFFSET_Y;
	this.unit     = 5;
	this.wait     = 10;
	this.fromNode = null;

	this.init();
}

BASmoothScrollAutoSetup.prototype = new BASmoothScroll;

BASmoothScrollAutoSetup.prototype.init = function() {
	// create scrolling destination nodes on page top and page bottom
	['top', 'bottom'].forEach(function(flagID) {
		if (!document.getElementByIdBA(flagID) && !document.getElementsByNameBA(flagID)[0]) {
			var node = document.createElementBA('ins');
			node.style.position   = 'absolute';
			node.style.left       = '0';
			node.style.top        = (flagID == 'top')    ? '0' : 'auto';
			node.style.display    = 'block';
			node.style.visibility = 'hidden';
			node.style.width      = '0';
			node.style.height     = '0';
			node.style.margin     = '0';
			node.style.padding    = '0';
			node.style.fontSize   = '0';
			node.setAttributeBA('id', flagID);
			document.getElementsByTagNameBA('body')[0].appendChildBA(node);
		}
	});

	// setup event handlers
	document.addEventListenerBA('click'     , this.stopScroll, this);
	document.addEventListenerBA('mousewheel', this.stopScroll, this);

	if (BA_SMOOTHSCROLL_FIND_ATONCE) {
		var nodes = BAConcatNodeList(['a', 'area'].map(function(tagName) { return document.getElementsByTagNameBA(tagName) }));
		nodes.forEach(function(node) {
			node.addEventListenerBA('click', function(e) {
				if (!this.timer) {
					this.startScrollByAnchor(e.currentTarget, e);
				}
			}, this);
		}, this);
	} else {
		document.addEventListenerBA('click', function(e) {
			if (!this.timer) {
				var node = e.target;
				if (['a', 'area'].indexOf(node.nodeName.toLowerCase()) == -1) {
					node = node.getAncestorsByTagNameBA('a')[0] || node.getAncestorsByTagNameBA('area')[0];
				}
				if (node) {
					this.startScrollByAnchor(node, e);
				}
			}
		}, this);
	}

	// setup callback funcs
	this.setCallBack('onComplete', BA_SMOOTHSCROLL_POSTPROCESS_FUNC);

}

BASmoothScrollAutoSetup.prototype.startScrollByAnchor = function(anchor, e) {
	var flagID = this.getInternalLinkFragmentID(anchor);
	if (flagID) {
		var target = document.getElementByIdBA(flagID) || document.getElementsByNameBA(flagID)[0];
		if (target && this.validateAnchor(anchor)) {
			anchor.blur();
			if (e) {
				e.preventDefault();
				e.stopPropagation();
			}
			this.fromNode = anchor;
			this.setTargetNode(target);
			this.startScroll();
		}
	}
}

BASmoothScrollAutoSetup.prototype.getInternalLinkFragmentID = function(node) {
	var ret  = '';
	var href = node.getAttributeBA('href');
	if (href && href.match(/#/)) {
		href = href.split('#');
		if (!href[0] || href[0] == location.href.split('#')[0]) {
			ret = href[1];
		}
	}
	return ret;
}

BASmoothScrollAutoSetup.prototype.validateAnchor = function (node) {
//	return true;
	return BA_SMOOTHSCROLL_NODE_VALIDATOR(node);
}






/* -------------------- Main : register start-up -------------------- */

if (typeof BA == 'object' && BA.ua.DOMok && BA_SMOOTHSCROLL_ENABLED) {
	BAAddOnload(function() {
		BA_SMOOTHSCROLL = BASingleton(BASmoothScrollAutoSetup);
	});
}
