Object.extend(Function.prototype, {
	/**
	 * Benchmarks the function
	 * @return {String}
	 */
	benchmark : function() {
		benchStart = (new Date()).getTime();
		returnVal = this.apply(this, arguments);
	    passedMilSec = (new Date()).getTime() - benchStart;
		return 'Function with return value ' + Object.inspect(returnVal) + ' is excuted in ' + passedMilSec + ' milliseconds';
	},
	
	bodyAsString : function() {
		var str = this.toString(); 
		str=str.replace(/[^{]+{/,"");
		str=str.substring(0,str.length-1);   
		str = str.replace(/\n/gi,"");
		return str.strip();
	} 
});

Object.extend(String.prototype, {
	
	/**
	 * Tries to make an element from a html code
	 * 
	 * return {Element}
	 */
	makeElement: function() {
		return this.wrapElement().down();
	},
	
	/**
	 * Wraps the string with element of type tag
	 *
	 * @param {String} tagName (optional) 
	 */
	wrapElement: function () {
		var node = new Element(arguments[0]||'div');
		return node.update(this);
	},
	
	/**
	 * Extract elements from HTML code with given selector
	 * 
	 * @param {String} selector
	 * @return {Array} Array of elements 
	 */
	extractElements: function() {
		return this.wrapElement().select(arguments[0]||'*');
	},
	
	/**
	 * Parse integer
	 * 
	 * @return {Integer}
	 */
	parseInteger: function() {
		return parseInt(this);
	},
	
	/**
	 * Parse float
	 * 
	 * @return {Float}
	 */
	parseFloat: function() {
		return parseFloat(this);
	}
})
Object.extend(Enumerable, {
	findIndex:  function(iterator) {
		var result;
	    this.each(function(value, index) {
		    if (iterator(value, index)) {
		        result = index;
		        throw $break;
		    }
		});
	    return result;
   }
})

Array.prototype.findIndex = Enumerable.findIndex;

Object.extend(Array.prototype,{
	
	/**
	 * Calculate intersection of arrays
	 * @param {Array} array
	 * @return {Array} returns an array containing all the values those present in array 
	 */
	intersect: function(array) {
		array = $A(arguments).flatten();
		return this.uniq().findAll(function(item) {
	    	return array.detect(function(value) { 
				return item === value 
			});
	    });
	},
	
	/**
	 * Computes the difference of arrays
	 * @param {Array} array
	 * @return {Array} returns an array containing all the values those do not present in array 
	 */
	diff: function(array) {
		array = $A(arguments).flatten();
		return this.uniq().findAll(function(item) {
	    	return !array.detect(function(value) { 
				return item === value 
			});
	    });
	},
	
	/**
	 * Splits array to small peaces
	 * @param {Object} length
	 */
	split: function(length) {
		out = $A(); array = this.clone();
		
		do {
			out.push(array.splice(0, length));			
		}while(array.length > 0);
		
		return out;
	}
	
});


Element.addMethods('select', {
	
	addOption: function(element) {
		element = $(element);
		element.appendChild(option = new Element('option', arguments[1]||{}));
		return option;
	},
	
	fill: function(element, fillWhat){
		element = $(element);
		for (var i in fillWhat) {
			opt = element.addOption()
			opt.text = fillWhat[i];
			opt.value = i;
		}
		return element;
	},
	
	fillAjax: function(element, url){
		element = $(element);
		options = arguments[2]||{};
		options['onSuccess'] = function(resp, json){
			this.empty();
			if (json) 
				this.fill(json)
			else if (resp.responseText.isJSON())
				this.fill(resp.responseText.evalJSON());				
	        
	    }.bind(element);
		
		if (Object.isFunction(options['onLoading'])) {
			options.onLoading.bind(element);
		}
		
		if (Object.isFunction(options['onComplete'])) {
			options.onComplete.bind(element);
		}
		
		new Ajax.Request(url, options);
		return element;
	},
	
	empty : function(element) {
		element = $(element);
		while(element.options.length > 0) {
			element.removeChild(element.options[0]);
		}
		return element;
	}
});

Element.addMethods({
	
	toHTML: function(element) {
		element = $($(element).cloneNode(true));
		var node = new Element('div');
		node.appendChild(element);
		return node.innerHTML;
	},
	
	scrollTo: function(element, options) {
		element = $(element);
		Object.extend({ x: true, y: true }, options || {});
		var pos = this.cumulativeOffset(element);
	  	window.scrollTo(
	    	options.x ? pos[0] : window.scrollX,
	    	options.y ? pos[1] : window.scrollY
	  	);
		return element;
	},
	
	replaceClassName: function(element, findClass, replaceWithClass) {
		element = $(element);
		element.removeClassName(findClass).addClassName(replaceWithClass);
		return element;
	},
	
	/**
	 * Remove one or more classes from elements class list
	 * @param {Element} element
	 * @param {String} classNames
	 */
	removeClassName: function(element, classNames) {
		classNames = $A(arguments).slice(1).flatten();
		element = $(element); existingClassNames = element.className.split(/\s/);
		element.className = existingClassNames.diff(classNames).join(' ')		
		return element;
	},
	
	/**
	 * In every function call the class name of the element
	 * is replaced by next class in the classNames parameter 
	 * 
	 * @param {Element} $element
	 * @param {Array} $classNames
	 * @return {Element.ClassNames}
	 */
	cycleClassNames: function(element, classNames) {
		
		if (!Object.isArray(classNames)) {
			return element;
		}
		element = $(element); classNames = classNames.uniq();  existingClassNames = element.className.split(/\s/);
		if (classNames.length == 1) {
			element.toggleClassName(className.first());
			return element;
		}
		
		includedClassName = existingClassNames.intersect(classNames)[0]||false;
		if (!includedClassName) {
			element.addClassName(classNames.first());
		}else {
			index = classNames.indexOf(includedClassName);
			element.replaceClassName(includedClassName, classNames[index+1]?classNames[index+1]:classNames[0]);
		}
		
		return element;
	}
});

Element.addMethods('A', {
	disableHrefFollow: function(element) {
		element = $(element);
		if (Object.isFunction(element.onclick)){
			element.observe('click', element.onclick);
			element.onclick = Prototype.K(false);
		}else {
			element.onclick = Prototype.K(false);
		}
		return element;
	}
})

Object.extend(Form.Methods, {
	
	addRules: function(element, rules){
		element = $(element); formEls = element.getElements();
		element._rules = Object.clone(rules);
		formEls.each(function(el){
			if (this._rules[el.name]){
				el.addRule(rules[el.name]);				
			}
		}.bind(element));
	},
	
	validate: function(element){
		element = $(element); formEls = element.getElements();
		var lastel = false;
		res = formEls.all(function(el){
			lastel = el;	
			return el.validate();
		})
		
		if (!res) {
			element.fire('form:invalidated', lastel)
		} else {
			element.fire('form:validated')
		}
		return res;
	},
	
	submitAjax: function(element, options) {
		element = $(element); options = options || {}
		options.parameters = element.serialize();
		options.method = element.method || 'POST';
		options.caching = false;
		new Ajax.Request(element.action, options);
	},
	
	applyCapcha: function(element, cname, fname) {
		 element = $(element);
		 var checkValue = Math.round(Math.random() * 10000000);
         element.insert(new Element('input', {type: 'hidden', name: fname, value : checkValue}));
	     document.cookie = cname + '=' + checkValue + ';path=/';
		 return element;
	},
	
	clear: function(element, exceptElementSelector) {
		element = $(element);
		if (arguments[1]) except  = element.select(exceptElementSelector).invoke('toHTML');
		else  except = $(A)
			
		$A(element.elements).each(function(el){
			if (except.indexOf(el.toHTML()) < 0) {
				el.setValue('');
			}
		})
	}
})

Object.extend(Form.Element.Methods, {
	addRule: function(element, rule){
		element = $(element);
		if (!element._validateRule) {
			element._validateRule = $A();
		}
		
		if (Object.isArray(rule)){
			$(rule).each(function(r){
				element.addRule(r);
			});
		}else if (rule.name && Object.isString(rule.name) && Form.Rules[rule.name]){
			rule.tester = Form.Rules[rule.name]
			element._validateRule.push(rule);
		}else if(rule.tester.test && Object.isFunction(rule.tester.test)) {
			element._validateRule.push(rule);
		}else {
			throw new Error('Unknown rule is given')
		}
		
		element.observe('change', function(){
		    this.validated = false;
			this._rulesChecked = false;
		}.bind(element));
		
		element.observe('blur', function(){
			this._rulesChecked = false;
		}.bind(element));
		
		element.validated = false;
	},
	
	validate: function(element){
		element = $(element);
		if (element._validateRule && element.validated != true) {
			
			if (!element._rulesChecked) {
				
				//reset last invalidated rule
				element._invalidRule = false;
				
				var lastRule = false;
				res = element._validateRule.all(function(r){
					lastRule = r;
					testFnc = r.tester.test.bind(element);
					return testFnc(element.getValue(), r.params || null);
				}.bind(element));
				element._rulesChecked = true;
				
			}else {
				res = false;
				lastRule = element._invalidRule;
			}
			
			if (res) {
			    ev ='element:validated'
			    element.validated = true;
			} else {
			    ev = 'element:invalidated';
			}
			
			//set invalidated rule
			element._invalidRule = lastRule;
			element.fire(ev, lastRule);
			return res;
		}
		return true;
	}
})


Element.addMethods();

Form.Rules  = {
	email     : {
	    test: function(value) {
	        return value.blank()?true:value.match(/^[\w\.\-\_]+@[\w\.\-]+\.[a-z]{2,4}$/);
	    }
	},
	
	url     : {
	    test: function(value) {
			
	        return value.blank()?true:value.match("^(https?://)"
        + "?(([0-9a-z_!~*'().&=+$%-]+: )?[0-9a-z_!~*'().&=+$%-]+@)?" //user@
        + "(([0-9]{1,3}\.){3}[0-9]{1,3}" // IP- 199.194.52.184
        + "|" // allows either IP or domain
        + "([0-9a-z_!~*'()-]+\.)*" // tertiary domain(s)- www.
        + "([0-9a-z][0-9a-z-]{0,61})?[0-9a-z]\." // second level domain
        + "[a-z]{2,6})" // first level domain- .com or .museum
        + "(:[0-9]{1,4})?" // port number- :80
        + "((/?)|" // a slash isn't required if there is no file name
        + "(/[0-9a-z_!~*'().;?:@&=+$,%#-]+)+/?)$");
	    }
	},
	
	numeric   : {
	    test: function(value) {
	        return value.length ==0?true:value.match(/^\d+$/);
	    }
	},
	minlength : {test: function(str, ml){
		return str.length >= parseInt(ml);
	}},
	
	maxlength : {test: function(str, ml){
		return value.length ==0?true:(str.length < parseInt(ml));
	}},
	
	minwords  : {test: function(value, count){
		return value.length ==0?true:value.split(/[\s,\.:;]+/).length >= parseInt(count)	
	}},
	
	maxwords  : {test: function(value, count){
		return value.length ==0?true: (value.split(/\s+/).length <= parseInt(count))	
	}},
	
	length : {test: function(value, len){
		return value.length == parseInt(len);
	}},
	
	required :{
		test: function(str){
			return !str.blank()
		}
	},
	
	compare : {
	    test: function(value, withEl) {
	        return (value == $(this.form.elements[withEl]).getValue());
	    }
	},
	
	alphanumeric: {
	    test: function(value) {
	        return value.match(/^\w+$/);
	    }
	}
}

var Cookie = {
    get : function(name) {
    	var cookies = document.cookie.split(";");
        for (var i = 0; i < cookies.length; i++) {
        	var a = cookies[i].split("=");
            if (a.length == 2) {
            	a[0] = a[0].trim();
                a[1] = a[1].trim();
                if (a[0] == name) {
                	return unescape(a[1]);
                }
            }
        }
        return "";
    },
        
	set : function(name, value) {
    	document.cookie = name + "=" + escape(value);
    },
        
	remove : function(name) {
    	document.cookie = name + "=; expires=Thu, 01-Jan-70 00:00:01 GMT";
    }
}

Ajax.Request.addMethods({
	
	request: Ajax.Request.prototype.request.wrap(function(callParent, url) {
		
		if (!this.options.caching || this.options.caching === false) {
			callParent(url);
		}else {
			
			var cacheId = Object.toJSON(this.options.parameters||{}) + url;
			if (!Ajax.Request.cache[cacheId]) {
				var onSuccess = this.options.onSuccess||Prototype.K; 
				this.options.onSuccess = function(response){
					Ajax.Request.cache[cacheId] = response;
					onSuccess(resp, resp.responseJSON||null);
				}
				
				// if ttl is set remove cache after it expires
				if (this.options.ttl && this.options.ttl > 0) {
					(function(){
						delete Ajax.Request.cache[cacheId];
					})().delay(this.options.ttl*60);
				}
				callParent(url);
			}else {
				(this.options.onSuccess||Prototype.K)(response, response.responseJSON||null);
				(this.options.onComplete||Prototype.K)(response, response.responseJSON||null);
			}
		}
	})	
});

Ajax.Request.cache = {};

/**
 * @author Ryan Johnson <http://saucytiger.com/>
 * @copyright 2008 PersonalGrid Corporation <http://personalgrid.com/>
 * @package LivePipe UI
 * @license MIT
 * @url http://livepipe.net/core
 * @require prototype.js
 */

if(typeof(Control) == 'undefined')
	Control = {};
	
var $proc = function(proc){
	return typeof(proc) == 'function' ? proc : function(){return proc};
};

var $value = function(value){
	return typeof(value) == 'function' ? value() : value;
};

Object.Event = {
	extend: function(object){
		object._objectEventSetup = function(event_name){
			this._observers = this._observers || {};
			this._observers[event_name] = this._observers[event_name] || [];
		};
		object.observe = function(event_name,observer){
			if(typeof(event_name) == 'string' && typeof(observer) != 'undefined'){
				this._objectEventSetup(event_name);
				if(!this._observers[event_name].include(observer))
					this._observers[event_name].push(observer);
			}else
				for(var e in event_name)
					this.observe(e,event_name[e]);
		};
		object.stopObserving = function(event_name,observer){
			this._objectEventSetup(event_name);
			if(event_name && observer)
				this._observers[event_name] = this._observers[event_name].without(observer);
			else if(event_name)
				this._observers[event_name] = [];
			else
				this._observers = {};
		};
		object.observeOnce = function(event_name,outer_observer){
			var inner_observer = function(){
				outer_observer.apply(this,arguments);
				this.stopObserving(event_name,inner_observer);
			}.bind(this);
			this._objectEventSetup(event_name);
			this._observers[event_name].push(inner_observer);
		};
		object.notify = function(event_name){
			this._objectEventSetup(event_name);
			var collected_return_values = [];
			var args = $A(arguments).slice(1);
			try{
				for(var i = 0; i < this._observers[event_name].length; ++i)
					collected_return_values.push(this._observers[event_name][i].apply(this._observers[event_name][i],args) || null);
			}catch(e){
				if(e == $break)
					return false;
				else
					throw e;
			}
			return collected_return_values;
		};
		if(object.prototype){
			object.prototype._objectEventSetup = object._objectEventSetup;
			object.prototype.observe = object.observe;
			object.prototype.stopObserving = object.stopObserving;
			object.prototype.observeOnce = object.observeOnce;
			object.prototype.notify = function(event_name){
				if(object.notify){
					var args = $A(arguments).slice(1);
					args.unshift(this);
					args.unshift(event_name);
					object.notify.apply(object,args);
				}
				this._objectEventSetup(event_name);
				var args = $A(arguments).slice(1);
				var collected_return_values = [];
				try{
					if(this.options && this.options[event_name] && typeof(this.options[event_name]) == 'function')
						collected_return_values.push(this.options[event_name].apply(this,args) || null);
					for(var i = 0; i < this._observers[event_name].length; ++i)
						collected_return_values.push(this._observers[event_name][i].apply(this._observers[event_name][i],args) || null);
				}catch(e){
					if(e == $break)
						return false;
					else
						throw e;
				}
				return collected_return_values;
			};
		}
	}
};

/* Begin Core Extensions */

//Element.observeOnce
Element.addMethods({
	observeOnce: function(element,event_name,outer_callback){
		var inner_callback = function(){
			outer_callback.apply(this,arguments);
			Element.stopObserving(element,event_name,inner_callback);
		};
		Element.observe(element,event_name,inner_callback);
	}
});

//mouseenter, mouseleave
//from http://dev.rubyonrails.org/attachment/ticket/8354/event_mouseenter_106rc1.patch
Object.extend(Event, (function() {
	var cache = Event.cache;

	function getEventID(element) {
		if (element._prototypeEventID) return element._prototypeEventID[0];
		arguments.callee.id = arguments.callee.id || 1;
		return element._prototypeEventID = [++arguments.callee.id];
	}

	function getDOMEventName(eventName) {
		if (eventName && eventName.include(':')) return "dataavailable";
		//begin extension
		if(!Prototype.Browser.IE){
			eventName = {
				mouseenter: 'mouseover',
				mouseleave: 'mouseout'
			}[eventName] || eventName;
		}
		//end extension
		return eventName;
	}

	function getCacheForID(id) {
		return cache[id] = cache[id] || { };
	}

	function getWrappersForEventName(id, eventName) {
		var c = getCacheForID(id);
		return c[eventName] = c[eventName] || [];
	}

	function createWrapper(element, eventName, handler) {
		var id = getEventID(element);
		var c = getWrappersForEventName(id, eventName);
		if (c.pluck("handler").include(handler)) return false;

		var wrapper = function(event) {
			if (!Event || !Event.extend ||
				(event.eventName && event.eventName != eventName))
					return false;

			Event.extend(event);
			handler.call(element, event);
		};
		
		//begin extension
		if(!(Prototype.Browser.IE) && ['mouseenter','mouseleave'].include(eventName)){
			wrapper = wrapper.wrap(function(proceed,event) {	
				var rel = event.relatedTarget;
				var cur = event.currentTarget;			 
				if(rel && rel.nodeType == Node.TEXT_NODE)
					rel = rel.parentNode;	  
				if(rel && rel != cur && !rel.descendantOf(cur))	  
					return proceed(event);   
			});	 
		}
		//end extension

		wrapper.handler = handler;
		c.push(wrapper);
		return wrapper;
	}

	function findWrapper(id, eventName, handler) {
		var c = getWrappersForEventName(id, eventName);
		return c.find(function(wrapper) { return wrapper.handler == handler });
	}

	function destroyWrapper(id, eventName, handler) {
		var c = getCacheForID(id);
		if (!c[eventName]) return false;
		c[eventName] = c[eventName].without(findWrapper(id, eventName, handler));
	}

	function destroyCache() {
		for (var id in cache)
			for (var eventName in cache[id])
				cache[id][eventName] = null;
	}

	if (window.attachEvent) {
		window.attachEvent("onunload", destroyCache);
	}

	return {
		observe: function(element, eventName, handler) {
			element = $(element);
			var name = getDOMEventName(eventName);

			var wrapper = createWrapper(element, eventName, handler);
			if (!wrapper) return element;

			if (element.addEventListener) {
				element.addEventListener(name, wrapper, false);
			} else {
				element.attachEvent("on" + name, wrapper);
			}

			return element;
		},

		stopObserving: function(element, eventName, handler) {
			element = $(element);
			var id = getEventID(element), name = getDOMEventName(eventName);

			if (!handler && eventName) {
				getWrappersForEventName(id, eventName).each(function(wrapper) {
					element.stopObserving(eventName, wrapper.handler);
				});
				return element;

			} else if (!eventName) {
				Object.keys(getCacheForID(id)).each(function(eventName) {
					element.stopObserving(eventName);
				});
				return element;
			}

			var wrapper = findWrapper(id, eventName, handler);
			if (!wrapper) return element;

			if (element.removeEventListener) {
				element.removeEventListener(name, wrapper, false);
			} else {
				element.detachEvent("on" + name, wrapper);
			}

			destroyWrapper(id, eventName, handler);

			return element;
		},

		fire: function(element, eventName, memo) {
			element = $(element);
			if (element == document && document.createEvent && !element.dispatchEvent)
				element = document.documentElement;

			var event;
			if (document.createEvent) {
				event = document.createEvent("HTMLEvents");
				event.initEvent("dataavailable", true, true);
			} else {
				event = document.createEventObject();
				event.eventType = "ondataavailable";
			}

			event.eventName = eventName;
			event.memo = memo || { };

			if (document.createEvent) {
				element.dispatchEvent(event);
			} else {
				element.fireEvent(event.eventType, event);
			}

			return Event.extend(event);
		}
	};
})());

Object.extend(Event, Event.Methods);

Element.addMethods({
	fire:			Event.fire,
	observe:		Event.observe,
	stopObserving:	Event.stopObserving
});

Object.extend(document, {
	fire:			Element.Methods.fire.methodize(),
	observe:		Element.Methods.observe.methodize(),
	stopObserving:	Element.Methods.stopObserving.methodize()
});

//mouse:wheel
(function(){
	function wheel(event){
		var delta;
		// normalize the delta
		if(event.wheelDelta) // IE & Opera
			delta = event.wheelDelta / 120;
		else if (event.detail) // W3C
			delta =- event.detail / 3;
		if(!delta)
			return;
		var custom_event = event.element().fire('mouse:wheel',{
			delta: delta
		});
		if(custom_event.stopped){
			event.stop();
			return false;
		}
	}
	document.observe('mousewheel',wheel);
	document.observe('DOMMouseScroll',wheel);
})();

/* End Core Extensions */

//from PrototypeUI
var IframeShim = Class.create({
	initialize: function() {
		this.element = new Element('iframe',{
			style: 'position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);display:none',
			src: 'javascript:void(0);',
			frameborder: 0 
		});
		$(document.body).insert(this.element);
	},
	hide: function() {
		this.element.hide();
		return this;
	},
	show: function() {
		this.element.show();
		return this;
	},
	positionUnder: function(element) {
		var element = $(element);
		var offset = element.cumulativeOffset();
		var dimensions = element.getDimensions();
		this.element.setStyle({
			left: offset[0] + 'px',
			top: offset[1] + 'px',
			width: dimensions.width + 'px',
			height: dimensions.height + 'px',
			zIndex: element.getStyle('zIndex') - 1
		}).show();
		return this;
	},
	setBounds: function(bounds) {
		for(prop in bounds)
			bounds[prop] += 'px';
		this.element.setStyle(bounds);
		return this;
	},
	destroy: function() {
		if(this.element)
			this.element.remove();
		return this;
	}
});

Event.observe(document, 'dom:loaded', function() {
	$$('form').each(function(el){
		el.applyCapcha('capcha_c', 'capcha_f');
	})
})