source: Dev/trunk/src/client/dojo/request/script.js @ 529

Last change on this file since 529 was 483, checked in by hendrikvanantwerpen, 11 years ago

Added Dojo 1.9.3 release.

File size: 6.2 KB
Line 
1define([
2        'module',
3        './watch',
4        './util',
5        '../_base/array',
6        '../_base/lang',
7        '../on',
8        '../dom',
9        '../dom-construct',
10        '../has',
11        '../_base/window'/*=====,
12        '../request',
13        '../_base/declare' =====*/
14], function(module, watch, util, array, lang, on, dom, domConstruct, has, win/*=====, request, declare =====*/){
15        has.add('script-readystatechange', function(global, document){
16                var script = document.createElement('script');
17                return typeof script['onreadystatechange'] !== 'undefined' &&
18                        (typeof global['opera'] === 'undefined' || global['opera'].toString() !== '[object Opera]');
19        });
20
21        var mid = module.id.replace(/[\/\.\-]/g, '_'),
22                counter = 0,
23                loadEvent = has('script-readystatechange') ? 'readystatechange' : 'load',
24                readyRegExp = /complete|loaded/,
25                callbacks = this[mid + '_callbacks'] = {},
26                deadScripts = [];
27
28        function attach(id, url, frameDoc){
29                var doc = (frameDoc || win.doc),
30                        element = doc.createElement('script');
31
32                element.type = 'text/javascript';
33                element.src = url;
34                element.id = id;
35                element.async = true;
36                element.charset = 'utf-8';
37
38                return doc.getElementsByTagName('head')[0].appendChild(element);
39        }
40
41        function remove(id, frameDoc, cleanup){
42                domConstruct.destroy(dom.byId(id, frameDoc));
43
44                if(callbacks[id]){
45                        if(cleanup){
46                                // set callback to a function that deletes itself so requests that
47                                // are in-flight don't error out when returning and also
48                                // clean up after themselves
49                                callbacks[id] = function(){
50                                        delete callbacks[id];
51                                };
52                        }else{
53                                delete callbacks[id];
54                        }
55                }
56        }
57
58        function _addDeadScript(dfd){
59                // Be sure to check ioArgs because it can dynamically change in the dojox/io plugins.
60                // See http://bugs.dojotoolkit.org/ticket/15890.
61                var options = dfd.response.options,
62                        frameDoc = options.ioArgs ? options.ioArgs.frameDoc : options.frameDoc;
63
64                deadScripts.push({ id: dfd.id, frameDoc: frameDoc });
65
66                if(options.ioArgs){
67                        options.ioArgs.frameDoc = null;
68                }
69                options.frameDoc = null;
70        }
71
72        function canceler(dfd, response){
73                if(dfd.canDelete){
74                        //For timeouts and cancels, remove the script element immediately to
75                        //avoid a response from it coming back later and causing trouble.
76                        script._remove(dfd.id, response.options.frameDoc, true);
77                }
78        }
79        function isValid(response){
80                //Do script cleanup here. We wait for one inflight pass
81                //to make sure we don't get any weird things by trying to remove a script
82                //tag that is part of the call chain (IE 6 has been known to
83                //crash in that case).
84                if(deadScripts && deadScripts.length){
85                        array.forEach(deadScripts, function(_script){
86                                script._remove(_script.id, _script.frameDoc);
87                                _script.frameDoc = null;
88                        });
89                        deadScripts = [];
90                }
91
92                return response.options.jsonp ? !response.data : true;
93        }
94        function isReadyScript(response){
95                return !!this.scriptLoaded;
96        }
97        function isReadyCheckString(response){
98                var checkString = response.options.checkString;
99
100                return checkString && eval('typeof(' + checkString + ') !== "undefined"');
101        }
102        function handleResponse(response, error){
103                if(this.canDelete){
104                        _addDeadScript(this);
105                }
106                if(error){
107                        this.reject(error);
108                }else{
109                        this.resolve(response);
110                }
111        }
112
113        function script(url, options, returnDeferred){
114                var response = util.parseArgs(url, util.deepCopy({}, options));
115                url = response.url;
116                options = response.options;
117
118                var dfd = util.deferred(
119                        response,
120                        canceler,
121                        isValid,
122                        options.jsonp ? null : (options.checkString ? isReadyCheckString : isReadyScript),
123                        handleResponse
124                );
125
126                lang.mixin(dfd, {
127                        id: mid + (counter++),
128                        canDelete: false
129                });
130
131                if(options.jsonp){
132                        var queryParameter = new RegExp('[?&]' + options.jsonp + '=');
133                        if(!queryParameter.test(url)){
134                                url += (~url.indexOf('?') ? '&' : '?') +
135                                        options.jsonp + '=' +
136                                        (options.frameDoc ? 'parent.' : '') +
137                                        mid + '_callbacks.' + dfd.id;
138                        }
139
140                        dfd.canDelete = true;
141                        callbacks[dfd.id] = function(json){
142                                response.data = json;
143                                dfd.handleResponse(response);
144                        };
145                }
146
147                if(util.notify){
148                        util.notify.emit('send', response, dfd.promise.cancel);
149                }
150
151                if(!options.canAttach || options.canAttach(dfd)){
152                        var node = script._attach(dfd.id, url, options.frameDoc);
153
154                        if(!options.jsonp && !options.checkString){
155                                var handle = on(node, loadEvent, function(evt){
156                                        if(evt.type === 'load' || readyRegExp.test(node.readyState)){
157                                                handle.remove();
158                                                dfd.scriptLoaded = evt;
159                                        }
160                                });
161                        }
162                }
163
164                watch(dfd);
165
166                return returnDeferred ? dfd : dfd.promise;
167        }
168        script.get = script;
169        /*=====
170        script = function(url, options){
171                // summary:
172                //              Sends a request using a script element with the given URL and options.
173                // url: String
174                //              URL to request
175                // options: dojo/request/script.__Options?
176                //              Options for the request.
177                // returns: dojo/request.__Promise
178        };
179        script.__BaseOptions = declare(request.__BaseOptions, {
180                // jsonp: String?
181                //              The URL parameter name that indicates the JSONP callback string.
182                //              For instance, when using Yahoo JSONP calls it is normally,
183                //              jsonp: "callback". For AOL JSONP calls it is normally
184                //              jsonp: "c".
185                // checkString: String?
186                //              A string of JavaScript that when evaluated like so:
187                //              "typeof(" + checkString + ") != 'undefined'"
188                //              being true means that the script fetched has been loaded.
189                //              Do not use this if doing a JSONP type of call (use `jsonp` instead).
190                // frameDoc: Document?
191                //              The Document object of a child iframe. If this is passed in, the script
192                //              will be attached to that document. This can be helpful in some comet long-polling
193                //              scenarios with Firefox and Opera.
194        });
195        script.__MethodOptions = declare(null, {
196                // method: String?
197                //              This option is ignored. All requests using this transport are
198                //              GET requests.
199        });
200        script.__Options = declare([script.__BaseOptions, script.__MethodOptions]);
201
202        script.get = function(url, options){
203                // summary:
204                //              Send an HTTP GET request using a script element with the given URL and options.
205                // url: String
206                //              URL to request
207                // options: dojo/request/script.__BaseOptions?
208                //              Options for the request.
209                // returns: dojo/request.__Promise
210        };
211        =====*/
212
213        // TODO: Remove in 2.0
214        script._attach = attach;
215        script._remove = remove;
216        script._callbacksProperty = mid + '_callbacks';
217
218        return script;
219});
Note: See TracBrowser for help on using the repository browser.