/**
 * @projectDescription Empas Prototye & UI
 * @copyright Empas Corp. <http://www.empas.com>
 * @author ifsnow ifsnow@empascorp.com
 * @version 1.0
 * 
 * Empas > Core > Ajax
 *
 * base : prototype.js v1.5.1.1
 */

if(typeof Prototype == "undefined")
	throw("empas.prototype.ajax.js requires including Prototype library");

if (!$D(Empas)) var Empas = {};

/*============================================================================*
 * Ajax
 *============================================================================*/
Empas.Ajax = {
	getTransport: function() {
		try { return new XMLHttpRequest() } catch (e) {}
		try { return new ActiveXObject('Msxml2.XMLHTTP') } catch (e) {}
		try { return new ActiveXObject('Microsoft.XMLHTTP') } catch (e) {}
		return false;
	},
	activeRequestCount: 0
};

var Ajax = Empas.Ajax;

Ajax.Responders = {
	responders: [],

	_each: function(iterator) {
		this.responders._each(iterator);
	},

	register: function(responder) {
		if (!this.include(responder))
			this.responders.push(responder);
	},

	unregister: function(responder) {
		this.responders = this.responders.without(responder);
	},

	dispatch: function(callback, request, transport, json) {
		this.each(function(responder) {
			if (typeof responder[callback] == 'function') {
				try {
					responder[callback].apply(responder, [request, transport, json]);
				} catch (e) {}
			}
		});
	}
};

Object.extend(Ajax.Responders, Enumerable);

Ajax.Responders.register({
	onCreate: function() {
		Ajax.activeRequestCount++;
	},
	onComplete: function() {
		Ajax.activeRequestCount--;
	}
});

Ajax.Base = function() {};
Ajax.Base.prototype = {
	setOptions: function(options) {
		this.options = {
			method:       'post',
			asynchronous: true,
			contentType:  'application/x-www-form-urlencoded',
			encoding:     'UTF-8',
			parameters:   ''
		};
		
		Object.extend(this.options, options || {});

		options = this.options;
		options.method = options.method.toLowerCase();
		if ($T(options.parameters) == 'string')
			options.parameters = options.parameters.toQueryParams();
	}
};

Ajax.Request = Class.create();
Ajax.Request.Events = ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];

Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
	_complete: false,

	initialize: function(url, options) {
		this.transport = Ajax.getTransport();
		this.setOptions(options);
		this.request(url);
	},

	request: function(url) {
		this.url = url;
		this.method = this.options.method;
		var params = Object.clone(this.options.parameters);

		if (!['get', 'post'].include(this.method)) {
			// simulate other verbs over post
			params['_method'] = this.method;
			this.method = 'post';
		}

		this.parameters = params;

		if ((params = Hash.toQueryString(params))) {
			// when GET, append parameters to URL
			if (this.method == 'get')
				this.url += (this.url.include('?') ? '&' : '?') + params;
			else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
				params += '&_=';
		}

		try {
			if (this.options.onCreate) this.options.onCreate(this.transport);
			Ajax.Responders.dispatch('onCreate', this, this.transport);

			this.transport.open(this.method.toUpperCase(), this.url,
				this.options.asynchronous);

			if (this.options.asynchronous)
			setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10);

			this.transport.onreadystatechange = this.onStateChange.bind(this);
			this.setRequestHeaders();

			this.body = this.method == 'post' ? (this.options.postBody || params) : null;
			this.transport.send(this.body);

			/* Force Firefox to handle ready state 4 for synchronous requests */
			if (!this.options.asynchronous && this.transport.overrideMimeType)
				this.onStateChange();
		}
		catch (e) {
			this.dispatchException(e);
		}
	},

	onStateChange: function() {
		var readyState = this.transport.readyState;
		if (readyState > 1 && !((readyState == 4) && this._complete))
			this.respondToReadyState(this.transport.readyState);
	},

	setRequestHeaders: function() {
		var headers = {
			'X-Requested-With': 'XMLHttpRequest',
			'X-Prototype-Version': Prototype.Version,
			'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
		};

		if (this.method == 'post') {
			headers['Content-type'] = this.options.contentType +
				(this.options.encoding ? '; charset=' + this.options.encoding : '');

			// Force "Connection: close" for older Mozilla browsers to work
			// around a bug where XMLHttpRequest sends an incorrect
			// Content-length header. See Mozilla Bugzilla #246651.
			if (this.transport.overrideMimeType &&
				(navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
				headers['Connection'] = 'close';
		}

		// user-defined headers
		if (typeof this.options.requestHeaders == 'object') {
			var extras = this.options.requestHeaders;

			if (typeof extras.push == 'function')
				for (var i = 0, length = extras.length; i < length; i += 2)
					headers[extras[i]] = extras[i+1];
			else
				$H(extras).each(function(pair) { headers[pair.key] = pair.value });
		}

		for (var name in headers)
			this.transport.setRequestHeader(name, headers[name]);
	},

	success: function() {
		return !this.transport.status
			|| (this.transport.status >= 200 && this.transport.status < 300);
	},

	respondToReadyState: function(readyState) {
		var state = Ajax.Request.Events[readyState];
		var transport = this.transport, json = this.evalJSON();

		if (state == 'Complete') {
			try {
				this._complete = true;
				(this.options['on' + this.transport.status]
					|| this.options['on' + (this.success() ? 'Success' : 'Failure')]
					|| Prototype.emptyFunction)(transport, json);
			} catch (e) {
				this.dispatchException(e);
			}

			var contentType = this.getHeader('Content-type');
			if (contentType && contentType.strip().
				match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i))
				this.evalResponse();
		}

		try {
			(this.options['on' + state] || Prototype.emptyFunction)(transport, json);
			Ajax.Responders.dispatch('on' + state, this, transport, json);
		} catch (e) {
			this.dispatchException(e);
		}

		if (state == 'Complete') {
			// avoid memory leak in MSIE: clean up
			this.transport.onreadystatechange = Prototype.emptyFunction;
		}
	},

	getHeader: function(name) {
		try {
			return this.transport.getResponseHeader(name);
		} catch (e) { return null }
	},

	evalJSON: function() {
		try {
			var json = this.getHeader('X-JSON');
			return json ? json.evalJSON() : null;
		} catch (e) { return null }
	},

	evalResponse: function() {
		try {
			return eval((this.transport.responseText || '').unfilterJSON());
		} catch (e) {
			this.dispatchException(e);
		}
	},

	dispatchException: function(exception) {
		(this.options.onException || Prototype.emptyFunction)(this, exception);
		Ajax.Responders.dispatch('onException', this, exception);
	}
});

Ajax.Updater = Class.create();

Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
	initialize: function(container, url, options) {
		this.container = {
			success: (container.success || container),
			failure: (container.failure || (container.success ? null : container))
		};

		this.transport = Ajax.getTransport();
		this.setOptions(options);

		var onComplete = this.options.onComplete || Prototype.emptyFunction;
		this.options.onComplete = (function(transport, param) {
			this.updateContent();
			onComplete(transport, param);
		}).bind(this);

		this.request(url);
	},

	updateContent: function() {
		var receiver = this.container[this.success() ? 'success' : 'failure'];
		var response = this.transport.responseText;

		if (!this.options.evalScripts) response = response.stripScripts();

		if (receiver = $(receiver)) {
			if (this.options.insertion) 
				new this.options.insertion(receiver, response);
			else
				receiver.update(response);
		}

		if (this.success()) {
			if (this.onComplete) setTimeout(this.onComplete.bind(this), 10);
		}
	}
});