


// ======================================================================
// Name:    	  Raskin's DOM Helper namespace
// Version:		  0.9.5 w/ JSON Serializer (requires rAjax.js or json.js)
// Created: 	  28/12/2006
// Last Modified: 02/07/2007
// Author: 		  Max Raskin ( ghunter@netvision.net.il )
// ======================================================================
//
// NOTES: Moved $ to rDom.$ so it won't conflict with prototype (when using LightBox, Cropper etc..)
//
var rDom = {

	
	/**
	 * Returns an element by id/class/name and automatically extends it with
	 * 
	 *
	 * [!] by class prefix with a period     - '.'
	 * [!] by name  prefix with an exc. mark - '!' 
	 * 				(use '!!' in order to escape to document.getElementById)
	 */
	$ : function(s,srcEl)
	{
		
		if (typeof s == 'undefined') return null;
		var els = [];
		
		if (typeof srcEl == 'undefined') {
			srcEl = document;
		}
		
		if (typeof s == 'object') {
			els[0] = s;
		} else if (s.charAt(0) == '.') { // by class);
			s = s.substring(1,s.length);
			
			var els = rDom.getElementsByClassName(s, srcEl);
		} else if (s.charAt(0) == '!' && s.charAt(1) != '!') {
			s = s.substring(1,s.length);
			rDom.getElementsByName(srcEl,s,els);
		} else if(typeof s == 'string') { // by id
			els[0] = document.getElementById(s);
		}
		
		for (var i = 0; i < els.length; i++) {
			if (els[i] && !els[i].____extended) {
				var nodeName = els[i].nodeName.toLowerCase();
				var type	 = els[i].type;
				// Extend with global functions
				rExt(els[i],rExtendableElements['global']);
				// Extend by node name
				if (typeof rExtendableElements[nodeName] !='undefined') {
					rExt(els[i],rExtendableElements[nodeName]);
				}
				// Extend by type
				if (typeof rExtendableElements[type] != 'undefined') {
					rExt(els[i],rExtendableElements[type]);
				}
			}
		}
		if (els.length == 1) {
			els = els[0];
		} else if (!els.length) {
			els = null;
		}
		
		return els;
	},
	
	/**
	 * Disables/enables all elements within a form
	 */
	disableForm : function(f, enable) {
		for (el in f.elements) {
			
				f.elements[el].disabled = !enable;
				
			
		}
	},
	
	/**
	 * @param mixed o - any object which isn't an array converted to an array
	 *
	 * @return Array
	 */
	toA : function(o) {

		if (o && (o.nodeName && o.nodeName.toLowerCase() == 'select') || (!this.isA(o) /*&& !this.isCol(o)*/)) {
			
			return [o];
		}
		
		
		return o;
	},
	
	/**
	 * Adds a css rule - creates a new stylesheet (*** only once - all rules are added to that stylesheet )
	 *
	 * @param string selector
	 * @param string properties
	 * @param object[optional] win - ref. to window object/iframe element
	 								 which contains the doc to add the css to (defaults to window)
	 */
	addCSSRule : function(selector, properties, win) {
		
		if (typeof win == 'undefined') {
			win = window;
			
		} else {
			// for iframes
			win =  (win.contentWindow) ? win.contentWindow : win;
		}
		
		var doc = (win.documentElement) ? win.documentElement : win.document;
		
		var isIE = (navigator.userAgent.toLowerCase().indexOf('msie') > -1 
					&& !window.opera
					&& doc.styleSheets);
		
		if (!win.styleEl) {
			win.styleEl = doc.createElement('style');
			win.styleEl.setAttribute('type', 'text/css');
			doc.getElementsByTagName('head')[0].appendChild(win.styleEl);
		}
		
		
		if (isIE) {
			if (!win.styleIE) {
				win.styleIE = doc.styleSheets[doc.styleSheets.length - 1];
				
				
			}
			var selectors = selector.split (',');
			for (var i = 0; i < selectors.length; i++) {
				win.styleIE.addRule(selectors[i], properties);
			}
			
		} else {
			win.styleEl.appendChild(doc.createTextNode(selector + '{' + properties + '}'));
			win.styleEl.appendChild(doc.createTextNode(selector + '{' + properties + '}'));
			
		}
		
	},
	
	/**
	 * Add multiple css rules to document
	 *
	 * @param string selector
	 * @param string properties
	 */
	addCSSRules : function(rules, win) {
		for (selector in rules) {
			if (rules.hasOwnProperty(selector)) {
				this.addCSSRule(selector, rules[selector], win);
			}
		}
	},
	
	/**
	 * Queries the user's browser for visited sites :-)
	 * @param string|Array urls - url or an array of urls to check
	 *
	 * @return array - an array of boolean values
	 *
	 */
	sniffVisitedSites : function (urls) {
		if (!this._visitedSitesRule) {
			this.addCSSRule('a.rDOMsniffURLClass:visited', 'color:#ccc !important;');
			this._visitedSitesRule = true;
		}
		
		if (typeof urls.push == 'undefined') {
			urls = [urls];
		}
		
		var div = rDom.createEl('div');
		for (var i = 0; i < urls.length; i++) {
			var a = rDom.createLink('link', urls[i]);
			a.className = 'rDOMsniffURLClass';
			div.appendChild(a);
		}
		document.body.appendChild(div);
		
		var visited = [];
		for (var i = 0; i < div.childNodes.length; i++) {
			var clr = rDom.getCurrentStyle(div.childNodes[i],'color');
			
			if (clr == 'rgb(204, 204, 204)' || clr == '#ccc' || clr == '#cccccc') {
				visited[i] = true;
			} else {
				visited[i] = false;
			}
			
			
		}
		
		document.body.removeChild(div);
		
		return visited;
	},

	/**
	 * Gets computed style of an element
	 *
	 * @param Object el - DOM element
	 * @param String prop - style property to get
	 *
	 * @return String
	 * from http://www.codehouse.com
	 */
	getCurrentStyle : function (el, prop)
	{
	   if(el&& el.currentStyle )
	   {   
	      var ar = prop.match(/\w[^-]*/g);
	      var s = ar[0];
	      
	      for(var i = 1; i < ar.length; ++i)		   
	      {
	         s += ar[i].replace(/\w/, ar[i].charAt(0).toUpperCase());
	      }
	           
	      return el.currentStyle[s]
	   }
	   else if( document.defaultView && document.defaultView.getComputedStyle )
	   {
	      return document.defaultView.getComputedStyle(el, null).getPropertyValue(prop);
	   }
	},
	
	/**
	 * Sets a handler for the onload event of window
	 * 
	 * @param Object obj - manager object **MUST** implement the init(ev) function
	 * @return bool
	 */
	setInitHandler : function(obj) {
		return this.addEvent(window, 'load', obj.init, obj);
	},

	/*
		Prevents default internal method of an event
		
		Params:
			object e - event
	*/
	preventDefault : function(e)
	{
		(e.preventDefault) ? e.preventDefault() : e.returnValue = false;
	},
	
	/*
		Stops event bubbling
		
		Params:
			object e - event
	*/
	stopPropagation : function(e)
	{
		(e.stopPropagation) ? e.stopPropagation() : (e.stopBubble = true);
	},
	
	/*
		Gets an event's targetted element
		
		Params:
			object - event
		
		Returns:
			object on success, false on failure.
	*/
	getTarget : function(e)
	{
		var target = window.event ? window.event.srcElement : e ? e.target : false;
		return target;
	},
	
	/**
	* Gets document's size
	*
	* @return object hash(width;height}
	*/
	getDocSize : function() {
		if (document.documentElement && document.documentElement.clientHeight)
		{
			w = document.documentElement.clientWidth;
			h = document.documentElement.clientHeight;
		}
		else if (document.body)
		{
			w = document.body.clientWidth;
			h = document.body.clientHeight;
		}

		return {width:w, height:h}
	},
	/*
		Gets closest sibling to specified element
		
		Params:
			object  el  - element which sibling's to get
			integer dir - direction from which to get sibling (-1 - backward, 1 - forward)
		Returns:
			object if found sibling
			false if not.
	*/
	closestSibling : function (el,dir)
	{
		if (dir > 0)
		{
			var cEl = el.nextSibling;
			while (cEl.nodeType != 1 && cEl != null)
				cEl = cEl.nextSibling;
		}
		else if (dir < 0)
		{
			var cEl = el.previousSibling;
			while (cEl.nodeType != 1 && cEl != null)
				cEl = cEl.previousSibling;
		}
		return (cEl != null) ? cEl : false;
	},
	
	/*
		Gets last sibling (according to document hierarchy) of a specified element
		
		Params:
			object el - the element of which sibling to get
		
		Return:
			object is there is a last sibling, false if not.
	*/
	lastSibling : function (el)
	{
		if (el.nextSibling == null)
			return false;
		
		var tmp = null;
		while(el != null)
		{
			if (el.nodeType == 1)
				tmp = el;
			el = el.nextSibling;
		}
		return (tmp != null) ? tmp : false;
	},
	
	/*
		Gets first sibling (according to document hierarchy) of a specified element
		
		Params:
			object el - the element of which sibling to get
		
		Return:
			object is there is a first sibling, false if not.
	*/
	firstSibling : function (el)
	{
		if (el.previousSibling == null)
			return false;
		
		var tmp = null;
		while(el != null)
		{
			if (el.nodeType == 1)
				tmp = el;
			el = el.previousSibling;
		}
		return (tmp != null) ? tmp : false;
	},
	
	/*
		Gets the first child text node value of an element.
		
		Params:
			object el - element of which text to get
		
		Returns:
			text on success, false on failure.
	*/
	getText : function (el)
	{
		if (!el.hasChildNodes())
			return false;
		var tmp = null;
		el = el.firstChild;
		var reg = /^\s+$/;
		while (el != null && !reg.test(el.nodeValue))
		{
			if (el.nodeType == 3)
				tmp = el;
			el = el.nextSibling;
		}
		return (tmp != null) ? tmp.nodeValue : false;
	},

	/*
		Sets the value of first child text node of an element if exists,
		if not appends a new child text node element.
		
		Params:
			object el  - the element of which text to set
			string txt - text to set
	
	*/
	setText : function (el,txt)
	{

		var tn = document.createTextNode(txt);
		if (!el.hasChildNodes())
		{
			el.appendChild(tn);
		}
		else
		{
			var tmp = el.firstChild;
			while (tmp != null && tmp.nodeType != 3)
			{
				tmp = tmp.nextChild;
			}
			if (tmp == null)
				el.appendChild(tn);
			else
				tmp.nodeValue = txt;
		}
		return true;
	},
	
	/*
		Creates an element
		
		Params:
			string el  - element's name
			string txt - element's inner text (optional)
		
		Returns:
			object el on success, null on failure.
	*/
	createEl : function (el,txt)
	{
		var tmpEl = document.createElement(el);
		if (txt != null) tmpEl.appendChild(document.createTextNode(txt));
		return tmpEl;
	},
	
	/*
		Create anchor element
		
		Params:
			string txt  - url link
			string href - link url
	
	*/
	createLink : function (txt, href)
	{
		var aEl = document.createElement('a');
		aEl.appendChild(document.createTextNode(txt));
		aEl.setAttribute('href', href);
		return aEl;
	},
	
	/*
		Check whether a class set in a specified element class list
		
		Params:
			object el - element to check
			string c  - class name to look for
	*/
	cssClassCheck : function (el,c)
	{
		if (!el || !el.className) return;
		var regex = new RegExp("\\b" + c +"\\b");
		return regex.test(el.className);
	},
	
	/*
		Removes a class from an element's class list
		
		Params:
			object el - element to remove class from
			string c  - class name to remove
	*/
	cssClassRemove : function (el, c)
	{
		var regex = new RegExp("\\s*\\b" + c +"\\b\\s*");
		el.className = el.className.replace(c, ' ');
		//alert(el.className);
	},
	
	trim : function (s)
	{
		while (s.substring(0,1) == ' ')
			s = s.substring(1, s.length - 1);
		while (s.substring(s.length - 1, 1) == ' ')
			s = s.substring(0, s.length - 1);
			
		return s;
	},
	
	/*
		Swaps a class within an element's classes list
		
		Params:
			object el - element to swap classes
			string c1 - first class name
			string c2 - second class name
	*/
	cssClassSwap : function (el,c1,c2)
	{
		
		if (this.cssClassCheck(el,c1))
			el.className = el.className.replace(c1,c2);
		else
			el.className = el.className.replace(c2,c1);
	},
	
	
	/*
		Adds a class to an element
		
		
		@param	object el - element to add a class to
		@param	string c  - name of class to add
		@return bool
	*/
	cssClassAdd : function (el,c)
	{
		if (!el) return false;
		if (!this.cssClassCheck(el,c))
		{
			if (el.className.length) 
				el.className += (' ' + c);
			else
				el.className = c;
		}
		return true;
	},
	
	/*
		Recursively gets elements by class name (recursive method)
		
		Params:
		string	c		 - class name to match
		Object|string el[optional] 		 - root element to start search in
		@return Array
	*/
	getElementsByClassName : function (c, el) {
		el = rDom.$(el) || document;
		
		var descendants = el.getElementsByTagName('*');
		var results = [];
		var regex = new RegExp("\\b" + c +"\\b");
		for (var i = 0, len = descendants.length; i < len; ++i) {
			
			if (regex.test(descendants[i].className) ) {
				results.push(descendants[i]);
				
			}
		}
		
	    return results;
	},
	
	/*
		Recursively gets elements by name (recursive method)
		
		Params:
			el 		 - root element to start search in
			s		 - name to match
			arr		 - output array to contain matching elements
	*/
	getElementsByName : function (el,s,arr)
	{
	
		if (el) 
		{
			for (var i = 0; i < el.childNodes.length; i++)
			{
				if (el.childNodes[i].nodeType == 1)
				{
								
					if (el.childNodes[i].getAttribute('name') == s) arr.push(el.childNodes[i]);
					this.getElementsByName(el.childNodes[i],s,arr);					
				}

			}
			
		}
		else
			return;
	},
	
	/*
		Recursively gets elements by type (recursive method)
		
		Params:
			el 		 - root element to start search in
			t		 - type to match
			arr		 - output array to contain matching elements
	*/
	getElementsByType : function (el,t,arr)
	{
		
		if (el)
		{
			for (var i = 0; i < el.childNodes.length; i++)
			{
				
				if (el.childNodes[i].nodeType == 1)
				{
					if (el.childNodes[i].type == t) arr.push(el.childNodes[i]);
					rDom.getElementsByType(el.childNodes[i],t,arr);					
				}
			}
			
		}
		else
			return;
	},
	
    /**
     * Hides/Unhides element(s) by a given class name
     * (extended by rDom.$ as element.showChildrenByClass and element.hideChildrenByClass)
     *
     * @param Object rootEl - root parent element                      
     * @param String c - class name
     * @param bool[optional] hide - deafaults to false (shows elements)
     *
     * @return int - number of elements modified
     */
    toggleChildrenByClass : function(rootEl, c, hide) {
        if (typeof hide == 'undefined') hide = false;

        var els = rDom.$(rootEl).$('.' + c);
        for (var i = 0, len = els.length; i < len; ++i) {
            if (hide)
                rDom.$(els[i]).hide();
            else
                rDom.$(els[i]).show();
        }
        
        return len;
    },
    
	/**
	 * Binds a function to an object - thusly, makes the function
	 * inherit all the object's properties via its 'this' object.
	 *
	 * (taken from prototype)
	 *
	 * @param Function f
	 * @param Object   obj
	 * @param Array[optional] extraArgs - append extra arguments
	 */
	bindToObj : function (f, obj, extraArgs) {

		
		return function() {
			var args = [];
			// convert to array
			for (var i = 0, len = arguments.length; i < len; ++i) {
				args.push(arguments[i]);
			}
			if (extraArgs) {
				for( var i = 0, len = extraArgs.length; i < len; ++i ) {
					args.push(extraArgs[i]); //extra args
				}
			}
			f.apply(obj, args);
		};
	},
	
	// is array
	isA : function(o) {
		return (o && typeof o.push != 'undefined');
	},
	
	// is collection
	isCol : function(o) {
		if (!o) return false;
		return (typeof o.length != 'undefined' && typeof o != 'string');
	},
	
	/**
	*	Attaches an event handler to an element or multiple elements
	*
	*	@param object|array el	 - the element/elements of which events to capture
	*	@param string|array ev 	 - event/events (without the 'on' prefix)
	*	@param function f 	 - handler function
	*	@param object bindObj[optional] - an optional object to bind handler function to
	*	
	*	@return int - number of succesfull event handlers set
	*/
	addEvent : function (el,ev,f,bindObj)
	{
		
		// if an array of events is given
		if (typeof ev.push != 'undefined') {
				
			for (var i = 0; i < ev.length; i++) {
			
				this.addEvent(el, ev[i], f, bindObj);
				
			}
			return;
		}
		
		var els = rDom.toA(el);
		
		var numSuccess = 0;
		
		for (i = 0; i < els.length; i++) {
			
			if(els[i] === null || typeof els[i] == 'undefined') continue;
			var func = f;
			
			
			
			if (typeof bindObj != 'undefined' 
			    && typeof func.apply == 'function') {
					func = rDom.bindToObj(f, bindObj);
			    }
			
			if (els[i].attachEvent) {
				els[i].attachEvent('on' + ev, func);
				
			} else if (els[i].addEventListener) {
				els[i].addEventListener(ev, func, false);
			} else {
			
				els[i]['on' + ev] = func;
			}
			
			numSuccess++;
		}
				
		return numSuccess;
	},

	/*
		Detaches an event handler
		
		Params:
			@param object el	  - the element of which events to capture
			@param string ev 	  - event (without on prefix)
			@param function func - subclass function
			@param bool useCap   - use event capturing (MSIE ignores this param)
			
	*/
	removeEvent : function(el,ev,func,useCap)
	{
		if (el.detachEvent)
		{
			el.detachEvent('on' + ev, func);
		}
		else if (el.removeEventListener)
		{
			if (useCap === null) useCap = true;
			el.removeEventListener(ev, func, useCap);
		}
		else
			el['on' + ev] = null;
	},
	
	/*
		Stops both bubbling and prevents default method call of an event
		
		Params:
			object e - event
	*/		
	stopEvent : function (e)
	{
		if (!e && !window.event) return;
		if (!e) e = window.event;
		this.stopPropagation(e);
		this.preventDefault(e);
	},
	
	/*
		Gets key code from an event
		
		Params:
			object e - event
	*/
	getKeyCode : function(e)
	{
		return (e) ? e.keyCode : window.event.keyCode;
	},
	
	/*
		Gets mouse coordinates
		
		Params:
			object e - event
	*/
	getMouseCoords : function(e)
	{
		e = e || window.event;
		
		if (typeof e.pageX != 'undefined')
			return {x:e.pageX, y:e.pageY};
		else
			return {
				x:e.clientX + document.body.scrollLeft - document.body.clientLeft,
				y:e.clientY + document.body.scrollTop  - document.body.clientTop
			};
	},
	/*
		Gets element position coordinates in pixels, relative to document
		
		Params:
			object el - element
	*/
	getElementPos : function(el)
	{
		var top = 0, left = 0;
		while (el.offsetParent)
		{
			left += el.offsetLeft;
			top  += el.offsetTop;
			el	  = el.offsetParent;
		}
		
		left += el.offsetLeft;
		top  += el.offsetTop;

		return {x:left,y:top};
	},
	
	/**
	* Toggle an element's visibility
	* NOTE: (style.display NOT style.visibility)
	* @param object el - reference to an elemnt
	*/
	
	toggleDisplay : function(el)
	{
		el.style.display = (el.style.display == 'none') ? 'block' : 'none';
	},
	
	/**
	* Swaps one node with another
	* @param object oldNode - node to swap
	* @param object newNode	- node to swap with
	*/
	swapNode : function(oldNode,newNode)
	{
		oldNode.parentNode.insertBefore(newNode, oldNode);
		oldNode.parentNode.removeChild(oldNode);
		if (typeof newNode.focus == 'function') newNode.focus();
	},
	
	/**
	* Adds events to every descended (excluding) of el
	* @param object el - parent to begin with
	* @param string event
	* @param function func
	*/
	addEventRecursive : function(el, event, func)
	{
		
		if (el.hasChildNodes())
		{
			for (var i = 0; i < el.childNodes.length; i++)
			{
				if (el.childNodes[i].nodeType == 1)
				{
					rDom.addEvent(el.childNodes[i], event, func);
					rDom.getElementsByType(el.childNodes[i],event,func);					
				}
			}
			
		}
		else
			return;
	},
	
	/*
	@todo: can be cool
	serializeFormToQueryString : function(form, namesArray) {
		
	},*/
	
	/** 
	 * DeSerializes object into a form by names - object has to to be in the following format:
	 * {'someElementName' : 'data'|1234|[1,2,3] ...}
	 * @param mixed form - id/class (prefixed with a '.')/object of the form element
	 * @param Array obj  - object to deserialize
	 *
	 */
	deserializeObjectToForm : function(form, obj) {
		var f = this.$(form);
		
		for (i in obj) {
			
		
			if (obj.hasOwnProperty(i)) {
				var el = f.elements[i];
				
				if (!el) {continue;}
				// turn a single element into array
				// that is because if we get an element which is a collection of
				// elements, like a group of radio boxes for instance, we'll have to
				// iterate through it separately otherwise
				if (typeof el.length == 'undefined') {
				
					el = [el];
				}
				
				// php based array counter
				var kk = 0;
				
				for (var j = 0; j < el.length; j++) {
							
					if (el[j].disabled || !el[j]) { // Don't deserialize disabled elements and undefined ones
						
						continue;
					}
					
					switch(el[j].nodeName.toLowerCase()) {
						
					// select option
						case 'option':
							var toSel = false;
							if (typeof obj[i] == 'object') {
								for (opt in obj[i]) {
									if (obj[i].hasOwnProperty(opt)) {
										if (obj[i][opt] && el[j].value == obj[i][opt]) {
											toSel = true;
										}
									}
								}
								
							} else {
								
								if (obj[i] && el[j].value == obj[i]) {
									toSel = true;
								}
							}
							el[j].selected = toSel;
							break;
						case 'input':
							switch (el[j].type.toLowerCase()) {
								case 'checkbox':
									// Check checkbox if true
									el[j].checked = (parseInt(obj[i]) > 0);
									break;
								case 'radio':
									// Check radio only if value matches
									if (el[j].value == obj[i] ) {
										el[j].checked = true;
									}
									break;
								default:
									// detect php array
									if (el[j].name.substring(el[j].name.length -2 ) == '[]') {
										el[j].value = obj[i][kk];
										kk++;
									} else {
										kk = 0
										el[j].value = obj[i];
									}
							}
							break;
						case 'textarea':
							el[j].value = obj[i];
							break;
					}
				}
			}
		}
		
		return true;
	},
	
	/** 
	 * Serializes form elements into an object
	 *
	 * @param mixed form - id/class (prefixed with a '.')/object of the form element
	 * @param Array namesArray - an array of strings with the elements u want to serialize
	 *
	 * @todo : allow namesArray to be * - all elements will be serialized
	 */
	serializeFormToObject : function(form, namesArray) {
		var f = this.$(form);
		var obj = {};
		for (var i = 0; i < namesArray.length; ++i) {
			
			var el = f.elements[namesArray[i]];
			
			if (!el) continue;
			
			// turn a single element into array
			// that is because if we get an element which is a collection of
			// elements, like a group of radio boxes for instance, we'll have to
			// iterate through it separately otherwise
			if (typeof el.length == 'undefined') {
				el = [el];
			}
			for (var j = 0; j < el.length; j++) {
						
				if (el[j].disabled || !el[j]) { // Don't serialize disabled elements and undefined ones
					continue;
				}
				
				switch(el[j].nodeName.toLowerCase()) {
					case 'option':
						
						if (el[j].selected) {
							if (!obj[namesArray[i]]) 
							{
								obj[namesArray[i]] = '';
							}
						
							if (typeof obj[namesArray[i]].push == 'undefined') {
								obj[namesArray[i]] = [];
							}
							obj[namesArray[i]].push(el[j].value);
							
						}
						break;
					case 'input':
						switch (el[j].type.toLowerCase()) {
							case 'radio':
								// Set radio's value only if checked
								if (el[j].checked) {
									obj[namesArray[i]] = el[j].value;
								}
								break;
							case 'checkbox':
								// A checkbox must be checked in order for its value
								// to be serialized
								if (!el[j].checked) continue;
								if (el.length > 1) {
									// we have an array of checkboxes
									if (typeof obj[namesArray[i]] == 'undefined') {
										obj[namesArray[i]] = [];
									}
									obj[namesArray[i]].push(el[j].value);
								} else {
									// not an array, add just its value
									obj[namesArray[i]] = el[j].value;
								}
								
								break;
							default: // other input elements - text, hidden
								// detect php arrays
								if (namesArray[i].substring(namesArray[i].length - 2) == '[]') {
									if (!this.isA(obj[namesArray[i]])) {
										obj[namesArray[i]] = [];
									}
									obj[namesArray[i]].push(el[j].value);
								} else {
									obj[namesArray[i]] = el[j].value;
								}
						}
						break;
					case 'textarea':
						obj[namesArray[i]] = el[j].value;
						break;
				}
			}
		}
		
		return obj;
	},
	
	/**
	 * Serializes form elements by name into a JSON string
	 *
	 * Note: requires JSON.js (obtain from http://www.json.org)
	 *
	 * @param mixed form - id/class (prefixed with a '.')/object of the form element
	 * @param Array namesArray - an array of strings with the elements u want to serialize
	 *
	 */
	serializeFormToJSON : function(form, namesArray) {
		if (!Object.prototype.toJSONString) return false;
		var obj = this.serializeFormToObject(form, namesArray);
		return obj.toJSONString();
	},
	
	/**
	* getPageSize()
	* Returns array with page width, height and window width, height
	* Core code from - quirksmode.org
	* Edit for Firefox by pHaez
	*
	* Edited by Max Raskin to return a hash table:
	* @return Array ['pageWidth', 'pageHeight', 'windowWidth', 'windowHeight']
	*/
	getPageSize : function () {
		var xScroll, yScroll;
		
		if (window.innerHeight && window.scrollMaxY) {	
			xScroll = document.body.scrollWidth;
			yScroll = window.innerHeight + window.scrollMaxY;
		} else if (document.body.scrollHeight > document.body.offsetHeight){ // all but Explorer Mac
			xScroll = document.body.scrollWidth;
			yScroll = document.body.scrollHeight;
		} else { // Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari
			xScroll = document.body.offsetWidth;
			yScroll = document.body.offsetHeight;
		}
		
		var windowWidth, windowHeight;
		if (self.innerHeight) {	// all except Explorer
			windowWidth = self.innerWidth;
			windowHeight = self.innerHeight;
		} else if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict Mode
			windowWidth = document.documentElement.clientWidth;
			windowHeight = document.documentElement.clientHeight;
		} else if (document.body) { // other Explorers
			windowWidth = document.body.clientWidth;
			windowHeight = document.body.clientHeight;
		}	
		
		// for small pages with total height less then height of the viewport
		if(yScroll < windowHeight){
			pageHeight = windowHeight;
		} else { 
			pageHeight = yScroll;
		}
	
		// for small pages with total width less then width of the viewport
		if(xScroll < windowWidth){	
			pageWidth = windowWidth;
		} else {
			pageWidth = xScroll;
		}
	
	
		arrayPageSize = [];
		
		arrayPageSize['pageWidth'] = pageWidth;
		arrayPageSize['pageHeight'] = pageHeight;
		arrayPageSize['windowWidth'] = windowWidth;
		arrayPageSize['windowHeight'] = windowHeight;
		return arrayPageSize;
	},

	/**
	* Creates a DOM on load handler, allows you to modify the DOM
	* when its finished loading (even if other elements weren't loaded yet)
	* Based on Solutions by: Dean Edwards/Matthias Miller/John Resig
	*
	* URL: http://dean.edwards.name/weblog/2006/06/again/
	*
	* Konqueror and Multiple Loaders via MSIE support added by Max Raskin (24.06.07).
	*
	* Browsers Tested with : IE6, IE7, FireFox 2.0/Win, FireFox 1.5/Linux, Konqueror 3.5.2/Linux
	*
	* Function Assembled by: Max Raskin
	*
	* @param Function func 			  - callback function to call when DOM's loaded.
	* @param Object[optional] bindObj - an object to bind the function to
	*
	* @return bool - true if succeeded setting a DOM event listener, false if
	*				 reverted to regular onload listener
	*/
	setDOMLoadHandler : function(f, bindObj) {
		var func = f;
		
		
		
		// Bind to an object if specified
		if (typeof bindObj != 'undefined' 
		    && typeof func.apply == 'function') {
				func = rDom.bindToObj(f, bindObj);
		}
		/* for Mozilla/Opera9 */
		if (document.addEventListener 
			&& !/WebKit/i.test(navigator.userAgent)
			&& !/Konqueror/i.test(navigator.userAgent)) {
					
		    document.addEventListener("DOMContentLoaded", func, false);
		    return true;
		}
		
		
		
		
		
		/* for Safari/Konqueror */
		if (/WebKit/i.test(navigator.userAgent) 
			|| /Konqueror/i.test(navigator.userAgent)) {
		    var _timer = setInterval(function() {
		        if (/loaded|complete/.test(document.readyState)) {
		        	// call the onload handler
		            var initfunc = function(event,_timer) {
		            	clearInterval(_timer);
		            	func(event);
		            }
		            initfunc(event,_timer);
		        }
		    }, 10);
		    return true;
		}
		
		// In case all above failed, use regular load event handler
		rDom.addEvent(window, 'load', func);
		return false;
	}
	
};

/**
* Dynamically includes a js file - lazy loadin' style baby! :-)
* @param string fileName - full file name and path to include
*/
function includeJs(fileName)
{
	var script = rDom.createEl('script');
	script.setAttribute('type', 'text/javascript');
	script.setAttribute('src', fileName);
	document.getElementsByTagName('head')[0].appendChild(script);
}

/**
* Dynamically includes a stylesheet file - lazy loadin' style baby! :-)
* @param string fileName - full file name and path to include
*/
function includeCss(fileName)
{
	var link = rDom.createEl('link');
	link.setAttribute('type', 'text/css');
	link.setAttribute('rel', 'stylesheet');
	link.setAttribute('href', fileName);
	document.getElementsByTagName('head')[0].appendChild(link);
	
}
/**
 * Checks whether a given value is in an array
 *
 * @param mixed needle - what to look for
 * @param array haystack - the array to look in
 */
function in_array(needle, haystack) {
	if (typeof haystack == 'undefined' || haystack === null) {
		return false;
	}
	for (i = 0; i < haystack.length; i ++) {
		if (needle == haystack[i]) {
			return true;
		}
	}
	return false;
}

function isset(o) {
	return (typeof o != 'undefined');
}

/**
 * Checks whether a given key is in an array
 *
 * @param mixed needle - what to look for
 * @param array haystack - the array to look in
 */
function is_array_key(needle, haystack) {
	if (typeof haystack == 'undefined' || haystack === null) {
		return false;
	}
	for (key in haystack) {
		if (needle === key) {
			return true;
		}
	}
	return false;
}


/**
 * Extendable function collections object
 * elements are extended either by their nodeName (select for instance)
 * or type attribute (for input elements)
 *
 * to create a new function collection simply name a type or nodeName as
 * an object within rExtendable and implement any functions you'd like,
 *
 * @var Object
 */
var rExtendableElements = {
	// Global functions which extend ANY element
	global : {
		/**
		 */
		$ : function (s) {
			return rDom.$(s,this);
		},
        
        hide : function() {
                       
            this.style.display = 'none';
        },
        
        show : function() {
            
                this.style.display = 'block';
            
        },
        
        
        removeNode : function() {
			this.parentNode.removeChild(this);
		},
		
        /** 
         * @see rDom.toggleChildrenByClass
         */
		hideChildrenByClass : function(c) {
            rDom.toggleChildrenByClass(this, c, true);
        },
        
        /** 
         * @see rDom.toggleChildrenByClass
         */
        showChildrenByClass : function(c) {
            rDom.toggleChildrenByClass(this, c);
        },
        
		up : function(tagName) {
			return rDom.$(this.findNode(tagName));
		},
		
		down : function(tagName) {
			return rDom.$(this.findNode(tagName, 1));
		},
		
		/**
		 * Looks up for an element of tagName either up or down
		 * relatively to a specified element.
		 *
		 * @param String tagName - which element to lookup for (css supported - i.e. div.monkey for class 
		 *														or div#donkey for id)
		 * @param int[optional] dir - if -1 (default) looks up, if 1 looks down
		 *
		 */
		findNode : function(tagName, dir) {
			var el = this;
			
			var isClass = false, isId = false, tmp;
			
			if (tagName.indexOf('.') > -1) {
				isClass = true;
				tmp = tagName.split('.'); // claass
				tagName = tmp[0];
			} else if (tagName.indexOf('#') > -1) {
				isId = true;
				tmp = tagName.split('#'); // id
				tagName = tmp[0];
			}
			tagName = tagName.toUpperCase();
			
			if (!dir || dir == -1) { // find element up
				while (el) {
					if (el.nodeName == tagName) {
						if (isClass) {
							if (this.cssClassCheck(el, tmp[1])) {
								return el;
							}
						} else if (isId) {
							if (el.id == tmp[1]) {
								return el;
							}
						} else {
							return el;
						}
					
						
					}
					el = el.parentNode;
				}
			}
		}

	},
	select : {
		/**
		 * Selects item(s) by value if found (and unselects all the rest)
		 * @param string|array val
		 *
		 * @return int - number of items selected
		 */
		selItemByVal : function(val) {
			var numSel = 0;
			
			if (typeof val.push == 'undefined') {
				val = [val]
			}
			
			for (var i = 0; i < this.options.length; i++) {
				for (j = 0; j < val.length; j ++)  {
					if (this.options[i].value == val[j]) {
						numSel ++;
						this.options[i].selected = true;
					} else {
						this.options[i].selected = false;
					}
				}
			}
			return numSel;
		},
		
		modifyItem : function(index, text, value) {
			this.options[index] = new Option(text, value, false, false);
		},
		
		/**
		 * Checks whether an index's in range
		 *
		 * @return bool
		 */
		isInRange : function(index) {
			return (index >= 0 && index < this.length);
		},
		
		/**
		 * Copies all items to destination select box
		 * @param object destList - destination select box
		 * @param bool[optional]	 clear - if true, clears destination list box
		 */
		copyTo : function(destList, clear) {
			if (clear) rDom.$(destList).clear();
			for (var i = 0; i < this.options.length; i++) {
				destList.options[i] = new Option(this.options[i].text, this.options[i].value, 
					   							 this.options[i].defaultSelected, this.options[i].selected);
			}
		},
		         
		/**
		 * Safely swaps items by indices
		 *
		 * @param int src  - source index
		 * @param int dest - destination index
		 *
		 * @return bool
		 */
		swapItems : function(src, dest) {
			
			// if target is out of range don't move
			if (!this.isInRange(dest)) {
				return false;
			} else {
				var cloneDest = new Option(this.options[dest].text, this.options[dest].value, 
									   this.options[dest].defaultSelected, this.options[dest].selected);
				
				this.options[dest] = new Option(this.options[src].text, this.options[src].value, 
									   this.options[src].defaultSelected, this.options[src].selected);
				// swap dest with src
				this.options[src] = cloneDest;
				return true;
			}
		},
		
		/**
		 * Inserts an option to a specified index in the list
		 * @param object option
		 * @param int index
		 *
		 */
		insertOptionToIndex : function (option, index) {
			
			if (this.options[index] == null) {
				this.options[index] = option;
			} else {
				var opArr = [];
				for (var i = index; i < this.length; i++) {
					opArr.push(this.options[i]);
				}
				
				this.options[index] = option;
				
				for (var i = 0; i < opArr.length; i++) {
					this.options[index + i + 1] = opArr[i];
					
					
				}
			}
			
		},
		
		
		
		/**
		 * Removes an item by value
		 * @param string val
		 * @param bool[optional] returnItem - returns removed item
		 *
		 * @return bool | object {option:Option,index:int}
		 */
		removeItemByVal : function(val, returnItem) {
			for (var i = 0; i < this.options.length; i++) {
				if (this.options[i].value == val) {
					if (returnItem) {
						var o = this.options[i];
						retObj = {option : o, index : i};
						this.options[i] = null;
						return retObj;
					}
					this.options[i] = null;
					return true;
				}
			}
			
			return false;
		},
		
		clear : function() {
			for (var i = this.options.length - 1; i >= 0 ; i--) {
				this.options[i] = null;
			}
		},
		/**
		 * Checks whether an item appears in the list by value
		 * @param string val
		 *
		 * @return bool
		 */
		isInListByVal : function(val) {
			
			for (var i = 0; i < this.options.length; i++) {
				if (this.options[i].value == val) {
					return true;
				}
			}
			
			return false;
		},
		
		/**
		 * Gets the selected item's value
		 * @param multiple - if true, returns an array
		 * @return String|Array
		 */
		getSelItemVal : function(multiple) {
			if (typeof multiple != 'undefined') {
				var items = [];
				for (var i = 0; i < this.options.length; i++) {
					if (this.options[i].selected) {
						items.push(val);
					}
				}
			} else {
				if (this.selectedIndex == -1) {
					return null;
				}
			}
			
			
			return (items) ? items : this.options[this.selectedIndex].value;
		},
		
		/**
		 * Changes a selected item
		 *
		 * @param String text
		 * @param String value
		 */
		changeSelItem : function(text, value) {
			this.options[this.selectedIndex].text = text;
			this.options[this.selectedIndex].value = value;
		},
		
		/**
		 * Removes selected items
		 *
		 * @bool selectItem[optional] - if true selects the first item
		 * @return int - number of items removed
		 */
		removeSelItems : function(selectItem) {
			var numRemoved = 0;
			
			for (var i = this.options.length - 1; i >= 0 ; i--) {
				if (this.options[i].selected) {
					this.options[i] = null;
					numRemoved++;
				}
			}
			if (selectItem) {
				this.selectedIndex = 0;
			}
			return numRemoved;
		},
		
		/**
		 * Inserts a new option
		 *
		 * @param string text
		 * @param string value
		 * @param bool   defaultSelected
		 * @param bool   selected
		 */
		insertItem : function(text, value, defaultSelected, selected) {
			this.options[this.length] = new Option(text, value, 
												   defaultSelected, selected);
		},
		
		/**
		 * Inserts items from a hash table
		 *
		 * @param Array hash
		 */
		insertItemsFromHash : function (hash) {
			for (var k in hash) {
				this.insertItem(hash[k], k);
			}
		},
		
		/**
		 * Deselects all items
		 *
		 */
		deselectAll : function() {
			for (var i = this.options.length - 1; i >= 0 ; i--) {
				this.options[i].selected = false;
			}
		}
	}
}


/**
 * Extends an element with functions from an object
 * [!] a function collection object is simply an object with an array of
 *      functions
 *
 * The function stores an array flag within the object to know which
 * function collections were used to extend it, so no unneccassary
 * operations will occur in case has already been extended.
 *
 * @param object el - the element to extend
 * @param object funcsColl - a functions collection
 */
function rExt(el, funcsColl) {
	if (typeof el['____extended'] == 'undefined') {
		el['____extended'] = [];
	}
	
	if (!in_array(funcsColl, el['____extended'])) {
		for (func in funcsColl) {
			if (funcsColl.hasOwnProperty(func)) {
				el[func] = funcsColl[func];
			}
		}
		el['____extended'].push(funcsColl);
	}
}

/*
	Gets GET variables from page's location.

	Returns:
		associative array or null on failure
	
*/
function getVars()
{
	if (window.location.href.indexOf('?') == -1) return null;
	
	var vars = window.location.href.substring(window.location.href.indexOf('?') + 1, window.location.href.length);
	
	vars = vars.split('&');
	var ret = new Array();
	
	for (var i = 0; i < vars.length; i++)
	{
		var h = vars[i].indexOf('#');
		if (h > -1) {
			vars[i] = vars[i].substring(0, h);
		}
		var tmp = vars[i].split('=');
		ret[tmp[0]] = tmp[1];
	}
	
	return ret;
}

//////////////////////////////////
/// built in objects extensions://
//////////////////////////////////
// Taken from http://www.mredkj.com/javascript/numberFormat.html
Number.prototype.addCommas = function() {
	nStr = this + '';
	x = nStr.split('.');
	x1 = x[0];
	x2 = x.length > 1 ? '.' + x[1] : '';
	var rgx = /(\d+)(\d{3})/;
	while (rgx.test(x1)) {
		x1 = x1.replace(rgx, '$1' + ',' + '$2');
	}
	if (x2.length && x2.length < 3) {
		x2 += '0';
	}
	return x1 + x2;
}

Number.prototype.NaN0 = function() {	
	return (isNaN(this)) ? 0 : this;
}

// ==================================================================================================
// Name:    	  Raskin Ajax namespace
// Version:		  0.62
// Created: 	  27/01/2007
// Last Modified: 11/07/2007
// Author: 		  Max Raskin ( ghunter@netvision.net.il )
// Based on: 	  http://www.drakware.com/?e=3 by drakware
//
// Features: 
//		##	Async: simultaneous async XMLhttp requests,
// 			 	   xhr id assigned per open connection internally.
//		##	Sync : Using openPostSync()/openGetSync()
//		## **NEW** : x.responseJSON support - data which sent with the [ application/x-json ]
//					 header is considered JSON, and is automatically evaluated into x.responseJSON.
//					 NOTE: JSON extensions taken from json.js (pasted below rAjax object) dl'd from
//						   http://www.json.org
//		## **NEW**  Connection instances are now identified by 'event Ids' (explained below)
//
// 
//    -----
// >|< BUGS >|<
//    -----
// Load handlers aren't working @ the moment... these are rarely used anyway, but I'll look in to it
// someday.
//
// Async Usage example:
// ~~~~~~~~~~~~~~~~~~~~
// var serviceXhrId = rAjax.openGet('onHello', 'service.php?param1=hello&param2=world',
//									 recievedHandler, loadHandler);
// var infoDiv = rDom.$('infoDiv');
// 
// function loadHandler(evId) {
//	 if (evId == 'onHello') infoDiv.innerHTML = '<strong>Loading...</strong>';
// }
//
// NOTE: the response arguments contains two or one objects - 
//		** responseJSON - available on a json response
//		** x 			- The active XMLHttpRequest object
// 
// function recievedHandlerFunc(response, evId) {
//   if (evId == 'onHello') {
//	    infoDiv.innerHTML = 'Got Data: ' + response.x.responseText;
//		serviceXhrId = -1;
//	 }
// }
// =============================== JSON EXAMPLE : ==========================
// if for instance, our json data is {someProperty : 'hello world!'}
//
// function recievedHandlerFunc(response, evId) {
//   if (evId == 'onHello') {
//	    infoDiv.innerHTML = response.responseJSON.someProperty;
//		// infoDiv will now contain 'hello world'
//		serviceXhrId = -1;
//	 }
// }
// ==================================================================================================
var rAjax = {
	// simultaneous connection limit number
	maxConns : 5,
	
	// array of all XHR instances
	xhrs    : null,
	// index of newest xhr in array
	currXhr : null,
	// recieve handler function, recieves two args: x (related xhr object) and int xId (its Id)
	recHandlers: null,
	// load handler function, recieves one arg: int xId (xhr's id)
	loadHandlers : null,
	
	/**
		Add a recieve handler to the recieve handlers private array.
		
		Handler array element structure:
			f  : func  - handler function
			id : xhrId - id of xhr (=index in xhrs array)
	*/
	addRecHandler : function(f,xId) {
		if (rAjax.recHandlers == null) rAjax.recHandlers = new Array();
		rAjax.recHandlers.push ( {func : f, xhrId : xId} );
	},
	
	/**
		Add a load handler to specified xhr.
		
		Handler array element structure:
			f  : func  - handler function
			id : xhrId - id of xhr (=index in xhrs array)

		Params:
			function f   : reference to handler function
			int		 xId : xhrId of requested element.
	*/
	addLoadHandler : function(f,xId) {
		if (rAjax.loadHandlers == null) rAjax.loadHandlers = new Array();
		rAjax.loadHandlers.push ( {func : f, xhrId : xId} );
	},
	
	/**
		Removes handler functions of a specified xhrId
		NOTE: called automatically by rAjax.abort()
		
		Params:
			int		 xhrId : xhrId of requested element.
	*/
	removeHandlers : function (xhrId) {
		for (var i = 0; i < rAjax.recHandlers.length; i++)
			if (rAjax.recHandlers[i] !== null && rAjax.recHandlers[i].xhrId == xhrId) 
				rAjax.recHandlers[i] = null;
			
		for (var i = 0; i < rAjax.loadHandlers.length; i++)
			if (typeof rAjax.loadHandlers[i]  == 'function' && rAjax.loadHandlers[i].xhrId == xhrId) 
				rAjax.loadHandlers[i] = null;
	},
	
	/**
		Call recieve handler(s) of a specified xhrId
	**/
	callRecHandlers : function (x, xhrId) {
		for (var i = 0; i < rAjax.recHandlers.length; i++)
			if (rAjax.recHandlers[i] !== null && rAjax.recHandlers[i].xhrId == xhrId) rAjax.recHandlers[i].func(x, rAjax.xhrs[xhrId].evId);
	},
	
	/*
		Call load handler(s) of a specified xhrId
	*/
	
	callLoadHandlers : function (xhrId) {
		for (var i = 0; i < rAjax.loadHandlers.length; i++)
			if (typeof rAjax.loadHandlers[i] == 'function'  && rAjax.loadHandlers[i].xhrId == xhrId) rAjax.loadHandlers[i].func(rAjax.xhrs[xhrId].evId);
	},
	
	/*
		XHR class
			public members:
			   	XMLHttpRequest  x
			   	bool			ready - true if done processing request otherwise, false.
	*/
	XHR : function() {
		this.ready = false;
	    if(window.XMLHttpRequest)
	    {
	    	try {
				this.x = new XMLHttpRequest();
	        } catch(e) {
				this.x = false;
	        }
	    } else if(window.ActiveXObject) {
	       	try {
	        	this.x = new ActiveXObject("Msxml2.XMLHTTP");
	      	} catch(e) {
	        	try {
	          		this.x = new ActiveXObject("Microsoft.XMLHTTP");
	        	} catch(e) {
	          		this.x = false;
	        	}
			}
	    }    
	},

	/*
		Gets free XHR if available, if not, creates a new one and returns it.
		
		Returns:
			array ( 0 => object XMLHttpRequest, 1 => free xhr index )
	*/
	getFreeXhr : function() {
		if (rAjax.xhrs === null)
			rAjax.xhrs = new Array();
		var freeXhr = null;
		for (var i = 0; i < rAjax.xhrs.length; i++)
			if (rAjax.xhrs[i].ready) { freeXhr = i; break; }
		if (freeXhr === null) {
			rAjax.xhrs.push (new rAjax.XHR() );
			freeXhr = rAjax.xhrs.length - 1;
		}

		return {x: rAjax.xhrs[freeXhr].x, xhrId: freeXhr};
	},

	/*
		Gets number of busy xhrs (ready state is false)
		
		Returns:
			int - number of busy xhrs
	
	*/
	getNumBusyXhrs : function() {
		if (rAjax.xhrs == null) return 0;
		var num = 0;
		for (var i = 0; i < rAjax.xhrs.length; i++)
			if (!rAjax.xhrs[i].ready) num ++;
		return num;
	},
	
	/*
		Sends a get request to specified url

		Params:
			string   evId - an identifier for the connection - for later use in RecvHandlers and LoadHandlers
			string   url - target
			function loadHandler - function that'll recieve notification upon loading.
			function recHandler  - function that'll recieve notification when data is 
								   finished transfering from the server
			bool     enableCache - if true enables browser to cache request (null by default = false)

		Returns:
			int -  (positive integer) Success: xhr id  
				   -1 : Error: exceeded maximum connection
				   -2 : Error: unable to create xhr object
			
	*/
	openGet : function(evId,url,recHandler,loadHandler,enableCache) {
		if (rAjax.getNumBusyXhrs() + 1 > rAjax.maxConns ) return -1;
		
		var ret = rAjax.getFreeXhr();
		var x = ret.x;
		var freeXhr = ret.xhrId;
		
		if (!x) return -2;
		
		this.xhrs[freeXhr].evId = evId;
		
		if (recHandler !== null) rAjax.addRecHandler(recHandler,freeXhr);
		if (loadHandler !== null) {
			 rAjax.addLoadHandler(loadHandler,freeXhr);
		}
		
		rAjax.xhrs[freeXhr].ready = false;
		
		x.open('get',url,true);

		// Browser request caching
		if (enableCache == null) x.setRequestHeader('If-Modified-Since', 'Sun, 01 Jan 2005 00:00:00 GMT');
		
		x.onreadystatechange = function () {rAjax.onReadyStateChange(freeXhr)};
		
		x.send(null);
		
		return freeXhr;	
	},

	/*
		Sends a post request to specified url

		Params:
			string   evId - an identifier for the connection - for later use in RecvHandlers and LoadHandlers
			string url - target
			string data
			function loadHandler - function that'll recieve notification upon loading.
			function recHandler  - function that'll recieve notification when data is 
		    					   finished transfering from the server
			bool   enableCache - if true enables browser to cache request (null by default = false)

		Returns:
			int -  (positive integer) Success: xhr id  
					-1 : Error: exceeded maximum connection
					-2 : Error: unable to create xhr object
	*/
	openPost : function(evId, url,data,recHandler,loadHandler,enableCache) {
		if (rAjax.getNumBusyXhrs() + 1 > rAjax.maxConns ) return -1;
		var ret = rAjax.getFreeXhr();
		var x = ret.x;
		var freeXhr = ret.xhrId;
		
		if (!x) return -2;
		
		this.xhrs[freeXhr].evId = evId;
		
		if (recHandler !== null) rAjax.addRecHandler(recHandler,freeXhr);
		if (loadHandler !== null) rAjax.addLoadHandler(loadHandler,freeXhr);
		
		rAjax.xhrs[freeXhr].ready = false;
		
		x.open('post',url,true);
		x.onreadystatechange = function () {rAjax.onReadyStateChange(freeXhr)};
		
		// Browser request caching
		if (enableCache == null) x.setRequestHeader('If-Modified-Since', 'Sun, 01 Jan 2005 00:00:00 GMT');
		x.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
		x.send(data);
		return freeXhr;
	},
	
	/**
	* send a POST synchronous connection 
	* (stops everything and returns when recv is done)
	* @param string url - target
	* @param string data - data to send
	* @param bool   enableCache - if true enables browser to cache request (null by default = false)
	*
	* @return string (resposneText)
	*/
	openPostSync : function(url,data, enableCache) {
		var x = new rAjax.XHR();
		x = x.x;
		if (!x) return -2;
		
		x.open('post',url,false);
 
		// Browser request caching
		if (enableCache == null) x.setRequestHeader('If-Modified-Since', 'Sun, 01 Jan 2005 00:00:00 GMT');
		x.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded;');
		x.send(data);
		return x.responseText;
	},
	
	/**
	* send a GET synchronous connection 
	* (stops everything and returns when recv is done)
	* @param string url - target
	* @param bool   enableCache - if true enables browser to cache request (null by default = false)
	*
	* @return string (resposneText)
	*/
	openGetSync : function(url,enableCache) {
		var x = new rAjax.XHR();
		x = x.x;
		if (!x) return -2;
		
		x.open('get',url,false);
 
		// Browser request caching
		if (enableCache == null) x.setRequestHeader('If-Modified-Since', 'Sun, 01 Jan 2005 00:00:00 GMT');
		x.send(null);
		return x.responseText;
	},


	/*
		XHR connection state callback, attached to each new XHR.

		When data is being loaded it'll call the rAjax.loadHandler() function is exists.
		When data is done loading and ready to be processed it'll call the
		rAjax.recHandler() function.

		Params:
			int xhrId - current xhr id (=index in the rAjax.xhrs array)
	*/
	onReadyStateChange : function(xhrId) {
		var x = rAjax.xhrs[xhrId].x;

		if (x.readyState == 4) {
			try { var status = x.status } catch (e) {}
			if (/200|304/.test(status)) {
				
				clearTimeout (rAjax.xhrs[xhrId].loadHanderTimeOutId);
				rAjax.xhrs[xhrId].ready = true;
				var response = {x : x}
				//alert(x.responseText);
				if (x.getResponseHeader('Content-Type') == 'application/x-json') {
					try {
						response.responseJSON = x.responseText.parseJSON();
					} catch (e) {
						alert('JSON Parse Error, Response Text : ' + x.responseText);
					}
				}
				
				rAjax.callRecHandlers(response, xhrId);
				
				rAjax.abort(xhrId);
			}
			else
				//ToDo: handle error
				alert("Error! status return: " + status);
		} else if (x.readyState == 1) {
			
			// The timeout below is used to bestow loadHandler function call
			// so that openGet/openPost() return the xhr id before
			// loadHandler is called, I know, its dirty, but it does the trick.
			rAjax.xhrs[xhrId].loadHanderTimeOutId = setTimeout('rAjax.callLoadHandlers(' + xhrId +')',10);
		}
		return false;
	},

	/**
	 *	Aborts a request by specifying an event id
	 *	
	 *	@param string evId
	 *	
	 *	@return bool
	 */
	abortByEvId : function(evId) {
		for (var i = 0; i < this.xhrs.length; i++) {
			if (this.xhrs[i].evId == evId) {
				this.abort(i);
				return true;
			}
		}
		
		return false;
	},
	
	/**
	 *	Aborts the operation of a specified xhr.
	 *	(used internally)
	 *	
	 *	@param int xhrId - current xhr id (=index in the rAjax.xhrs array)
	 */
	abort : function (xhrId) {
		if (rAjax.xhrs[xhrId].loadHanderTimeOutId) clearTimeout (rAjax.xhrs[xhrId].loadHanderTimeOutId);
		rAjax.removeHandlers(xhrId);
		rAjax.xhrs[xhrId].ready = true;
		rAjax.xhrs[xhrId].x.onreadystatechange = function () {}
		rAjax.xhrs[xhrId].x.abort();
	}
};

/*
    json.js
    2007-06-11

    Public Domain

    This file adds these methods to JavaScript:

        array.toJSONString()
        boolean.toJSONString()
        date.toJSONString()
        number.toJSONString()
        object.toJSONString()
        string.toJSONString()
            These methods produce a JSON text from a JavaScript value.
            It must not contain any cyclical references. Illegal values
            will be excluded.

            The default conversion for dates is to an ISO string. You can
            add a toJSONString method to any date object to get a different
            representation.

        string.parseJSON(filter)
            This method parses a JSON text to produce an object or
            array. It can throw a SyntaxError exception.

            The optional filter parameter is a function which can filter and
            transform the results. It receives each of the keys and values, and
            its return value is used instead of the original value. If it
            returns what it received, then structure is not modified. If it
            returns undefined then the member is deleted.

            Example:

            // Parse the text. If a key contains the string 'date' then
            // convert the value to a date.

            myData = text.parseJSON(function (key, value) {
                return key.indexOf('date') >= 0 ? new Date(value) : value;
            });

    It is expected that these methods will formally become part of the
    JavaScript Programming Language in the Fourth Edition of the
    ECMAScript standard in 2008.

    This file will break programs with improper for..in loops. See
    http://yuiblog.com/blog/2006/09/26/for-in-intrigue/

    This is a reference implementation. You are free to copy, modify, or
    redistribute.

    Use your own copy. It is extremely unwise to load untrusted third party
    code into your pages.
*/

/*jslint evil: true */

// Augment the basic prototypes if they have not already been augmented.

if (!Object.prototype.toJSONString) {

    Array.prototype.toJSONString = function () {
        var a = [],     // The array holding the member texts.
            i,          // Loop counter.
            l = this.length,
            v;          // The value to be stringified.


// For each value in this array...

        for (i = 0; i < l; i += 1) {
            v = this[i];
            switch (typeof v) {
            case 'object':

// Serialize a JavaScript object value. Ignore objects thats lack the
// toJSONString method. Due to a specification error in ECMAScript,
// typeof null is 'object', so watch out for that case.

                if (v) {
                    if (typeof v.toJSONString === 'function') {
                        a.push(v.toJSONString());
                    }
                } else {
                    a.push('null');
                }
                break;

            case 'string':
            case 'number':
            case 'boolean':
                a.push(v.toJSONString());

// Values without a JSON representation are ignored.

            }
        }

// Join all of the member texts together and wrap them in brackets.

        return '[' + a.join(',') + ']';
    };


    Boolean.prototype.toJSONString = function () {
        return String(this);
    };


    Date.prototype.toJSONString = function () {

// Ultimately, this method will be equivalent to the date.toISOString method.

        function f(n) {

// Format integers to have at least two digits.

            return n < 10 ? '0' + n : n;
        }

        return '"' + this.getFullYear() + '-' +
                f(this.getMonth() + 1) + '-' +
                f(this.getDate()) + 'T' +
                f(this.getHours()) + ':' +
                f(this.getMinutes()) + ':' +
                f(this.getSeconds()) + '"';
    };


    Number.prototype.toJSONString = function () {

// JSON numbers must be finite. Encode non-finite numbers as null.

        return isFinite(this) ? String(this) : 'null';
    };


    Object.prototype.toJSONString = function () {
        var a = [],     // The array holding the member texts.
            k,          // The current key.
            v;          // The current value.

// Iterate through all of the keys in the object, ignoring the proto chain.

        for (k in this) {
            if (this.hasOwnProperty(k)) {
                v = this[k];
                switch (typeof v) {
                case 'object':

// Serialize a JavaScript object value. Ignore objects that lack the
// toJSONString method. Due to a specification error in ECMAScript,
// typeof null is 'object', so watch out for that case.

                    if (v) {
                        if (typeof v.toJSONString === 'function') {
                            a.push(k.toJSONString() + ':' + v.toJSONString());
                        }
                    } else {
                        a.push(k.toJSONString() + ':null');
                    }
                    break;

                case 'string':
                case 'number':
                case 'boolean':
                    a.push(k.toJSONString() + ':' + v.toJSONString());

// Values without a JSON representation are ignored.

                }
            }
        }

// Join all of the member texts together and wrap them in braces.

        return '{' + a.join(',') + '}';
    };


    (function (s) {

// Augment String.prototype. We do this in an immediate anonymous function to
// avoid defining global variables.

// m is a table of character substitutions.

        var m = {
            '\b': '\\b',
            '\t': '\\t',
            '\n': '\\n',
            '\f': '\\f',
            '\r': '\\r',
            '"' : '\\"',
            '\\': '\\\\'
        };


        s.parseJSON = function (filter) {
            var j;

            function walk(k, v) {
                var i;
                if (v && typeof v === 'object') {
                    for (i in v) {
                        if (v.hasOwnProperty(i)) {
                            v[i] = walk(i, v[i]);
                        }
                    }
                }
                return filter(k, v);
            }


// Parsing happens in three stages. In the first stage, we run the text against
// a regular expression which looks for non-JSON characters. We are especially
// concerned with '()' and 'new' because they can cause invocation, and '='
// because it can cause mutation. But just to be safe, we will reject all
// unexpected characters.

            if (/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/.
                    test(this)) {

// In the second stage we use the eval function to compile the text into a
// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
// in JavaScript: it can begin a block or an object literal. We wrap the text
// in parens to eliminate the ambiguity.

                try {
                    j = eval('(' + this + ')');
                } catch (e) {
                    throw new SyntaxError('parseJSON');
                }
            } else {
                throw new SyntaxError('parseJSON');
            }

// In the optional third stage, we recursively walk the new structure, passing
// each name/value pair to a filter function for possible transformation.

            if (typeof filter === 'function') {
                j = walk('', j);
            }
            return j;
        };


        s.toJSONString = function () {

// If the string contains no control characters, no quote characters, and no
// backslash characters, then we can simply slap some quotes around it.
// Otherwise we must also replace the offending characters with safe
// sequences.

            if (/["\\\x00-\x1f]/.test(this)) {
                return '"' + this.replace(/([\x00-\x1f\\"])/g, function (a, b) {
                    var c = m[b];
                    if (c) {
                        return c;
                    }
                    c = b.charCodeAt();
                    return '\\u00' +
                        Math.floor(c / 16).toString(16) +
                        (c % 16).toString(16);
                }) + '"';
            }
            return '"' + this + '"';
        };
    })(String.prototype);
}
/*Site global js file*/

var Global = {
	
	// css classes declared in css/he/global.css
	overlayCSSClass		 : 'overlay', 	  // Background overlay
	loginDialogCSSClass  : 'loginDialog', // Login modal dialog form
	
	
	searchKeyPressed 	 : false,
	searchStringLabel 	 : 'חיפוש כללי',
	searchStringCSSClass : 'searchStringLabel',

	init : function() {
	/*
		// Event handlers for search string input box
		if (rDom.$('searchString')) {
			// handle searchString area events - key press, focus and blur
			rDom.addEvent( rDom.$('searchString'), 
							   ['keypress', 'focus', 'blur'], 
							   this.onSearchString, 
							   this );
			
			rDom.addEvent( rDom.$('submitGeneralSearch'), 
							   'click',
							   this.onSubmitGeneralSearch,
							   this );
			
			if (!rDom.$('searchString').value) {
				rDom.$('searchString').value = this.searchStringLabel;
				rDom.cssClassAdd(rDom.$('searchString'), this.searchStringCSSClass);
			} else {
				this.searchKeyPressed = true;
			}
		}
	*/	
		// Event handler for login anchor
		if (rDom.$('loginAnchor') && rDom.$('pageName') != 'login') {
			rDom.addEvent(rDom.$('loginAnchor'), 'click', this.onClickLogin, this);
		}
	},
	
	onClickLogin : function(ev) {
		// create overlay
		if (!this.o) {
			this.o = document.createElement('div');
			this.o.className = this.overlayCSSClass;
			document.body.appendChild(this.o);
			this.o.style.width = '100%';
			// create login dialog form
			this.loginDlg = document.createElement('div');
			this.loginDlg.className = this.loginDialogCSSClass;
			this.loginDlg.innerHTML = 
			'<form id="loginFormDialog" method="post" action="?action=login&amp;srcPage=' + rDom.$('pageName').value + '">' +
				'<fieldset>' +
				  '<h1>התחברות</h1>' +
				
				   '<label for="login_nick">' + 
						'<span>שם משתמש:</span>' +
						'<input type="text" maxlength="32" id="login_nick" name="login_nick" />' +
					'</label>' +
					
					'<label for="login_password">' +
						'<span>סיסמה:</span>' +
						'<input type="password" maxlength="32" id="login_password" name="login_password" />' +
					'</label>' +
					
					'<label for="remember_me" id="remember_me_cont">' +
						'<span>לזכור להבא?</span> ' +
						'<input type="checkbox" id="remember_me" name="remember_me" />' +
					'</label>' +
					
					'<div class="lostPass">' +
						'<a href="?action=lost_pass">איבדתם או אינכם זוכרים את הסיסמה? הקליקו כאן</a>' +
					'</div>' +
					
					'<div class="buttonsArea">' +
						'<input class="firstChild" type="submit" value="התחבר" name="submitLogin" />' +
						'<input type="button" value="ביטול" name="cancelLogin" />' +
					'</div>' +
				'</fieldset>' +
			'</form>';
			
			document.body.appendChild(this.loginDlg);
			rDom.addEvent(rDom.$('loginFormDialog').submitLogin, 'click', this.onClickLoginSubmit, this);
			rDom.addEvent(rDom.$('loginFormDialog').cancelLogin, 'click', this.onClickLoginCancel, this);
		}
		
		this.o.style.display = 'block';
		this.loginDlg.style.display = 'block';
		rDom.$('loginFormDialog').login_nick.focus();
		var pageSize = rDom.getPageSize();
		this.o.style.height = pageSize['pageHeight'] + 'px';
		
		// Window resize timer
		this.oTimer = setInterval(rDom.bindToObj (function() {
									var pageSize = rDom.getPageSize();
									this.o.style.height = pageSize['pageHeight'] + 'px';
									}, this), 250);
		
		
		rDom.stopEvent(ev);
	},
	
	onClickLoginSubmit : function(ev) {
		
	},
	
	onClickLoginCancel : function(ev) {
		
		if (this.o) {
			clearInterval(this.oTimer);
			this.o.style.display = 'none';
			this.loginDlg.style.display = 'none';
		}
	},
	
	onSubmitGeneralSearch : function (ev) {
		if (!this.searchKeyPressed) {
			rDom.stopEvent(ev);
			alert('אנא הכנס/י מילות חיפוש');
			rDom.$('searchString').focus();
			
		}
	},
	
	// Adds/removes 'on value' label of the search input text field
	onSearchString : function (ev) {
		
		var el = rDom.getTarget(ev);
		switch(ev.type) {
			case 'blur':
			
				if (!this.searchKeyPressed) {
					rDom.cssClassAdd(el, this.searchStringCSSClass);
					el.value = this.searchStringLabel;
				}
				break;
			
			case 'focus':
			
				if (!this.searchKeyPressed) {
					
					el.value = '';
					rDom.cssClassRemove(el, this.searchStringCSSClass);
				}
				break;
				
			case 'keypress':
				this.searchKeyPressed = true;
				break;
		}
		
		
	}
	
}

/* On off home object */
var home = {
	homeUrl : 'http://www.on-off.co.il/',
	title : 'On-off',
	
	determineMSIEver : function() {

		var agent	= navigator.userAgent.toLowerCase();
		var pos 	= agent.indexOf('msie')+1;
		
		if(pos)
			return agent.charAt(pos+4);
		return 0;
	
	}, 
	
	setAsHomePage : function(obj) {
		
		if(this.determineMSIEver()>=4){ 
			obj.style.behavior='url(#default#homepage)';
			obj.setHomePage(this.homeUrl);
		}
		else {
		
			alert('דפדפן זה אינו תומך בהצבה אוטומתית של דף הבית');
		}

	},
	
	bookmarksite : function() {
		this.title = document.title;
	
		if (window.sidebar) // firefox
			window.sidebar.addPanel(this.title, this.homeUrl, "");
		else if(window.opera && window.print){ // opera
			var elem = document.createElement('a');
			elem.setAttribute('href', this.homeUrl);
			elem.setAttribute('title',this.title);
			elem.setAttribute('rel','sidebar');
			elem.click();
		} 
		else if(document.all)// ie
			window.external.AddFavorite(this.homeUrl, this.title);
			
		return false;
	}
	
}

rDom.setDOMLoadHandler(Global.init, Global);

var isIE6 = (navigator.userAgent.indexOf("MSIE 6.") != -1);
var isIE5 = (navigator.userAgent.indexOf("MSIE 5.5") != -1);
var doc_  = document;
var imgs_ = new Array();
function getStyle(oElm, strCssRule)
	{
	var strValue = "";
	if(document.defaultView && document.defaultView.getComputedStyle)
		strValue = document.defaultView.getComputedStyle(oElm, "").getPropertyValue(strCssRule);
	else if(oElm.currentStyle)
		{
		strCssRule = strCssRule.replace(/\-(\w)/g, function (strMatch, p1)
			{
			return p1.toUpperCase();
			});
		strValue = oElm.currentStyle[strCssRule];
		}
	return strValue;
	}



function loadPNG()
	{
	if (!(isIE6 || isIE5))
		return ;
	var nodes_ = 	document.getElementsByTagName("*");
	var len_ 	= 	nodes_.length;
	for (var i = 0; i < len_ ; i++)
		{
		background_style = getStyle(nodes_[i],"backgroundImage");
		if (background_style != "none" && background_style != "")
			{
			background_url = background_style.replace("url(\"","").replace("\")","");
			background_ext = background_url.substr(background_url.length-3,3).toUpperCase();
			/*if (background_ext == "PNG")
				{
				
				background_url = "images/"+getStyle(nodes_[i],"backgroundImage").match("([0-9a-zA-Z_]*)[\.](png)")[0];
				nodes_[i].style.background = "";
				nodes_[i].style.width = nodes_[i].offsetWidth + " px";
				nodes_[i].style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true,src='"+background_url+"',sizingMethod='image')";
				}*/
			}
		if (nodes_[i].tagName == "IMG")
			{
			image_url = nodes_[i].src;
			image_ext = image_url.substr(image_url.length-3,3).toUpperCase();
			if (image_ext == "PNG")
				{
				image_url = "images/" + image_url.match("([0-9a-zA-Z_]*)[\.](png)")[0];
				nodes_[i].style.height = nodes_[i].offsetHeight + " px";
				nodes_[i].style.width = nodes_[i].offsetWidth + " px";
				nodes_[i].style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true,src='"+image_url+"',sizingMethod='image')";
				nodes_[i].src = "images/blank.gif";
				}
			}
		}
	}
function initLoad()
	{
		

	
	if (!(isIE6 || isIE5))
		return;
	loadPNG();
	}

// =======================================================
// Name: 		  Raskin Debug Console namespace
// Version: 	  0.5
// Created: 	  28/12/2006
// Last modified: 12/07/2007
// Author:		  Max Raskin ( ghunter@netvision.net.il )
//
// A simple output 'divWindow' (a div based window)
// that lets you print debug output into it, move it,
// and minimize it.
// =======================================================
/*
Skeleton of public functions:
	rDBG = {
		create(string title, bool isMinimized);
		print(string text);
		clear();
	};
*/

var rDBG = {
	// constants
	cssClassName  : 'rDebugConsole',
	defaultTitle  : 'rDebugConsole',
	// vars 
	isDragging    : false,
	mouseOffset   : null,
	container     : null,
	outputList    : null,
	normalHeight  : 0,
	minHeight	  : 0,
	
	/*
		Creates the debugging output console divWindow
		(called with default params by print() if you don't call it manually)
		
		Params:
			string title - [optional] title of divWindow
			bool   isMinimized - [optional] starts divWindow in minmized state
	*/
	create : function (title, isMinimized)
	{
		
		this.container = rDom.createEl('div');
		document.body.insertBefore(this.container,document.body.firstChild);
		
		this.container.className = this.cssClassName;
		
		if (title == null) title = this.defaultTitle;
		
		var windowTitle = rDom.createEl('h1', title);
		this.container.appendChild(windowTitle);
		
		this.normalHeight = this.container.offsetHeight;
		
		this.minHeight	   = windowTitle.offsetHeight;
		
		// Window drag n drop events
		rDom.addEvent(windowTitle, 'mousedown', this.onMouseDown, this);
		rDom.addEvent(windowTitle, 'mouseup',   this.onMouseUp, this);
		rDom.addEvent(windowTitle, 'dblclick',  this.onDblClick, this);
		rDom.addEvent(document,    'mousemove', this.onMouseMove, this);

		var listContainer = rDom.createEl('div');
		this.container.appendChild(listContainer);
		
		this.outputList = rDom.createEl('ul');
		listContainer.appendChild(this.outputList);
		
		if (isMinimized == true) this.onDblClick();
		
	},
	
	// Handles divWindow dropping event
	onMouseUp : function(e)
	{
		this.isDragging = false;
	},
	
	// Handles divWindow double click event (minimize/maximize)
	onDblClick : function (e)
	{
		if (this.container.offsetHeight >= this.normalHeight)
		{
			 this.container.style.height = this.minHeight + 'px';
			 this.outputList.style.display = 'none';
		}
		else
		{
		 	this.container.style.height =  this.normalHeight + 'px';
		 	this.outputList.style.display = 'block';
		}
	},
	
	// Handles divWindow moving event
	onMouseMove : function(e) 
	 {
	 	if (this.isDragging)
	 	{
		 	var coords = rDom.getMouseCoords(e);
		 	this.container.style.left = (coords.x - (this.mouseOffset.x)) + 'px';
		 	this.container.style.top  = (coords.y - (this.mouseOffset.y)) + 'px';
	 	}
	 },
	
	// Handles signaling of start drag event
	onMouseDown : function(e)  
	{
		this.isDragging = true;
		if (this.mouseOffset == null) this.mouseOffset = new Array();
		var coords = rDom.getMouseCoords(e);
		var elCoords = rDom.getElementPos(this.container);
		this.mouseOffset.x = coords.x - elCoords.x;
		this.mouseOffset.y = coords.y - elCoords.y;
	},
	
	/*
		Prints a message into the console
		
		Params:
			string text
	*/
	print : function(text)
	{
		if (this.container == null) this.create();
		this.outputList.insertBefore(rDom.createEl('li', text),this.outputList.firstChild); 
	},
	
	// Cleans up console output
	clear : function()
	{
		this.outputList.innerHTML = '';
	}

}
