* @singleton
*/
/*jshint latedef:false */
-/*global sha1 */
( function ( $ ) {
'use strict';
trackHandlers = [],
trackQueue = [];
+ /**
+ * FNV132 hash function
+ *
+ * This function implements the 32-bit version of FNV-1.
+ * It is equivalent to hash( 'fnv132', ... ) in PHP, except
+ * its output is base 36 rather than hex.
+ * See <https://en.wikipedia.org/wiki/FNV_hash_function>
+ *
+ * @private
+ * @param {string} str String to hash
+ * @return {string} hash as an seven-character base 36 string
+ */
+ function fnv132( str ) {
+ /*jshint bitwise:false */
+ var hash = 0x811C9DC5,
+ i;
+
+ for ( i = 0; i < str.length; i++ ) {
+ hash += ( hash << 1 ) + ( hash << 4 ) + ( hash << 7 ) + ( hash << 8 ) + ( hash << 24 );
+ hash ^= str.charCodeAt( i );
+ }
+
+ hash = ( hash >>> 0 ).toString( 36 );
+ while ( hash.length < 7 ) {
+ hash = '0' + hash;
+ }
+
+ return hash;
+ }
+
/**
* Create an object that can be read from or written to from methods that allow
* interaction both with single and multiple properties at once.
},
/**
- * Add (does not replace) parameters for `N$` placeholder values.
+ * Add (does not replace) parameters for `$N` placeholder values.
*
* @param {Array} parameters
* @chainable
log.deprecate = !Object.defineProperty ? function ( obj, key, val ) {
obj[ key ] = val;
} : function ( obj, key, val, msg ) {
+ /*globals Set */
msg = 'Use of "' + key + '" is deprecated.' + ( msg ? ( ' ' + msg ) : '' );
+ var logged, loggedIsSet, uniqueTrace;
+ if ( window.Set ) {
+ logged = new Set();
+ loggedIsSet = true;
+ } else {
+ logged = {};
+ loggedIsSet = false;
+ }
+ uniqueTrace = function () {
+ var trace = new Error().stack;
+ if ( loggedIsSet ) {
+ if ( logged.has( trace ) ) {
+ return false;
+ }
+ logged.add( trace );
+ return true;
+ } else {
+ if ( logged.hasOwnProperty( trace ) ) {
+ return false;
+ }
+ logged[ trace ] = 1;
+ return true;
+ }
+ };
Object.defineProperty( obj, key, {
configurable: true,
enumerable: true,
get: function () {
- mw.track( 'mw.deprecate', key );
- mw.log.warn( msg );
+ if ( uniqueTrace() ) {
+ mw.track( 'mw.deprecate', key );
+ mw.log.warn( msg );
+ }
return val;
},
set: function ( newVal ) {
- mw.track( 'mw.deprecate', key );
- mw.log.warn( msg );
+ if ( uniqueTrace() ) {
+ mw.track( 'mw.deprecate', key );
+ mw.log.warn( msg );
+ }
val = newVal;
}
} );
var hashes = $.map( modules, function ( module ) {
return registry[ module ].version;
} );
- // Trim for consistency with server-side ResourceLoader::makeHash. It also helps
- // save precious space in the limited query string. Otherwise modules are more
- // likely to require multiple HTTP requests.
- return sha1( hashes.join( '' ) ).slice( 0, 12 );
+ return fnv132( hashes.join( '' ) );
}
/**
* OO.compare( [ 1 ], [ 1 ] );
* } );
*
- * @param {string|Array} dependencies Module name or array of modules names the callback
- * dependends on to be ready before executing
+ * @param {string|Array} dependencies Module name or array of modules names the
+ * callback depends on to be ready before executing
* @param {Function} [ready] Callback to execute when all dependencies are ready
* @param {Function} [error] Callback to execute if one or more dependencies failed
* @return {jQuery.Promise}
require: function ( moduleName ) {
var state = mw.loader.getState( moduleName );
- // Only ready mudules can be required
+ // Only ready modules can be required
if ( state !== 'ready' ) {
// Module may've forgotten to declare a dependency
throw new Error( 'Module "' + moduleName + '" is not loaded.' );