Be more clear about new default
[mootools.git] / Source / Request / Request.js
blob5c10524bf1d5f10a29baa894aba86485dc3f14bf
1 /*
2 ---
4 name: Request
6 description: Powerful all purpose Request Class. Uses XMLHTTPRequest.
8 license: MIT-style license.
10 requires: [Object, Element, Chain, Events, Options, Browser]
12 provides: Request
14 ...
17 (function(){
19 var empty = function(){},
20         progressSupport = ('onprogress' in new Browser.Request);
22 var Request = this.Request = new Class({
24         Implements: [Chain, Events, Options],
26         options: {/*
27                 onRequest: function(){},
28                 onLoadstart: function(event, xhr){},
29                 onProgress: function(event, xhr){},
30                 onComplete: function(){},
31                 onCancel: function(){},
32                 onSuccess: function(responseText, responseXML){},
33                 onFailure: function(xhr){},
34                 onException: function(headerName, value){},
35                 onTimeout: function(){},
36                 user: '',
37                 password: '',*/
38                 url: '',
39                 data: '',
40                 headers: {
41                         'X-Requested-With': 'XMLHttpRequest',
42                         'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
43                 },
44                 async: true,
45                 format: false,
46                 method: 'post',
47                 link: 'ignore',
48                 isSuccess: null,
49                 emulation: true,
50                 urlEncoded: true,
51                 encoding: 'utf-8',
52                 evalScripts: false,
53                 evalResponse: false,
54                 timeout: 0,
55                 noCache: false
56         },
58         initialize: function(options){
59                 this.xhr = new Browser.Request();
60                 this.setOptions(options);
61                 this.headers = this.options.headers;
62         },
64         onStateChange: function(){
65                 var xhr = this.xhr;
66                 if (xhr.readyState != 4 || !this.running) return;
67                 this.running = false;
68                 this.status = 0;
69                 Function.attempt(function(){
70                         var status = xhr.status;
71                         this.status = (status == 1223) ? 204 : status;
72                 }.bind(this));
73                 xhr.onreadystatechange = empty;
74                 if (progressSupport) xhr.onprogress = xhr.onloadstart = empty;
75                 clearTimeout(this.timer);
77                 this.response = {text: this.xhr.responseText || '', xml: this.xhr.responseXML};
78                 if (this.options.isSuccess.call(this, this.status))
79                         this.success(this.response.text, this.response.xml);
80                 else
81                         this.failure();
82         },
84         isSuccess: function(){
85                 var status = this.status;
86                 return (status >= 200 && status < 300);
87         },
89         isRunning: function(){
90                 return !!this.running;
91         },
93         processScripts: function(text){
94                 if (this.options.evalResponse || (/(ecma|java)script/).test(this.getHeader('Content-type'))) return Browser.exec(text);
95                 return text.stripScripts(this.options.evalScripts);
96         },
98         success: function(text, xml){
99                 this.onSuccess(this.processScripts(text), xml);
100         },
102         onSuccess: function(){
103                 this.fireEvent('complete', arguments).fireEvent('success', arguments).callChain();
104         },
106         failure: function(){
107                 this.onFailure();
108         },
110         onFailure: function(){
111                 this.fireEvent('complete').fireEvent('failure', this.xhr);
112         },
114         loadstart: function(event){
115                 this.fireEvent('loadstart', [event, this.xhr]);
116         },
118         progress: function(event){
119                 this.fireEvent('progress', [event, this.xhr]);
120         },
122         timeout: function(){
123                 this.fireEvent('timeout', this.xhr);
124         },
126         setHeader: function(name, value){
127                 this.headers[name] = value;
128                 return this;
129         },
131         getHeader: function(name){
132                 return Function.attempt(function(){
133                         return this.xhr.getResponseHeader(name);
134                 }.bind(this));
135         },
137         check: function(){
138                 if (!this.running) return true;
139                 switch (this.options.link){
140                         case 'cancel': this.cancel(); return true;
141                         case 'chain': this.chain(this.caller.pass(arguments, this)); return false;
142                 }
143                 return false;
144         },
146         send: function(options){
147                 if (!this.check(options)) return this;
149                 this.options.isSuccess = this.options.isSuccess || this.isSuccess;
150                 this.running = true;
152                 var type = typeOf(options);
153                 if (type == 'string' || type == 'element') options = {data: options};
155                 var old = this.options;
156                 options = Object.append({data: old.data, url: old.url, method: old.method}, options);
157                 var data = options.data, url = String(options.url), method = options.method.toLowerCase();
159                 switch (typeOf(data)){
160                         case 'element': data = document.id(data).toQueryString(); break;
161                         case 'object': case 'hash': data = Object.toQueryString(data);
162                 }
164                 if (this.options.format){
165                         var format = 'format=' + this.options.format;
166                         data = (data) ? format + '&' + data : format;
167                 }
169                 if (this.options.emulation && !['get', 'post'].contains(method)){
170                         var _method = '_method=' + method;
171                         data = (data) ? _method + '&' + data : _method;
172                         method = 'post';
173                 }
175                 if (this.options.urlEncoded && ['post', 'put'].contains(method)){
176                         var encoding = (this.options.encoding) ? '; charset=' + this.options.encoding : '';
177                         this.headers['Content-type'] = 'application/x-www-form-urlencoded' + encoding;
178                 }
180                 if (!url) url = document.location.pathname;
182                 var trimPosition = url.lastIndexOf('/');
183                 if (trimPosition > -1 && (trimPosition = url.indexOf('#')) > -1) url = url.substr(0, trimPosition);
185                 if (this.options.noCache)
186                         url += (url.indexOf('?') > -1 ? '&' : '?') + String.uniqueID();
188                 if (data && (method == 'get' || method == 'delete')){
189                         url += (url.indexOf('?') > -1 ? '&' : '?') + data;
190                         data = null;
191                 }
193                 var xhr = this.xhr;
194                 if (progressSupport){
195                         xhr.onloadstart = this.loadstart.bind(this);
196                         xhr.onprogress = this.progress.bind(this);
197                 }
199                 xhr.open(method.toUpperCase(), url, this.options.async, this.options.user, this.options.password);
200                 if (this.options.user && 'withCredentials' in xhr) xhr.withCredentials = true;
202                 xhr.onreadystatechange = this.onStateChange.bind(this);
204                 Object.each(this.headers, function(value, key){
205                         try {
206                                 xhr.setRequestHeader(key, value);
207                         } catch (e){
208                                 this.fireEvent('exception', [key, value]);
209                         }
210                 }, this);
212                 this.fireEvent('request');
213                 xhr.send(data);
214                 if (!this.options.async) this.onStateChange();
215                 else if (this.options.timeout) this.timer = this.timeout.delay(this.options.timeout, this);
216                 return this;
217         },
219         cancel: function(){
220                 if (!this.running) return this;
221                 this.running = false;
222                 var xhr = this.xhr;
223                 xhr.abort();
224                 clearTimeout(this.timer);
225                 xhr.onreadystatechange = empty;
226                 if (progressSupport) xhr.onprogress = xhr.onloadstart = empty;
227                 this.xhr = new Browser.Request();
228                 this.fireEvent('cancel');
229                 return this;
230         }
234 var methods = {};
235 ['get', 'post', 'put', 'delete', 'GET', 'POST', 'PUT', 'DELETE'].each(function(method){
236         methods[method] = function(data){
237                 var object = {
238                         method: method
239                 };
240                 if (data != null) object.data = data;
241                 return this.send(object);
242         };
245 Request.implement(methods);
247 Element.Properties.send = {
249         set: function(options){
250                 var send = this.get('send').cancel();
251                 send.setOptions(options);
252                 return this;
253         },
255         get: function(){
256                 var send = this.retrieve('send');
257                 if (!send){
258                         send = new Request({
259                                 data: this, link: 'cancel', method: this.get('method') || 'post', url: this.get('action')
260                         });
261                         this.store('send', send);
262                 }
263                 return send;
264         }
268 Element.implement({
270         send: function(url){
271                 var sender = this.get('send');
272                 sender.send({data: this, url: url || sender.options.url});
273                 return this;
274         }
278 })();