( function ( mw ) {
	var maxBusy = 50;

	mw.requestIdleCallbackInternal = function ( callback ) {
		setTimeout( function () {
			var start = mw.now();
			callback( {
				didTimeout: false,
				timeRemaining: function () {
					return Math.max( 0, maxBusy - ( mw.now() - start ) );
				}
			} );
		}, 1 );
	};

	/**
	 * Schedule a deferred task to run in the background.
	 *
	 * This allows code to perform tasks in the main thread without impacting
	 * time-critical operations such as animations and response to input events.
	 *
	 * Basic logic is as follows:
	 *
	 * - User input event should be acknowledged within 100ms per [RAIL].
	 * - Idle work should be grouped in blocks of upto 50ms so that enough time
	 *   remains for the event handler to execute and any rendering to take place.
	 * - Whenever a native event happens (e.g. user input), the deadline for any
	 *   running idle callback drops to 0.
	 * - As long as the deadline is non-zero, other callbacks pending may be
	 *   executed in the same idle period.
	 *
	 * See also:
	 *
	 * - <https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback>
	 * - <https://w3c.github.io/requestidlecallback/>
	 * - <https://developers.google.com/web/updates/2015/08/using-requestidlecallback>
	 * [RAIL]: https://developers.google.com/web/fundamentals/performance/rail
	 *
	 * @member mw
	 * @param {Function} callback
	 * @param {Object} [options]
	 * @param {number} [options.timeout] If set, the callback will be scheduled for
	 *  immediate execution after this amount of time (in milliseconds) if it didn't run
	 *  by that time.
	 */
	mw.requestIdleCallback = window.requestIdleCallback ?
		window.requestIdleCallback.bind( window ) : // Bind because it throws TypeError if context is not window
		mw.requestIdleCallbackInternal;
	// Note: Polyfill was previously disabled due to
	// https://bugs.chromium.org/p/chromium/issues/detail?id=647870
	// See also <http://codepen.io/Krinkle/full/XNGEvv>
}( mediaWiki ) );
