1 | define([ |
---|
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 | }); |
---|