/*	JAVASCRIPT LIBRARY
	version 1.0
	by Al Kahler (kahlerweb.com)
	inspired by Caio Chassot (http://v2studio.com/k/code/)
*/



/*	-------------------------------------------------------------------
	ARRAY EXTENSIONS
*/
	
	if (!Array.prototype.push) Array.prototype.push = function() {/*
		Mimics standard push for IE5, which doesn't implement it.
		*/
		for (var i=0; i<arguments.length; i++) this[this.length] = arguments[i];
		return this.length;
	}
	
	
	Array.prototype.find = function(value, start) {/*
	find(value [, start])
		searches Array for [value] starting at [start] (if [start] is not provided, 
		searches from the beginning). returns index of [value]'s first occurence
		if found, otherwise returns false;
		*/
		start = start || 0;
		for (var i=start; i<this.length; i++) if (this[i]==value) return i;
		return false; 
	}
	
	
	Array.prototype.has = function(value) {/*
		returns true if [value] is found in Array, otherwise false;
		*/
		return this.find(value)!==false;
	}
	
	
	Array.prototype.count = function(value) {/*
		counts occurences of [value] in Array
		*/
		var pos, start = 0, count = 0;
		while ((pos = this.find(value, start))!==false) {
			start = pos + 1;
			count++;
		}
		return count;
	}
	
	
	Array.prototype.remove = function(value,all) {/*
		remove(value [, all])
		removes first occurentce of [value] in array
		if [all] is provided and true, removes all occurences of [value]
		*/
		while (this.has(value)) {
			this.splice(this.find(value),1);
			if (!all) break
		}
		return this;
	}
	
	
	Array.prototype.last = function() {/*
		returns last element of Array
		*/
		return this[this.length-1];
	}
	
	
	Array.prototype.sjoin = function() { return this.join(' ') }
	
	Array.prototype.njoin = function() { return this.join('\n') }
	
	Array.prototype.cjoin = function() { return this.join(', ') }
	
	function isArray() {/*
		taken from... http://www.planetpdf.com/developer/article.asp?ContentID=6383
		*/
		if (typeof arguments[0] == 'object') {
			var criterion = arguments[0].constructor.toString().match(/array/i);
			return (criterion != null);
		} else { return false; }
	}

	
	
/*	-------------------------------------------------------------------
	FUNCTIONAL
*/
	
	function __strfn(args, fn) { /*
		this is used internally by map, filter and reduce to accept strings as 
		functions.
		
		[args] is a string of comma separated names of the function arguments
		[fn] is the function body
		
		if [fn] does not contain a return statement, a return keyword will be added 
		before the last statement. the last statement is determined by removing the
		trailing semicolon (';') (if it exists) and then searching for the last 
		semicolon, hence, caveats may apply (i.e. if the last statement has a 
		string or regex containing the ';' character things will go wrong)
		*/
		function quote(s) { return '"' + s.replace(/"/g,'\\"') + '"' }
		if (!/\breturn\b/.test(fn)) {
			fn = fn.replace(/;\s+$/, '');
			fn = fn.insert(fn.lastIndexOf(';')+1, ' return ');
		}
		return eval('new Function({0},{1})'.subArgs(
			map(args.split(/\s*,\s*/), quote).join(),
			quote(fn)));
	}
	
	
	function map(list, fn) {/*
		traverses [list], applying [fn] to [list], returning an array of values 
		returned by [fn]
	
		[fn] should be a function, but may also be a string containing a function
		body, in which case the name of the paremeters passed to it	will be 'item', 
		'idx' and 'list'.
		
		se doc for __strfn for peculiarities about passing strings for [fn]
		
		if [fn] is not provided, the list item is returned itself. this is an easy 
		way to transform fake arrays (e.g. the arguments object of a function or 
		nodeList objects) into real javascript arrays.
		
		map also provides a safe way for traversing only an array's indexed items, 
		ignoring its other properties. (as opposed to how for-in works)
	
		this is a simplified version of python's map. parameter order is different, 
		only a single list (array) is accepted, and the parameters passed to [fn]
		are different:
		[fn] takes the current item, then, optionally, the current index and a 
		reference to the list (so that [fn] can modify list)
		*/
		if (typeof(fn)=='string') return map(list, __strfn('item,idx,list', fn));
	
		var result = [];
		fn = fn || function(v) {return v};
		for (var i=0; i < list.length; i++) result.push(fn(list[i], i, list)); 
		return result;
	}
	
	
	function filter(list, fn) { /*
		returns an array of items in [list] for which fn(value) is true
		
		[fn] should be a function, but may also be a string containing a function
		body, in which case the name of the paremeters passed to it	will be 'item', 
		'idx' and 'list'.
		
		se doc for __strfn for peculiarities about passing strings for [fn]
		
		if [fn] is not specified the values are evaluated themselves, that is, 
		filter will return an array of the values in [list] which evaluate to true
		
		this is a similar to python's filter, but parameter order is inverted
		*/
		if (typeof(fn)=='string') return filter(list, __strfn('item,idx,list', fn));
	
		var result = [];
		fn = fn || function(v) {return v};
		map(list, function(item,idx,list) { if (fn(item,idx,list)) result.push(item) } );
		return result;
	}
	
	
	function reduce(list, fn, initial) { /*
		similar to python's reduce. paremeter onder inverted... 
		TODO: document this properly
	
		[fn] should be a function, but may also be a string containing a function
		body, in which case the name of the paremeters passed to it	will be 'a'	and 
		'b';
		
		se doc for __strfn for peculiarities about passing strings for [fn]
	*/
		if (typeof(fn)=='string') return reduce(list, __strfn('a,b', fn), initial);
		if (!isUndefined(initial)) list.splice(0,0,initial);
		if (list.length===0) return false;
		if (list.length===1) return list[0];
		var result = list[0];
		var i = 1;
		while(i<list.length) result = fn(result,list[i++]);
		return result;
	}
	
	
	
	
/*	-------------------------------------------------------------------
	STRING EXTENSIONS
*/
	
	String.prototype.trimLeft = function() { return this.replace(/^\s+/,''); }
	
	String.prototype.trimRight = function() { return this.replace(/\s+$/,''); }
	
	String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g,''); }
	
	
	String.prototype.insert = function(idx,value) { /*
		returns the string with [value] inserted at position [idx]
		*/
		return this.slice(0,idx) + value + this.slice(idx);
	}
	
	
	String.prototype.strip = function(idx1,idx2) { /*
		returns the string with characters from [idx1] up to but not including 
		[idx2] removed
		*/
		if (arguments.length==1) idx2 = this.length;
		return this.slice(0,idx1) + this.slice(idx2);
	}
	
	
	String.prototype.splice = function(idx,count,value) { /*
		returns the string with characters from [idx] up to but not including 
		[idx]+[count] removed, and [value] inserted at [idx]
		
		the original string is not modified (as opposed to Array.splice)
		*/
		return this.strip(idx, idx+count).insert(idx, value);
	}
	
	
	String.prototype.subArgs = function() {/*
	subArgs(value_0 [,...,value_n])
		returns string with occurrences of {n} replaced with [value_n]
		*/
		var args = arguments;
		return this.replace(/\{(\d+)\}/g, function(s,i){return args[i]} );
	//	return this.replace(/(?:^|[^\\])\{(\d+)\}/g, function(s,i){return args[i]} ).replace(/\\\{/g, '{' );
	//  does not replace when { is preceded by a \ [str notation: '\\{...'] . Doesn't sound necessary
	}
	
	
	String.prototype.subDict = function(dict) {/*
		returns string with occurences of {key} replaced by dict['key']
		undefined keys are not changed
		*/
		return this.replace(/\{(\w+)\}/g, function(s,k) { return !isUndefined(dict[k]) ? dict[k] : s });
	}
	
	
	String.prototype.wrap = function(left,right) { /*
		wrap(left [, right]);
		returns string wrapped in [left] and [right]; right=left if not provided;
		*/
		if (isUndefined(left)) throw 'S.wrap takes 1 argument (none given)';
		if (isUndefined(right)) right = left;
		return left + this + right;
	}
	
	
	String.prototype.quote = function() { /*
		returns string quoted in double quotes
		*/
		return this.wrap('"');
	}
	
	
	String.prototype.squote = function() { /*
		returns string quoted in single quotes
		*/
		return this.wrap("'");
	}
	
	
	String.prototype.pad = function(side, len, chr) { /*
		TODO: doc
		*/
		if (isUndefined(chr)) chr = ' ';
		var s = this;
		var left = side.toLowerCase()=='left';
		while (s.length<len) s = left? chr + s : s + chr;
		return s;
	}
	
	
	String.prototype.padLeft = function(len, chr) { /*
		TODO: doc
		*/
		return this.pad('left',len,chr);
	}
	
	
	String.prototype.padRight = function(len, chr) { /*
		TODO: doc
		*/
		return this.pad('right',len,chr);
	}
	
	
	String.prototype.zerofill = function(len) { /*
		return string left filled with 0s to match length [len]
		*/
		var s = this;
		var ix = /^[+-]/.test(s) ? 1 : 0;
		while (s.length<len) s = s.insert(ix, '0');
		return s;
	}
	
	
	String.prototype.isEmpty = function(donttrim) { /*
		isEmpty([donttrim])
		true if string contains no characters.
		unless [donttrim] is specified and set to True, the string is trimmed before
		testing
		*/
		return !(donttrim? this : this.trim()).length;
	}
	
	
	
	
/*	-------------------------------------------------------------------
	MISC UTILITIES
*/

	function isUndefined(v) { /*
		returns true if [v] is not defined, false otherwise
	
		IE 5.0 does not support the undefined keyword, so we cannot do a direct 
		comparison such as v===undefined. 
		*/
		var isUndefined;
		return v===isUndefined;
	}
	
	
	function list(s, sep) { /*
		takes a string containing [sep]-separated values and splits it into an array
		sep is optional. the default separator is a comma surrounded by optional 
		whitespace.
		if sep is true, the separator becomes a simple comma
		if sep is any string or regex, then sep is the separator
		*/
		if (typeof sep != 'string' && !(sep instanceof RegExp)) 
			sep = sep? ',' : /\s*,\s*/;
		return s.split(sep);
	}
	
	
	function range(start,stop,step) { /*
		identical to python's range.
	    range(stop)
		range(start,stop)
		range(start,stop,step)
	    
	    Return a list containing an arithmetic progression of integers.
	    range(i, j) returns [i, i+1, i+2, ..., j-1]; start (!) defaults to 0.
	    When step is given, it specifies the increment (or decrement).
	    For example, range(4) returns [0, 1, 2, 3].  The end point is omitted!
		[from python's range's docstring]
		*/
		if (isUndefined(stop)) return range(0,start,step);
		if (isUndefined(step)) step = 1;
		var ss = (step/Math.abs(step)); // step sign
		var r = [];
		for (i=start; i*ss<stop*ss; i=i+step) r.push(i);
		return r;
	}
	
	
	function maprange(start, stop, fn) { /*
		maprange(stop, fn)
		maprange(start, stop, fn)
		
		return map(range(start,stop), fn)
	
		this is pure laziness.
		*/
		if (arguments.length==2) return maprange(0, start, stop);
		if (arguments.length!=3) throw "maprange takes 2 or 3 arguments";
		return map(range(start,stop), fn);
	}


	function getQueryStringParam(name) {
		var value = '';
		
		var querystring = unescape(document.location.href.slice(document.location.href.indexOf("?") + 1));
		var qsPairs = querystring.split("&");
		for (i = 0; i < qsPairs.length; i++) {
			var pair = qsPairs[i].split("=");
			if (pair[0].toLowerCase() == name.toLowerCase()) value = pair[1];
		}
		
		return value;
	}

	
	
	
	
	
/*	-------------------------------------------------------------------
	DOM AND DOM EVENTS UTILITIES
*/
	
	function getElem(elem) { /*
		returns an element in document. [elem] can be the id of such element or the 
		element itself (in which case the function does nothing, merely returning
		it)
	
		this function is useful to enable other functions to take either an	element 
		directly or an element id as parameter.
		
		if [elem] is string and there's no element with such id, it throws an error.
		if [elem] is an object but not an Element, it's returned anyway
		*/
		if (document.getElementById) {
			if (typeof elem == "string") {
				elem = document.getElementById(elem);
				if (elem===null) throw 'cannot get element: element does not exist';
			} else if (typeof elem != "object") {
				throw 'cannot get element: invalid datatype';
			}
		} else throw 'cannot get element: unsupported DOM';
		return elem;
	}
	
	
	function getElementsByClass(className, tagName, parentNode) { /*
		getElementsByClass(className [, tagName [, parentNode]])
		Returns elements having class className, optionally being a tag tagName 
		(otherwise any tag), optionally being a descendant of parentNode (otherwise
		the whole document is searched)
		*/
		parentNode = !isUndefined(parentNode)? getElem(parentNode) : document;
		if (isUndefined(tagName)) tagName = '*';
		return filter(parentNode.getElementsByTagName(tagName), 
			function(elem) { return hasClass(elem, className) });
	}
	
	
	function hasClass(elem, className) { /*
		return how many times [className] occurs in the class list of getElem(elem)
	
		The tested element can have multiple space-separated classes. className must 
		be a single class (i.e. can't be a list).
		*/
		return getElem(elem).className.split(' ').count(className);
	}
	
	
	function remClass(elem, className, all) { /*
		remClass(elem, className [, all])
		removes first occurence of [className] in getElem(elem)'s class list
		if [all] is provided and true, removes all occurences.
		*/
		elem = getElem(elem);
		elem.className = elem.className.split(' ').remove(className,all).join(' ');
	}
	
	
	function addClass(elem, className, allowDuplicates) { /*
		addClass(elem, className [, allowDuplicates])
		adds [className] to getElem(elem)'s class list.
		
		[allowDuplicates] determintes whether or not a class should be inserted if
		it already exists in the element's class list.
		[allowDuplicates] is optional and defaults to false.
		*/
		elem = getElem(elem);
		if (!allowDuplicates && elem.className.split(' ').has(className)) return;
		elem.className += ' ' + className;
	}
	
	
	function getAll(tagName, parent) { /* 
		getAll([tagName [, parent]])
		shorthand for parent.getElementsByTagName(tagName).
		[tagName] is optional and defaults to '*'
		[parent] is optional and defaults to document
		*/
		return (!isUndefined(parent)? getElem(parent) : document).
			getElementsByTagName(!isUndefined(tagName)? tagName : '*');
	}
	
	
	function listen(event, elem, func) { /*
		x-browser function to add event listeners
		
		listens for event on elem with func
		event is string denoting the event name without the on- prefix. e.g. 'click'
		elem is either the element object or the element's id
		func is the function to call when the event is triggered
		
		in IE, func is wrapped and this wrapper passes in a W3CDOM_Event (a faux
		simplified Event object)
		*/
		elem = getElem(elem);
		if (elem.addEventListener)  // W3C DOM 
			elem.addEventListener(event,func,false);  
		else if (elem.attachEvent)  // IE DOM
			elem.attachEvent('on'+event, function(){ func(new W3CDOM_Event(elem)) } );
			// for IE we use a wrapper function that passes in a simplified faux Event object. 
		else throw 'cannot add event listener';
	}
	
	
	function mlisten(event, elem_list, func) { /*
		same as listen but takes an element list (a NodeList, Array, etc) instead of 
		an element.
		*/
		map(elem_list, function(elem) { listen(event, elem, func) } );
	}
	
	
	function W3CDOM_Event(currentTarget) { /*
		this is a faux Event constructor. it's used by listen and mlisten.
		it's passed in IE where a function expects a real Event object. 
		
		The currentTarget value must be passed as a paremeter at the moment	of 
		construction.
	
		For now only few properties and methods are implemented. It'll be expanded 
		as needed.
		*/
		this.currentTarget   = currentTarget;
		this.preventDefault  = function() { window.event.returnValue  = false }
		this.stopPropagation = function() { window.event.cancelBubble = true }
		this.target  = window.event.srcElement;
		this.clientX = event.clientX;
		this.clientY = event.clientY;
		return this;
	}
	
	
	function getEvent(e) { return (e) ? e : ((window.event) ? window.event : ""); }
	
	
	function getEventTarget(e) {
		var e = getEvent(e);
		return (e.target) ? e.target : e.srcElement;
	}
	
	
	function display(elm, type) {/*
	show(elm [, type])
		uses CSS display & visibility properties to show [elm] using the [type] 
		element display type.  If not supplied, [display] defaults to 'block'.
		*/
		if (isUndefined(type)) type = 'block';
		
		var obj = getElem(elm);
		obj.style.display = type;
		obj.style.visibility = 'visible';
	}
    
	
    function hide(elm, collapse) {/*
	hide(elm [, collapse])
		uses CSS display & visibility properties to hide [elm].  [collapse] indicates 
		whether the element space should be maintained/collapsed while hidden and 
		defaults to true.
		*/
		if (isUndefined(collapse)) collapse = true;
		
		var obj = getElem(elm);
   		if (collapse) obj.style.display = 'none';
   		obj.style.visibility = 'hidden';
    }
	
	
	function toggleDisplay(elm, collapse, display) {/*
	toggleDisplay(elm [, display , collapse])
		toggles CSS display & visibility properties for [elm].  [display] is used for 
		the element display type.  If not supplied, [display] defaults to 'block'.  
		[collapse] indicates whether the element space should be maintained/collapsed 
		while hidden and defaults to true.
		*/
		if (isUndefined(collapse)) {
			collapse = true;
			display = 'block';
		} else if (isUndefined(display)) display = 'block';
		
		var obj = getElem(elm);
   		if (obj.style.display == 'none') obj.style.display = display; else if (collapse) obj.style.display = 'none';
   		obj.style.visibility = (obj.style.visibility == 'hidden') ? 'visible' : 'hidden';
	}
	
	
	function disable(elm) { getElem(elm).disabled = true; }
	
	function enable(elm) { getElem(elm).disabled = false; }
	
	function toggleStatus(elm) {
		var obj = getElem(elm);
		obj.disabled = !obj.disabled;
	}
	
	
	
	
/*	-------------------------------------------------------------------
	FORM UTILITIES
*/
	
	function toggleChecked(elm) {
		var obj = getElem(elm);
		obj.checked = !obj.checked;
	}	
	
	
	function appendOption(elm, text, value) {/*
		adds a new option to the end of [elm] with a display text of [text] and a value of [value].
		*/
		var obj = getElem(elm);
		obj.options[obj.options.length] = new Option(text, value);
	}
	
	
	function prependOption(elm, text, value) {/*
		adds a new option to the top of [elm] with a display text of [text] and a value of [value].
		*/
		appendOption(elm, text, value);
		
		var obj = getElem(elm);
		for (i = obj.options.length - 1; i >= 0; i--) {
			obj.options[i].value = (i == 0) ? value : obj.options[i - 1].value;
			obj.options[i].text = (i == 0) ? text : obj.options[i - 1].text;
			if (i == obj.selectedIndex && i < obj.options.length) obj.selectedIndex++;
		}
	}
	
	
	function removeOptionByIndex(elm, index) {/*
		removes an option from [elm] with an index of [index].
		*/
		var obj = getElem(elm);
		obj.options[index] = null;
	}
	
	
	function removeOptionByValue(elm, value) {/*
		removes all options from [elm] with an matching value of [value].
		*/
		var obj = getElem(elm);
		for (i = obj.options.length - 1; i >= 0; i--) if (obj.options[i].value == value) obj.options[i] = null;
	}
	
	
	function sortOptions(elm, ascending) {/*
	sortOptions(elm [, ascending])
		sorts the elements in [elm] based on the optional [ascending] direction 
		parameter, which defaults to true.
		*/
		var obj = getElem(elm);
		var options = new Array(obj.options.length);
		for (var i = 0; i < options.length; i++) options[i] = new Option(obj.options[i].text, obj.options[i].value, obj.options[i].defaultSelected, obj.options[i].selected);
		
		if (isUndefined(ascending)) ascending = true;
		var compareRoutine = (ascending) ? compareOptionTextAsc : compareOptionTextDesc;
		
		options.sort(compareRoutine);
		obj.options.length = 0;
		for (var i = 0; i < options.length; i++) obj.options[i] = options[i];
	}
	
	
	function compareOptionTextAsc(a, b) { return (isNaN(a.text) && isNaN(b.text)) ? (a.text < b.text ? -1 : (a.text > b.text ? 1 : 0)) : (parseFloat(a.text) < parseFloat(b.text) ? -1 : (parseFloat(a.text) > parseFloat(b.text) ? 1 : 0)); }
	function compareOptionTextDesc(b, a) { return (isNaN(a.text) && isNaN(b.text)) ? (a.text < b.text ? -1 : (a.text > b.text ? 1 : 0)) : (parseFloat(a.text) < parseFloat(b.text) ? -1 : (parseFloat(a.text) > parseFloat(b.text) ? 1 : 0)); }
	
	
	function selectOptionByIndex(elm, index) {/*
		selects the [index] option of [elm].
		*/
		var obj = getElem(elm);
		if (index < obj.options.length) obj.selectedIndex = index;
	}
	
	
	function selectOptionByValue(elm, value) {/*
		selects the option of [elm] with a value of [value].
		*/
		var obj = getElem(elm);
		for (i = obj.options.length - 1; i >= 0; i--) if (obj.options[i].value == value) { obj.options[i].selected = true; }
	}
	
	
	
	
/*	-------------------------------------------------------------------
	WINDOW & FRAME UTILITIES
*/
	
	function spawnWindow(url, name, width, height, resize, scroll, menu, tools, location, status) {/*
		opens [url] in a new window named [name].
		*/
		var ua = navigator.userAgent.toLowerCase();
		var an = navigator.appName.toLowerCase();
		var av = parseFloat(navigator.appVersion.slice(0, navigator.appVersion.indexOf(' ')));
		if (ua.indexOf('msie') >= 0) av = parseFloat(ua.slice(ua.indexOf('msie') + 5, ua.indexOf(';', ua.indexOf('msie') + 5)));
		
		if (ua.indexOf('mac') >= 0 && an.indexOf('microsoft internet explorer') >= 0 && av >= 4 && av < 5) height = parseInt(height + 17);
		
		var win = window.open(url, name, ('width=' + width + ', height=' + height + ', resizable=' + Number(resize) + ', scrollbars=' + Number(scroll) + ', menubar=' + Number(menu) + ', toolbar=' + Number(tools) + ', location=' + Number(location) + ', status=' + Number(status)));
		if (win != null) if (win.opener == null) win.opener = self;
		win.focus();
		
		return false;
	}
	
	
	
	
/*	-------------------------------------------------------------------
	IMAGE UTILITIES
*/
	
	function ImagePreloader(images, call, callback) {/*
		This class begins preloading the image urls containted in the 
		array [images].  Upon completion of each image load, [call] is 
		run.  Once all images have been loaded, the [callback] routine
		is processed.
		*/
		this.call = call;
		this.callback = callback;
		this.length = images.length;
		this.loaded = 0;
		this.processed = 0;
		this.images = new Array;
		this.captions = new Array;
		
		for (var i = 0; i < images.length; i++) this.preload(images[i]);
	}
	
	ImagePreloader.prototype.preload = function(object) {/*
		Creates a new image [image], establishing event handlers to 
		watch for errors and/or abortions. */
		var img = new Image;
		this.images.push(img);
		
		img.onload = ImagePreloader.prototype.onload;
		img.onerror = ImagePreloader.prototype.onerror;
		img.onabort = ImagePreloader.prototype.onabort;
		
		img.oImagePreloader = this;
		img.loaded = false;
		img.error = false;
		img.abort = false;
		
		var image;
		var caption = '';

		if (isArray(object)) {
			image = object[0];
			if (object.length > 0) caption = object[1];
		} else { image = object; }

		img.src = image;
		this.captions.push(caption);
	}
	
	ImagePreloader.prototype.onComplete = function() {
		this.processed++;
		if (this.call != null) this.call(this.length, this.loaded, this.processed);
		if (this.processed == this.length && this.callback != null) this.callback(this.images, this.loaded, this.captions);
	}
	
	ImagePreloader.prototype.onload = function() {
		this.loaded = true;
		this.oImagePreloader.loaded++;
		this.oImagePreloader.onComplete();
	}
	
	ImagePreloader.prototype.onerror = function() {
		this.error = true;
		this.oImagePreloader.onComplete();
	}
	
	ImagePreloader.prototype.onabort = function() {
		this.abort = true;
		this.oImagePreloader.onComplete();
	}


	function changeImage(elm, img) { if (img.loaded) if (document.getElementById) getElem(elm).setAttribute("src", img.src); else document.images[elm].src = img.src; }
	
	
	function RolloverPreloader(elm, active, inactive) {
		var rollover = new ImagePreloader([active, inactive], null, function(images, loaded) { if (loaded == images.length) attachRollover(elm, images); });
		return rollover;
	}
		
	function attachRollover(elm, images) {
		var parent = getElem(elm).parentNode;
		
		parent.onmouseover = function(e) { changeImage(getEventTarget(e), images[(getEventTarget(e).src == images[0].src) ? 1 : 0]); }
		parent.onmouseout = function(e) { changeImage(getEventTarget(e), images[(getEventTarget(e).src == images[0].src) ? 1 : 0]); }
	}

