jsonp.js 7.05 KB
import $ from 'jquery';
/* eslint-disable */
// Noop
function noop() {
}

// Generic callback
function genericCallback( data ) {
	lastValue = [ data ];
}

// Call if defined
function callIfDefined( method , object , parameters ) {
	return method && method.apply && method.apply( object.context || object , parameters );
}

// 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,

	// IE < 10
	oldIE = !!$( "<div>" ).html( "<!--[if IE]><i><![endif]-->" ).find("i").length;

// ###################### 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, xOptions ] );
			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 ( oldIE ) {
			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;

export default jsonp;
/* eslint-disable */