/* -------------------------------------------------------------------------- */
/** 
 *    @fileoverview
 *       Balloon (Floating Layer) Controls.
 *
 *    @version rev012.2008-06-12
 *    @requires common.js
 *    @requires balloon.css
 */
/* -------------------------------------------------------------------------- */


var BA_BALLOON_AS;



/* -------------------- Settings for BABalloonAutoSetup -------------------- */

var BA_BALLOON_AS_ENABLED          = true;
var BA_BALLOON_AS_FOLLOW_CURSOR    = true;
var BA_BALLOON_AS_BASENODE_ID      = 'BABalloonAS';
var BA_BALLOON_AS_OFFSET_X         = 0;
var BA_BALLOON_AS_OFFSET_Y         = 10;
var BA_BALLOON_AS_SHOW_DELAY       = 500;
var BA_BALLOON_AS_SHOW_SUSTAIN     = 5000;
var BA_BALLOON_AS_DETECT_CNAME     = 'useBalloon';
var BA_BALLOON_AS_ATTR_FOR_CONTENT = 'title';
var BA_BALLOON_AS_DATAOBJ_NAME     = 'BA_BALLOON_AS_DATA';



/* -------------------- Constructor : BABalloon inherits BAObservable -------------------- */

function BABalloon(setting) {
	if (typeof setting != 'object' || setting == null) {
		setting = {};
	}

	this.node         = null;
	this.active       = false;
	this.isPosFixed   = false;
	this.className    = 'BABalloon';
	this.id           = setting.id      || '';
	this.posX         = setting.posX    || 0;
	this.posY         = setting.posY    || 0;
	this.offsetX      = setting.offsetX || 0;
	this.offsetY      = setting.offsetY || 0;
	this.delay        = setting.delay   || 0;
	this.sustain      = setting.sustain || 0;
	this.posRevise    = { X : Boolean(setting.posReviseX), Y : Boolean(setting.posReviseY) };
	this.delayTimer   = null;
	this.sustainTimer = null;

	if (BA.env.isDOMReady) {
		this.init();
		this.setContent(setting.content);
	}
}

BABalloon.prototype = new BAObservable;

BABalloon.prototype.init = function() {
	var body  = document.getElementsByTagNameBA('body')[0];
	this.node = document.createElementBA('ins');
	this.node.appendClassNameBA(this.className);
	if (this.id) {
		this.node.setAttributeBA('id', this.id);
	}
	body.appendChildBA(this.node);
	this.hide();
}

BABalloon.prototype.isActive = function() {
	return this.active;
}

BABalloon.prototype.setPositionFixed = function(ignoreX, ignoreY, autoHide) {
	this.isPosFixed = true;
	this.node.setPositionFixedBA(ignoreX, ignoreY, autoHide);
	this.doCallBack('onPositionFixed', ignoreX, ignoreY, autoHide);
}

BABalloon.prototype.setContent = function(content) {
	this.clearContent();
	this.addContent(content);
}

BABalloon.prototype.addContent = function(content) {
	if (content === undefined || content === null || content === '') {
		return;
	} else if (content.instanceOf != 'BAElement' && content.instanceOf != 'BATag' && typeof content != 'string') {
		throw 'BABalloon.addContent: first argument must be a BAElement node, or a BATag instance, or a string.';
	} else {
		this.node.appendChildBA(content, true); // always force as 'HTML'.
		this.doCallBackByName('onContentChange');
	}
}

BABalloon.prototype.clearContent = function() {
	this.node.removeAllChildrenBA();
	this.doCallBackByName('onContentChange');
}

BABalloon.prototype.getGeometry = function() {
	return {
		posX    : this.posX,
		posY    : this.posY,
		offsetX : this.offsetX,
		offsetY : this.offsetY,
		width   : this.node.offsetWidth,
		height  : this.node.offsetHeight
	};
}

BABalloon.prototype.show = function(x, y) {
	if (this.delayTimer  ) this.delayTimer  .clearTimer();
	if (this.sustainTimer) this.sustainTimer.clearTimer();
	if (this.delay > 0) {
		this.delayTimer = new BASetTimeout(_showMain, this.delay, this);
	} else {
		_showMain.call(this);
	}
	if (this.sustain > 0) {
		this.sustainTimer = new BASetTimeout(this.hide, this.delay + this.sustain, this);
	}

	function _showMain() {
		this.moveTo(x, y);
		this.visible();
		this.doCallBackByName('onShow');
	}
}

BABalloon.prototype.resizeTo = function(width, height) {
	if (typeof width != 'number' || typeof height != 'number' || width < 0 || height < 0) {
		throw 'BABalloon.resizeTo: \'width\' and \'height\' arguments must be a positive number.';
	} else {
		this.node.style.width  = (width  == 0) ? 'auto' : width  + 'px';
		this.node.style.height = (height == 0) ? 'auto' : height + 'px';

		var offsetWidth  = this.node.offsetWidth;
		var offsetHeight = this.node.offsetHeight;
		var reviseWidth  = 2 * width  - offsetWidth;
		var reviseHeight = 2 * height - offsetHeight;
		if (offsetWidth  != width  && reviseWidth  >= 0) this.node.style.width  = reviseWidth  + 'px';
		if (offsetHeight != height && reviseHeight >= 0) this.node.style.height = reviseHeight + 'px';

		this.doCallBackByName('onResize');
	}
}

BABalloon.prototype.moveTo = function(x, y) {
	var oldX = this.posX;
	var oldY = this.posY;
	if (typeof x == 'number') this.posX = x;
	if (typeof y == 'number') this.posY = y;

	this.revisePosition();

	this.node.style.left = (this.posX + this.offsetX) + 'px';
	this.node.style.top  = (this.posY + this.offsetY) + 'px';

	if (oldX != this.posX || oldY != this.posY) {
		this.doCallBackByName('onMove');
	}
}

BABalloon.prototype.moveToCenter = function(moveAxis) {
	var geom = BAGetGeometry();
	var useX = (typeof moveAxis == 'object' && moveAxis.X || !moveAxis);
	var useY = (typeof moveAxis == 'object' && moveAxis.Y || !moveAxis);
	var posX = (useX) ? (geom.windowW - this.node.offsetWidth ) / 2 : this.posX; if (posX < 0) posX = 0;
	var posY = (useY) ? (geom.windowH - this.node.offsetHeight) / 2 : this.posY; if (posY < 0) posY = 0;
	var scrX = (this.isPosFixed) ? 0 : geom.scrollX;
	var scrY = (this.isPosFixed) ? 0 : geom.scrollY;
	this.moveTo(posX + scrX, posY + scrY);
}

BABalloon.prototype.revisePosition = function() {
	if (this.posRevise.X || this.posRevise.Y) {
		var geom       = BAGetGeometry();
		var edgeRight  = (BA.ua.isSafari || BA.ua.isMacIE || BA.ua.isIE70 || geom.pageH <= geom.windowH) ? 0 : geom.scrollBar;
		var edgeBottom = (BA.ua.isSafari || BA.ua.isMacIE || BA.ua.isIE70 || geom.pageW <= geom.windowW) ? 0 : geom.scrollBar;
	
		if (BA.ua.isWinIE && BA.ua.revision < 7) {
			edgeRight   = geom.scrollBar + 4;
			edgeBottom += 4;
		}
	
		var reviseX = (geom.windowW + geom.scrollX) - (this.posX + this.offsetX + this.node.offsetWidth  + edgeRight );
		var reviseY = (geom.windowH + geom.scrollY) - (this.posY + this.offsetY + this.node.offsetHeight + edgeBottom);
		var posX    = (this.posX < geom.scrollX) ? geom.scrollX : this.posX + ((reviseX < 0) ? reviseX : 0);
		var posY    = (this.posY < geom.scrollY) ? geom.scrollY : this.posY + ((reviseY < 0) ? reviseY : 0);
		var revised = (this.posX != posX || this.posY != posY);
	
		if (this.posRevise.X) this.posX = posX;
		if (this.posRevise.Y) this.posY = posY;
		if (revised)          this.doCallBackByName('onPosRevise');
	}
}

BABalloon.prototype.visible = function() {
	this.active = true;
	this.node.style.visibility = 'visible';
}

BABalloon.prototype.hide = function(){
	this.active = false;
	this.node.style.visibility = 'hidden';
	if (this.delayTimer)   this.delayTimer  .clearTimer();
	if (this.sustainTimer) this.sustainTimer.clearTimer();
	this.doCallBackByName('onHide');
}

BABalloon.prototype.doCallBackByName = function(name) {
	this.doCallBack(name, this.getGeometry());
}






/* --------------- Constructor : BABalloonAutoSetup --------------- */

function BABalloonAutoSetup() {
	this.balloon          = null;
	this.data             = {};
	this.supplementIdPrfx = 'BABalloonAS_ID_';
	this.obstructAttr     = ['title', 'alt'];
	this.evacuatePrfx     = 'bA:BalloonASAttr-';

	if (BA.env.isDOMReady) {
		this.init();
	}
}

BABalloonAutoSetup.prototype.init = function() {
	this.initData();
	this.balloon = new BABalloon({
		id         : BA_BALLOON_AS_BASENODE_ID,
		offsetX    : BA_BALLOON_AS_OFFSET_X,
		offsetY    : BA_BALLOON_AS_OFFSET_Y,
		delay      : BA_BALLOON_AS_SHOW_DELAY,
		sustain    : BA_BALLOON_AS_SHOW_SUSTAIN,
		posReviseX : true,
		posReviseY : true
	});
	document.addEventListenerBA('mouseover', this.initFromAttr, this);
	document.addEventListenerBA('click'    , this.hide        , this);
}

BABalloonAutoSetup.prototype.initData = function() {
	if (typeof window[BA_BALLOON_AS_DATAOBJ_NAME] == 'object') {
		this.data = window[BA_BALLOON_AS_DATAOBJ_NAME];
	}
	for (var id in this.data) {
		this.setEventsTo(id);
	}
}

BABalloonAutoSetup.prototype.initFromAttr = function(e) {
	var tnode = (e.target.hasClassNameBA(BA_BALLOON_AS_DETECT_CNAME)) ?
	            	e.target :
	            	e.target.getAncestorsByClassNameBA(BA_BALLOON_AS_DETECT_CNAME)[0];
	if (tnode && !tnode.__BABalloonAS_inited__) {
		var value = tnode.getAttributeBA(BA_BALLOON_AS_ATTR_FOR_CONTENT);
		if (value) {
			var id = tnode.getAttributeBA('id') || (this.supplementIdPrfx + (new Date()).getTime());
			tnode.setAttributeBA('id', id);
			this.setData(id, value);
			this.show({ currentTarget : tnode });
		}
	}
}

BABalloonAutoSetup.prototype.setEventsTo = function(id) {
	var node = document.getElementByIdBA(id);
	if (node && !node.__BABalloonAS_inited__) {
		node.__BABalloonAS_inited__ = true;
		node.addEventListenerBA('mouseover', this.show, this);
		node.addEventListenerBA('mouseout' , this.hide, this);
		if (BA_BALLOON_AS_FOLLOW_CURSOR) {
			node.addEventListenerBA('mousemove', this.move, this);
		}
	}
}

BABalloonAutoSetup.prototype.setData = function(id, content) {
	this.data[id] = content;
	this.setEventsTo(id);
}

BABalloonAutoSetup.prototype.show = function(e) {
	if (!this.balloon.isActive()) {
		var node    = e.currentTarget;
		var id      = node.getAttributeBA('id');
		var content = this.data[id];

		if (content) {
			this.evacuateObstructAttr(node);

			var geom = BAGetGeometry();
			this.balloon.setContent(content);
			this.balloon.show(geom.mouseX, geom.mouseY);

			new BASetTimeout(function() {
				var geom = BAGetGeometry();
				this.balloon.moveTo(geom.mouseX, geom.mouseY);
			}, BA_BALLOON_AS_SHOW_DELAY, this);
		}
	}
}

BABalloonAutoSetup.prototype.move = function(e) {
	var geom = BAGetGeometry();
	this.evacuateObstructAttr(e.target);
	this.balloon.moveTo(geom.mouseX, geom.mouseY);
}

BABalloonAutoSetup.prototype.hide = function(e) {
	var flag = !e.relatedTarget || (function (node) {
		if (node == e.currentTarget) {
			return false;
		} else if (node.parentNode) {
			return arguments.callee(node.parentNode);
		} else {
			return true;
		}
	})(e.relatedTarget);
	if (flag) {
		this.revertEvacuatedAttr(e.target);
		this.balloon.clearContent();
		this.balloon.hide();
	}
}

BABalloonAutoSetup.prototype.evacuateObstructAttr = function(node) {
	if (node.__BABalloonAS_attrEvacuated__) {
		return;
	} else {
		node.__BABalloonAS_attrEvacuated__ = true;
		this.obstructAttr.forEach(function(attr) {
			try {     // IE causes JS error when node is 'table' element.
				var value = node.getAttributeBA(attr);
			} catch(err) { }
			if (value) {
				node.setAttributeBA(attr, '');
				node.setAttributeBA(this.evacuatePrfx + attr, value);
			}
		}, this);
		if (node.parentNode && node.parentNode.nodeType == 1) {
			this.evacuateObstructAttr(node.parentNode);
		}
	}
}

BABalloonAutoSetup.prototype.revertEvacuatedAttr = function(node) {
	if (!node.__BABalloonAS_attrEvacuated__) {
		return;
	} else {
		node.__BABalloonAS_attrEvacuated__ = false;
		this.obstructAttr.forEach(function(attr) {
			try {     // IE causes JS error when node is 'table' element.
				var value = node.getAttributeBA(this.evacuatePrfx + attr);
			} catch(err) { }
			if (value) {
				node.setAttributeBA(attr, value);
			}
		}, this);
		if (node.parentNode && node.parentNode.nodeType == 1) {
			this.revertEvacuatedAttr(node.parentNode);
		}
	}
}






/* --------------- Constructor : BABalloonSetting --------------- */

function BABalloonSetting() {
	this.content    = '';
	this.id         = '';
	this.posX       = 0;
	this.posY       = 0;
	this.offsetX    = 0;
	this.offsetY    = 0;
	this.delay      = 0;
	this.sustain    = 0;
	this.posReviseX = false;
	this.posReviseY = false;
}



/* --------------- Constructor : BABalloonGeometry --------------- */

function BABalloonGeometry() {
	this.posX    = 0;
	this.posY    = 0;
	this.offsetX = 0;
	this.offsetY = 0;
	this.width   = 0;
	this.height  = 0;
}






/* -------------------- Main : register start-up -------------------- */

if (typeof BA == 'object' && BA.ua.isDOMReady && BA_BALLOON_AS_ENABLED) {
	BAAddOnload(function() {
		BA_BALLOON_AS = BASingleton(BABalloonAutoSetup);
	});
}
