]>
Commit | Line | Data |
---|---|---|
f0cfe83e AD |
1 | define("dojo/request/script", [ |
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 += queryParameter + | |
135 | (options.frameDoc ? 'parent.' : '') + | |
136 | mid + '_callbacks.' + dfd.id; | |
137 | } | |
138 | ||
139 | dfd.canDelete = true; | |
140 | callbacks[dfd.id] = function(json){ | |
141 | response.data = json; | |
142 | dfd.handleResponse(response); | |
143 | }; | |
144 | } | |
145 | ||
146 | if(util.notify){ | |
147 | util.notify.emit('send', response, dfd.promise.cancel); | |
148 | } | |
149 | ||
150 | if(!options.canAttach || options.canAttach(dfd)){ | |
151 | var node = script._attach(dfd.id, url, options.frameDoc); | |
152 | ||
153 | if(!options.jsonp && !options.checkString){ | |
154 | var handle = on(node, loadEvent, function(evt){ | |
155 | if(evt.type === 'load' || readyRegExp.test(node.readyState)){ | |
156 | handle.remove(); | |
157 | dfd.scriptLoaded = evt; | |
158 | } | |
159 | }); | |
160 | } | |
161 | } | |
162 | ||
163 | watch(dfd); | |
164 | ||
165 | return returnDeferred ? dfd : dfd.promise; | |
166 | } | |
167 | script.get = script; | |
168 | /*===== | |
169 | script = function(url, options){ | |
170 | // summary: | |
171 | // Sends a request using a script element with the given URL and options. | |
172 | // url: String | |
173 | // URL to request | |
174 | // options: dojo/request/script.__Options? | |
175 | // Options for the request. | |
176 | // returns: dojo/request.__Promise | |
177 | }; | |
178 | script.__BaseOptions = declare(request.__BaseOptions, { | |
179 | // jsonp: String? | |
180 | // The URL parameter name that indicates the JSONP callback string. | |
181 | // For instance, when using Yahoo JSONP calls it is normally, | |
182 | // jsonp: "callback". For AOL JSONP calls it is normally | |
183 | // jsonp: "c". | |
184 | // checkString: String? | |
185 | // A string of JavaScript that when evaluated like so: | |
186 | // "typeof(" + checkString + ") != 'undefined'" | |
187 | // being true means that the script fetched has been loaded. | |
188 | // Do not use this if doing a JSONP type of call (use `jsonp` instead). | |
189 | // frameDoc: Document? | |
190 | // The Document object of a child iframe. If this is passed in, the script | |
191 | // will be attached to that document. This can be helpful in some comet long-polling | |
192 | // scenarios with Firefox and Opera. | |
193 | }); | |
194 | script.__MethodOptions = declare(null, { | |
195 | // method: String? | |
196 | // This option is ignored. All requests using this transport are | |
197 | // GET requests. | |
198 | }); | |
199 | script.__Options = declare([script.__BaseOptions, script.__MethodOptions]); | |
200 | ||
201 | script.get = function(url, options){ | |
202 | // summary: | |
203 | // Send an HTTP GET request using a script element with the given URL and options. | |
204 | // url: String | |
205 | // URL to request | |
206 | // options: dojo/request/script.__BaseOptions? | |
207 | // Options for the request. | |
208 | // returns: dojo/request.__Promise | |
209 | }; | |
210 | =====*/ | |
211 | ||
212 | // TODO: Remove in 2.0 | |
213 | script._attach = attach; | |
214 | script._remove = remove; | |
215 | script._callbacksProperty = mid + '_callbacks'; | |
216 | ||
217 | return script; | |
218 | }); |