1 include(bbq.gui.error.NotLoggedIn);
  2 include(bbq.gui.error.ServerError);
  3 include(bbq.util.BBQUtil);
  4 
  5 bbq.ajax.AJAXRequest = Class.create(/** @lends bbq.ajax.AJAXRequest.prototype */ {
  6 	/**
  7 	 * Holds the options for this object.
  8 	 *
  9 	 * @type {Object}
 10 	 */
 11 	options: null,
 12 	_timeOut: null,
 13 	_interval: null,
 14 
 15 	/**
 16 	 * <p>Wrapper for Prototype Ajax class which dds extras such as an independent server timeout and error handling.</p>
 17 	 *
 18 	 * <p>Typically you would use a subclass such as <code class="language-javascript">bbq.ajax.JSONRequest</code>.</p>
 19 	 *
 20 	 * @constructs
 21 	 * @param	 {Object} options
 22 	 * @param {String} options.url Where to send the request to.
 23 	 * @param {String} [options.method="post"] "post" or "get".
 24 	 * @param {Object} [options.args] key/value pairs.
 25 	 * @param {Function} [options.onSuccess] Everything went as expected - eg. received HTTP 200.
 26 	 * @param {Function} [options.onFailure] The call failed - eg. did not receive HTTP 200.
 27 	 * @param {Function} [options.onException] An exception was thrown while attempting to make the call.
 28 	 * @param {String} [options.postBody] A string to send as the body of the request.  Subclasses will tend to use options.args over this value.
 29 	 * @param {Object} [options.headers] A list of key/value pairs to send as request headers.
 30 	 * @param {String} [options.contentType] The request content-type.
 31 	 * @see bbq.ajax.JSONRequest
 32 	 */
 33 	initialize: function(options) {
 34 		this.options = options;
 35 
 36 		if(!this.options.method) {
 37 			this.options.method = "post";
 38 		}
 39 
 40 		if(!this.options.args) {
 41 			this.options.args = {};
 42 		}
 43 
 44 		if(!this.options.headers) {
 45 			this.options.headers = {};
 46 		}
 47 
 48 		this._sendRequest();
 49 	},
 50 
 51 	_getPostBody: function() {
 52 		return this.options.postBody;
 53 	},
 54 
 55 	/**
 56 	 * Sends the request via the specified method
 57 	 * 
 58 	 * @return	void
 59 	 */
 60 	_sendRequest: function() {
 61 		try {
 62 			var requestHeaders = this._createRequestHeaders();
 63 
 64 			for(var key in requestHeaders) {
 65 				this.options.headers[key] = requestHeaders[key];
 66 			}
 67 
 68 			var request = new Ajax.Request(this.options.url, {
 69 				method: this.options.method, 
 70 				postBody: this._getPostBody(),
 71 				onSuccess: this._onSuccess.bind(this),
 72 				onFailure: this._onFailure.bind(this),
 73 				onExcepton: this._onException.bind(this),
 74 				requestHeaders: this.options.headers,
 75 				contentType: this.options.contentType
 76 			});
 77 
 78 			if(typeof(firebug) != "undefined" && firebug.watchXHR instanceof Function) {
 79 				// enable firebug lite to watch the ajax call status
 80 				request.transport._name = this.options.method.toUpperCase() + ' ' + this.options.url;
 81 				firebug.watchXHR(request.transport);
 82 			}
 83 
 84 			// get time out data from server configuration
 85 			if(typeof(ServerConfig) != "undefined" && ServerConfig["timeout"]) {
 86 				this._timeOut = ServerConfig["timeout"];
 87 			} else {
 88 				this._timeOut = 30;
 89 			}
 90 
 91 			// check timeout every second
 92 			this._interval = setInterval(this._checkTimeOut.bind(this), 1000);
 93 
 94 			if(typeof(NotificationArea) != "undefined") {
 95 				NotificationArea.startLoad();
 96 			}
 97 		} catch(e) {
 98 			Log.error("Could not send request", e);
 99 		}
100 	},
101 
102 	_createRequestHeaders: function() {
103 		return {
104 
105 		};
106 	},
107 
108 	/**
109 	 * @access	protected
110 	 * @param {String} handlerName
111 	 * @param {Object} args
112 	 */
113 	_callHandler: function(handlerName, args) {
114 		try {
115 			if(typeof(NotificationArea) != "undefined") {
116 				NotificationArea.stopLoad();
117 			}
118 
119 			clearInterval(this._interval);
120 
121 			if(this.options[handlerName] && this.options[handlerName] instanceof Function) {
122 				this.options[handlerName].apply(this, args);
123 			}
124 		} catch(e) {
125 			Log.error("Error encountered while invoking handler " + handlerName, e);
126 		}
127 	},
128 
129 	/**
130 	 * @param {Object} serverResponse
131 	 */
132 	_onSuccess: function(serverResponse) {
133 		try {
134 			var responseType = serverResponse.getResponseHeader("X-BBQ-ResponseType");
135 
136 			if(responseType < 0) {
137 				if(this.options.onFailure) {
138 					// have been passed a failure callback
139 					this._onFailure(serverResponse);
140 				} else {
141 					// fall back to default behaviour
142 					var code = "error" + responseType;
143 					var handler = bbq.ajax.AJAXRequest.errorHandlers[code];
144 
145 					if (handler && handler instanceof Function) {
146 						handler.call(this, this, serverResponse);
147 					} else {
148 						Log.error("Received error code " + responseType + " but have no handler to handle it.");
149 						Log.error("Either pass an onFaliure callback to this bbq.ajax.AJAXRequest or define bbq.ajax.AJAXRequest.errorHandlers['" + code + "'] = function(serverResponse) { ... };");
150 					}
151 				}
152 
153 				return;
154 			}
155 
156 			this._callHandler("onSuccess", $A(arguments));
157 		} catch(e) {
158 			Log.error("Could not invoke onSuccess handler", e);
159 		}
160 	},
161 
162 	_onFailure: function() {
163 		try {
164 			Log.error('Request to ' + this.options.method.toUpperCase() + ' ' + this.options.url + " failed");
165 			this._callHandler("onFailure", $A(arguments));
166 		} catch(e) {
167 			Log.error("Could not invoke onFaliure handler", e);
168 		}
169 	},
170 
171 	_onException: function() {
172 		try {
173 			Log.error('Request to ' + this.options.method.toUpperCase() + ' ' + this.options.url + " threw exception");
174 			this._callHandler("onException", $A(arguments));
175 		} catch(e) {
176 			Log.error("Could not invoke onException handler", e);
177 		}
178 	},
179 
180 	/**
181 	 * Checks to see if the timer has reached 0.  If so the server request has timed out.
182 	 * 
183 	 * @return	void
184 	 */
185 	_checkTimeOut: function() {
186 		if(this._timeOut == 0) {
187 			this._timedOut();
188 			clearInterval(this._interval);
189 		} else {
190 			this._timeOut--;
191 		}
192 	},
193 
194 	/**
195 	 * Shows the user a warning
196 	 * 
197 	 * @return	void
198 	 */
199 	_timedOut: function() {
200 		Log.warn("Ajax call to " + this.options.url + " timed out");
201 
202 		if(typeof(NotificationArea) != "undefined" && typeof(Language) != "undefined" && Language.get instanceof Function) {
203 			NotificationArea.stopLoad();
204 			NotificationArea.setMessage(
205 				Language.get("bbq.ajax.AJAXRequest.ajaxtimeoutheader"), 
206 				Language.get("bbq.ajax.AJAXRequest.ajaxtimeoutmessage"), 
207 				"error"
208 			);
209 			NotificationArea.setMessage(
210 				Language.get("bbq.ajax.AJAXRequest.reloadpageheader"), 
211 				Language.get("bbq.ajax.AJAXRequest.reloadpagemessage"), 
212 				"error"
213 			);
214 		}
215 	}
216 });
217 
218 /**
219  * Add custom error handlers to this map.  Error handlers should
220  * be a function with the following signature:
221  * 
222  * <pre><code class="language-javascript">
223  * function(bbq.ajax.AJAXRequest, XMLHttpRequest);
224  * </code></pre>
225  *
226  * @see bbq.ajax.AJAXRequest
227  */
228 bbq.ajax.AJAXRequest.errorHandlers = {};
229 bbq.ajax.AJAXRequest.errorHandlers["error-100"] = function(request, response) {
230 	var errorMessage = new bbq.gui.error.ServerError({
231 		url: request.options.url,
232 		args: request.options.args,
233 		serverResponse: BBQUtil.urlDecode(response.getResponseHeader("X-bbq-responseMessage"))
234 	});
235 	errorMessage.appear();
236 };
237 bbq.ajax.AJAXRequest.errorHandlers["error-99"] = function(request, response) {
238 	var errorMessage = new bbq.gui.error.NotLoggedIn();
239 	errorMessage.appear();
240 };
241