function $timer(fn, arguments, repeats) {
	repeats = ($defined(repeats))? repeats : 1;
	var arguments = $splat(arguments);
	var start = $time();
	while (repeats > 0) {
		fn.attempt(arguments);
		repeats--;
	}
	return ($time() - start) / 1000;
}

Hash.implement({add: function(key, value) {
	var currentValue = this.get(key);
	if (currentValue !== null) {
		if ($type(currentValue) === 'array') {
			currentValue.push(value);
		} else {
			this.parent.set(key, [currentValue, value]);
		}
	} else {
		this.set(key, [value]);
	}
}});

Request.injects = [];
Request.implement({inject: function(options) {
		var self = this;
		switch ($type(options)) {
			case 'string': case 'element':
				options = {data: options};
		}
		options = $extend({data: this.options.data, url: this.options.url, method: this.options.method}, options);
		var paramString = "?";
	    switch ($type(options.data)) {
	      case "element":
	        paramString += $(options.data).toQueryString();
	        break;
	      case "object":
	      case "hash":
	        paramString += Hash.toQueryString(options.data);
	    }
		if (paramString !== "?") {
			paramString += "&"
		}
		if (this.options.format) {
			paramString += "format=" + this.options.format + "&";
		}
	    if (this.options.emulation && options.method !== "get") {
			paramString += "_method=" + options.method + "&";
	    }
		if (this.options.noCache) {
			paramString += "noCache=" + $random(1,9999) + $time() + "&";
		}
		var index = Request.injects.length;
		var url = options.url + paramString + "callback=Request.injects[" + index + "].callback";
		var element = new Element('script', {type: "text/javascript", src: url, text: ""});
		
		Request.injects.push({
			callback: function(response){
				switch($type(response)) {
					case 'string':
						self.response = {text: responce, xml: null};
						break;
					case 'element':
						var container = new Element('div').grab(responce);
						var htmlText = container.get('html');
						container.destory();
						self.response = {text: htmlText, xml: responce};
						break;
					case false:
						self.response = {text: null, xml: null};
						break;
					default:
						self.response = {text: JSON.encode(response), xml: null, json: response};
				}
				self.success(self.response.text, self.response.xml);
				if (self.options.keepInject !== true) {
					Request.injects[index] = null;
					element.destroy();
				}
			},
			element: element
		});
		
		var location = $type(this.options.injectLocation) === "element"? this.options.injectLocation : document.head;
		location.grab(element);
		this.fireEvent('request');
		
		return this;
}});
$extend(Request.prototype.options, {
	keepInject: false,
	noCache: false
});

Request.WCSJSON = new Class({

	Extends: Request.JSON,
		
	/*Constructor*/
	initialize: function(options){
		this.parent(options);
		this.addEvent('success', function(response){
			if (response == null) {
				this.fireEvent('invalid');
				throw new new Error("invalid response");
			} else if(response.status == 'error') {
				this.fireEvent('rejected', response.exception);
			} else if(response.status == 'failed') {
				this.fireEvent('wcs_failed', response.message);
			} else {
				this.fireEvent('accepted', response.result);
			}
		});
	}
	
});


Request.WCSJSONP = new Class({

	Extends: Request.JSONP,
		
	/*Constructor*/
	initialize: function(options){
		this.parent(options);
		this.addEvent('complete', function(response){
			if (response == null) {
				this.fireEvent('invalid');
				throw new new Error("invalid response");
			} else if(response.status == 'error') {
				this.fireEvent('rejected', response.exception);
			} else if(response.status == 'failed') {
				this.fireEvent('wcs_failed', response.message);
			} else {
				this.fireEvent('accepted', response.result);
			}
		});
	}
	
});


URL = new Class({

	address: null,
	
	parameters: null,
	
	initialize: function(address, params){
		this.address = address;
		this.parameters = new Hash(params);
		this['$family'] = {name: 'url'};
		this.toString = function() {
			if (this.parameters.getLength() > 0) {
				var params = [];
				this.parameters.each(function(value, key) {
					switch($type(value)) {
						case 'collection':
						case 'array':
							Array.each(value, function(arrayValue){
								params.push(key + '=' + encodeURIComponent(arrayValue));
							});
							break;
						default:
							params.push(key + '=' + encodeURIComponent(value));
					}
				});
				return this.address + '?' + params.join('&');
			} else {
				return this.address;
			}
		};
	}
	
});

function $url(url) {
	switch($type(url)) {
		case 'url': return url;
		case 'string': return new URL(url);
	}
	return null;
}

function WCSChain(firstURL) {
	firstURL = $url(firstURL);
	if (firstURL !== null) {
		var tail = firstURL;
		for (var i = 1; i < arguments.length; i++) {
			var url = $url(arguments[i]);
			if (url !== null) {
				tail.parameters.set('URL', url);
				tail = url;
			}
		}
	}
	return firstURL;
}

function parseHTML(htmlText) {
	return new Element('div', {html: htmlText}).childNodes;
}

function parsePrice(norprice, ofprice) {
	if($defined(norprice) && $defined(ofprice)) {
		return parseHTML(norprice.strike() + "<br/>" + ofprice);
	} else {
		return norprice;
	}
}

