define(['../core', './var/nonce', './var/rquery', '../ajax'], function(
  jQuery,
  nonce,
  rquery
) {
  var oldCallbacks = [],
    rjsonp = /(=)\?(?=&|$)|\?\?/;

  // Default jsonp settings
  jQuery.ajaxSetup({
    jsonp: 'callback',
    jsonpCallback: function() {
      var callback = oldCallbacks.pop() || jQuery.expando + '_' + nonce++;
      this[callback] = true;
      return callback;
    },
  });

  // Detect, normalize options and install callbacks for jsonp requests
  jQuery.ajaxPrefilter('json jsonp', function(s, originalSettings, jqXHR) {
    var callbackName,
      overwritten,
      responseContainer,
      jsonProp =
        s.jsonp !== false &&
        (rjsonp.test(s.url)
          ? 'url'
          : typeof s.data === 'string' &&
            !(s.contentType || '').indexOf(
              'application/x-www-form-urlencoded'
            ) &&
            rjsonp.test(s.data) &&
            'data');

    // Handle iff the expected data type is "jsonp" or we have a parameter to set
    if (jsonProp || s.dataTypes[0] === 'jsonp') {
      // Get callback name, remembering preexisting value associated with it
      callbackName = s.jsonpCallback = jQuery.isFunction(s.jsonpCallback)
        ? s.jsonpCallback()
        : s.jsonpCallback;

      // Insert callback into url or form data
      if (jsonProp) {
        s[jsonProp] = s[jsonProp].replace(rjsonp, '$1' + callbackName);
      } else if (s.jsonp !== false) {
        s.url +=
          (rquery.test(s.url) ? '&' : '?') + s.jsonp + '=' + callbackName;
      }

      // Use data converter to retrieve json after script execution
      s.converters['script json'] = function() {
        if (!responseContainer) {
          jQuery.error(callbackName + ' was not called');
        }
        return responseContainer[0];
      };

      // force json dataType
      s.dataTypes[0] = 'json';

      // Install callback
      overwritten = window[callbackName];
      window[callbackName] = function() {
        responseContainer = arguments;
      };

      // Clean-up function (fires after converters)
      jqXHR.always(function() {
        // Restore preexisting value
        window[callbackName] = overwritten;

        // Save back as free
        if (s[callbackName]) {
          // make sure that re-using the options doesn't screw things around
          s.jsonpCallback = originalSettings.jsonpCallback;

          // save the callback name for future use
          oldCallbacks.push(callbackName);
        }

        // Call if it was a function and we have a response
        if (responseContainer && jQuery.isFunction(overwritten)) {
          overwritten(responseContainer[0]);
        }

        responseContainer = overwritten = undefined;
      });

      // Delegate to script
      return 'script';
    }
  });
});
