You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
237 lines
6.6 KiB
237 lines
6.6 KiB
define([ |
|
'module', |
|
'./watch', |
|
'./util', |
|
'../_base/kernel', |
|
'../_base/array', |
|
'../_base/lang', |
|
'../on', |
|
'../dom', |
|
'../dom-construct', |
|
'../has', |
|
'../_base/window'/*=====, |
|
'../request', |
|
'../_base/declare' =====*/ |
|
], function(module, watch, util, kernel, array, lang, on, dom, domConstruct, has, win/*=====, request, declare =====*/){ |
|
has.add('script-readystatechange', function(global, document){ |
|
var script = document.createElement('script'); |
|
return typeof script['onreadystatechange'] !== 'undefined' && |
|
(typeof global['opera'] === 'undefined' || global['opera'].toString() !== '[object Opera]'); |
|
}); |
|
|
|
var mid = module.id.replace(/[\/\.\-]/g, '_'), |
|
counter = 0, |
|
loadEvent = has('script-readystatechange') ? 'readystatechange' : 'load', |
|
readyRegExp = /complete|loaded/, |
|
callbacks = kernel.global[mid + '_callbacks'] = {}, |
|
deadScripts = []; |
|
|
|
function attach(id, url, frameDoc, errorHandler){ |
|
var doc = (frameDoc || win.doc), |
|
element = doc.createElement('script'); |
|
|
|
if (errorHandler) { |
|
on.once(element, 'error', errorHandler); |
|
} |
|
|
|
element.type = 'text/javascript'; |
|
try { |
|
element.src = url; |
|
} catch(err) { |
|
errorHandler && errorHandler(element); |
|
} |
|
|
|
element.id = id; |
|
element.async = true; |
|
element.charset = 'utf-8'; |
|
|
|
return doc.getElementsByTagName('head')[0].appendChild(element); |
|
} |
|
|
|
function remove(id, frameDoc, cleanup){ |
|
domConstruct.destroy(dom.byId(id, frameDoc)); |
|
|
|
if(callbacks[id]){ |
|
if(cleanup){ |
|
// set callback to a function that deletes itself so requests that |
|
// are in-flight don't error out when returning and also |
|
// clean up after themselves |
|
callbacks[id] = function(){ |
|
delete callbacks[id]; |
|
}; |
|
}else{ |
|
delete callbacks[id]; |
|
} |
|
} |
|
} |
|
|
|
function _addDeadScript(dfd){ |
|
// Be sure to check ioArgs because it can dynamically change in the dojox/io plugins. |
|
// See http://bugs.dojotoolkit.org/ticket/15890. |
|
var options = dfd.response.options, |
|
frameDoc = options.ioArgs ? options.ioArgs.frameDoc : options.frameDoc; |
|
|
|
deadScripts.push({ id: dfd.id, frameDoc: frameDoc }); |
|
|
|
if(options.ioArgs){ |
|
options.ioArgs.frameDoc = null; |
|
} |
|
options.frameDoc = null; |
|
} |
|
|
|
function canceler(dfd, response){ |
|
if(dfd.canDelete){ |
|
//For timeouts and cancels, remove the script element immediately to |
|
//avoid a response from it coming back later and causing trouble. |
|
script._remove(dfd.id, response.options.frameDoc, true); |
|
} |
|
} |
|
function isValid(response){ |
|
//Do script cleanup here. We wait for one inflight pass |
|
//to make sure we don't get any weird things by trying to remove a script |
|
//tag that is part of the call chain (IE 6 has been known to |
|
//crash in that case). |
|
if(deadScripts && deadScripts.length){ |
|
array.forEach(deadScripts, function(_script){ |
|
script._remove(_script.id, _script.frameDoc); |
|
_script.frameDoc = null; |
|
}); |
|
deadScripts = []; |
|
} |
|
|
|
return response.options.jsonp ? !response.data : true; |
|
} |
|
function isReadyScript(response){ |
|
return !!this.scriptLoaded; |
|
} |
|
function isReadyCheckString(response){ |
|
var checkString = response.options.checkString; |
|
|
|
return checkString && eval('typeof(' + checkString + ') !== "undefined"'); |
|
} |
|
function handleResponse(response, error){ |
|
if(this.canDelete){ |
|
_addDeadScript(this); |
|
} |
|
if(error){ |
|
this.reject(error); |
|
}else{ |
|
this.resolve(response); |
|
} |
|
} |
|
|
|
function script(url, options, returnDeferred){ |
|
var response = util.parseArgs(url, util.deepCopy({}, options)); |
|
url = response.url; |
|
options = response.options; |
|
|
|
var dfd = util.deferred( |
|
response, |
|
canceler, |
|
isValid, |
|
options.jsonp ? null : (options.checkString ? isReadyCheckString : isReadyScript), |
|
handleResponse |
|
); |
|
|
|
lang.mixin(dfd, { |
|
id: mid + (counter++), |
|
canDelete: false |
|
}); |
|
|
|
if(options.jsonp){ |
|
var queryParameter = new RegExp('[?&]' + options.jsonp + '='); |
|
if(!queryParameter.test(url)){ |
|
url += (~url.indexOf('?') ? '&' : '?') + |
|
options.jsonp + '=' + |
|
(options.frameDoc ? 'parent.' : '') + |
|
mid + '_callbacks.' + dfd.id; |
|
} |
|
|
|
dfd.canDelete = true; |
|
callbacks[dfd.id] = function(json){ |
|
response.data = json; |
|
dfd.handleResponse(response); |
|
}; |
|
} |
|
|
|
if(util.notify){ |
|
util.notify.emit('send', response, dfd.promise.cancel); |
|
} |
|
|
|
if(!options.canAttach || options.canAttach(dfd)){ |
|
var node = script._attach(dfd.id, url, options.frameDoc, function (error) { |
|
if (!(error instanceof Error)) { |
|
var newError = new Error('Error loading ' + (error.target ? error.target.src : 'script')); |
|
newError.source = error; |
|
error = newError; |
|
} |
|
dfd.reject(error); |
|
script._remove(dfd.id, options.frameDoc, true); |
|
}); |
|
|
|
if(!options.jsonp && !options.checkString){ |
|
var handle = on(node, loadEvent, function(evt){ |
|
if(evt.type === 'load' || readyRegExp.test(node.readyState)){ |
|
handle.remove(); |
|
dfd.scriptLoaded = evt; |
|
} |
|
}); |
|
} |
|
} |
|
|
|
watch(dfd); |
|
|
|
return returnDeferred ? dfd : dfd.promise; |
|
} |
|
script.get = script; |
|
/*===== |
|
script = function(url, options){ |
|
// summary: |
|
// Sends a request using a script element with the given URL and options. |
|
// url: String |
|
// URL to request |
|
// options: dojo/request/script.__Options? |
|
// Options for the request. |
|
// returns: dojo/request.__Promise |
|
}; |
|
script.__BaseOptions = declare(request.__BaseOptions, { |
|
// jsonp: String? |
|
// The URL parameter name that indicates the JSONP callback string. |
|
// For instance, when using Yahoo JSONP calls it is normally, |
|
// jsonp: "callback". For AOL JSONP calls it is normally |
|
// jsonp: "c". |
|
// checkString: String? |
|
// A string of JavaScript that when evaluated like so: |
|
// "typeof(" + checkString + ") != 'undefined'" |
|
// being true means that the script fetched has been loaded. |
|
// Do not use this if doing a JSONP type of call (use `jsonp` instead). |
|
// frameDoc: Document? |
|
// The Document object of a child iframe. If this is passed in, the script |
|
// will be attached to that document. This can be helpful in some comet long-polling |
|
// scenarios with Firefox and Opera. |
|
}); |
|
script.__MethodOptions = declare(null, { |
|
// method: String? |
|
// This option is ignored. All requests using this transport are |
|
// GET requests. |
|
}); |
|
script.__Options = declare([script.__BaseOptions, script.__MethodOptions]); |
|
|
|
script.get = function(url, options){ |
|
// summary: |
|
// Send an HTTP GET request using a script element with the given URL and options. |
|
// url: String |
|
// URL to request |
|
// options: dojo/request/script.__BaseOptions? |
|
// Options for the request. |
|
// returns: dojo/request.__Promise |
|
}; |
|
=====*/ |
|
|
|
// TODO: Remove in 2.0 |
|
script._attach = attach; |
|
script._remove = remove; |
|
script._callbacksProperty = mid + '_callbacks'; |
|
|
|
return script; |
|
});
|
|
|