[483] | 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 | }); |
---|