/* browser detect http://www.quirksmode.org/js/detect.html */

jlib._browserSearchString = function (data) {
	var dataString, dataProp, i;
	for (i = 0; i < data.length; i++) {
		dataString = data[i].string;
		dataProp = data[i].prop;
		jlib._browserVersionSearchString = data[i].versionSearch || data[i].identity;
		if (dataString) {
			if (dataString.indexOf(data[i].subString) != -1) {
				return data[i].identity;
			}
		} else if (dataProp) {
			return data[i].identity;
		}
	}
};

jlib._browserSearchVersion = function (dataString) {
	var index = dataString.indexOf(jlib._browserVersionSearchString);
	if (index == -1) {
		return;
	}
	return parseFloat(dataString.substring(index + jlib._browserVersionSearchString.length + 1));
};

jlib._dataBrowserName = [
	{
		string: navigator.userAgent,
		subString: 'OmniWeb',
		versionSearch: 'OmniWeb/',
		identity: 'OmniWeb'
	},
	{
		string: navigator.vendor,
		subString: 'Apple',
		identity: 'Safari'
	},
	{
		prop: window.opera,
		identity: 'Opera'
	},
	{
		string: navigator.vendor,
		subString: 'iCab',
		identity: 'iCab'
	},
	{
		string: navigator.vendor,
		subString: 'KDE',
		identity: 'Konqueror'
	},
	{
		string: navigator.userAgent,
		subString: 'Firefox',
		identity: 'Firefox'
	},
	{
		string: navigator.vendor,
		subString: 'Camino',
		identity: 'Camino'
	},
	{	// for newer Netscapes (6+)
		string: navigator.userAgent,
		subString: 'Netscape',
		identity: 'Netscape'
	},
	{
		string: navigator.userAgent,
		subString: 'MSIE',
		identity: 'Explorer',
		versionSearch: 'MSIE'
	},
	{
		string: navigator.userAgent,
		subString: 'Gecko',
		identity: 'Mozilla',
		versionSearch: 'rv'
	},
	{	// for older Netscapes (4-)
		string: navigator.userAgent,
		subString: 'Mozilla',
		identity: 'Netscape',
		versionSearch: 'Mozilla'
	}
];

jlib._dataBrowserOS = [
	{
		string: navigator.platform,
		subString: 'Win',
		identity: 'Windows'
	},
	{
		string: navigator.platform,
		subString: 'Mac',
		identity: 'Mac'
	},
	{
		string: navigator.platform,
		subString: 'Linux',
		identity: 'Linux'
	}
];

jlib._dataBrowserEngine = [
	{
		string: navigator.userAgent,
		subString: 'WebKit',
		identity: 'WebKit'
	},
	{
		prop: window.opera,
		identity: 'Presto'
	},
	{
		string: navigator.vendor,
		subString: 'iCab',
		identity: 'iCab'
	},
	{
		string: navigator.vendor,
		subString: 'KDE',
		identity: 'KHTML'
	},
	{
		string: navigator.userAgent,
		subString: 'Gecko',
		identity: 'Gecko'
	},
	{
		string: navigator.userAgent,
		subString: 'MSIE',
		identity: 'Trident'
	},
	{	// for older Netscapes (4-)
		string: navigator.userAgent,
		subString: 'Mozilla',
		identity: 'Netscape'
	}
];

jlib.browserName = jlib._browserSearchString(jlib._dataBrowserName) || '';
jlib.browserVersion = jlib._browserSearchVersion(navigator.userAgent) || jlib._browserSearchVersion(navigator.appVersion) || 0;
jlib.browserOS = jlib._browserSearchString(jlib._dataBrowserOS) || '';
jlib.browserEngine = jlib._browserSearchString(jlib._dataBrowserEngine) || '';



/* add event http://dean.edwards.name/weblog/2005/10/add-event/ */

jlib._preventDefault = function () {
	this.returnValue = false;
};

jlib._stopPropagation = function () {
	this.cancelBubble = true;
};

jlib._fixEvent = function (event) {
	event.target = event.srcElement;
	event.relatedTarget = (event.type == 'mouseover') ? event.fromElement : event.toElement;
	event.preventDefault = jlib._preventDefault;
	event.stopPropagation = jlib._stopPropagation;
	return event;
};

jlib._handleEvent = function (event) {
	var returnValue;
	var win = (this.ownerDocument || this.document || this).parentWindow || window;
	event = event || jlib._fixEvent(win.event);
	var type = event.type;
	var isbeforeunload = (this == win && type == 'beforeunload');
	var listeners = this._events[type];
	var ret, i;
	if (!isbeforeunload) {
		returnValue = true;
	}
	for (i in listeners) {
		this._eventListener = listeners[i];
		ret = this._eventListener(event);
		if (isbeforeunload) {
			if (typeof event.returnValue == 'string') {
				returnValue = event.returnValue;
			} else if (typeof ret == 'string') {
				returnValue = ret;
			}
		} else {
			if (event.returnValue === false || ret === false) {
				returnValue = false;
			}
		}
	}
	this._eventListener = null;
	return returnValue;
},

jlib._eventGUID = 1;

jlib.addEventListener = function (element, type, listener) {
	var listeners;
	if (!element || !type || !listener) {
		return;
	}
	if (element.addEventListener && jlib.browserEngine != 'WebKit') {
		element.addEventListener(type, listener, false);
	} else {
		if (!listener._eventGUID) {
			listener._eventGUID = jlib._eventGUID++;
		}
		if (!element._events) {
			element._events = {};
		}
		listeners = element._events[type];
		if (!listeners) {
			listeners = element._events[type] = {};
			if (element['on' + type]) {
				listeners[0] = element['on' + type];
			}
			element['on' + type] = jlib._handleEvent;
		}
		listeners[listener._eventGUID] = listener;
	}
};

jlib.removeEventListener = function (element, type, listener) {
	if (element.removeEventListener && jlib.browserEngine != 'WebKit') {
		element.removeEventListener(type, listener, false);
	} else {
		if (element._events && element._events[type]) {
			delete element._events[type][listener._eventGUID];
		}
	}
};



/* dom */

jlib.isDescendant = function (desc, asc) {
	if (!desc || !asc) {
		return false;
	}
	while (desc && desc != asc && desc.nodeName != 'BODY') {
		desc = desc.parentNode;
	}
	return (desc == asc);
};

jlib.getChildrenByTagName = function (element, tag) {
	var children = [];
	var childNodes, node, i;
	if (!element || !tag || !element.childNodes) {
		return children;
	}
	tag = tag.toString().toUpperCase();
	childNodes = element.childNodes;
	for (i = 0; i < childNodes.length; i++) {
		node = childNodes[i];
		if (node.nodeType == 1 && node.nodeName == tag) {
			children.push(node);
		}
	}
	return children;
};



/* css class */

jlib._cssClassREs = {};

jlib.hasCSSClass = function (element, cssClass) {
	var re;
	if (!element || !cssClass) {
		return false;
	}
	cssClass = cssClass.toString();
	re = jlib._cssClassREs[cssClass];
	if (!re) {
		re = jlib._cssClassREs[cssClass] = new RegExp('\\b' + cssClass + '\\b');
	}
	return (element.className && element.className.search(re) != -1);
};

jlib.toggleCSSClass = function (element, cssClass, doAdd) {
	var has;
	if (!element || !cssClass) {
		return false;
	}
	cssClass = cssClass.toString();
	has = jlib.hasCSSClass(element, cssClass);
	if (doAdd == null) {
		doAdd = !has;
	}
	if (doAdd) {
		if (!has) {
			element.className = (element.className) ? element.className + ' ' + cssClass : cssClass;
		}
	} else {
		if (has) {
			element.className = element.className.replace(jlib._cssClassREs[cssClass], '').replace(/^ +| +$/g, '').replace(/ {2,}/g, ' ');
		}
	}
	return true;
};

jlib.addCSSClass = function (element, cssClass) {
	return jlib.toggleCSSClass(element, cssClass, true);
};

jlib.removeCSSClass = function (element, cssClass) {
	return jlib.toggleCSSClass(element, cssClass, false);
};



/* css class radio */

jlib.CSSClassRadio = function (buttons, stations, cssClass, trigger, selectDelay, deselectDelay) {
	var b, s, max, i;

	if (!buttons || !stations || !cssClass || !trigger) {
		return;
	}

	b = [];
	s = [];
	max = (buttons.length <= stations.length) ? buttons.length : stations.length;
	for (i = max - 1; i >= 0; i--) {
		b[i] = buttons[i];
		s[i] = stations[i];
	}
	selectDelay = (selectDelay != null) ? parseInt(selectDelay, 10) : 0;
	deselectDelay = (deselectDelay != null) ? parseInt(deselectDelay, 10) : 0;

	this.buttons = b;
	this.stations = s;
	this.targets = null;
	this.trigger = trigger.toString().toLowerCase();
	this.timeoutId = null;
	this.selectedStation = null;
	this.cssClass = cssClass.toString();
	this.selectDelay = (!isNaN(selectDelay) && selectDelay > 0) ? selectDelay : 0;
	this.deselectDelay = (!isNaN(deselectDelay) && deselectDelay > 0) ? deselectDelay : 0;
	this.init();
};

jlib.CSSClassRadio.prototype = {
	dispatchEvent: function (type, el, target) {
		var e;
		if (!type || !el) {
			return false;
		}
		e = {
			type: type,
			button: el._cssClassRadioButton,
			station: el._cssClassRadioStation,
			target: target
		};
		return (this['on' + type]) ? this['on' + type](e) : true;
	},

	clearScheduled: function () {
		if (this.timeoutId) {
			clearTimeout(this.timeoutId);
			this.timeoutId = null;
		}
	},

	select: function (el) {
		var station;
		if (!el || el._cssClassRadio != this) {
			return false;
		}
		station = el._cssClassRadioStation;
		if (station == this.selectedStation) {
			this.clearScheduled();
			return true;
		}
		if (!this.deselect()) {
			return false;
		}
		if (this.dispatchEvent('select', el) === false) {
			return false;
		}
		this.clearScheduled();
		jlib.addCSSClass(station, this.cssClass);
		this.selectedStation = station;
		return true;
	},

	deselect: function (el) {
		var station;
		if (el && el._cssClassRadio != this) {
			return false;
		}
		station = this.selectedStation;
		if (!station) {
			this.clearScheduled();
			return true;
		}
		if (el && el._cssClassRadioStation != station) {
			return true;
		}
		if (this.dispatchEvent('deselect', station) === false) {
			return false;
		}
		this.clearScheduled();
		jlib.removeCSSClass(station, this.cssClass);
		this.selectedStation = null;
		return true;
	},

	scheduleSelect: function (el) {
		var me = this;
		var delay = this.selectDelay;
		var station;
		if (!el || el._cssClassRadio != this) {
			return false;
		}
		station = el._cssClassRadioStation;
		if (station == this.selectedStation) {
			this.clearScheduled();
		} else {
			if (this.selectedStation) {
				this.select(el);
			} else if (!this.timeoutId) {
				if (this.dispatchEvent('scheduleselect', el) === false) {
					return false;
				}
				if (delay == 0) {
					this.select(el);
				} else {
					this.timeoutId = setTimeout(function () { me.select(el); me.timeoutId = null; }, delay);
				}
			}
		}
		return true;
	},

	scheduleDeselect: function (el) {
		var me = this;
		var delay = this.deselectDelay;
		var station;
		if (!el || el._cssClassRadio != this) {
			return false;
		}
		station = el._cssClassRadioStation;
		if (station == this.selectedStation) {
			if (!this.timeoutId) {
				if (this.dispatchEvent('scheduledeselect', el) === false) {
					return false;
				}
				if (delay == 0) {
					this.deselect(el);
				} else {
					this.timeoutId = setTimeout(function () { me.deselect(el); me.timeoutId = null; }, delay);
				}
			}
		} else {
			this.clearScheduled();
		}
		return true;
	},

	click: function (e) {
		var radio = this._cssClassRadio;
		if (!e) {
			return;
		}
		e.preventDefault();
		if (radio.dispatchEvent('click', this, e.target) === false) {
			return;
		}
		radio.select(this);
		if (this.blur) {
			this.blur();
		}
	},

	mouseover: function (e) {
		var radio = this._cssClassRadio;
		var from, to;
		if (!e) {
			return;
		}
		from = e.relatedTarget;
		to = e.target;
		if (!jlib.isDescendant(from, this) && jlib.isDescendant(to, this)) {
			if (radio.dispatchEvent('mouseover', this, e.target) === false) {
				return;
			}
			radio.scheduleSelect(this);
		}
	},

	mouseout: function (e) {
		var radio = this._cssClassRadio;
		var from, to;
		if (!e) {
			return;
		}
		from = e.target;
		to = e.relatedTarget;
		if (jlib.isDescendant(from, this) && !jlib.isDescendant(to, this)) {
			if (radio.dispatchEvent('mouseout', this, e.target) === false) {
				return;
			}
			radio.scheduleDeselect(this);
		}
	},

	focus: function (e) {
		var radio = this._cssClassRadio;
		if (radio.dispatchEvent('focus', this, e.target) === false) {
			return;
		}
		radio.select(this);
	},

	blur: function (e) {
		var radio = this._cssClassRadio;
		if (radio.dispatchEvent('blur', this, e.target) === false) {
			return;
		}
		radio.deselect(this);
	},

	init: function () {
		var buttons = this.buttons;
		var stations = this.stations;
		var targets = [];
		var click = this.click;
		var mouseover = this.mouseover;
		var mouseout = this.mouseout;
		var focus = this.focus;
		var blur = this.blur;
		var button, station, el, i;

		if (!buttons || !stations) {
			return;
		}

		for (i = buttons.length - 1; i >= 0; i--) {
			button = buttons[i];
			station = stations[i];
			button._cssClassRadioButton = station._cssClassRadioButton = button;
			button._cssClassRadioStation = station._cssClassRadioStation = station;
			button._cssClassRadio = station._cssClassRadio = this;
		}

		switch (this.trigger) {
		case 'click':
			for (i = buttons.length - 1; i >= 0; i--) {
				jlib.addEventListener(buttons[i], 'click', click);
			}
			break;

		case 'mouseover':
		case 'focus':
			for (i = buttons.length - 1; i >= 0; i--) {
				button = buttons[i];
				station = stations[i];
				el = (!jlib.isDescendant(button, station)) ? button : station;
				jlib.addEventListener(el, 'mouseover', mouseover);
				jlib.addEventListener(el, 'mouseout', mouseout);
				jlib.addEventListener(el, 'focus', focus);
				jlib.addEventListener(el, 'blur', blur);
				if (jlib.browserEngine == 'Trident') {
					jlib.addEventListener(el, 'activate', focus);
					jlib.addEventListener(el, 'deactivate', blur);
				}
				targets[i] = el;
			}
			break;
		}
	},

	uninit: function (keepSelected) {
		var buttons = this.buttons;
		var stations = this.stations;
		var targets = this.targets;
		var click = this.click;
		var mouseover = this.mouseover;
		var mouseout = this.mouseout;
		var focus = this.focus;
		var blur = this.blur;
		var button, station, el, i;

		if (!buttons || !stations) {
			return;
		}

		if (!keepSelected) {
			this.deselect();
		}
		this.clearScheduled();

		switch (this.trigger) {
		case 'click':
			for (i = buttons.length - 1; i >= 0; i--) {
				jlib.removeEventListener(buttons[i], 'click', click);
			}
			break;

		case 'mouseover':
		case 'focus':
			for (i = targets.length - 1; i >= 0; i--) {
				el = targets[i];
				jlib.removeEventListener(el, 'mouseover', mouseover);
				jlib.removeEventListener(el, 'mouseout', mouseout);
				jlib.removeEventListener(el, 'focus', focus);
				jlib.removeEventListener(el, 'blur', blur);
				if (jlib.browserEngine == 'Trident') {
					jlib.removeEventListener(el, 'activate', focus);
					jlib.removeEventListener(el, 'deactivate', blur);
				}
			}
			break;
		}

		for (i = buttons.length - 1; i >= 0; i--) {
			button = buttons[i];
			station = stations[i];
			button._cssClassRadioButton = station._cssClassRadioButton = null;
			button._cssClassRadioStation = station._cssClassRadioStation = null;
			button._cssClassRadio = station._cssClassRadio = null;
		}

		this.targets = null;
	}
};
