269 lines
7.3 KiB
JavaScript
269 lines
7.3 KiB
JavaScript
/*
|
|
* jQuery JSONP Core Plugin 1.1.0 (2009-10-06)
|
|
*
|
|
* http://code.google.com/p/jquery-jsonp/
|
|
*
|
|
* Copyright (c) 2009 Julian Aubourg
|
|
*
|
|
* This document is licensed as free software under the terms of the
|
|
* MIT License: http://www.opensource.org/licenses/mit-license.php
|
|
*/
|
|
(function($){
|
|
|
|
// ###################### UTILITIES ##
|
|
// Test a value is neither undefined nor null
|
|
var defined = function(v) {
|
|
return v!==undefined && v!==null;
|
|
},
|
|
// Call if defined
|
|
callIfDefined = function(method,object,parameters) {
|
|
defined(method) && method.apply(object,parameters);
|
|
},
|
|
// Let the current thread running
|
|
later = function(functor) {
|
|
setTimeout(functor,0);
|
|
},
|
|
// String constants (for better minification)
|
|
empty="",
|
|
amp="&",
|
|
qMark="?",
|
|
success = "success",
|
|
error = "error",
|
|
|
|
// Head element (for faster use)
|
|
head = $("head"),
|
|
// Page cache
|
|
pageCache = {},
|
|
|
|
// ###################### DEFAULT OPTIONS ##
|
|
xOptionsDefaults = {
|
|
//beforeSend: undefined,
|
|
//cache: false,
|
|
callback: "C",
|
|
//callbackParameter: undefined,
|
|
//complete: undefined,
|
|
//data: ""
|
|
//dataFilter: undefined,
|
|
//error: undefined,
|
|
//pageCache: false,
|
|
//success: undefined,
|
|
//timeout: 0,
|
|
url: location.href
|
|
},
|
|
|
|
// ###################### MAIN FUNCTION ##
|
|
jsonp = function(xOptions) {
|
|
|
|
// Build data with default
|
|
xOptions = $.extend({},xOptionsDefaults,xOptions);
|
|
|
|
// References to beforeSend (for better minification)
|
|
var beforeSendCallback = xOptions.beforeSend,
|
|
|
|
// Abort/done flag
|
|
done = 0;
|
|
|
|
// Put a temporary abort
|
|
xOptions.abort = function() { done = 1; };
|
|
|
|
// Call beforeSend if provided (early abort if false returned)
|
|
if (defined(beforeSendCallback) && (beforeSendCallback(xOptions,xOptions)===false || done))
|
|
return xOptions;
|
|
|
|
// References to xOptions members (for better minification)
|
|
var successCallback = xOptions.success,
|
|
completeCallback = xOptions.complete,
|
|
errorCallback = xOptions.error,
|
|
dataFilter = xOptions.dataFilter,
|
|
callbackParameter = xOptions.callbackParameter,
|
|
successCallbackName = xOptions.callback,
|
|
cacheFlag = xOptions.cache,
|
|
pageCacheFlag = xOptions.pageCache,
|
|
url = xOptions.url,
|
|
data = xOptions.data,
|
|
timeout = xOptions.timeout,
|
|
|
|
// Misc variables
|
|
splitUrl,splitData,i,j;
|
|
|
|
// Control entries
|
|
url = defined(url)?url:empty;
|
|
data = defined(data)?((typeof data)=="string"?data:$.param(data)):empty;
|
|
|
|
// Add callback parameter if provided as option
|
|
defined(callbackParameter)
|
|
&& (data += (data==empty?empty:amp)+escape(callbackParameter)+"=?");
|
|
|
|
// Add anticache parameter if needed
|
|
!cacheFlag && !pageCacheFlag
|
|
&& (data += (data==empty?empty:amp)+"_"+(new Date()).getTime()+"=");
|
|
|
|
// Search for ? in url
|
|
splitUrl = url.split(qMark);
|
|
// Also in parameters if provided
|
|
// (and merge arrays)
|
|
if (data!=empty) {
|
|
splitData = data.split(qMark);
|
|
j = splitUrl.length-1;
|
|
j && (splitUrl[j] += amp + splitData.shift());
|
|
splitUrl = splitUrl.concat(splitData);
|
|
}
|
|
// If more than 2 ? replace the last one by the callback
|
|
i = splitUrl.length-2;
|
|
i && (splitUrl[i] += successCallbackName + splitUrl.pop());
|
|
|
|
// Build the final url
|
|
var finalUrl = splitUrl.join(qMark),
|
|
|
|
// Utility function
|
|
notifySuccess = function(json) {
|
|
// Apply the data filter if provided
|
|
defined(dataFilter) && (json = dataFilter.apply(xOptions,[json]));
|
|
// Call success then complete
|
|
callIfDefined(successCallback,xOptions,[json,success]);
|
|
callIfDefined(completeCallback,xOptions,[xOptions,success]);
|
|
},
|
|
notifyError = function(type) {
|
|
// Call error then complete
|
|
callIfDefined(errorCallback,xOptions,[xOptions,type]);
|
|
callIfDefined(completeCallback,xOptions,[xOptions,type]);
|
|
},
|
|
|
|
// Get from pageCache
|
|
pageCached = pageCache[finalUrl];
|
|
|
|
// Check page cache
|
|
if (pageCacheFlag && defined(pageCached)) {
|
|
later(function() {
|
|
// If an error was cached
|
|
defined(pageCached.s)
|
|
? notifySuccess(pageCached.s)
|
|
: notifyError(error);
|
|
});
|
|
return xOptions;
|
|
}
|
|
|
|
|
|
// Create & write to the iframe (sends the request)
|
|
// We let the hand to current code to avoid
|
|
// pre-emptive callbacks
|
|
|
|
// We also install the timeout here to avoid
|
|
// timeout before the code has been dumped to the frame
|
|
// (in case of insanely short timeout values)
|
|
later(function() {
|
|
|
|
// If it has been aborted, do nothing
|
|
if (done) return;
|
|
|
|
// Create an iframe & add it to the document
|
|
var frame = $("<iframe />").appendTo(head),
|
|
|
|
// Get the iframe's window and document objects
|
|
tmp = frame[0],
|
|
window = tmp.contentWindow || tmp.contentDocument,
|
|
document = window.document,
|
|
|
|
// Declaration of cleanup function
|
|
cleanUp,
|
|
|
|
// Declaration of timer for timeout (so we can clear it eventually)
|
|
timeoutTimer,
|
|
|
|
// Error function
|
|
errorFunction = function (_,type) {
|
|
// If pure error (not timeout), cache if needed
|
|
pageCacheFlag && !defined(type) && (pageCache[finalUrl] = empty);
|
|
// Cleanup
|
|
cleanUp();
|
|
// Call error then complete
|
|
notifyError(defined(type)?type:error);
|
|
},
|
|
|
|
// Iframe variable cleaning function
|
|
removeVariable = function(varName) {
|
|
window[varName] = undefined;
|
|
try { delete window[varName]; } catch(_) {}
|
|
},
|
|
|
|
// Error callback name
|
|
errorCallbackName = successCallbackName=="E"?"X":"E";
|
|
|
|
// Control if we actually retrieved the document
|
|
if(!defined(document)) {
|
|
document = window;
|
|
window = document.getParentNode();
|
|
}
|
|
|
|
// We have to open the document before
|
|
// declaring variables in the iframe's window
|
|
// Don't ask me why, I have no clue
|
|
document.open();
|
|
|
|
// Install callbacks
|
|
window[successCallbackName] = function(json) {
|
|
// Set as treated
|
|
done = 1;
|
|
// Pagecache is needed
|
|
pageCacheFlag && (pageCache[finalUrl] = {s: json});
|
|
// Give hand back to frame
|
|
// To finish gracefully
|
|
later(function(){
|
|
// Cleanup
|
|
cleanUp();
|
|
// Call success then complete
|
|
notifySuccess(json);
|
|
});
|
|
};
|
|
|
|
window[errorCallbackName] = function(state) {
|
|
// If not treated, mark
|
|
// then give hand back to iframe
|
|
// for it to finish gracefully
|
|
(!state || state=="complete") && !done++ && later(errorFunction);
|
|
};
|
|
|
|
// Clean up function (declaration)
|
|
xOptions.abort = cleanUp = function() {
|
|
// Clear the timeout (is it exists)
|
|
clearTimeout(timeoutTimer);
|
|
// Open the iframes document & clean
|
|
document.open();
|
|
removeVariable(errorCallbackName);
|
|
removeVariable(successCallbackName);
|
|
document.write(empty);
|
|
document.close();
|
|
frame.remove();
|
|
};
|
|
|
|
// Write to the document
|
|
document.write([
|
|
'<html><head><script src="',
|
|
finalUrl,'" onload="',
|
|
errorCallbackName,'()" onreadystatechange="',
|
|
errorCallbackName,'(this.readyState)"></script></head><body onload="',
|
|
errorCallbackName,'()"></body></html>'
|
|
].join(empty)
|
|
);
|
|
|
|
// Close (makes some browsers happier)
|
|
document.close();
|
|
|
|
// If a timeout is needed, install it
|
|
timeout>0 && (timeoutTimer = setTimeout(function(){
|
|
!done && errorFunction(empty,"timeout");
|
|
},timeout));
|
|
});
|
|
|
|
return xOptions;
|
|
}
|
|
|
|
// ###################### SETUP FUNCTION ##
|
|
jsonp.setup = function(xOptions) {
|
|
$.extend(xOptionsDefaults,xOptions);
|
|
};
|
|
|
|
// ###################### INSTALL in jQuery ##
|
|
$.jsonp = jsonp;
|
|
|
|
})(jQuery); |