/* -------------------------------------------------------------------------- */
/** 
 *    @fileoverview
 *       Smooth Scroll w/auto setup
 *
 *    @version rev010.2006-03-29
 *    @requires common.js
 */
/* -------------------------------------------------------------------------- */


var BA_SMOOTHSCROLL;



/* -------------------- Settings for BASmoothScrollAutoSetup -------------------- */

var BA_SMOOTHSCROLL_AUTOSETUP_ENABLED         = true;
var BA_SMOOTHSCROLL_AUTOSETUP_USE_POSTPROCESS = false;
var BA_SMOOTHSCROLL_AUTOSETUP_OFFSET_X        = 0;
var BA_SMOOTHSCROLL_AUTOSETUP_OFFSET_Y        = 0;



/* -------------------- Constructor : BASmoothScroll -------------------- */
/**
 * provide smooth scroll behavior for the pages.
 * @class smooth scroller for the page
 * @constructor
 */
function BASmoothScroll() {
	/** X-distance from original scroll destination position (px)
	    @type Number */
	this.offsetX =  0;
	/** Y-distance from original scroll destination position (px)
	    @type Number */
	this.offsetY =  0;
	/** The number of partitions for calculating quantity of unit scroll.
	    Bigger number let scrolling slower, and smoother.
	    @type Number @const @private */
	this.unit    =  5;
	/** interval time to perform unit scroll. (ms)
	    Smaller number let scrolling faster, but It is very likely that scrolling becomes stiff.
		@type Number @const @private */
	this.wait    = 10;
	/** store point of scrolling interval timer.
	    @type BASetInterval @private */
	this.timer   = null;
	/** current scroll position (X-axis) (px)
	    @type Number @private */
	this.cuX     = 0;
	/** current scroll position (Y-axis) (px)
	    @type Number @private */
	this.cuY     = 0;
	/** X coordinate of scroll destination (px)
	    @type Number @private */
	this.toX     = 0;
	/** Y coordinate of scroll destination (px)
	    @type Number @private */
	this.toY     = 0;
	/** current scroll destination node
	    @type BAElement @private */
	this.targetNode     = null;
	/** source element node of scrolling (event)
	    @type BAElement @private */
	this.fromNode       = null;
	/** flag of do post processing or not.
	    @type Boolean @const @private */
	this.usePostProcess = false;
}

BASmoothScroll.prototype = {
	/**
	 * set scroll destination by node specifying.
	 * @param {BAElement} node    element node as scroll destination
	 */
	setTargetNode : function(node) {
		this.stopScroll();
		this.targetNode = node;
		this.timer      = null;
	},
	
	/**
	 * set source element node of scrolling
	 * @param {BAElement} node    element node as source of scrolling
	 */
	setFromNode : function(node) {
		this.fromNode = node;
	},
	
	/**
	 * process unit scrolling, this calls itself recursively until arriving to destination.
	 */
	startScroll : function() {
		BAGetGeometry();
		if (!this.timer) {
			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 absOst = this.targetNode.getAbsoluteOffsetBA();
			var posX   = absOst.X + this.offsetX;
			var posY   = absOst.Y + this.offsetY;
			this.cuX   = BA.geom.scrollX;
			this.cuY   = BA.geom.scrollY;
			this.toX   = (posX < 0) ? 0 : (posX > maxX) ? maxX : posX;
			this.toY   = (posY < 0) ? 0 : (posY > maxY) ? maxY : posY;
			this.timer = new BASetInterval(arguments.callee, this.wait, 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);
		if (newX == this.toX && newY == this.toY) {
			this.stopScroll();
		}
	},
	
	/**
	 * stop scrolling.
	 */
	stopScroll : function() {
		if (this.timer) {
			this.timer.clearTimer();
			this.timer = null;
			this.postProcess();
		}
	},
	
	/**
	 * do post processing (by sawatarisan).
	 * @private
	 */
	postProcess : function() {
		if (this.usePostProcess && this.offsetX == 0 && this.offsetY == 0 &&
		    this.fromNode && (BA.ua.isGecko || BA.ua.isWinIE)) {
			var href = this.fromNode.getAttributeBA('href');
			if (href) location.href = href;
		}
	}
}



/* -------------------- Constructor : BASmoothScrollField -------------------- */
/**
 * provide smooth scroll behavior for the scrollable part domain.
 * @class smooth scroller for the page
 * @constructor
 * @param {BAElement} node    element node that constitutes scroll domain (required)
 */
function BASmoothScrollField(node) {
	/** element node that constitutes scroll domain
	    @type BAElement @const */
	this.node    = node;
	/** X-distance from original scroll destination position (px)
	    @type Number @const */
	this.offsetX = 0;
	/** Y-distance from original scroll destination position (px)
	    @type Number @const */
	this.offsetY = 0;
	/** The number of partitions for calculating quantity of unit scroll.
	    Bigger number let scrolling slower, and smoother.
	    @type Number @const @private */
	this.unit    = 10;
	/** interval time to perform unit scroll.
	    Smaller number let scrolling faster, but It is very likely that scrolling becomes stiff.
		@type Number @const @private */
	this.wait    = 10;
	/** X coordinate of scroll destination (px)
	    @type Number @private */
	this.toX     = 0;
	/** Y coordinate of scroll destination (px)
	    @type Number @private */
	this.toY     = 0;
	/** store point of scrolling interval timer.
	    @type BASetInterval @private */
	this.timer      = null;
	/** current scroll destination node
	    @type BAElement @private */
	this.targetNode = null;
}

BASmoothScrollField.prototype = {
	/**
	 * set scroll destination by node specifying.
	 * @param {BAElement} node    element node as scroll destination
	 */
	setTargetNode : function(node) {
		this.stopScroll();
		this.targetNode = node;
		this.timer      = null;
	},
	
	/**
	 * process unit scrolling, this calls itself recursively until arriving to destination.
	 */
	startScroll : function() {
		if (!this.timer) {
			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 fldOst = this.node.getAbsoluteOffsetBA();
			var tgtOst = this.targetNode.getAbsoluteOffsetBA();
			var posX   = (tgtOst.X - fldOst.Y) + this.offsetX;
			var posY   = (tgtOst.Y - fldOst.Y) + this.offsetY;
			this.toX   = (posX < 0) ? 0 : (posX > maxX) ? maxX : posX;
			this.toY   = (posY < 0) ? 0 : (posY > maxY) ? maxY : posY;
			this.timer = new BASetInterval(arguments.callee, this.wait, 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;
		if (this.node.scrollLeft == this.toX && this.node.scrollTop == this.toY) {
			this.stopScroll();
		}
	},
	
	/**
	 * stop scrolling.
	 */
	stopScroll : function() {
		if (this.timer) {
			this.timer.clearTimer();
			this.timer = null;
		}
	}
}



/* -------------------- Function : BASmoothScrollAutoSetup -------------------- */
/**
 * auto setup event handlers for BASmoothScroll.
 * @type void
 * @see BASmoothScroll
 */
function BASmoothScrollAutoSetup() {
	var html    = document.getElementsByTagNameBA('html')[0];
	var body    = document.getElementsByTagNameBA('body')[0];
	var div     = document.createElementBA('div');
	var anchors = document.getElementsByTagNameBA('a');

	if (!document.getElementById('top'   )) html.setAttributeBA('id', 'top'   );
	if (!document.getElementById('bottom'))  div.setAttributeBA('id', 'bottom');
	div.style.margin  = 0;
	div.style.padding = 0;
	div.style.clear   = 'both';
	body.appendChildBA(div);

	BA_SMOOTHSCROLL = new BASmoothScroll;
	BA_SMOOTHSCROLL.offsetX        = BA_SMOOTHSCROLL_AUTOSETUP_OFFSET_X;
	BA_SMOOTHSCROLL.offsetY        = BA_SMOOTHSCROLL_AUTOSETUP_OFFSET_Y;
	BA_SMOOTHSCROLL.usePostProcess = BA_SMOOTHSCROLL_AUTOSETUP_USE_POSTPROCESS;

	document.addEventListenerBA('click'     , function(e) { BA_SMOOTHSCROLL.stopScroll() });
	document.addEventListenerBA('mousewheel', function(e) { BA_SMOOTHSCROLL.stopScroll() });

	for (var i = 0, n = anchors.length; i < n; i++) {
		var ident = _getInternalLinkFragmentIdentifier(anchors[i]);
		if (ident) {
			var target = document.getElementById(ident) || document.getElementsByName(ident)[0];
			if (target) {
				anchors[i].__BASmoothScrollAutoSetup_targetNode__ = target;
				anchors[i].addEventListenerBA('click', function(e) {
					e.preventDefault();
					e.stopPropagation();
					e.currentTarget.blur();
					BA_SMOOTHSCROLL.setTargetNode(e.currentTarget.__BASmoothScrollAutoSetup_targetNode__);
					BA_SMOOTHSCROLL.setFromNode(e.currentTarget);
					BA_SMOOTHSCROLL.startScroll();
				});
			}
		}
	}

	/**
	 * get fragment-id of link target of the anchor that links to a same page.
	 * @param {BAElement} node    anchor element node (a, area) (required)
	 * @return fragment-id, if the anchor links to a same page. not then returns null string.
	 * @type String
	 */
	function _getInternalLinkFragmentIdentifier(node) {
		var ret      = '';
		var linkHref = node.getAttributeBA('href');
		if (linkHref && linkHref.match(/#/)) {
			linkHref = linkHref.split('#');
			if (!linkHref[0] || linkHref[0] == location.href.split('#')[0]) {
				ret = linkHref[1];
			}
		}
		return ret;
	}
}





/* -------------------- Main : register start-up -------------------- */

if (typeof BA == 'object' && BA.ua.DOMok && BA_SMOOTHSCROLL_AUTOSETUP_ENABLED) {
	BAAddOnload(BASmoothScrollAutoSetup);
}
