/* * jQuery JSONP Core Plugin 2.3.1 (2012-05-16) * * https://github.com/jaubourg/jquery-jsonp * * Copyright (c) 2012 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 ## // Noop function noop() { } // Generic callback function genericCallback( data ) { lastValue = [ data ]; } // Call if defined function callIfDefined( method , object , parameters , returnFlag ) { try { returnFlag = method && method.apply( object.context || object , parameters ); } catch( _ ) { returnFlag = !1; } return returnFlag; } // Give joining character given url function qMarkOrAmp( url ) { return /\?/ .test( url ) ? "&" : "?"; } var // String constants (for better minification) STR_ASYNC = "async", STR_CHARSET = "charset", STR_EMPTY = "", STR_ERROR = "error", STR_INSERT_BEFORE = "insertBefore", STR_JQUERY_JSONP = "_jqjsp", STR_ON = "on", STR_ON_CLICK = STR_ON + "click", STR_ON_ERROR = STR_ON + STR_ERROR, STR_ON_LOAD = STR_ON + "load", STR_ON_READY_STATE_CHANGE = STR_ON + "readystatechange", STR_READY_STATE = "readyState", STR_REMOVE_CHILD = "removeChild", STR_SCRIPT_TAG = "<script>", STR_SUCCESS = "success", STR_TIMEOUT = "timeout", // Window win = window, // Deferred Deferred = $.Deferred, // Head element head = $( "head" )[ 0 ] || document.documentElement, // Page cache pageCache = {}, // Counter count = 0, // Last returned value lastValue, // ###################### DEFAULT OPTIONS ## xOptionsDefaults = { //beforeSend: undefined, //cache: false, callback: STR_JQUERY_JSONP, //callbackParameter: undefined, //charset: undefined, //complete: undefined, //context: undefined, //data: "", //dataFilter: undefined, //error: undefined, //pageCache: false, //success: undefined, //timeout: 0, //traditional: false, url: location.href }, // opera demands sniffing :/ opera = win.opera; // ###################### MAIN FUNCTION ## function jsonp( xOptions ) { // Build data with default xOptions = $.extend( {} , xOptionsDefaults , xOptions ); // References to xOptions members (for better minification) var successCallback = xOptions.success, errorCallback = xOptions.error, completeCallback = xOptions.complete, dataFilter = xOptions.dataFilter, callbackParameter = xOptions.callbackParameter, successCallbackName = xOptions.callback, cacheFlag = xOptions.cache, pageCacheFlag = xOptions.pageCache, charset = xOptions.charset, url = xOptions.url, data = xOptions.data, timeout = xOptions.timeout, pageCached, // Abort/done flag done = 0, // Life-cycle functions cleanUp = noop, // Support vars supportOnload, supportOnreadystatechange, // Request execution vars firstChild, script, scriptAfter, timeoutTimer; // If we have Deferreds: // - substitute callbacks // - promote xOptions to a promise Deferred && Deferred(function( defer ) { defer.done( successCallback ).fail( errorCallback ); successCallback = defer.resolve; errorCallback = defer.reject; }).promise( xOptions ); // Create the abort method xOptions.abort = function() { !( done++ ) && cleanUp(); }; // Call beforeSend if provided (early abort if false returned) if ( callIfDefined( xOptions.beforeSend, xOptions , [ xOptions ] ) === !1 || done ) { return xOptions; } // Control entries url = url || STR_EMPTY; data = data ? ( (typeof data) == "string" ? data : $.param( data , xOptions.traditional ) ) : STR_EMPTY; // Build final url url += data ? ( qMarkOrAmp( url ) + data ) : STR_EMPTY; // Add callback parameter if provided as option callbackParameter && ( url += qMarkOrAmp( url ) + encodeURIComponent( callbackParameter ) + "=?" ); // Add anticache parameter if needed !cacheFlag && !pageCacheFlag && ( url += qMarkOrAmp( url ) + "_" + ( new Date() ).getTime() + "=" ); // Replace last ? by callback parameter url = url.replace( /=\?(&|$)/ , "=" + successCallbackName + "$1" ); // Success notifier function notifySuccess( json ) { if ( !( done++ ) ) { cleanUp(); // Pagecache if needed pageCacheFlag && ( pageCache [ url ] = { s: [ json ] } ); // Apply the data filter if provided dataFilter && ( json = dataFilter.apply( xOptions , [ json ] ) ); // Call success then complete callIfDefined( successCallback , xOptions , [ json , STR_SUCCESS ] ); callIfDefined( completeCallback , xOptions , [ xOptions , STR_SUCCESS ] ); } } // Error notifier function notifyError( type ) { if ( !( done++ ) ) { // Clean up cleanUp(); // If pure error (not timeout), cache if needed pageCacheFlag && type != STR_TIMEOUT && ( pageCache[ url ] = type ); // Call error then complete callIfDefined( errorCallback , xOptions , [ xOptions , type ] ); callIfDefined( completeCallback , xOptions , [ xOptions , type ] ); } } // Check page cache if ( pageCacheFlag && ( pageCached = pageCache[ url ] ) ) { pageCached.s ? notifySuccess( pageCached.s[ 0 ] ) : notifyError( pageCached ); } else { // Install the generic callback // (BEWARE: global namespace pollution ahoy) win[ successCallbackName ] = genericCallback; // Create the script tag script = $( STR_SCRIPT_TAG )[ 0 ]; script.id = STR_JQUERY_JSONP + count++; // Set charset if provided if ( charset ) { script[ STR_CHARSET ] = charset; } opera && opera.version() < 11.60 ? // onerror is not supported: do not set as async and assume in-order execution. // Add a trailing script to emulate the event ( ( scriptAfter = $( STR_SCRIPT_TAG )[ 0 ] ).text = "document.getElementById('" + script.id + "')." + STR_ON_ERROR + "()" ) : // onerror is supported: set the script as async to avoid requests blocking each others ( script[ STR_ASYNC ] = STR_ASYNC ) ; // Internet Explorer: event/htmlFor trick if ( STR_ON_READY_STATE_CHANGE in script ) { script.htmlFor = script.id; script.event = STR_ON_CLICK; } // Attached event handlers script[ STR_ON_LOAD ] = script[ STR_ON_ERROR ] = script[ STR_ON_READY_STATE_CHANGE ] = function ( result ) { // Test readyState if it exists if ( !script[ STR_READY_STATE ] || !/i/.test( script[ STR_READY_STATE ] ) ) { try { script[ STR_ON_CLICK ] && script[ STR_ON_CLICK ](); } catch( _ ) {} result = lastValue; lastValue = 0; result ? notifySuccess( result[ 0 ] ) : notifyError( STR_ERROR ); } }; // Set source script.src = url; // Re-declare cleanUp function cleanUp = function( i ) { timeoutTimer && clearTimeout( timeoutTimer ); script[ STR_ON_READY_STATE_CHANGE ] = script[ STR_ON_LOAD ] = script[ STR_ON_ERROR ] = null; head[ STR_REMOVE_CHILD ]( script ); scriptAfter && head[ STR_REMOVE_CHILD ]( scriptAfter ); }; // Append main script head[ STR_INSERT_BEFORE ]( script , ( firstChild = head.firstChild ) ); // Append trailing script if needed scriptAfter && head[ STR_INSERT_BEFORE ]( scriptAfter , firstChild ); // If a timeout is needed, install it timeoutTimer = timeout > 0 && setTimeout( function() { notifyError( STR_TIMEOUT ); } , timeout ); } return xOptions; } // ###################### SETUP FUNCTION ## jsonp.setup = function( xOptions ) { $.extend( xOptionsDefaults , xOptions ); }; // ###################### INSTALL in jQuery ## $.jsonp = jsonp; } )( jQuery );