/* -------------------------------------------------------------------------- */
/** 
 *    @fileoverview
 *       Common Script Libraries.
 *
 *    @version rev010.2006-12-19
 */
/* -------------------------------------------------------------------------- */

var BA;
var BA_STATUSMSG;
var BA_KEYEQUIV;



/* --------------- Constructor : BAEnvironment --------------- */
/**
 * constructor of 'BA' global single object.
 * @class common settings and environment variables set.
 * @constructor
 */
function BAEnvironment() {
	/** document
	    @type Document @const @private */
	var d  = document;
	/** document.implementation
	    @type Object @const @private */
	var di = d.implementation;
	/** document.documentElement
	    @type Node @const @private */
	var de = d.documentElement;
	/** navigator.userAgent
	    @type String @const @private */
	var ua = navigator.userAgent;
	/** location.protocol
	    @type String @const @private */
	var lp = location.protocol;
	/** location.hostname
	    @type String @const @private */
	var lh = location.hostname;

	/** associative array of urls/paths to frequently used directories.
	    @type Object @const  */
	this.url = {};
	this.url.commonDir   = BAGetCommonDir('shared');
	this.url.cssDir      = this.url.commonDir + 'css/';
	this.url.scriptDir   = this.url.commonDir + 'js/';

	/** associative array of frequently used namespaces.
	    @type Object @const  */
	this.ns = {};
	this.ns.defaultNS    = (de && de.namespaceURI) ? de.namespaceURI : (de && de.tagUrn) ? de.tagUrn : null;
	this.ns.xhtml1       = 'http://www.w3.org/1999/xhtml';
	this.ns.xhtml2       = 'http://www.w3.org/2002/06/xhtml2';
	this.ns.bAattrs      = 'urn:bA.attrs';

	/** associative array of frequently used namespace prefixes.
	    @type Object @const  */
	this.prefix = {};
	this.prefix.bAattrs  = 'bAattrs:';

	/** associative array of browser distinction results.
	    @type Object @const  */
	this.ua = {};
	this.ua.isGecko      = ua.match(/Gecko\//);
	this.ua.isSafari     = ua.match(/AppleWebKit/);
	this.ua.isOpera      = window.opera;
	this.ua.isOpera6     = (this.ua.isOpera && ua.match(/Opera.6/));    // Opera 6.x
	this.ua.isIE         = (d.all && !this.ua.isGecko && !this.ua.isSafari && !this.ua.isOpera);
	this.ua.isIE40       = (this.ua.isIE && ua.match(/MSIE 4\.0/));     // IE 4.0x
	this.ua.isIE45       = (this.ua.isIE && ua.match(/MSIE 4\.5/));     // IE 4.5x
	this.ua.isIE50       = (this.ua.isIE && ua.match(/MSIE 5\.0/));     // IE 5.0x
	this.ua.isIE55       = (this.ua.isIE && ua.match(/MSIE 5\.5/));     // IE 5.5x
	this.ua.isIE60       = (this.ua.isIE && ua.match(/MSIE 6\.0/));     // IE 6.0x
	this.ua.isIE70       = (this.ua.isIE && ua.match(/MSIE 7\.0/));     // IE 7.0x
	this.ua.isNN4        = d.layers;                                    // NN 4.x
	this.ua.isMac        = ua.match(/Mac/);
	this.ua.isWin        = ua.match(/Win/);
	this.ua.isWinIE      = this.ua.isWin && this.ua.isIE;
	this.ua.isMacIE      = this.ua.isMac && this.ua.isIE;
	this.ua.productSub   = navigator.productSub;
	this.ua.revision     = (this.ua.isGecko)  ? parseFloat(ua.match(/; rv:([\d\.]+)/)[1])         :
	                       (this.ua.isSafari) ? parseFloat(ua.match(/AppleWebKit\/([\d\.]+)/)[1]) :
	                                            0;
	this.ua.DOMok        = (di) ? di.hasFeature('HTML','1.0') : (this.ua.isIE && de);

	/** associative array of misc environment values.
	    @type Object @const  */
	this.env = {};
	this.env.online      = (lp == 'http:' && !lh.match(/.local\.?$/));
	this.env.referer     = (typeof document.referrer == 'string') ? document.referrer : '';

	/** associative array of revise css settings.
	    @type Object @const  */
	this.css = {};
	this.css.revise      = {
//		'Safari'   : 'revise_safari.css',
//		'IE50.Win' : 'revise_winie50.css'
	};
	this.css.reviseTitle = '';

	/** associative array of browser geometries.
	    @type Object @see Window#BAGetGeometry  */
	this.geom            = {};

	/** debug mode flag
	    @type Boolean */
	this.debugMode       = false;
}





/* --------------- EventHandler : BAErrorHandler --------------- */
/**
 * on debug mode, show notify when error occurred.
 * @return true (suppress browser-native error dialog)
 * @type Boolean
 */
window.onerror = function() {
	if (BA.debugMode) {
		var msg = 'Error: ' + arguments[0] + '\n' +
		          'File: '  + arguments[1] + '\n' + 
		          'Line: '  + arguments[2];
		alert(msg);
	}
	return true;
}





/* ---------- Custom methods / Shortage methods of built-in objects ---------- */

/* ----- Function.apply() ----- */

if (!Function.prototype.apply) {
	/**
	 * allows you to apply the method of another object in the context of a different object (the calling object).
	 * (implement emulation for MacIE).
	 * @param {Object} aThisObject the object that will be a global object ('this') in aCallBack func.
	 * @param {Array}  anArray     the Array to apply to the function.
	 * @param {Object} anArray     the 'arguments' object.
	 * @return returned value from the applied func.
	 * @@type AnyType
	 */
	Function.prototype.apply = function(aThisObject, anArray) {
		if (typeof anArray != 'null' && typeof anArray != 'undefined' && typeof anArray != 'object') {
			throw 'TypeError: second argument to Function.prototype.apply must be an array.';
		} else {
			if (typeof aThisObject != 'object') {
				aThisObject = window;
			}
			if (!anArray) {
				anArray = [];
			}
			var prop = '__Function_Apply__storedFunc__';
			var args = [];
			for (var i = 0, n = anArray.length; i < n; i++) {
				args.push('anArray[' + i + ']');
			}

			aThisObject[prop] = this;
			var ret = eval('aThisObject.' + prop + '(' + args.join(',') + ')');
			delete aThisObject[prop];
			return ret;
		}
	}
}

/* ----- Function.call() ----- */

if (!Function.prototype.call) {
	/**
	 * allows you to call (execute) a method of another object in the context of a different object (the calling object).
	 * (implement emulation for MacIE).
	 * @param {Object}  aThisObject the object that will be a global object ('this') in aCallBack func.
	 * @param {AnyType} args        the arguments for called function.
	 * @return returned value from the called func.
	 * @@type AnyType
	 */
	Function.prototype.call = function(aThisObject /* , arg1, arg2 ... */) {
		var args = [];
		for (var i = 1, n = arguments.length; i < n; i++) {
			args.push(arguments[i]);
		}
		return this.apply(aThisObject, args);
	}
}

/* ----- Array.pop() ----- */

if (!Array.prototype.pop) {
	/**
	 * removes the last element from an array and returns that element.
	 * (implement emulation for MacIE).
	 * @return last element of the array.
	 * @type Object
	 */
	Array.prototype.pop = function() {
		if (!this.length) {
			return null;
		} else {
			var last = this[this.length - 1];
			--this.length;
			return last;
		}
	}
}

/* ----- Array.push() ----- */

if (!Array.prototype.push) {
	/**
	 * adds one or more elements to the end of an array and returns the new length of the array. 
	 * (implement emulation for MacIE).
	 * @param {Object} args    any kind of object(s) to add to array (required)
	 * @return the number of elements in the array after processing.
	 * @type Number
	 */
	Array.prototype.push = function(args) {
		for (var i = 0, n = arguments.length; i < n; i++) {
			this[this.length] = arguments[i];
		}
		return this.length;
	}
}

/* ----- Array.shift() ----- */

if (!Array.prototype.shift) {
	/**
	 * removes the first element from an array and returns that element.
	 * (implement emulation for MacIE).
	 * @return first element of the array.
	 * @type Object
	 */
	Array.prototype.shift = function() {
		if (!this.length) {
			return null;
		} else {
			this.reverse();
			var ret = this.pop();
			this.reverse();
			return ret;
		}
	}
}

/* ----- Array.unshift() ----- */

if (!Array.prototype.unshift) {
	/**
	 * adds one or more elements to the front of an array and returns the new length of the array.
	 * (implement emulation for MacIE).
	 * @param {Object} args    any kind of object(s) to add to array (required)
	 * @return the number of elements in the array after processing.
	 * @type Number
	 */
	Array.prototype.unshift = function(args) {
		this.reverse();
		for (var i = arguments.length - 1; i >= 0; i--) {
			this.push(arguments[i]);
		}
		this.reverse();
		return this.length;
	}
}

/* ----- Array.indexOf() ----- */

if (!Array.prototype.indexOf) {
	/**
	 * returns the first index of an element within the array equal to the specified value, or -1 if none is found.
	 * (implement emulation of the method defined in JavaScript1.6)
	 * @param {Object} aSearchElement    the item to search (required)
	 * @param {Object} aFromIndex        index number to start searching
	 * @return index number
	 * @type Number
	 */
	Array.prototype.indexOf = function(aSearchElement, aFromIndex) {
		if (typeof aFromIndex != 'number') {
			aFromIndex = 0;
		} else if (aFromIndex < 0) {
			aFromIndex = this.length + aFromIndex;
		}
		for (var i = aFromIndex, n = this.length; i < n; i++) {
			if (this[i] === aSearchElement) {
				return i;
			}
		}
		return -1;
	}
}

/* ----- Array.lastIndexOf() ----- */

if (!Array.prototype.lastIndexOf) {
	/**
	 * returns the last index of an element within the array equal to the specified value, or -1 if none is found. 
	 * (implement emulation of the method defined in JavaScript1.6)
	 * @param {Object} aSearchElement    the item to search (required)
	 * @param {Object} aFromIndex        index number to start searching
	 * @return index number
	 * @type Number
	 */
	Array.prototype.lastIndexOf = function(aSearchElement, aFromIndex) {
		if (typeof aFromIndex != 'number') {
			aFromIndex = this.length - 1;
		} else if (aFromIndex < 0) {
			aFromIndex = this.length + aFromIndex;
		}
		for (var i = aFromIndex; i >= 0; i--) {
			if (this[i] === aSearchElement) {
				return i;
			}
		}
		return -1;
	}
}

/* ----- Array.forEach() ----- */

if (!Array.prototype.forEach) {
	/**
	 * calls a function for each element in the array.
	 * (implement emulation of the method defined in JavaScript1.6)
	 * @param {Function} aCallBack      the function to exec for every element (required)
	 * @param {Object}   aThisObject    the object that will be a global object ('this') in aCallBack func.
	 */
	/* arguments of callback function 'aCallBack(anElement, anIndex, anArray)' :
	 *      {Object} anElement    current processing element of the Array.
	 *      {Number} anIndex      current processing index-num of the Array.
	 *      {Array}  anArray      the Array itself.
	 */
	Array.prototype.forEach = function(aCallBack, aThisObject) {
		for (var i = 0, n = this.length; i < n; i++) {
			aCallBack.call(aThisObject, this[i], i, this);
		}
	}
}

/* ----- Array.map() ----- */

if (!Array.prototype.map) {
	/**
	 * creates a new array with the results of calling a provided function on every element in this array. 
	 * (implement emulation of the method defined in JavaScript1.6)
	 * @param {Function} aCallBack      the function to exec for every element (required)
	 * @param {Object}   aThisObject    the object that will be a global object ('this') in aCallBack func.
	 * @return array that consisted of returned values.
	 * @type Array
	 */
	/* arguments of callback function 'aCallBack(anElement, anIndex, anArray)' :
	 *      {Object} anElement    current processing element of the Array.
	 *      {Number} anIndex      current processing index-num of the Array.
	 *      {Array}  anArray      the Array itself.
	 *
	 *      return value must be : {Object} result value of 'aCallBack' function.
	 */
	Array.prototype.map = function(aCallBack, aThisObject) {
		var ret = [];
		for (var i = 0, n = this.length; i < n; i++) {
			ret.push(aCallBack.call(aThisObject, this[i], i, this));
		}
		return ret;
	}
}

/* ----- Array.filter() ----- */

if (!Array.prototype.filter) {
	/**
	 * creates a new array with all of the elements of this array for which the provided filtering function returns true. 
	 * (implement emulation of the method defined in JavaScript1.6)
	 * @param {Function} aCallBack      the function to test all elements (required)
	 * @param {Object}   aThisObject    the object that will be a global object ('this') in aCallBack func.
	 * @return array that consisted of only adapted elements.
	 * @type Array
	 */
	/* arguments of callback function 'aCallBack(anElement, anIndex, anArray)' :
	 *      {Object} anElement    current processing element of the Array.
	 *      {Number} anIndex      current processing index-num of the Array.
	 *      {Array}  anArray      the Array itself.
	 *
	 *      return value must be : {Boolean} result value of 'aCallBack' function.
	 */
	Array.prototype.filter = function(aCallBack, aThisObject) {
		var ret = [];
		for (var i = 0, n = this.length; i < n; i++) {
			if (aCallBack.call(aThisObject, this[i], i, this)) {
				ret.push(this[i]);
			}
		}
		return ret;
	}
}

/* ----- Array.some() ----- */

if (!Array.prototype.some) {
	/**
	 * returns true if at least one element in this array satisfies the provided testing function.
	 * (implement emulation of the method defined in JavaScript1.6)
	 * @param {Function} aCallBack      the function to test condition of the elements (required)
	 * @param {Object}   aThisObject    the object that will be a global object ('this') in aCallBack func.
	 * @return did some elements satisfy the condition?
	 * @type Boolean
	 */
	/* arguments of callback function 'aCallBack(anElement, anIndex, anArray)' :
	 *      {Object} anElement    current processing element of the Array.
	 *      {Number} anIndex      current processing index-num of the Array.
	 *      {Array}  anArray      the Array itself.
	 *
	 *      return value must be : {Boolean} result value of 'aCallBack' function.
	 */
	Array.prototype.some = function(aCallBack, aThisObject){
		for (var i = 0, n = this.length; i < n; i++) {
			if (aCallBack.call(aThisObject, this[i], i, this)) return true;
		}
		return false;
	}
}

/* ----- Array.every() ----- */

if (!Array.prototype.every) {
	/**
	 * returns true if every element in this array satisfies the provided testing function.
	 * (implement emulation of the method defined in JavaScript1.6)
	 * @param {Function} aCallBack      the function to test condition of the elements (required)
	 * @param {Object}   aThisObject    the object that will be a global object ('this') in aCallBack func.
	 * @return did all elements satisfy the condition?
	 * @type Boolean
	 */
	/* arguments of callback function 'aCallBack(anElement, anIndex, anArray)' :
	 *      {Object} anElement    current processing element of the Array.
	 *      {Number} anIndex      current processing index-num of the Array.
	 *      {Array}  anArray      the Array itself.
	 *
	 *      return value must be : {Boolean} result value of 'aCallBack' function.
	 */
	Array.prototype.every = function(aCallBack, aThisObject){
		for (var i = 0, n = this.length; i < n; i++) {
			if(!aCallBack.call(aThisObject, this[i], i, this)) return false;
		}
		return true;
	}
}

/* ----- Array.equal() ----- */

if (!Array.prototype.equal) {
	/**
	 * returns true if all elements of the arrays accord each other.
	 * @param {Array} anArray      the comparison array (required)
	 * @return did all elements accord each other?
	 * @type Boolean
	 */
	Array.prototype.equal = function(anArray) {
		if (!anArray || this.length != anArray.length) {
			return false;
		} else {
			return this.every(function(value, i) {
				return (value === anArray[i]);
			});
		}
	}
}

/* ----- Number.formatNumberBA() ----- */
/**
 * number formatter (super tiny!).
 * @param {String} format     fomatter string (required)
 * @return formatted string
 * @type String
 */
Number.prototype.formatNumberBA = function(format) {
	if (!format || typeof format != 'string') {
		return this;
	} else {
		format  = format.split('');
		var str = this.toString().split('');
		var ret = [];
		for (var i = 0, n = (format.length - str.length); i < n; i++) {
			ret.push(format[i]);
		}
		for (var i = 0, n = str.length; i < n; i++) {
			ret.push(str[i]);
		}
		return ret.join('');
	}
}

/* ----- String.formatNumberBA() ----- */
/**
 * simple number formatter. (super tiny!)
 * @param {String} format     fomatter string (required)
 * @return formatted string
 * @type String
 * @see Number#formatNumberBA
 */
String.prototype.formatNumberBA = function(format) {
	var num = parseInt(this, 10);
	if (isNaN(num)) {
		return this;
	} else {
		return num.formatNumberBA(format);
	}
}

/* ----- String.getAfterBA() ----- */
/**
 * get string after 'word'.
 * @param {String} word     find keyword (requred)
 * @return strings after 'word'.
 * @type String
 */
String.prototype.getAfterBA = function(word) {
	var offset = this.indexOf(word);
	return (offset == -1) ? '' : this.substring(offset + word.length, this.length);
}

/* ----- String.formatTextBA() ----- */
/**
 * simple text formatter. (super tiny!)
 * @param {Array} strArray    an array of strings
 * @return formatted (replaced) strings.
 * @type String
 */
 /* 
  * example '${0}HOGE${1}FUGA${2}'.formatTextBA(['xxxx', 'yyyy', 'zzzz'])
  *      -> 'xxxxHOGEyyyyFUGAzzzz'
  */
String.prototype.formatTextBA = function(strArray) {
	var str = this;
	if (strArray && strArray.constructor == Array) {
		for (var i = 0, n = strArray.length; i < n; i++) {
			str = str.replace(new RegExp('\\$\\{' + i + '\\}', 'g'), strArray[i]);
		}
	}
	return str;
}



/* ---------- Constructor : BAElement (Abstract Class) ---------- */
/**
 * custom DOM methods for cross-browser-integrated handling, useful shortcuts.
 * @class this class abstract class, and used as container of prototypes of custom DOM methods.
 * @constructor
 */
function BAElement() { }

BAElement.prototype = {
	/** alternate for 'instanceof' operator (MacIE cannot understand that operator)
	    @type String @const */
	instanceOf : 'BAElement',

	/* ----- Node.addEventListenerBA() ----- */
	/**
	 * cross-browser-integrated addEventListener().
	 * @param {String}   type           event type         (required)
	 * @param {Function} listener       function or method (required)
	 * @param {Boolean}  useCapture     use event capture? (effective in starndard compliant browsers only)
	 */
	addEventListenerBA : function(type, listener, useCapture) {
		/**
		 * construct a fake event object for IE.
		 * @class fake of standard DOM's Event object for IE.
		 * @constructor
		 * @param {BAElement} _node     reference to object that added event listener (required)
		 */
		function _Event_IE(_node) {
			var e  = window.event;
			var de = document.documentElement;
			var db = document.body;
			this.currentTarget   = _node;
			if (!e) return;
			this.type            = e.type;
			this.target          = e.srcElement;
			this.relatedTarget   = (e.srcElement == e.toElement) ? e.fromElement : e.toElement;
			this.clientX         = e.clientX;
			this.clientY         = e.clientY;
			this.pageX           = (de.scrollLeft ? de.scrollLeft : (db ? db.scrollLeft : 0)) + e.clientX;
			this.pageY           = (de.scrollTop  ? de.scrollTop  : (db ? db.scrollTop  : 0)) + e.clientY;
			this.charCode        = /* (this.type == 'keypress') ? */ e.keyCode /* : 0 */;
			this.keyCode         = /* (this.type != 'keypress') ? */ e.keyCode /* : 0 */;
			this.ctrlKey         = e.ctrlKey;
			this.shiftKey        = e.shiftKey;
			this.altKey          = e.altKey;
			this.metaKey         = e.metaKey;
			this.detail          = (this.type == 'mousewheel') ? e.wheelDelta * -1 : e.detail;
			this.stopPropagation = function() { e.cancelBubble = true  };
			this.preventDefault  = function() { e.returnValue  = false };
		}

		/**
		 * construct a fake event object for Safari.
		 * @class fake of standard DOM's Event object for Safari.
		 * @constructor
		 * @param {Event} _e     original native event object (required)
		 */
		function _Event_Safari(_e) {
			for (var i in _e) this[i] = _e[i];
			try { this.target = (_e.target.nodeType == 3) ? _e.target.parentNode : _e.target } catch (err) { }
			this.detail          = (this.type == 'mousewheel') ? this.wheelDelta * -1 : this.detail;
			this.preventDefault  = function() { _e.currentTarget['on' + type] = function() { return false } };
			this.stopPropagation = function() { window.event_safari_cancelBubble[_e.type] = true };
		}

		if (this.addEventListener) {
			if (BA.ua.isGecko && type == 'mousewheel') {
				type = 'DOMMouseScroll';
			}
			if (!BA.ua.isSafari) {
				this.addEventListener(type, listener, useCapture);
				if (BA.ua.isGecko && this == window && type == 'scroll') {
					document.addEventListener('DOMMouseScroll', listener, useCapture);
				}
			} else {
				this.addEventListener(type, function(e) {
					if (typeof window.event_safari_cancelBubble != 'object') {
						window.event_safari_cancelBubble = {};
					} else if (typeof window.event_safari_cancelBubble[e.type] != 'boolean') {
						document.addEventListener(e.type, function(_e) {
							window.event_safari_cancelBubble[_e.type] = false;
						}, false);
					}
					if (!window.event_safari_cancelBubble[e.type]) {
						listener(new _Event_Safari(e));
					}
				}, useCapture);
			}
		} else {
			var _this  = (this.window) ? this.window : this; // measure for WinIE
			var exists = _this['on' + type];
			_this['on' + type] = (exists) ?
				function() { exists(); listener(new _Event_IE(_this)) } :
				function() {           listener(new _Event_IE(_this)) } ;
		}
	},

	/* ----- Node.dispatchEventBA() ----- */
	/**
	 * cross-browser-integrated dispatchEvent().
	 * (incomplete)
	 * @param {Event} e    event object
	 */
	
	dispatchEventBA : function(e) {
		if (this.dispatchEvent) {
			this.dispatchEvent(e);
		} else if (this.fireEvent) {
			this.fireEvent('on' + e.type);
		} else if (this['on' + e.type]) {
			this['on' + e.type]();
		}
	},

	/* ----- Node.createElementBA() ----- */
	/**
	 * namespace handlings integrated createElement().
	 * @param {String} tagName     element name to create (required)
	 * @return new element node
	 * @type BAElement
	 * @see Window#BARegisterDOMMethodsTo
	 */
	/* specifying tagName example:
	 *   - 'elmName'    ... elements 'elmName' (in HTML), or 'elmName' of default namespace (in XML).
	 *   - 'NS:elmName' ... elements 'NS:elmName' (in HTML), or 'elmName' of 'NS' namespace (in XML).
	 * notice: specifying with namespace prefix in HTMLdoc is not available in Safari 1.2.x or earlier.
	 */
	createElementBA : function(tagName) {
		var node = (BA.ns.defaultNS && document.createElementNS && tagName.match(/:/)) ?
		           	document.createElementNS(BA.ns[tagName.split(':')[0]], tagName.split(':')[1]) :
		           	(BA.ns.defaultNS && document.createElementNS) ?
		           		document.createElementNS(BA.ns.defaultNS, tagName) : 
		           		document.createElement(tagName) ;
		return BARegisterDOMMethodsTo(node);
	},

	/* ----- Node.getElementsByTagNameBA() ----- */
	/**
	 * namespace handlings and cross-browser integrated getElementsByTagName().
	 * @param {String} tagName     element name to get NodeList (required)
	 * @return node list (but difference from original NodeList is that returned list is sometimes 'static'.)
	 * @type NodeList/{@link Array}
	 */
	/* specifying tagName example:
	 *   - 'elmName'    ... elements 'elmName' (in HTML), or 'elmName' of default namespace (in XML).
	 *   - 'NS:elmName' ... elements 'NS:elmName' (in HTML), or 'elmName' of 'NS' namespace (in XML).
	 * notice: specifying with namespace prefix in HTMLdoc is not available in Safari 1.2.x or earlier.
	 */
	getElementsByTagNameBA : function(tagName) {
		if (tagName == '*') {
			var nodes = this.getElementsByTagName(tagName);
			if (!BA.ua.isIE && nodes.length > 0) {
				return nodes;
			} else {
				var nodes = (document.all && this === document) ?
					document.all :
					(function(_node) {
						var _nodes = _node.childNodes;
						var _ret   = [];
						for (var i = 0, n = _nodes.length; i < n; i++) {
							if (_nodes[i].nodeType == 1) {
								_ret.push(_nodes[i]);
							}
							_ret = _ret.concat(arguments.callee(_nodes[i]));
						}
						return _ret;
					})(this);
				return nodes;
			}
		} else if (tagName.match(/:/)) {
			var prfx  = tagName.split(':')[0];
			var name  = tagName.split(':')[1];
			var nodes = (BA.ns.defaultNS && this.getElementsByTagNameNS) ?
			            	this.getElementsByTagNameNS(BA.ns[prfx], name) :
			            	this.getElementsByTagName(tagName) ;
			if (nodes.length == 0) {
				var nodes = (name == '*') ? this.getElementsByTagNameBA(name) : this.getElementsByTagName(name);
				for (var nodes_ = [], i = 0, n = nodes.length; i < n; i++){
					if (BA.ns.defaultNS && nodes[i].namespaceURI == BA.ns[prfx] || nodes[i].tagUrn == BA.ns[prfx]) {
						nodes_.push(nodes[i]);
					}
				}
				if (nodes_.length == 0) {
					var nodes = (name == '*') ? nodes : this.getElementsByTagNameBA('*');
					for (var nodes_ = [], i = 0, n = nodes.length; i < n; i++) {
						var prfx_ = nodes[i].nodeName.split(':')[0];
						var name_ = nodes[i].nodeName.split(':')[1];
						if (name_ && prfx_ == prfx && (name == '*' || name_.toLowerCase() == name.toLowerCase())) {
							nodes_.push(nodes[i]);
						}
					}
				}
				nodes = nodes_;
			}
			return nodes;
		} else {
			var nodes = (BA.ns.defaultNS && this.getElementsByTagNameNS) ?
			            	this.getElementsByTagNameNS(BA.ns.defaultNS, tagName) :
			            	(tagName.match(/^body$/i) && document.body) ?
			            		/* measure for Netscape7.1 */ [document.body] :
			            		this.getElementsByTagName(tagName) ;
			if (typeof document.documentElement.tagUrn == 'string') {
				for (var nodes_ = [], i = 0, n = nodes.length; i < n; i++){
					if (!nodes[i].tagUrn || nodes[i].tagUrn == BA.ns.defaultNS) {
						nodes_.push(nodes[i]);
					}
				}
				nodes = nodes_;
			}
			return nodes;
		}
	},

	/* ----- Node.getElementsByClassNameBA() ----- */
	/**
	 * get element node list by class name (and element name).
	 * @param {String} className     class name to get NodeList  (required)
	 * @param {String} tagName       element name to narrow down
	 * @return 'static' node list.
	 * @type Array
	 * @see #getElementsByTagNameBA
	 */
	getElementsByClassNameBA : function(className, tagName) {
		if (!className) return undefined;
		if (!tagName)   tagName = '*';
		var nodes = this.getElementsByTagNameBA(tagName);
		var ret   = [];
		for (var i = 0, n = nodes.length; i < n; i++) {
			if (nodes[i].hasClassNameBA && nodes[i].hasClassNameBA(className)) {
				ret.push(nodes[i]);
			}
		}
		return ret;
	},

	/* ----- Node.appendChildBA() ----- */
	/**
	 * append node or string to element node as it's last child.
	 * if literal string is given, a textNode that has specified string as nodeValue will be create,
	 * then the textNode will be appended to the node.
	 * @param {Element} content    node object to append    (required)
	 * @param {BATag}   content    BATag instance to append (required)
	 * @param {String}  content    literal string to append (required)
	 * @return node that appended.
	 * @type BAElement
	 */
	appendChildBA : function(content, forceAsHTML) {
		if (content.nodeType == 1 || content.nodeType == 3) {
			return this.appendChild(content);
		} else if (content.instanceOf == BATag || forceAsHTML) { // 'instanceof' operator always causes error in MacIE.
			this.innerHTML += content.toString();
			var nodes = this.getElementsByTagNameBA('*');
			for (var i = 0, n = nodes.length; i < n; i++) {
				BARegisterDOMMethodsTo(nodes[i]);
			}
			return content;
		} else if (content.toString) {
			    content = content.toString();
			var node    = document.createTextNode(content);
			if (BA.ua.isMac && BA.ua.isIE50) {
				this.innerHTML += content;
				return node;
			} else {
				return this.appendChild(node);
			}
		}
	},

	/* ----- Node.removeAllChildsBA() ----- */
	/**
	 * remove all child nodes.
	 */
	removeAllChildsBA : function() {
		while (this.hasChildNodes()) {
			this.removeChild(this.firstChild);
		}
	},

	/* ----- Node.getInnerTextBA() ----- */
	/**
	 * get whole inner texts (within alt texts) in the node.
	 * @return whole inner texts.
	 * @type String
	 */
	getInnerTextBA : function() {
		var nodes = this.childNodes;
		var ret   = [];
		for (var i = 0, n = nodes.length; i < n; i++) {
			if (nodes[i].hasChildNodes()) {
				ret.push(nodes[i].getInnerTextBA());
			} else if (nodes[i].nodeType == 3) {
				ret.push(nodes[i].nodeValue);
			} else if (nodes[i].alt) {
				ret.push(nodes[i].alt);
			}
		}
		return ret.join('').replace(/\s+/g, ' ');
	},

	/* ----- Node.getAttributeBA() ----- */
	/**
	 * namespace handlings and cross-browser integrated getAttribute().
	 * @param {String} attr     attribute name to get value (required)
	 * @return attribute value
	 * @type String
	 */
	getAttributeBA : function(attr) {
		if (BA.ua.isIE && attr == 'class') {
			attr += 'Name';
		}
		var ret = this.getAttribute(attr);
		if (!ret && this.getAttributeNS && attr.match(/:/)) {
			var prfx = attr.split(':')[0];
			var attr = attr.split(':')[1];
			return this.getAttributeNS(BA.ns[prfx], attr)
		}
		return ret;
	},

	/* ----- Node.setAttributeBA() ----- */
	/**
	 * namespace handlings and cross-browser integrated setAttribute().
	 * @param {String} attr     attribute name to set value (required)
	 * @param {String} value    attribute value to set      (required)
	 */
	setAttributeBA : function(attr, value) {
		if (attr.match(/:/)) {
			var prfx = attr.split(':')[0];
			var attr = attr.split(':')[1];
			if (this.setAttributeNS && this.namespaceURI || BA.ua.isSafari) {
				this.setAttributeNS(BA.ns[prfx], attr, value);
			} else {
				this.setAttribute('xmlns:' + prfx, BA.ns[prfx]);
				this.setAttribute(prfx + ':' + attr, value);
			}
		} else {
			if (BA.ua.isIE && attr == 'class') attr += 'Name';
			this.setAttribute(attr, value);
		}
	},

	/* ----- Node.hasClassNameBA() ----- */
	/**
	 * examine existence of specified class name.
	 * @param {String} className     value to examine existence (required)
	 * @return boolean
	 * @type Boolean
	 * @see #getAttributeBA
	 */
	hasClassNameBA : function(className) {
		if (this.nodeType != 1) return false;
		var flag  = false;
		var cname = this.getAttributeBA('class');
		if (cname) {
			cname = cname.split(' ');
			for (var i = 0, n = cname.length; i < n && !flag; i++) {
				flag = (cname[i] == className);
			}
		}
		return flag;
	},

	/* ----- Node.appendClassNameBA() ----- */
	/**
	 * add specified class name to the node.
	 * @param {String} className     class name to add (required)
	 * @see #hasClassNameBA
	 * @see #setAttributeBA
	 */
	appendClassNameBA : function(className) {
		if (className && !this.hasClassNameBA(className)) {
			var cnames = this.getAttributeBA('class');
			this.setAttributeBA('class', ((cnames) ? cnames + ' ' + className : className));
		}
	},

	/* ----- Node.removeClassNameBA() ----- */
	/**
	 * remove only specified class name from the node.
	 * @param {String} className     class name to remove (required)
	 * @see #hasClassNameBA
	 * @see #getAttributeBA
	 * @see #setAttributeBA
	 */
	removeClassNameBA : function(className) {
		if (className && this.hasClassNameBA(className)) {
			var cname = this.getAttributeBA('class');
			var cntmp = [];
			if (cname) {
				cname = cname.split(' ');
				for (var i = 0, n = cname.length; i < n; i++) if (cname[i] != className) cntmp.push(cname[i]);
			}
			this.setAttributeBA('class', cntmp.join(' '));
		}
	},

	/* ----- Node.normalizeTextNodeBA() ----- */
	/**
	 * remove text nodes that have only white spaces, and
	 * normalize space of each text nodes.
	 * @param {Boolean} deep     process recursive node tree?
	 */
	normalizeTextNodeBA : function(deep) {
		(function(curNode) {
			for (var i = 0, n = curNode.childNodes.length; i < n; i++) {
				var node = curNode.childNodes[i];
				if (node.nodeType == 3) {
					node.nodeValue = node.nodeValue.replace(/^\s+/, '');
					node.nodeValue = node.nodeValue.replace(/\s+$/, '');
					if (node.nodeValue.match(/^\s*$/))  {
						node.parentNode.removeChild(node);
						i--;
					}
				} else if (deep && node.nodeType == 1 && node.hasChildNodes()) {
					arguments.callee(node);
				}
			}
		})(this);
	},

	/* ----- Node.getAbsoluteOffsetBA() ----- */
	/**
	 * get horizontal/vertical offset length from topleft of the page.
	 * @return associative array of {X, Y}
	 * @type Object
	 */
	getAbsoluteOffsetBA : function() {
		var offset = { X : this.offsetLeft, Y : this.offsetTop };
		if (this.offsetParent) {
			var op = this.offsetParent.getAbsoluteOffsetBA();
			// IE returns wrong value if 'offsetParent block' is position-relative...
			offset.X += op.X;
			offset.Y += op.Y;
		}
		return offset;
	},

	/* ----- Node.getCurrentStyleBA() ----- */
	/**
	 * get computed style value.
	 * @param {String} property     style property name to get value (required)
	 * @param {String} pseudo       pseudo element/class name        (effective in gecko browsers only)
	 * @return conputed style value string
	 * @type String
	 */
	getCurrentStyleBA : function(property, pseudo) {
		return (window.getComputedStyle) ?
			window.getComputedStyle(this, pseudo)[property] : (this.currentStyle) ?
				this.currentStyle[property] : '';
	},

	/* ----- Node.setPositionFixedBA() ----- */
	/**
	 * set pseudo 'position:fixed' behavior (for IE).
	 * @param {Object} setting    associative array of behavior settings { ignoreX, ignoreY, autoHide }.
	 */
	/*
	 * properties of parameter 'setting' :
	 *    {Boolean} ignoreX     don't fix the position at X-scrolling?
	 *    {Boolean} ignoreY     don't fix the position at Y-scrolling?
	 *    {Boolean} autoHide    hide position-fixed block during scrolling?
	 */
	setPositionFixedBA : function(setting) {
		if (this.__setPositionFixedBAisApplied__) return;
		this.__setPositionFixedBAisApplied__ = true;

		var ignoreX  = (setting && setting.ignoreX);
		var ignoreY  = (setting && setting.ignoreY);
		var autoHide = (setting && setting.autoHide);

		var reviseLeft = 0;
		var reviseTop  = 0;
		if (BA.ua.isMacIE) {
			this.style.position = 'fixed';
			var paddingLeft = this.getCurrentStyleBA('paddingLeft') || '0';
			var paddingTop  = this.getCurrentStyleBA('paddingTop' ) || '0';
			var reviseLeft  = (paddingLeft.match(/px$/)) ? -1 * parseFloat(paddingLeft) : 0;
			var reviseTop   = (paddingTop.match(/px$/) ) ? -1 * parseFloat(paddingTop)  : 0;
		} else if (BA.ua.isWinIE) {
			var origDisplay = this.getCurrentStyleBA('display') || 'block';
			this.style.display = 'block';
		} else {
			this.style.position = 'fixed';
		}

		if (!BA.ua.isMacIE) {
			this.style.position = 'absolute';
		}
		this.style.left     = (this.getAbsoluteOffsetBA().X + reviseLeft) + 'px';
		this.style.top      = (this.getAbsoluteOffsetBA().Y + reviseTop ) + 'px';
		this.style.right    = 'auto';
		this.style.bottom   = 'auto';
		this.style.margin   = '0';
		if (!BA.ua.isSafari) {
			this.style.position = 'absolute';
		}
		if (BA.ua.isWinIE) {
			this.style.display = origDisplay;
		}

		var node = this;
		var timer;
		_movePosition();
		window.addEventListenerBA('scroll', _movePosition);

		function _movePosition(e) {
			if (timer) timer.clearTimer();
			if (BA.ua.isGecko && e) {
				new BASetTimeout(arguments.callee, 1);
				return;
			}
			BAGetGeometry();
			if (!ignoreX) node.style.marginLeft = BA.geom.scrollX + 'px';
			if (!ignoreY) node.style.marginTop  = BA.geom.scrollY + 'px';
			if (autoHide) {
				node.style.visibility = 'hidden'
				timer = new BASetTimeout(function() { node.style.visibility = 'visible'  }, 100);
			}
		}

	}
}





/* --------------- Add custom DOM methods ------------------ */

/**
 * regist custom DOM methods.
 * in browsers that has good implements, using OOP way here.
 * in other poor browsers, using 'round robin' way after page onload.
 * @see BAElement
 * @see Window#BARegisterDOMMethodsTo
 * @see Window#BARegisterDOMMethodsToEveryNode
 */
function BARegisterDOMMethods() {
	window.addEventListenerBA = BAElement.prototype.addEventListenerBA;
	if (typeof Node == 'object' && Node.prototype) {
		for (var i in BAElement.prototype) {
			Node.prototype[i] = BAElement.prototype[i];
		}
	}
	BARegisterDOMMethodsTo(document);
}

/**
 * regist custom DOM methods to specific node.
 * @param {Element} node     node to regist (required)
 * @param {Boolean} deep     process recursive node tree?
 * @return node that custom methods was registered.
 * @type BAElement
 * @see BAElement
 */
function BARegisterDOMMethodsTo(node, deep) {
	if (node && node.instanceOf != 'BAElement' && (node === document || node.nodeType == 1)) {
		for (var i in BAElement.prototype) {
			node[i] = BAElement.prototype[i];
		}
	}
	if (deep && node.hasChildNodes()) {
		for (var i = 0, n = node.childNodes.length; i < n; i++) {
			arguments.callee(node.childNodes[i]);
		}
	}
	return node;
}

/* --------------- define onload func register --------------- */

/**
 * shortcut to window.addEventListenerBA().
 * @param {Function} func     function/method (required)
 * @see   BAElement#addEventListenerBA
 */
function BAAddOnload(func) {
	if (BA.ua.isGecko) {
		document.addEventListenerBA('DOMContentLoaded', func);
	} else {
		window.addEventListenerBA('load', func);
	}
}

/**
 * periodic execute functions during page loading.
 * @param {Function} func     function/method   (required)
 * @param {Number}   wait     periodic interval in milisecond
 */
function BAAddDuringLoad(func, wait) {
	if (!wait) wait = 500;
	var oFunc = arguments.callee;
	    oFunc.__store__ = [];
	    oFunc.__store__.push(new BASetInterval(func, wait));
	BAAddOnload(func);
	BAAddOnload(function() {
		for (var i = 0, n = oFunc.__store__.length; i < n; i++) {
			oFunc.__store__[i].clearTimer();
		}
	});
}



/* --------------- Function : BAGetCommonDir --------------- */
/**
 * get URL/path of common directory. (At least one external CSS that placed in 'common dir' is linked from HTML)
 * @param {String} dirName     name of common directory (required)
 * @return URL/path of common directory.
 * @type String
 */
function BAGetCommonDir(dirName) {
	var sheets = document.styleSheets;
	var ptn    = new RegExp('(.*\\/?' + dirName + '\\/).+$');
	return (sheets && sheets.length && sheets[0].href && sheets[0].href.match(ptn)) ? RegExp.$1 : '';
}



/* --------------- Function : BASingleton --------------- */
/**
 * create object as single instance. or put existing instance.
 * @param {Function} _constructor     constructor (required)
 * @return single instance.
 * @type Object
 */
function BASingleton(_constructor) {
	return _constructor.__BASingleInstance__ || (_constructor.__BASingleInstance__ = new _constructor());
}



/* --------------- Function : BACreateDelegate --------------- */
/**
 * create delegate function to change 'this' scope. (*** MacIE not work! ***)
 * @param {Function} func         function for delegate (required)
 * @param {Object}   aThisObject  the object that will be a global object ('this') in the function
 * @return delegate function.
 * @type Function
 */
function BACreateDelegate(func, aThisObject){
	var delegate = function(){
		return func.apply(aThisObject, arguments);
	};
	delegate.func        = func;
	delegate.aThisObject = aThisObject;
	return delegate; 
}



/* --------------- Function : BAAlreadyApplied --------------- */
/**
 * return 'true' if the browser not supports DOM, or
 * function/method has already applied.
 * @param {Function} func     typically 'arguments.callee' (required)
 * @return boolean
 * @type Boolean
 */
function BAAlreadyApplied(func) {
	if (!BA.ua.DOMok || func.__BAAlreadyApplied__) return true;
	func.__BAAlreadyApplied__ = true;
	return false;
}



/* --------------- Function : BAConcatNodeList --------------- */
/**
 * concat complex node lists to single plain node list array.
 * @param {Object} arguments     Node/NodeList/Array objects to concat (required)
 * @return array of nodes
 * @type Array
 */
function BAConcatNodeList() {
	var nodes = [];
	(function(list) {
		for (var i = 0, n = list.length; i < n; i++) {
			if (list[i].nodeType == 1) {
				nodes.push(list[i]);
			} else if (list[i].length > 0) {
				arguments.callee(list[i]);
			}
		}
	})(arguments);
	return nodes;
}



/* --------------- Function : BAPreloadImage --------------- */
/**
 * create Image object (load image).
 * @param {String} src     image url to load (required)
 * @return image object
 * @type Image
 */
function BAPreloadImage(src) {
	var img = new Image();
	img.src = src;
	return img;
}



/* --------------- Function : BAOpenWindow --------------- */
/**
 * open new window.
 * @param {String}  url          url to open in new window      (required)
 * @param {String}  target       window target name
 * @param {Number}  width        width of new window
 * @param {Number}  height       height of new window
 * @param {String}  options      window options wo/width,height
 * @param {Boolean} moveFlag     window options wo/width,height
 * @return new window object
 * @type Window
 */
function BAOpenWindow(url, target, width, height, options, moveFlag) {
	if (!target) target = '_blank';
	var optVariations = {
		'exampleTarget1' : 'toolbar=yes,location=yes,directories=yes,status=yes,menubar=yes,scrollbars=yes,resizable=yes',
		'exampleTarget2' : 'toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=no'
	}
	options = options || optVariations[target] || '';
	width += (options.match('scrollbars=yes')) ? 16 : 0;
	if (width && height) options = 'width=' + width + ',height=' + height + (options ? ',' + options : '');
	var newWin = window.open(url, target, options);
	newWin.focus();
	if (moveFlag) newWin.moveTo(0, 0);
	if (window.event) window.event.returnValue = false;
	return newWin;
}



/* --------------- Function : BAOpenFullscreenWindow --------------- */
/**
 * open new fullscreen window.
 * @param {String} url         url to open in new window      (required)
 * @param {String} target      window target name
 * @param {String} options     window options wo/width,height
 * @return new window object
 * @type Window
 */
function BAOpenFullscreenWindow(url, target, options) {
	options = options || 'toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=no';
	return BAOpenWindow(url, target, screen.availWidth, screen.availHeight, options, true);
}



/* --------------- Function : BAAppendJS --------------- */
/**
 * append (load) external JavaScript file.
 * @param {String} src     script's URL to load (required)
 * @see BATag
 */
function BAAppendJS(src) {
	var E = new BATag('script');
	E.setAttributeBA('type', 'text/javascript');
	E.setAttributeBA('src' , src.replace(/~/g, '%7E'));
	E.appendChildBA('');
	document.write(E.toString());
}



/* --------------- Function : BAAppendCSS --------------- */
/**
 * append (load) external CSS file.
 * @param {String} href      CSS url to load (required)
 * @param {String} title     style title
 * @see BATag
 */
function BAAppendCSS(href, title) {
	var act = BAGetActiveCSSTitle();
	var E   = new BATag('link');
	E.setAttributeBA('rel', ((!title || !act || title == act) ? 'stylesheet' : 'alternate stylesheet'));
	E.setAttributeBA('type', 'text/css');
	E.setAttributeBA('media', (!BA.ua.isNN4 ? 'screen, tv' : 'screen'));
	E.setAttributeBA('href', href.replace(/~/g, '%7E'));
	if (title) E.setAttributeBA('title', title);
	document.write(E.toString());
}



/* --------------- Function : BAGetActiveCSSTitle --------------- */
/**
 * get active style title.
 * @return active style title
 * @type String
 */
function BAGetActiveCSSTitle() {
	var sheets = document.styleSheets;
	if (sheets) {
		for (var i = 0, n = sheets.length; i < n; i++) {
			if (!sheets[i].disabled && sheets[i].title) {
				return sheets[i].title;
			}
		}
	}
	if (BA.ua.DOMok) {
		// measure for Safari's lack of implement of document.styleSheets!
		var nodes = document.getElementsByTagNameBA('link');
		for (var i = 0, n = nodes.length; i < n; i++) {
			if (nodes[i].rel.match(/^stylesheet$/i)) {
				return nodes[i].title
			}
		}
	}
	return null;
}



/* --------------- Function : BAGetGeometry --------------- */
/**
 * get window geometry and store to 'BA.geom' object.
 * @param {Event} e     exists when called as event handler.
 * @see Window#BAStartGeometryMeasure
 */
function BAGetGeometry(e) {
	var w = window;
	var d = document.documentElement;
	var b = document.getElementsByTagNameBA('body')[0];
	var isWinIEqm = BA.ua.isWinIE && (!document.compatMode || document.compatMode == 'BackCompat');
	var isMacIE   = BA.ua.isMacIE;
	var isSafari  = BA.ua.isSafari;

	BA.geom.scrollX  = w.scrollX     || d.scrollLeft || b.scrollLeft || 0;
	BA.geom.scrollY  = w.scrollY     || d.scrollTop  || b.scrollTop  || 0;
	BA.geom.windowW  = w.innerWidth  || (isMacIE ? b.scrollWidth  : d.offsetWidth );
	BA.geom.windowH  = w.innerHeight || (isMacIE ? b.scrollHeight : d.offsetHeight);
	BA.geom.pageW    = (isMacIE) ? d.offsetWidth  : (isWinIEqm) ? b.scrollWidth  : d.scrollWidth ;
	BA.geom.pageH    = (isMacIE) ? d.offsetHeight : (isWinIEqm) ? b.scrollHeight : d.scrollHeight;
	BA.geom.windowX  = (!e) ? (BA.geom.windowX  ||  0) : e.clientX - ( isSafari ? BA.geom.scrollX : 0);
	BA.geom.windowY  = (!e) ? (BA.geom.windowY  ||  0) : e.clientY - ( isSafari ? BA.geom.scrollY : 0);
	BA.geom.mouseX   = (!e) ? (BA.geom.mouseX   ||  0) : e.clientX + (!isSafari ? BA.geom.scrollX : 0);
	BA.geom.mouseY   = (!e) ? (BA.geom.mouseY   ||  0) : e.clientY + (!isSafari ? BA.geom.scrollY : 0);
	BA.geom.nodeName = (!e) ? (BA.geom.nodeName || '') : e.target.nodeName;

	if (BA.debugMode) {
		BA_STATUSMSG.set(   'pageW: '   + BA.geom.pageW   + ' | pageH: '   + BA.geom.pageH   +
		                ' | windowX: ' + BA.geom.windowX + ' | windowY: ' + BA.geom.windowY +
		                ' | scrollX: ' + BA.geom.scrollX + ' | scrollY: ' + BA.geom.scrollY +
		                ' | left: '    + BA.geom.mouseX  + ' | top: '     + BA.geom.mouseY  +
		                ' | node: '    + BA.geom.nodeName);
	}
}



/* ----- Function : BAGetZoomRatio ----- */
/**
 * get zoom ratio of the viewport (for WinIE 7.0)
 * @return current zoom ratio (0 - 1)
 * @type Number
 */
function BAGetZoomRatio() {
	var per = 1;
	if (BA.ua.isIE70) {
		if (!document.compatMode || document.compatMode == 'BackCompat') {
			per = document.documentElement.offsetWidth / document.body.offsetWidth;
		} else {
			BAGetGeometry();
			var ins = document.createElementBA('ins');
			ins.style.display  = 'block';
			ins.style.position = 'absolute';
			ins.style.top      = '-100px';
			ins.style.left     = '0px';
			ins.style.width    = (BA.geom.pageW * 10) + 'px';
			document.body.appendChildBA(ins);
			BAGetGeometry();
			per = BA.geom.pageW / ins.offsetWidth;
			document.body.removeChild(ins);
		}
	}
	return per;
}



/* --------------- Function : BASetWording --------------- */
/**
 * register wording data to hierarchic wording object.
 * @param {String} expression    expression of store point
 * @param {String} str           string to store
 */
function BASetWording(expression, str) {
	if (typeof expression != 'string' || typeof str != 'string') {
		throw 'BA_SETWORDING_INVALID_ARGUMENT_TYPE';
	} else if (!expression) {
		throw 'BA_SETWORDING_EXPRESSION_IS_EMPTY';
	} else {
		var props  = expression.split('.');
		var target = 'window';
		while (props.length > 1) {
			var type   = null;
			    target += '.' + props.shift();
			try {
				type = (typeof eval(target));
			} catch(err) { }
			switch (type) {
				case 'undefined' :
					eval(target + ' = {}');
					break;
				case 'object' :
					break;
				default  : 
					throw 'BA_SETWORDING_SPECIFIED_EXPRESSION_IS_UNAVAILABLE';
					return;
			}
		}
		var type = null;
		try {
			type = typeof eval('window.' + expression);
		} catch(err) {
			throw 'BA_SETWORDING_SPECIFIED_EXPRESSION_IS_UNAVAILABLE';
		}
		if (type == 'undefined' || type == 'string') {
			eval(expression + ' = "' + str + '"');
		}
	}
}



/* --------------- Constructor : BASetTimeout --------------- */
/**
 * an alternate method of 'setTimeout()'.
 * @class extended 'setTimeout'
 * @constructor
 * @param {Function} func           function / method       (required)
 * @param {Number}   ms             milliseconds to timeout (required)
 * @param {Object}   aThisObject    the object that will be a global object ('this') in the func
 */
function BASetTimeout(func, ms, aThisObject) {
	/** index number of stored func.
	    @type String */
	this.storeIndex = 0;
	/** timer ID.
	    @type Number @private */
	this.timer      = null;

	if (arguments.length) this.storeFunc(func, ms, aThisObject);
}

BASetTimeout.prototype = {
	/**
	 * store func to the entry point, and set timeout timer.
	 * @param {Function} func           function / method       (required)
	 * @param {Number}   ms             milliseconds to timeout (required)
	 * @param {Object}   aThisObject    the object that will be a global object ('this') in the func
	 * @private
	 */
	storeFunc : function(func, ms, aThisObject) {
		if (!window.__BATimeout_store__) {
			window.__BATimeout_store__ = [];
			__BATimeout_store__.unlock = function(num) {
				delete this[num].__BATimeout_locked__;
			};
		}
		var len = __BATimeout_store__.length;
		while (this.storeIndex < len && __BATimeout_store__[this.storeIndex].__BATimeout_locked__) {
			this.storeIndex++;
		}
		__BATimeout_store__[this.storeIndex] = BACreateDelegate(func, aThisObject);
		__BATimeout_store__[this.storeIndex].__BATimeout_locked__ = true;
		this.setTimer(ms);
	},

	/**
	 * set timeout timer.
	 * @param {Number} ms    milliseconds to timeout (required)
	 * @private
	 */
	setTimer : function(ms) {
		this.timer = setTimeout('__BATimeout_store__[' + this.storeIndex + ']()', ms);
		this.removeFunc(ms);
	},
	
	/**
	 * clear timeout timer.
	 */
	clearTimer : function() {
		if (this.timer) {
			clearTimeout(this.timer);
			this.timer = null;
			this.removeFunc(0);
		}
	},

	/**
	 * remove stored func.
	 * @param {Number} delay    remove delay (milliseconds) (required)
	 * @private
	 */
	removeFunc : function(delay) {
		setTimeout('__BATimeout_store__.unlock(' + this.storeIndex + ')', delay);
	}
}



/* --------------- Constructor : BASetInterval inherits BASetTimeout --------------- */
/**
 * an alternate method of 'setInterval()'.
 * @class extended 'setInterval'
 * @constructor
 * @param {Function} func           function / method         (required)
 * @param {Number}   ms             milliseconds for interval (required)
 * @param {Object}   aThisObject    the object that will be a global object ('this') in the func
 */
function BASetInterval(func, ms, aThisObject) {
	if (arguments.length) this.storeFunc(func, ms, aThisObject);
}

BASetInterval.prototype = new BASetTimeout;

/**
 * set interval timer
 * @param {Number} ms    milliseconds for interval (required)
 * @private
 */
BASetInterval.prototype.setTimer = function(ms) {
	this.timer = setInterval('__BATimeout_store__[' + this.storeIndex + ']()', ms);
}

/**
 *  clear interval timer.
 */
BASetInterval.prototype.clearTimer = function() {
	if (this.timer) {
		clearInterval(this.timer);
		this.timer = null;
		this.removeFunc(0);
	}
}



/* --------------- Instance : BAStatusDisplay --------------- */
/**
 * provides status display system.
 * @class status display
 * @constructor
 * @param {BAElement} node          element node to display status (required)
 * @param {String}    defaultMsg    default status string          (required)
 */
function BAStatusDisplay(node, defaultMsg) {
	/** element node to display status
	    @type BAElement @const @private */
	this.statusNode = node;
	/** default message of status display.
	    @type String */
	this.defaultMsg = defaultMsg;

	if (arguments.length) this.init();
}

BAStatusDisplay.prototype = {
	/**
	 * initialize.
	 * @private
	 */
	init : function() {
		if (!this.defaultMsg) {
			this.setDefaultMsg(this.statusNode.getInnerTextBA());
		}
		this.statusNode.removeAllChildsBA();
		this.statusNode.appendChildBA('');
		this.unset();
	},

	/**
	 * set default message text.
	 * @param {String} msg    message as default
	 */
	setDefaultMsg : function(msg) {
		this.defaultMsg = msg;
	},

	/**
	 * set and indicate status message.
	 * @param {String} msg         message to indicate
	 * @param {Number} delay       indicate delay      (ms) 
	 * @param {Number} sustain     indicate sustain    (ms)
	 */
	set : function(msg, delay, sustain) {
		this.msg     = (typeof msg     != 'undefined' && msg        ) ? msg     : '';
		this.delay   = (typeof delay   == 'number'    && delay   > 0) ? delay   :  0;
		this.sustain = (typeof sustain == 'number'    && sustain > 0) ? sustain :  0;
		if (this.delay > 0) {
			this.delayTimer = new BASetTimeout(this.setProcess, this.delay, this);
		} else {
			this.setProcess();
		}
	},

	/**
	 * remove status message immediately.
	 */
	unset : function() {
		this.clearTimer();
		this.unsetMsg();
	},

	/**
	 * set or unset message, and set sustain timer.
	 * @private
	 */
	setProcess : function() {
		this.clearTimer();
		if (this.msg) {
			this.setMsg();
			if (this.sustain > 0) {
				this.sustainTimer = new BASetTimeout(this.unset, this.sustain, this);
			}
		} else {
			this.unset();
		}
	},

	/**
	 * set message to the node 
	 * @private
	 */
	setMsg : function() {
		if (this.statusNode) {
			this.statusNode.firstChild.nodeValue = this.msg;
		}
	},

	/**
	 * unset (reset to default) msessage
	 * @private
	 */
	unsetMsg : function() {
		if (this.statusNode) {
			this.statusNode.firstChild.nodeValue = this.defaultMsg;
		}
	},

	/**
	 * clear timers
	 * @private
	 */
	clearTimer : function() {
		if (this.delayTimer  ) this.delayTimer.clearTimer();
		if (this.sustainTimer) this.sustainTimer.clearTimer();
	}
}



/* --------------- constructor : BAStatusMsg inherits BAStatusDisplay --------------- */
/**
 * constructor of 'BA_STATUSMSG' object that will be used as singleton.
 * @class window.status message controller.
 * @constructor
 */
function BAStatusMsg() {
	/** default message of window.status
	    @type String @const */
	this.defaultMsg = window.defaultStatus || '';
}

BAStatusMsg.prototype = new BAStatusDisplay;

/**
 * set message to 'window.status'
 * @private
 */
BAStatusMsg.prototype.setMsg = function() {
	window.status = this.msg;
}

/**
 * unset (reset to default) msessage
 * @private
 */
BAStatusMsg.prototype.unsetMsg = function() {
	window.status = this.defaultMsg;
}



/* --------------- Constructor : BATimer --------------- */
/**
 * construct elapsed timer object.
 * @class simple elapsed timer.
 * @constructor
 */
function BATimer() { 
	this.reset();
}

BATimer.prototype = {
	/**
	 * reset timer.
	 */
	reset : function() {
		this.startTime = (new Date()).getTime();
	},
	
	/**
	 * get acquire time progress in milisecond.
	 * @return acquire time progress in milisecond.
	 * @type Number
	 */
	getTime : function() {
		return (new Date()).getTime() - this.startTime;
	},
	
	/**
	 * get acquire time progress in second.
	 * @return acquire time progress in second.
	 * @type Number
	 */
	getSeconds : function() {
		return this.getTime() / 1000;
	}
}



/* --------------- Constructor : BATag --------------- */
/**
 * create tag string object for document.write().
 * @class tagstring as element object.
 * @constructor
 * @param {String} tagName     element name to create (required)
 * @param {Object} attrs       associative array { attr1 : value1, attr2 : value2 ... }
 */
function BATag(tagName, attrs) {
	/** tag name (element name) to create
	    @type String @const */
	this.tagName    = tagName;
	/** associative array of attributes. { attr1 : value1, attr2 : value2 ... }
	    @type Object */
	this.attributes = attrs || {};
	/** array of childNodes instances (BATag instances).
	    @type Array */
	this.childNodes = [];
	/** alternate for 'instanceof' operator (MacIE cannot understand that operator)
	    @type BATag @const */
	this.instanceOf = BATag;
}

BATag.prototype = {
	/**
	 * set attribute value.
	 * @param {String} attrName     attribute name (required)
	 * @param {String} value        value to set   (required)
	 */
	setAttributeBA : function(attrName, value) {
		this.attributes[attrName] = value;
	},

	/**
	 * append child instance.
	 * arg is valid also BATag and String.
	 * @param {BATag}  arg     BATag instance to append as child node (required)
	 * @param {String} arg     string to append as child text node    (required)
	 */
	appendChildBA : function(arg) {
		this.childNodes.push(arg);
	},

	/**
	 * output instance data as tag string. typically to use document.write().
	 * @param {Boolean} debug     debug mode - escaped output
	 * @return HTML tag string
	 * @type String
	 */
	toString : function(debug) {
		var tagOpen    = (debug) ? '&lt;' : '<';
		var tagClose   = (debug) ? '&gt;' : '>';
		var tag        = tagOpen + this.tagName;
		var content    = (this.childNodes.length) ? '' : null;
		for (var i = 0, n = this.childNodes.length; i < n; i++) {
			content += this.childNodes[i].toString(debug);
		}
		for (var attr in this.attributes) {
			tag += ' ' + attr + '="' + this.attributes[attr] + '"';
		}
		tag += (content != null) ?
		       	tagClose + content + tagOpen + '/' + this.tagName + tagClose :
		       	' /' + tagClose;
		return tag;
	}
}



/* --------------- Constructor : BAKeyEquivalents --------------- */
/**
 * provide keyboard equivalents system.
 * @class keyboard equivalents
 * @constructor
 */
function BAKeyEquivalents() {
	/** associative array of key asign. { keymark : { aliasName, keyCode, DOMName }, ... }
	    @type Object @const @private */
	this.keyAlias = {
		'$' : { aliasName : 'Shift'       , keyCode : 16, DOMName : 'shiftKey' },
		'%' : { aliasName : 'Ctrl'        , keyCode : 17, DOMName : 'ctrlKey'  },
		'~' : { aliasName : 'Alt/Option'  , keyCode : 18, DOMName : 'altKey'   },
		'&' : { aliasName : 'Meta'        , keyCode : 91, DOMName : null       },  /* (works on WinIE, Win Firefox) */
		'#' : { aliasName : 'Enter/Return', keyCode : 13, DOMName : null       },
		'|' : { aliasName : 'Tab'         , keyCode :  9, DOMName : null       },
		'!' : { aliasName : 'Esc'         , keyCode : 27, DOMName : null       },  /* (works on Win Firefox) */
		'<' : { aliasName : '\u2190'      , keyCode : 37, DOMName : null       },  /* left  */
		'{' : { aliasName : '\u2191'      , keyCode : 38, DOMName : null       },  /* up    */
		'>' : { aliasName : '\u2192'      , keyCode : 39, DOMName : null       },  /* right */
		'}' : { aliasName : '\u2193'      , keyCode : 40, DOMName : null       }   /* down  */
	}
	/** associative array of key equivalents. { name : { key, aCallBack, aThisObject }, ... }
	    @type Object @private */
	this.equivalents = {};
	/** number of equivalents
	    @type Number @private */
	this.numOfEquivs = 0;
}

BAKeyEquivalents.prototype = {
	/**
	 * initialize. (call this after window.onload)
	 * @private
	 */
	init : function() {
		document.addEventListenerBA('keydown', BACreateDelegate(this.dispatchEvent, this));
	},

	/**
	 * register key equivalent for function call.
	 * @param {String}   key            key bind setting string     (required)
	 * @param {String}   name           name for the equivalent
	 * @param {Function} aCallBack      function for the equivalent (required)
	 * @param {Object}   aThisObject    the object that will be a global object ('this') in aCallBack func
	 * @return name of the equivalent
	 * @type String
	 */
	addKeyEquivalent : function(key, name, aCallBack, aThisObject) {
		this.numOfEquivs++;
		if (!name) name = 'BAKeyEquivalents' + this.numOfEquivs;
		this.equivalents[name] = { key : key, aCallBack : aCallBack, aThisObject : aThisObject };
		return name;
	},
	
	/**
	 * get key bind string in human readable format.
	 * @param {String} name    equivalent name (required)
	 * @return key bind string
	 * @type String
	 */
	getKeybind : function(name) {
		var equiv = this.equivalents[name];
		var keys  = equiv.key.split('');
		var ret   = [];
		for (var i = 0, n = keys.length; i < n; i++) {
			ret.push(this.keyAlias[keys[i]] ? this.keyAlias[keys[i]].aliasName : keys[i].toUpperCase());
		}
		return ret;
	},
	
	/**
	 * call registered function in purpose (without key operation).
	 * @param {String} name    equivalent name (required)
	 */
	execFunc : function(name, e) {
		if (name && this.equivalents[name]) {
			var equiv = this.equivalents[name];
			equiv.aCallBack.call(equiv.aThisObject, e);
		}
	},
	
	/**
	 * call registered function by event driven.
	 * @param {Event} e    event object
	 */
	dispatchEvent : function(e) {
		for (var name in this.equivalents) {
			var equiv = this.equivalents[name];
			var keys  = equiv.key.split('');
			var flag  = true;
			for (var i = 0, n = keys.length; (i < n && flag); i++) {
				var key      = keys[i].toLowerCase();
				var keyAlias = this.keyAlias[key];
				if (keyAlias && keyAlias.DOMName) {
					flag = (flag && e[keyAlias.DOMName]);
				} else if (keyAlias && keyAlias.keyCode) {
					flag = (flag && (e.keyCode == keyAlias.keyCode));
				} else {
					var chr = String.fromCharCode(e.charCode ? e.charCode : e.keyCode).toLowerCase();
					flag = (flag && (key == chr));
				}
			}
			if (flag) {
				this.execFunc(name, e);
			}
		}
	}
}





/* --------------- Startup Functions (non-DOM / pre onload) --------------- */

/* ----- BAAppendReviseCSS ----- */
/**
 * Load revise CSS file for specific browsers.
 * @see Window#BAAppendCSS
 */
function BAAppendReviseCSS() {
	for (var i in BA.css.revise) {
		var ua = i.split('.')[0];
		var os = i.split('.')[1];
		if (os && BA.ua['is' + os] && BA.ua['is' + ua] || !os && BA.ua['is' + ua]) {
			BAAppendCSS(BA.url.cssDir + BA.css.revise[i], BA.css.reviseTitle);
			break;
		}
	}
}





/* --------------- Startup Functions (DOM / post onload) --------------- */

/* ----- BARegisterDOMMethodsToEveryNode ----- */
/**
 * Add custom methods to all existing nodes (WinIE/MacIE/Safari/etc).
 * @see Window#BARegisterDOMMethodsTo
 */
function BARegisterDOMMethodsToEveryNode() {
	if (typeof Node == 'object' && Node.prototype) return;
	if (BAAlreadyApplied(arguments.callee))        return;

	var nodes = document.getElementsByTagNameBA('*');
	for (var i = 0, n = nodes.length; i < n; i++) {
		BARegisterDOMMethodsTo(nodes[i], false);
	}
}

/* ----- BAStartGeometryMeasure ----- */
/**
 * Start measuring window geometry.
 * @see Window#BAGetGeometry
 */
function BAStartGeometryMeasure() {
	if (!BA.debugMode || BAAlreadyApplied(arguments.callee)) return;

	BAGetGeometry();
	document.addEventListenerBA('mousemove' , BAGetGeometry);
	document.addEventListenerBA('mousewheel', BAGetGeometry);
}

/* ----- BAScrollToFragmentIDTarget ----- */
/**
 * Auto scroll to the block that specified by fragment identifier of URL (Safari only).
 * Why needed this, Safari fails native cue scroll in the case of
 * stylesheet(s) is/are dynamically appended by 'document.write'! (that in BAAppendCSS())
 */
function BAScrollToFragmentIDTarget() {
	if (BAAlreadyApplied(arguments.callee)) return;
	if (!BA.ua.isSafari || !BA.css.revise.Safari) return;

	var target = location.hash                   || '';
	    target = target.split('#')[1]            || '';
	    target = document.getElementById(target) || null;
	if (target) {
		window.scrollTo(0, target.getAbsoluteOffsetBA().Y);
	}
}






/* --------------- Main --------------- */

BA           = BASingleton(BAEnvironment);
BA_STATUSMSG = BASingleton(BAStatusMsg);
BA_KEYEQUIV  = BASingleton(BAKeyEquivalents);

BARegisterDOMMethods();
BAAppendReviseCSS();

BAAddOnload(function() {
	BARegisterDOMMethodsToEveryNode();
	BAScrollToFragmentIDTarget();
	BA_KEYEQUIV.init();
	BA_STATUSMSG.unset();
	BAStartGeometryMeasure();
});
