1 // ==========================================================================
  2 // Project:   The M-Project - Mobile HTML5 Application Framework
  3 // Copyright: (c) 2010 M-Way Solutions GmbH. All rights reserved.
  4 //            (c) 2011 panacoda GmbH. All rights reserved.
  5 // Creator:   Sebastian
  6 // Date:      28.10.2010
  7 // License:   Dual licensed under the MIT or GPL Version 2 licenses.
  8 //            http://github.com/mwaylabs/The-M-Project/blob/master/MIT-LICENSE
  9 //            http://github.com/mwaylabs/The-M-Project/blob/master/GPL-LICENSE
 10 // ==========================================================================
 11 
 12 m_require('core/foundation/object.js');
 13 
 14 /**
 15  * @class
 16  *
 17  * The root class for every request. Makes ajax requests. Is used e.g. for querying REST web services.
 18  * First M.Request.init needs to be called, then send.
 19  *
 20  * @extends M.Object
 21  */
 22 M.Request = M.Object.extend(
 23 /** @scope M.Request.prototype */ {
 24 
 25     /**
 26      * The type of this object.
 27      *
 28      * @type String
 29      */
 30     type: 'M.Request',
 31 
 32     /**
 33      * The HTTP method to use.
 34      *
 35      * Defaults to GET.
 36      *
 37      * @type String
 38      */
 39     method: 'GET',
 40 
 41     /**
 42      * The URL this request is sent to.
 43      *
 44      * @type String
 45      */
 46     url: null,
 47 
 48     /**
 49      * Sends the request asynchronously instead of blocking the browser.
 50      * You should almost always make requests asynchronous. You can change this
 51      * options with the async() helper option (or simply set it directly).
 52      *
 53      * Defaults to YES.
 54      *
 55      * @type Boolean
 56      */
 57     isAsync: YES,
 58 
 59 
 60     /**
 61      * Processes the request and response as JSON if possible.
 62      *
 63      * Defaults to NO.
 64      *
 65      * @type Boolean
 66      */
 67     isJSON: NO,
 68 
 69     /**
 70      * Optional timeout value of the request in milliseconds.
 71      *
 72      * @type Number
 73      */
 74     timeout: null,
 75 
 76     /**
 77      * If set, contains the request's callbacks in sub objects. There are three
 78      * possible callbacks that can be used:
 79      *
 80      *   - beforeSend
 81      *   - success
 82      *   - error
 83      *
 84      * A callback object consists of at least an action but can also specify a
 85      * target object that determines the scope for that action. If a target is
 86      * specified, the action can either  be a string (the name if a method within
 87      * the specified scope) or a function. If there is no target specified, the
 88      * action must be a function. So a success callback could e.g. look like:
 89      *
 90      *   callbacks: {
 91      *     success: {
 92      *       target: MyApp.MyController,
 93      *       action: 'successCallback'
 94      *     }
 95      *   }
 96      *
 97      * Or it could look like:
 98      *
 99      *   callbacks: {
100      *     success: {
101      *       target: MyApp.MyController,
102      *       action: function() {
103      *         // do something...
104      *       }
105      *     }
106      *   }
107      *
108      * Depending on the type of callback, there are different parameters, that
109      * are automatically passed to the callback:
110      *
111      *   - beforeSend(request)
112      *   - success(data, msg, request)
113      *   - error(request, msg)
114      *
115      * For further information about that, take a look at the internal callbacks
116      * of M.Request:
117      *
118      *   - internalBeforeSend
119      *   - internalOnSuccess
120      *   - internalOnError
121      *
122      * @type Object
123      */
124     callbacks: null,
125 
126     /**
127      * This property can be used to specify whether or not to cache the request.
128      * Setting this to YES will set the 'Cache-Control' property of the request
129      * header to 'no-cache'.
130      *
131      * @Boolean
132      */
133     sendNoCacheHeader: YES,
134 
135     /**
136      * This property can be used to specify whether or not to send a timestamp
137      * along with the request in order to make every request unique. Since some
138      * browsers (e.g. Android) automatically cache identical requests, setting
139      * this property to YES will add an additional parameter to the request,
140      * containing the current timestamp.
141      *
142      * So if you have any trouble with caching of requests, try setting this to
143      * YES. But note, that it might also cause trouble on the server-side if they
144      * do not expect this additional parameter.
145      *
146      * @Boolean
147      */
148     sendTimestamp: NO,
149 
150     /**
151      * The data body of the request.
152      *
153      * @type String, Object
154      */
155     data: null,
156 
157     /**
158      * Holds the jQuery request object
159      *
160      * @type Object
161      */
162     request: null,
163 
164     /**
165      * Initializes a request. Sets the parameter of this request object with the passed values.
166      *
167      * @param {Object} obj The parameter object. Includes:
168      * * method: the http method to use, e.g. 'POST'
169      * * url: the request url, e.g. 'twitter.com/search.json' (needs a proxy to be set because of Same-Origin-Policy)
170      * * isAsync: defines whether request should be made async or not. defaults to YES. Should be YES.
171      * * isJSON: defines whether to process request and response as JSON
172      * * timout: defines timeout in milliseconds
173      * * data: the data to be transmitted
174      * * beforeSend: callback that is called before request is sent
175      * * onError: callback that is called when an error occured
176      * * onSuccess: callback that is called when request was successful
177      */
178     init: function(obj){
179         obj = obj ? obj : {};
180         return this.extend({
181             method: obj['method'] ? obj['method'] : this.method,
182             url: obj['url'] ? obj['url'] : this.url,
183             isAsync: obj['isAsync'] ? obj['isAsync'] : this.isAsync,
184             isJSON: obj['isJSON'] ? obj['isJSON'] : this.isJSON,
185             timeout: obj['timeout'] ? obj['timeout'] : this.timeout,
186             data: obj['data'] ? obj['data'] : this.data,
187             callbacks: obj['callbacks'],
188             sendNoCacheHeader: obj['sendNoCacheHeader'],
189             sendTimestamp: obj['sendTimestamp'],
190             beforeSend: obj['beforeSend'] ? obj['beforeSend'] : this.beforeSend,
191             onError: obj['onError'] ? obj['onError'] : this.onError,
192             onSuccess: obj['onSuccess'] ? obj['onSuccess'] : this.onSuccess
193         });
194     },
195 
196     /**
197      * A pre-callback that is called right before the request is sent.
198      *
199      * Note: This method will be removed with v1.0! Use the callbacks
200      * property instead.
201      *
202      * @deprecated
203      * @param {Object} request The XMLHttpRequest object.
204      */
205     beforeSend: function(request){},
206 
207     /**
208      * The callback to be called if the request failed.
209      *
210      * Note: This method will be removed with v1.0! Use the callbacks
211      * property instead.
212      *
213      * @deprecated
214      * @param {Object} request The XMLHttpRequest object.
215      * @param {String} msg The error message.
216      */
217     onError: function(request, msg){},
218 
219     /**
220      * The callback to be called if the request succeeded.
221      *
222      * Note: This method will be removed with v1.0! Use the callbacks
223      * property instead.
224      *
225      * @deprecated
226      * @param {String|Object} data The data returned from the server.
227      * @param {String} msg A String describing the status.
228      * @param {Object} request The XMLHttpRequest object.
229      */
230     onSuccess: function(data, msg, request){},
231 
232     /**
233      * This method is an internal callback that is called right before a
234      * request is send.
235      *
236      * @param {Object} request The XMLHttpRequest object.
237      */
238     internalBeforeSend: function(request){
239         if(this.sendNoCacheHeader) {
240             request.setRequestHeader('Cache-Control', 'no-cache');
241         }
242 
243         if(!this.callbacks && this.beforeSend) {
244             this.beforeSend(request);
245         }
246 
247         if(this.callbacks && this.callbacks['beforeSend'] && M.EventDispatcher.checkHandler(this.callbacks['beforeSend'])) {
248             M.EventDispatcher.callHandler(this.callbacks['beforeSend'], null, NO, [request]);
249         }
250     },
251 
252     /**
253      * This method is an internal callback that is called if a request
254      * failed.
255      *
256      * @param {Object} request The XMLHttpRequest object.
257      * @param {String} msg The error message.
258      */
259     internalOnError: function(request, msg){
260         if(!this.callbacks && this.onError) {
261             this.onError(request, msg);
262         }
263 
264         if(this.callbacks && this.callbacks['error'] && M.EventDispatcher.checkHandler(this.callbacks['error'])) {
265             M.EventDispatcher.callHandler(this.callbacks['error'], null, NO, [request, msg]);
266         }
267     },
268 
269     /**
270      * This method is an internal callback that is called if the request
271      * succeeded.
272      *
273      * @param {String|Object} data The data returned from the server.
274      * @param {String} msg A String describing the status.
275      * @param {Object} request The XMLHttpRequest object.
276      */
277     internalOnSuccess: function(data, msg, request){
278         if(!this.callbacks && this.onSuccess) {
279             this.onSuccess(data, msg, request);
280         }
281 
282         if(this.callbacks && this.callbacks['success'] && M.EventDispatcher.checkHandler(this.callbacks['success'])) {
283             M.EventDispatcher.callHandler(this.callbacks['success'], null, NO, [data, msg, request]);
284         }
285     },
286 
287     /**
288      * Sends an Ajax request by using jQuery's $.ajax().
289      * Needs init first!
290      */
291     send: function(){
292         this.request = $.ajax({
293             type: this.method,
294             url: this.url,
295             async: this.isAsync,
296             dataType: this.isJSON ? 'json' : 'text',
297             contentType: this.isJSON ? 'application/json' : 'application/x-www-form-urlencoded',
298             timeout: this.timeout,
299             data: this.data ? this.data : '',
300             context: this,
301             beforeSend: this.internalBeforeSend,
302             success: this.internalOnSuccess,
303             error: this.internalOnError,
304             cache: !this.sendTimestamp
305         });
306     },
307 
308     /**
309      * Aborts this request. Delegate to jQuery
310      *
311      * @return Boolean Indicating whether request exists and is aborted or not
312      */
313     abort: function() {
314         if(this.request) {
315             this.request.abort();
316             return YES;
317         }
318         return NO;
319     }
320 
321 });