/* -------------------------------------------------------------------------- */
/** 
 *    @fileoverview Common Script Libraries
 *    @version rev001.2008/10/16
 */
/* -------------------------------------------------------------------------- */


var ITV;



/* ============================== Common preparations ============================== */

/* --------------- Constructor : ITVEnvironment --------------- */

function ITVEnvironment() {
	this.debugMode     = true;
	this.exceptMacIE   = true;
	this.exceptWinIE50 = true;

    var d  = document;
	var di = d.implementation;
	var de = d.documentElement;
	var ua = navigator.userAgent;
	var lp = location.protocol;
	var lh = location.hostname;

	this.url = {};
	this.url.commonDir   = ITVGetCommonDir('common') || ITVGetCommonDir('shared');

    this.url.cssDir      = this.url.commonDir + 'css/';
	this.url.jsDir       = this.url.commonDir + 'js/';	
	
	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.ITVattrs      = 'urn:ITV.attrs';

	this.prefix = {};
	this.prefix.ITVattrs  = 'ITVattrs:';

	this.ua = {};
	this.ua.isGecko      = ua.match(/Gecko\//);
	this.ua.isSafari     = ua.match(/AppleWebKit/);
	this.ua.isOpera      = window.opera;
	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.isIE    ) ? parseFloat(ua.match(/MSIE ([\d\.]+)/)[1])         :
	                       (this.ua.isGecko ) ? parseFloat(ua.match(/; rv:([\d\.]+)/)[1])         :
	                       (this.ua.isSafari) ? parseFloat(ua.match(/AppleWebKit\/([\d\.]+)/)[1]) :
	                       (this.ua.isOpera ) ? parseFloat(ua.match(/Opera.([\d\.]+)/)[1])        :
	                                            0;
	this.ua.DOMok        = (this.exceptMacIE   && this.ua.isMacIE                  ) ? false                        :
	                       (this.exceptWinIE50 && this.ua.isWinIE && this.ua.isIE50) ? false                        :
	                       (di)                                                      ? di.hasFeature('HTML', '1.0') :
	                                                                                   (this.ua.isIE && de)         ;
	this.ua.isWinIEQM    = this.ua.isWinIE && (!document.compatMode || document.compatMode == 'BackCompat');

	this.env = {};
	this.env.online      = (lp == 'http:' || lp == 'https:');
	this.env.referer     = (typeof document.referrer == 'string') ? document.referrer : '';

	this.css = {};
	this.css.revise      = {
//		'Safari'   : 'revise_safari.css',
//		'IE50.Win' : 'revise_winie50.css'
	};
	this.css.reviseTitle = '';

	this.geom            = {};
}



/* --------------- EventHandler : window.onerror --------------- */

window.onerror = function() {
	if (typeof ITV == 'object') {
		if (ITV.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 ==================== */

/* ----- window.encodeURIComponent() ----- */

if (!window.encodeURIComponent) {
	window.encodeURIComponent = function(str) {
		return escape(str);
	}
}

/* ----- Function.apply() ----- */

if (!Function.prototype.apply) {
	Function.prototype.apply = function(aThisObject, anArray) {
		if (typeof anArray != 'null' && typeof anArray != 'undefined' && typeof anArray != 'object') {
			throw 'Function.apply: second argument must be an array.';
		} else {
			if (typeof aThisObject != 'object' || !aThisObject) {
				aThisObject = window;
			}
			if (!anArray) {
				anArray = [];
			}
			var prop = '__Function_Apply_stored__';
			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(',') + ')');
			try {
				delete aThisObject[prop];
			} catch(err) {
				aThisObject[prop] = null;
			}
			return ret;
		}
	}
}

/* ----- Function.call() ----- */

if (!Function.prototype.call) {
	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) {
	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) {
	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) {
	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) {
	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.splice() ----- */

if (!Array.prototype.splice) {
	Array.prototype.splice = function(index, howMany /* , element1, element2 ... */) {
		index     = (index < 0) ? Math.max(index + this.length, 0) : Math.min(index, this.length);
		howMany   = Math.min(Math.max(howMany || this.length, 0), this.length - index);
		var left  = this.slice(0, index);
		var right = this.slice(index + howMany);
		var ret   = this.slice(index, index + howMany);
		Array.prototype.slice.call(arguments, 2).forEach(function(item) { left.push(item) });
		this.length = 0;
		this.push.apply(this, left );
		this.push.apply(this, right);
		return ret;
	}
}

/* ----- Array.indexOf() ----- */

if (!Array.prototype.indexOf) {
	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) {
	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) {
	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) {
	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) {
	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) {
	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) {
	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) {
	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.formatNumberITV() ----- */

Number.prototype.formatNumberITV = function(format) {
	if (!format || typeof format != 'string') {
		throw 'Number.formatNumberITV: first argument must be a formatting string.';
	} else {
		var ret       = [];
		var intFormat = format.split('.')[0].split('');
		var decFormat = format.split('.')[1] || '';
		var value     = (decFormat) ? Math.abs(this) : Math.round(Math.abs(this));
		var sign      = (this < 0) ? '-' : '';
		var intValue  = value.toString().split('.')[0].split('');
		do {
			var _value  = intValue .pop() || '';
			var _format = intFormat.pop() || '';
			switch (_format) {
				case '0' : ret.push(_value  ? _value : '0');                        break;
				case '#' : ret.push(_value  ? _value : '' );                        break;
				case ''  : /* exit do-while loop */          intValue = [];         break;
				default  : ret.push(_format               ); intValue.push(_value); break;
			}
		} while (intValue.length > 0 || intFormat.length > 0);
		ret = ret.reverse().join('').replace(/^\D+/, '');
		if (decFormat) {
			var scale     = Math.pow(10, decFormat.length);
			var rounded   = Math.round(value * scale) / scale;
			if (rounded - ret == 1) {
				ret++;
			}
			var decValue  = rounded.toString().split('.')[1] || '0';
			    decValue  = decValue .split('').reverse().join('');
			    decFormat = decFormat.split('').reverse().join('');
			    ret       = ret + '.' + decValue.formatNumberITV(decFormat).split('').reverse().join('');
		}
		if (decFormat.startsWithITV('#') && ret.endsWithITV('.0')) {
			ret = ret.getBeforeITV('.0');
		}
		return sign + ret;
	}
}

/* ----- String.formatNumberITV() ----- */

String.prototype.formatNumberITV = function(format) {
	var num = parseFloat(this, 10);
	if (isNaN(num)) {
		throw 'String.formatNumberITV: this string is not a number string.';
	} else {
		return num.formatNumberITV(format);
	}
}

/* ----- String.formatTextITV() ----- */

String.prototype.formatTextITV = 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;
}

/* ----- String.getBeforeITV() ----- */

String.prototype.getBeforeITV = function(str, include) {
	var idx = this.indexOf(str);
	return (idx == -1) ? '' : this.substring(0, idx) + (include ? str : '');
}

/* ----- String.getAfterITV() ----- */

String.prototype.getAfterITV = function(str, include) {
	var idx = this.indexOf(str);
	return (idx == -1) ? '' : (include ? str : '') + this.substring(idx + str.length, this.length);
}

/* ----- String.startsWithITV() ----- */

String.prototype.startsWithITV = function(str) {
	return (this.indexOf(str) == 0);
}

/* ----- String.endsWithITV() ----- */

String.prototype.endsWithITV = function(str) {
	var idx = this.lastIndexOf(str);
	return (idx > -1 && idx + str.length == this.length);
}

/* ----- String.relToAbsITV() ----- */

String.prototype.relToAbsITV = function(base) {
	var b   = base.split('/');
	var t   = this.split('/');
	var ptn = /^(\/|\w+:)/;
	if (!base.match(ptn)) {
		throw 'String.relToAbsITV: first argument must be an absolute path/URL.';
	} else if (this.match(ptn)) {
		return this;
	} else if (this.charAt(0) == '#' || this.charAt(0) == '?') {
		return base + this;
	} else if (t[0] == '.' || t[0] == '..') {
		return t.slice(1, t.length).join('/').relToAbsITV(b.slice(0, b.length - t[0].length).join('/') + '/');
	} else {
		return b.slice(0, b.length - 1).join('/') + '/' + this;
	}
}

/* ----- String.absToRelITV() ----- */

String.prototype.absToRelITV = function(base) {
	var ptn = /^(\/|\w+:)/;
	if (!base.match(ptn)) {
		throw 'String.absToRelITV: first argument must be an absolute path/URL.';
	} else if (!this.match(ptn)) {
		throw 'String.absToRelITV: String must be an absolute path/URL.';
	} else {
		return _compare(base, this) || base;
	}

	function _compare(base, trgt) {
		var b = base.split('/');
		var t = trgt.split('/');
		if (!base) {
			return trgt;
		} else if (!trgt) {
			return _goup(base);
		} else if (b[0] != t[0]) {
			return _goup(base) + trgt;
		} else {
			return arguments.callee(b.slice(1, b.length).join('/'), t.slice(1, t.length).join('/'));
		}
	}
	
	function _goup(path) {
		path = path.split('/');
		path.shift();
		path.forEach(function(elem, idx) { path[idx] = '..' });
		return path.join('/') + '/';
	}
}

/* ----- String.getSanitizedStringITV() ----- */

String.prototype.getSanitizedStringITV = function() {
	var pairs = {
		"&"      : "&amp;",
		"<"      : "&lt;",
		">"      : "&gt;",
		"\u0022" : "&quot;",
		"\u0027" : "&apos;"
	};
	var ret = this;
	for (var key in pairs) {
		ret = ret.replace(new RegExp(key, "g"), pairs[key]);
	}
	return ret;
}






/* ==================== Custom DOM methods for built-in DOM objects ==================== */

/* ---------- Constructor : ITVDOM (Abstract Class) ---------- */

function ITVDOM() {
	this.instanceOf = 'ITVElement';
}

/* ----- ITVDOM.addEventListenerITV() ----- */

ITVDOM.prototype.addEventListenerITV = function(type, listener, aThisObject) {
	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          = e.detail;
		this.wheelDelta      = e.wheelDelta;
		this.stopPropagation = function() { e.cancelBubble = true  };
		this.preventDefault  = function() { e.returnValue  = false };
	}

	function _callListeners(e) {
		var nodeType = 0;
		try { nodeType = e.target.nodeType } catch(err) { }

		if (nodeType == 3 && ITV.ua.isSafari) { // in earlier Safari (before 2.0?), a text node is treated as 'event target'...
			e.target.parentNode.dispatchEvent(e);
		} else {
			var stored = this.__addEventListenerITV_stored__;
			var type   = e.type;
			if (typeof ITV == 'object' && typeof ITVRegisterDOMMethodsTo == 'function') {
				ITVRegisterDOMMethodsTo(e.target);
				ITVRegisterDOMMethodsTo(e.currentTarget);
				ITVRegisterDOMMethodsTo(e.relatedTarget);
				if (ITV.ua.isGecko && type == 'DOMMouseScroll') {
					e.wheelDelta = e.detail * -40;
				} else if (ITV.ua.isSafari && ITV.ua.revision < 412) { // preventDefault() doesn't work on Safari before 2.0.4...
					e.preventDefault = function() { this.currentTarget['on' + this.type] = function() { return false } };
				}
				if (stored && stored[type]) {
					stored[type].forEach(function(func) { func(e) });
				}
			}
		}
	}

	// preparations.
	if (!type || typeof type != 'string') {
		throw 'ITVDOM.addEventListenerITV: first argument must be a string (event type).';
	} else if (!listener || typeof listener != 'function') {
		throw 'ITVDOM.addEventListenerITV: second argument must be a function (event listener).';
	} else if (ITV.ua.isOpera && ITV.ua.revision < 9.02 && type == 'mousewheel') {
		return;  // Opera before 9.02 has a bug on 'mousewheel' event (user cannot scroll-up by mousewheel).
	} else if (ITV.ua.isGecko && type == 'mousewheel') {
		type = 'DOMMouseScroll';
	}

	// create event caller.
	var stored = this.__addEventListenerITV_stored__;
	if (!stored) {
		stored = this.__addEventListenerITV_stored__ = {};
	}
	var funcs = stored[type];
	if (!funcs) {
		funcs = stored[type] = [];
		if (this.addEventListener) {                          // standard compliant browsers
			this.addEventListener(type, _callListeners, false);
		} else {                                              // WinIE/MacIE
			var _this = (this.window) ? this.window : this;   // workaround for WinIE
			if (_this == window && type == 'load' && window.attachEvent) {
				window.attachEvent('onload', function() { _callListeners.call(_this, new _event_IE(_this)) });
			} else {
				var exists = _this['on' + type];
				_this['on' + type] = (exists) ?
					function() { exists(); _callListeners.call(_this, new _event_IE(_this)) } :
					function() {           _callListeners.call(_this, new _event_IE(_this)) } ;
			}
		}
	}

	// register event listener.
	if (funcs.indexOf(listener) > -1) {
		return;
	} else if (typeof aThisObject == 'object' && aThisObject != null) {
		funcs.push(ITVCreateDelegate(listener, aThisObject));
	} else {
		funcs.push(listener);
	}

	// preparations for ITVCleanUpEventListeners()
	var nodes = window.ITV_EVENTLISTENER_STORED_NODES;
	if (!nodes) {
		nodes = window.ITV_EVENTLISTENER_STORED_NODES = [];
	}
	if (nodes.indexOf(this) == -1) {
		nodes.push(this);
	}
}



/* ---------- Constructor : ITVWindow (Abstract Class) inherits ITVDOM ---------- */

function ITVWindow() { }

ITVWindow.prototype = new ITVDOM;



/* ---------- Constructor : ITVDocument (Abstract Class) inherits ITVDOM ---------- */

function ITVDocument() { }

ITVDocument.prototype = new ITVDOM;

/* ----- ITVDocument.getElementsByTagNameITV() ----- */

ITVDocument.prototype.getElementsByTagNameITV = function(tagName) {
	var ret = [];
	if (!tagName || typeof tagName != 'string') {
		throw 'ITVDocument.getElementsByTagNameITV: first argument must be a string (tagName).';
	} else if (tagName == '*') {
		ret = this.getElementsByTagName(tagName);
		if (ITV.ua.isIE || ret.length == 0) {
			ret = (document.all && this === document) ?
				document.all :
				(function(_node) {
					var _nodes = _node.childNodes;
					var _ret   = [];
					for (var i = 0, n = _nodes.length; i < n; i++) {
						var __node = _nodes[i];
						if (__node.nodeType == 1 && __node.nodeName != '!') {
							_ret.push(__node);
						}
						_ret = _ret.concat(arguments.callee(__node));
					}
					return _ret;
				})(this);
		}
	} else if (tagName.match(/:/)) {
		var prfx = tagName.split(':')[0];
		var name = tagName.split(':')[1];
		     ret = (ITV.ns.defaultNS && this.getElementsByTagNameNS) ?
		           	this.getElementsByTagNameNS(ITV.ns[prfx], name) :
		           	this.getElementsByTagName(tagName) ;
		if (ret.length == 0) {
			       ret = (name == '*') ? this.getElementsByTagNameITV(name) : this.getElementsByTagName(name);
			var _nodes = [];
			for (var i = 0, n = ret.length; i < n; i++){
				var _node = ret[i];
				if (ITV.ns.defaultNS && _node.namespaceURI == ITV.ns[prfx] || _node.tagUrn == ITV.ns[prfx]) {
					_nodes.push(_node);
				}
			}
			if (_nodes.length == 0) {
				       ret = (name == '*') ? ret : this.getElementsByTagNameITV('*');
				var _nodes = [];
				for (var i = 0, n = ret.length; i < n; i++) {
					var _node = ret[i];
					var _prfx = _node.nodeName.split(':')[0];
					var _name = _node.nodeName.split(':')[1];
					if (_name && _prfx == prfx && (name == '*' || _name.toLowerCase() == name.toLowerCase())) {
						_nodes.push(_node);
					}
				}
			}
			ret = _nodes;
		}
	} else {
		ret = (ITV.ns.defaultNS && this.getElementsByTagNameNS) ?
		      	this.getElementsByTagNameNS(ITV.ns.defaultNS, tagName) :
		      	(tagName.match(/^body$/i) && document.body) ?
		      		/* workaround for Netscape7.1 */ [document.body] :
		      		this.getElementsByTagName(tagName) ;
		if (typeof document.documentElement.tagUrn == 'string') {
			var _nodes = [];
			for (var i = 0, n = ret.length; i < n; i++){
				var _node = ret[i];
				if (!_node.tagUrn || _node.tagUrn == ITV.ns.defaultNS) {
					_nodes.push(_node);
				}
			}
			ret = _nodes;
		}
	}

	['indexOf', 'lastIndexOf', 'forEach', 'map', 'filter', 'some', 'every', 'equal'].forEach(function(method) {
		if (!ret[method]) ret[method] = function() { return Array.prototype[method].apply(this, arguments) }
	});
	ret.forEach(ITVRegisterDOMMethodsTo);
	return ret;
}

/* ----- ITVDocument.getElementsByClassNameITV() ----- */

ITVDocument.prototype.getElementsByClassNameITV = function(className, tagName) {
	if (!className || typeof className != 'string') {
		throw 'ITVDocument.getElementsByClassNameITV: first argument must be a string (className).';
	} else {
		var nodes = this.getElementsByTagNameITV(tagName || '*');
		var func = function(node) {
			return (node.hasClassNameITV && node.hasClassNameITV(className));
		}
		if (typeof(nodes.filter) != "function") {
			return Array.prototype.filter.apply(nodes, [func]);
		} else {
			return this.getElementsByTagNameITV(tagName || '*').filter(func);
		}
	}
}

/* ----- ITVDocument.getElementsByNameITV() ----- */

ITVDocument.prototype.getElementsByNameITV = function(name) {
	if (!name || typeof name != 'string') {
		throw 'ITVDocument.getElementsByNameITV: first argument must be a string (name attribute).';
	} else {
		return document.getElementsByName(name);
	}
}

/* ----- ITVDocument.getElementByIdITV() ----- */

ITVDocument.prototype.getElementByIdITV = function(id) {
	if (!id || typeof id != 'string') {
		throw 'ITVDocument.getElementByIdITV: first argument must be a string (fragment id).';
	} else {
		return ITVRegisterDOMMethodsTo(document.getElementById(id));
	}
}

/* ----- ITVDocument.createElementITV() ----- */

ITVDocument.prototype.createElementITV = function(tagName) {
	if (!tagName || typeof tagName != 'string') {
		throw 'ITVDocument.createElementITV: first argument must be a string (tagName).';
	} else {
		var node = (ITV.ns.defaultNS && document.createElementNS && tagName.match(/:/)) ?
		           	document.createElementNS(ITV.ns[tagName.split(':')[0]], tagName.split(':')[1]) :
		           	(ITV.ns.defaultNS && document.createElementNS) ?
		           		document.createElementNS(ITV.ns.defaultNS, tagName) : 
		           		document.createElement(tagName) ;
		return ITVRegisterDOMMethodsTo(node);
	}
}



/* ---------- Constructor : ITVElement (Abstract Class) inherits ITVDocument ---------- */

function ITVElement() { }

ITVElement.prototype = new ITVDocument;

ITVElement.prototype.createElementITV = function() { }

/* ----- ITVElement.getAncestorsByTagNameITV() ----- */

ITVElement.prototype.getAncestorsByTagNameITV = function(tagName) {
	if (!tagName || typeof tagName != 'string') {
		throw 'ITVElement.getAncestorsByTagNameITV: first argument must be a string (tagName).';
	} else {
		var fNode = document.getElementsByTagNameITV(tagName);
		var cNode = this.parentNode;
		var ret   = [];
		while (cNode) {
			var idx = fNode.indexOf(cNode);
			if (idx != -1) {
				ret.push(fNode[idx]);
			}
			cNode = cNode.parentNode;
		}
		return ret;
	}
}

/* ----- ITVElement.appendChildITV() ----- */

ITVElement.prototype.appendChildITV = function(content, forceAsHTML) {
	if (typeof content == 'undefined' || typeof content == 'object' && !content) {
		throw 'ITVElement.appendChildITV: first argument must be a node or a string or a ITVTag instance.';
	} else if (content.nodeType == 1 || content.nodeType == 3 || content.nodeType == 11) {
		if (content.__DOCUMENT_FRAGMENT_ITV_NODE__) {
			while (content.hasChildNodes()) {
				this.appendChild(content.firstChild);
			}
			return content;
		} else {
			return this.appendChild(content);
		}
	} else if (content.instanceOf == 'ITVTag' || forceAsHTML) {
		content = content.toString();
		if (document.createRange) {            // Gecko/Safari
			var range = document.createRange();
			range.selectNode(this);
			content = range.createContextualFragment(content); // createContextualFragment() is not standard-DOM?
			this.appendChild(content);
		} else if (this.insertAdjacentHTML) {  // WinIE/MacIE
			this.insertAdjacentHTML('beforeEnd', content);
		} else {                               // Others
			this.innerHTML += content;
		}
		ITVRegisterDOMMethodsTo(this, true);
		return content;
	} else if (content.toString) {
		content  = content.toString();
		var node = document.createTextNode(content);
		if (this.insertAdjacentText) {         // WinIE/MacIE
			this.insertAdjacentText('beforeEnd', content);
			return node;
		} else {                               // Others
			return this.appendChild(node);
		}
	}
}

/* ----- ITVElement.removeChildITV() ----- */

ITVElement.prototype.removeChildITV = function(node) {
	if (!node || typeof node.nodeType != 'number') {
		throw 'ITVElement.removeChildITV: first argument must be a DOM node.';
	} else {
		return ITVRegisterDOMMethodsTo(this.removeChild(node));
	}
}

/* ----- ITVElement.removeAllChildrenITV() ----- */

ITVElement.prototype.removeAllChildrenITV = function() {
	var ret = [];
	while (this.hasChildNodes()) {
		ret.push(this.removeChildITV(this.firstChild));
	}
	return ret;
}

/* ----- ITVElement.getAttributeITV() ----- */

ITVElement.prototype.getAttributeITV = function(attr) {
	if (!attr || typeof attr != 'string') {
		throw 'ITVElement.getAttributeITV: first argument must be a string (atribute name).';
	} else {
		if (ITV.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(ITV.ns[prfx], attr)
		}
		return ret;
	}
}

/* ----- ITVElement.setAttributeITV() ----- */

ITVElement.prototype.setAttributeITV = function(attr, value) {
	if (!attr || typeof attr != 'string') {
		throw 'ITVElement.setAttributeITV: first argument must be a string (atribute name).';
	} else if (attr.match(/:/)) {
		var prfx = attr.split(':')[0];
		var attr = attr.split(':')[1];
		if (this.setAttributeNS && this.namespaceURI || ITV.ua.isSafari) {
			this.setAttributeNS(ITV.ns[prfx], attr, value);
		} else {
			this.setAttribute('xmlns:' + prfx, ITV.ns[prfx]);
			this.setAttribute(prfx + ':' + attr, value);
		}
	} else {
		if (ITV.ua.isIE && attr == 'class') attr += 'Name';
		this.setAttribute(attr, value);
	}
}

/* ----- ITVElement.hasClassNameITV() ----- */

ITVElement.prototype.hasClassNameITV = function(className) {
	if (!className || typeof className != 'string') {
		throw 'ITVElement.hasClassNameITV: first argument must be a string (className).';
	} else if (this.nodeType != 1) {
		return false;
	} else {
		return (this.getAttributeITV('class') || '').split(' ').some(function(name) { return (name == className) });
	}
}

/* ----- ITVElement.appendClassNameITV() ----- */

ITVElement.prototype.appendClassNameITV = function(className) {
	if (!className || typeof className != 'string') {
		throw 'ITVElement.appendClassNameITV: first argument must be a string (className).';
	} else if (!this.hasClassNameITV(className)) {
		var cname = (this.getAttributeITV('class') || '').split(' ');
		cname.push(className);
		this.setAttributeITV('class', cname.join(' '));
	}
},

/* ----- ITVElement.removeClassNameITV() ----- */

ITVElement.prototype.removeClassNameITV = function(className) {
	if (!className || typeof className != 'string') {
		throw 'ITVElement.removeClassNameITV: first argument must be a string (className).';
	} else if (this.hasClassNameITV(className)) {
		var cname = this.getAttributeITV('class').split(' ');
		var index = cname.indexOf(className);
		if (index > -1) {
			cname.splice(index, 1);
			this.setAttributeITV('class', cname.join(' '));
		}
	}
}

/* ----- ITVElement.getAbsoluteOffsetITV() ----- */

ITVElement.prototype.getAbsoluteOffsetITV = function() {
	var offset = { X : this.offsetLeft, Y : this.offsetTop };
	if (this.offsetParent) {
		var op = ITVRegisterDOMMethodsTo(this.offsetParent).getAbsoluteOffsetITV();
		// IE returns wrong value if 'offsetParent block' is position-relative...
		offset.X += op.X;
		offset.Y += op.Y;
	}
	return offset;
}

/* ----- ITVElement.getCurrentStyleITV() ----- */

ITVElement.prototype.getCurrentStyleITV = function(property, pseudo) {
	if (!property || typeof property != 'string') {
		throw 'ITVElement.getCurrentStyleITV: first argument must be a string (property name).';
	} else {
		try {
			return (window.getComputedStyle) ?
				window.getComputedStyle(this, pseudo)[property] : (this.currentStyle) ?
					this.currentStyle[property] : '';
		} catch (err) { // netscape 7.0x causes exception above.
			return '';
		}
	}
}






/* ======================================== Global Functions ======================================== */

/* --------------- Function : ITVRegisterDOMMethodsTo --------------- */

function ITVRegisterDOMMethodsTo(node, deep) {
	var validNodeType;
	try { // prevent exception that raises when 'node' is 'div.anonymous-div' contained by the input nodes in Gecko.
		validNodeType = (node.nodeType == 1 || node.nodeType == 11);
	} catch(err) { }
	if (node && node.instanceOf != 'ITVElement' && validNodeType) {
		for (var i in ITVElement.prototype) {
			node[i] = ITVElement.prototype[i];
		}
	}
	if (deep === true && deep && node && node.hasChildNodes()) {
		for (var i = 0, n = node.childNodes.length; i < n; i++) {
			arguments.callee(node.childNodes[i], true);
		}
	}
	return node;
}



/* --------------- Function : ITVAddOnload --------------- */

function ITVAddOnload(func, aThisObject) {
	var target = (ITV.ua.isGecko || ITV.ua.isOpera) ? document           : window;
	var type   = (ITV.ua.isGecko)                  ? 'DOMContentLoaded' : 'load';

	if (!ITV.ua.isIE) {
		target.addEventListenerITV(type, func, aThisObject);
	} else {
		target.addEventListenerITV(type, function(e) {
			new ITVSetTimeout(function() { func.call(this, e) }, 1, this);
		}, aThisObject);
	}
}



/* --------------- Function : ITVAddOnunload --------------- */

function ITVAddOnunload(func) {
	var target = (ITV.ua.isOpera) ? document : window;
	target.addEventListenerITV('unload', func);
}



/* --------------- Function : ITVGetCommonDir --------------- */

function ITVGetCommonDir(dirName) {
	var sheets = ITVGetStyleSheets();
	
	var ret    = '';
	if (sheets && sheets[0]) {
		var dir  = '/' + dirName + '/';
		var href = sheets[0].href || '';
		var slashSupplemented;
		
		if (!href.match(/^(\.\.?\/|\w+:)/) && !href.startsWithITV('/')) {   // if href is not absolute url (workaround for IE)
			href = '/' + href;
			slashSupplemented = true;

		}
		if (href.indexOf(dir) > -1) {
	
			ret = href.getBeforeITV(dir, true);
			if (!ret.match(/^\w+:/)) {   // if ret is not absolute url (workaround for IE)
				var base = document.getElementsByTagName('base')[0];
				var burl = (base) ? base.href : location.href;
				if (slashSupplemented) {
					ret = ret.substr(1);
				}
				if (ret.startsWithITV('/')) {
					ret = burl.match(/^\w+:\/*[^\/]+/) + ret;
				} else {
					ret = ret.relToAbsITV(burl);
				}
			}
		}
	}
	return ret;
}



/* --------------- Function : ITVGetStyleSheets --------------- */

function ITVGetStyleSheets() {
	var sheets = document.styleSheets;
	// workaround for Safari's lack of implement of document.styleSheets!
	if (sheets && sheets.length == 0 && document.getElementsByTagName) {
		var nodes  = document.getElementsByTagName('link');
            sheets = [];
		for (var i = 0, n = nodes.length; i < n; i++) {
			var rel = nodes[i].getAttribute('rel') || '';
			if (rel.endsWithITV('stylesheet')) {
				sheets.push(nodes[i]);
			}
		}
	}

	return sheets;
}



/* --------------- Function : ITVSingleton --------------- */

function ITVSingleton(_constructor) {
	return _constructor.__ITVSingleInstance__ || (_constructor.__ITVSingleInstance__ = new _constructor());
}



/* --------------- Function : ITVCreateDelegate --------------- */

function ITVCreateDelegate(func, aThisObject){
	var delegate = function(){
		return func.apply(aThisObject, arguments);
	};
	delegate.func        = func;
	delegate.aThisObject = aThisObject;
	return delegate; 
}



/* --------------- Function : ITVAlreadyApplied --------------- */

function ITVAlreadyApplied(func) {
	if (!ITV.ua.DOMok || func.__ITVAlreadyApplied__) return true;
	func.__ITVAlreadyApplied__ = true;
	return false;
}



/* --------------- Function : ITVConcatNodeList --------------- */

function ITVConcatNodeList() {
	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 : ITVPreloadImage --------------- */

function ITVPreloadImage(src) {
	var img = new Image();
	img.src = src;
	return img;
}



/* --------------- Function : ITVAppendStateClassName --------------- */

function ITVAppendStateClassName(className) {
	if (typeof className != 'string' || !className) {
		throw 'ITVSetStateClassName: argument must be a string.';
	} else {
		var body = document.getElementsByTagNameITV('body')[0];
		if (!body) {
			throw 'ITVSetStateClassName: <body> element not exists.';
		} else {
			body.appendClassNameITV(className);
		}
	}
}



/* --------------- Function : ITVRemoveStateClassName --------------- */

function ITVRemoveStateClassName(className) {
	if (typeof className != 'string' || !className) {
		throw 'ITVSetStateClassName: argument must be a string.';
	} else {
		var body = document.getElementsByTagNameITV('body')[0];
		if (!body) {
			throw 'ITVSetStateClassName: <body> element not exists.';
		} else {
			body.removeClassNameITV(className);
		}
	}
}



/* --------------- Function : ITVGetGeometry --------------- */

function ITVGetGeometry(e) {
	var w = window;
	var d = document.documentElement;
	var b = document.getElementsByTagNameITV('body')[0];
	var isWinIEqm = ITV.ua.isWinIEQM;
	var isMacIE   = ITV.ua.isMacIE;
	var isSafari  = ITV.ua.isSafari;

	ITV.geom.scrollBar = _getScrollBarWidth();
	ITV.geom.scrollX   = w.scrollX     || d.scrollLeft || b.scrollLeft || 0;
	ITV.geom.scrollY   = w.scrollY     || d.scrollTop  || b.scrollTop  || 0;
	ITV.geom.windowW   = w.innerWidth  || (isMacIE ? b.scrollWidth  : d.offsetWidth );
	ITV.geom.windowH   = w.innerHeight || (isMacIE ? b.scrollHeight : d.offsetHeight);
	ITV.geom.pageW     = (isMacIE) ? d.offsetWidth  : (isWinIEqm) ? b.scrollWidth  : d.scrollWidth ;
	ITV.geom.pageH     = (isMacIE) ? d.offsetHeight : (isWinIEqm) ? b.scrollHeight : d.scrollHeight;
	ITV.geom.windowX   = (!e) ? (ITV.geom.windowX  ||  0) : e.clientX - ( isSafari ? ITV.geom.scrollX : 0);
	ITV.geom.windowY   = (!e) ? (ITV.geom.windowY  ||  0) : e.clientY - ( isSafari ? ITV.geom.scrollY : 0);
	ITV.geom.mouseX    = (!e) ? (ITV.geom.mouseX   ||  0) : e.clientX + (!isSafari ? ITV.geom.scrollX : 0);
	ITV.geom.mouseY    = (!e) ? (ITV.geom.mouseY   ||  0) : e.clientY + (!isSafari ? ITV.geom.scrollY : 0);
	ITV.geom.nodeName  = (!e) ? (ITV.geom.nodeName || '') : e.target.nodeName;

	return ITV.geom;

	function _getScrollBarWidth() {
		var id   = 'ITVGetGeometry_getScrollBarWidth_testNode';
		var node = document.getElementByIdITV(id);
		if (!node) {
			node = document.createElementITV('ins');
			node.id = id;
			node.style.display    = 'block';
			node.style.visibility = 'hidden';
			node.style.overflow   = 'scroll';
			node.style.position   = 'absolute';
			node.style.border     = 'none';
			node.style.top        = node.style.left    = '-10000px';
			node.style.width      = node.style.height  = '100px';
			node.style.margin     = node.style.padding = '0';
			b.appendChildITV(node);
		}
		return (node.offsetWidth - node.clientWidth);
	}
}



/* ----- Function : ITVGetZoomRatio ----- */

function ITVGetZoomRatio() {
	var per = 1;
	if (ITV.ua.isIE70) {
		if (ITV.ua.isWinIEQM) {
			per = document.documentElement.offsetWidth / document.body.offsetWidth;
		} else {
			ITVGetGeometry();
			var ins = document.createElementITV('ins');
			ins.style.display  = 'block';
			ins.style.position = 'absolute';
			ins.style.top      = '-100px';
			ins.style.left     = '0px';
			ins.style.width    = (ITV.geom.pageW * 10) + 'px';
			document.body.appendChildITV(ins);
			ITVGetGeometry();
			per = ITV.geom.pageW / ins.offsetWidth;
			document.body.removeChildITV(ins);
		}
	}
	return per;
}






/* ============================ Other Constructors ============================ */

/* --------------- Constructor : ITVSetTimeout --------------- */

function ITVSetTimeout(func, ms, aThisObject) {
	this.storeIndex  = 0;
	this.timer       = null;
	this.storeName   = 'ITV_SETTIMEOUT_STOREDFUNC';
	this.removerName = 'ITV_SETTIMEOUT_STOREDFUNC_REMOVER';

	if (arguments.length) this.storeFunc(func, ms, aThisObject);
}

ITVSetTimeout.prototype = {
	storeFunc : function(func, ms, aThisObject) {
		if (!window[this.storeName]) {
			window[this.removerName] = [];
			window[this.storeName]   = [];
			window[this.storeName].remove = function(_idx) {
				this[_idx] = null;
			};
		}

		this.storeIndex = window[this.storeName].push(ITVCreateDelegate(func, aThisObject));
		this.storeIndex--;
		this.setTimer(ms);
	},

	setTimer : function(ms) {
		var func   = 'window.' + this.storeName + '[' + this.storeIndex + ']()';
		this.timer = (ITV.ua.isIE) ?
		             	setTimeout(func, ms, 'JScript') : // workaround to the page weaved with vbscript.
		             	setTimeout(func, ms           ) ;
		this.removeFunc(ms);
	},
	
	clearTimer : function() {
		if (this.timer) {
			this.clearTimerMain();
			clearTimeout(window[this.removerName][this.storeIndex]);
			this.timer = null;
			this.removeFunc(0);
		}
	},

	clearTimerMain : function() {
		clearTimeout(this.timer);
	},

	removeFunc : function(ms) {
		var func  = 'window.' + this.storeName + '.remove(' + this.storeIndex + ')';
		var delay = ms + 1000;
		window[this.removerName][this.storeIndex] = (ITV.ua.isIE) ?
		                                            	setTimeout(func, delay, 'JScript') : // workaround to the page weaved with vbscript.
		                                            	setTimeout(func, delay           ) ;
	}
}



/* --------------- Constructor : ITVSetInterval inherits ITVSetTimeout --------------- */

function ITVSetInterval(func, ms, aThisObject) {
	this.storeName   = 'ITV_SETINTERVAL_STOREDFUNC';
	this.removerName = 'ITV_SETINTERVAL_STOREDFUNC_REMOVER';

	if (arguments.length) this.storeFunc(func, ms, aThisObject);
}

ITVSetInterval.prototype = new ITVSetTimeout;

ITVSetInterval.prototype.setTimer = function(ms) {
	var func   = 'window.' + this.storeName + '[' + this.storeIndex + ']()';
	this.timer = (ITV.ua.isIE) ?
	             	setInterval(func, ms, 'JScript') : // workaround to the page weaved with vbscript.
	             	setInterval(func, ms           ) ;
}

ITVSetInterval.prototype.clearTimerMain = function() {
	clearInterval(this.timer);
}



/* --------------- Constructor : ITVTimer --------------- */

function ITVTimer() { 
	this.reset();
}

ITVTimer.prototype = {
	reset : function() {
		this.startTime = (new Date()).getTime();
	},
	
	getTime : function() {
		return (new Date()).getTime() - this.startTime;
	},
	
	getSeconds : function() {
		return this.getTime() / 1000;
	}
}



/* --------------- Constructor : ITVTag --------------- */

function ITVTag(tagName, attrs) {
	this.tagName    = tagName;
	this.attributes = attrs || {};
	this.childNodes = [];
	this.instanceOf = 'ITVTag';
}

ITVTag.prototype = {
	setAttributeITV : function(attrName, value) {
		this.attributes[attrName] = value;
	},

	appendChildITV : function(arg) {
		this.childNodes.push(arg);
	},

	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 : ITVObservable -------------------- */

function ITVObservable() {
	this.callBackChains = {};
}

ITVObservable.prototype.doCallBack = function(name /* , arg1, arg2, ... */) {
	if (this.callBackChains[name] == null) {
		return null;
	} else {
		var ret;
		var args = [];
		for (var i = 1, n = arguments.length; i < n; i++) {
			args.push(arguments[i]);
		}
		this.callBackChains[name].forEach(function(func) {
			ret = func.apply(null, args);
		});
		return ret;
	}
}

ITVObservable.prototype.setCallBack = function(name, func, aThisObject) {
	if (this.callBackChains[name] == null) {
		this.callBackChains[name] = [];
	}
	this.callBackChains[name].push(ITVCreateDelegate(func, aThisObject));
}






/* =============== Startup Functions (non-DOM / pre onload) =============== */

/* ----- ITVRegisterDOMMethods ----- */

function ITVRegisterDOMMethods() {
	// register to Document object

	for (var name in ITVDocument.prototype) {
		document[name] = ITVDocument.prototype[name];
	}

	// register to Window object
	for (var name in ITVWindow.prototype) {
		window[name] = ITVWindow.prototype[name];
	}
}






/* =============== Startup Functions (DOM / post onload) =============== */

function ITVAddImageTitle() {
	if (!ITVAlreadyApplied(arguments.callee) && !ITV.ua.isIE) {
		ITVConcatNodeList(
			  document.getElementsByTagNameITV('img'  )
			, document.getElementsByTagNameITV('area' )
			, document.getElementsByTagNameITV('input')
		).forEach(function(node) {
			if (!node.title && node.alt) {
				node.title = node.alt;
			}
		});
	}
}




/* =============== Postprocess Functions (DOM / onunload) =============== */

/* ----- ITVCleanUpEventListeners ----- */

function ITVCleanUpEventListeners() {
	if (ITV_EVENTLISTENER_STORED_NODES) {
		ITV_EVENTLISTENER_STORED_NODES.forEach(function(node) {
			for (var type in node.__addEventListenerITV_stored__) {
				if (type != 'unload') {
					node['on' + type] = null;
				}
			}
			node.__addEventListenerITV_stored__ = null;
		});
	}
}


/* ============================== Main ============================== */



ITV = ITVSingleton(ITVEnvironment);

if (typeof ITV == 'object' && ITV.ua.DOMok) {
	ITVRegisterDOMMethods();
	
	ITVAddOnload(function() {
		ITVAppendStateClassName('dom-enabled');
		ITVAddImageTitle();
	});
	
	ITVAddOnunload(function() {
		ITVCleanUpEventListeners();
	});
}

