X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;f=resources%2Fmediawiki%2Fmediawiki.js;h=585483b7649bb35374a8518ca606d98a508ea2c6;hb=731bd1e461a07e32ed8d91b4f152a53c5b462b57;hp=c9bcdec38620bdaf2dbd2f945c9f1ecb8cba1237;hpb=2f7dac47cee39ead2f4982eacfb35def54daec75;p=lhc%2Fweb%2Fwiklou.git diff --git a/resources/mediawiki/mediawiki.js b/resources/mediawiki/mediawiki.js index c9bcdec386..585483b764 100644 --- a/resources/mediawiki/mediawiki.js +++ b/resources/mediawiki/mediawiki.js @@ -8,7 +8,6 @@ var mw = ( function ( $, undefined ) { /* Private Members */ var hasOwn = Object.prototype.hasOwnProperty; - /* Object constructors */ /** @@ -130,6 +129,20 @@ var mw = ( function ( $, undefined ) { } Message.prototype = { + /** + * Simple message parser, does $N replacement and nothing else. + * This may be overridden to provide a more complex message parser. + * + * This function will not be called for nonexistent messages. + */ + parser: function() { + var parameters = this.parameters; + return this.map.get( this.key ).replace( /\$(\d+)/g, function ( str, match ) { + var index = parseInt( match, 10 ) - 1; + return parameters[index] !== undefined ? parameters[index] : '$' + match; + } ); + }, + /** * Appends (does not replace) parameters for replacement to the .parameters property. * @@ -150,7 +163,9 @@ var mw = ( function ( $, undefined ) { * @return string Message as a string in the current form or if key does not exist. */ toString: function() { - if ( !this.map.exists( this.key ) ) { + var text; + + if ( !this.exists() ) { // Use as text if key does not exist if ( this.format !== 'plain' ) { // format 'escape' and 'parse' need to have the brackets and key html escaped @@ -158,28 +173,24 @@ var mw = ( function ( $, undefined ) { } return '<' + this.key + '>'; } - var text = this.map.get( this.key ), - parameters = this.parameters; - - text = text.replace( /\$(\d+)/g, function ( str, match ) { - var index = parseInt( match, 10 ) - 1; - return parameters[index] !== undefined ? parameters[index] : '$' + match; - } ); if ( this.format === 'plain' ) { - return text; + // @todo FIXME: Although not applicable to core Message, + // Plugins like jQueryMsg should be able to distinguish + // between 'plain' (only variable replacement and plural/gender) + // and actually parsing wikitext to HTML. + text = this.parser(); } + if ( this.format === 'escaped' ) { - // According to Message.php this needs {{-transformation, which is - // still todo - return mw.html.escape( text ); + text = this.parser(); + text = mw.html.escape( text ); } - - /* This should be fixed up when we have a parser - if ( this.format === 'parse' && 'language' in mw ) { - text = mw.language.parse( text ); + + if ( this.format === 'parse' ) { + text = this.parser(); } - */ + return text; }, @@ -236,6 +247,11 @@ var mw = ( function ( $, undefined ) { * @var constructor Make the Map constructor publicly available. */ Map: Map, + + /** + * @var constructor Make the Message constructor publicly available. + */ + Message: Message, /** * List of configuration values @@ -322,7 +338,7 @@ var mw = ( function ( $, undefined ) { * 'dependencies': ['required.foo', 'bar.also', ...], (or) function() {} * 'group': 'somegroup', (or) null, * 'source': 'local', 'someforeignwiki', (or) null - * 'state': 'registered', 'loading', 'loaded', 'ready', or 'error' + * 'state': 'registered', 'loading', 'loaded', 'ready', 'error' or 'missing' * 'script': ..., * 'style': ..., * 'messages': { 'key': 'value' }, @@ -346,8 +362,11 @@ var mw = ( function ( $, undefined ) { queue = [], // List of callback functions waiting for modules to be ready to be called jobs = [], - // Flag inidicating that document ready has occured + // Flag indicating that document ready has occured ready = false, + // Whether we should try to load scripts in a blocking way + // Set with setBlocking() + blocking = false, // Selector cache for the marker element. Use getMarker() to get/use the marker! $marker = null; @@ -435,18 +454,22 @@ var mw = ( function ( $, undefined ) { ' -> ' + deps[n] ); } + + // Add to unresolved + unresolved[unresolved.length] = module; recurse( deps[n], resolved, unresolved ); + // module is at the end of unresolved + unresolved.pop(); } } resolved[resolved.length] = module; - unresolved.splice( $.inArray( module, unresolved ), 1 ); } /** * Gets a list of module names that a module depends on in their proper dependency order * * @param module string module name or array of string module names - * @return list of dependencies + * @return list of dependencies, including 'module'. * @throws Error if circular reference is detected */ function resolve( module ) { @@ -463,10 +486,6 @@ var mw = ( function ( $, undefined ) { } return modules; } else if ( typeof module === 'string' ) { - // Undefined modules have no dependencies - if ( registry[module] === undefined ) { - return []; - } resolved = []; recurse( module, resolved, [] ); return resolved; @@ -476,12 +495,13 @@ var mw = ( function ( $, undefined ) { /** * Narrows a list of module names down to those matching a specific - * state. Possible states are 'undefined', 'registered', 'loading', - * 'loaded', or 'ready' + * state (see comment on top of this scope for a list of valid states). + * One can also filter for 'unregistered', which will return the + * modules names that don't have a registry entry. * * @param states string or array of strings of module states to filter by - * @param modules array list of module names to filter (optional, all modules - * will be used by default) + * @param modules array list of module names to filter (optional, by default the entire + * registry is used) * @return array list of filtered module names */ function filter( states, modules ) { @@ -504,7 +524,7 @@ var mw = ( function ( $, undefined ) { for ( m = 0; m < modules.length; m += 1 ) { if ( registry[modules[m]] === undefined ) { // Module does not exist - if ( states[s] === 'undefined' ) { + if ( states[s] === 'unregistered' ) { // OK, undefined list[list.length] = modules[m]; } @@ -568,15 +588,15 @@ var mw = ( function ( $, undefined ) { } /** - * Adds a script tag to the body, either using document.write or low-level DOM manipulation, - * depending on whether document-ready has occured yet. + * Adds a script tag to the DOM, either using document.write or low-level DOM manipulation, + * depending on whether document-ready has occured yet and whether we are in blocking mode. * * @param src String: URL to script, will be used as the src attribute in the script tag * @param callback Function: Optional callback which will be run when the script is done */ function addScript( src, callback ) { - var done = false, script; - if ( ready ) { + var done = false, script, head; + if ( ready || !blocking ) { // jQuery's getScript method is NOT better than doing this the old-fashioned way // because jQuery will eval the script's code, and errors will not have sane // line numbers. @@ -611,13 +631,27 @@ var mw = ( function ( $, undefined ) { } }; } - document.body.appendChild( script ); + + if ( window.opera ) { + // Appending to the blocks rendering completely in Opera, + // so append to the after document ready. This means the + // scripts only start loading after the document has been rendered, + // but so be it. Opera users don't deserve faster web pages if their + // browser makes it impossible + $( function() { document.body.appendChild( script ); } ); + } else { + // IE-safe way of getting the . document.documentElement.head doesn't + // work in scripts that run in the + head = document.getElementsByTagName( 'head' )[0]; + ( document.body || head ).appendChild( script ); + } } else { document.write( mw.html.element( 'script', { 'type': 'text/javascript', 'src': src }, '' ) ); if ( $.isFunction( callback ) ) { // Document.write is synchronous, so this is called when it's done + // FIXME: that's a lie. doc.write isn't actually synchronous callback(); } } @@ -736,15 +770,15 @@ var mw = ( function ( $, undefined ) { if ( arguments.length > 1 ) { jobs[jobs.length] = { 'dependencies': filter( - ['undefined', 'registered', 'loading', 'loaded'], + ['registered', 'loading', 'loaded'], dependencies ), 'ready': ready, 'error': error }; } - // Queue up any dependencies that are undefined or registered - dependencies = filter( ['undefined', 'registered'], dependencies ); + // Queue up any dependencies that are registered + dependencies = filter( ['registered'], dependencies ); for ( n = 0; n < dependencies.length; n += 1 ) { if ( $.inArray( dependencies[n], queue ) === -1 ) { queue[queue.length] = dependencies[n]; @@ -822,15 +856,13 @@ var mw = ( function ( $, undefined ) { // Appends a list of modules from the queue to the batch for ( q = 0; q < queue.length; q += 1 ) { - // Only request modules which are undefined or registered - if ( registry[queue[q]] === undefined || registry[queue[q]].state === 'registered' ) { + // Only request modules which are registered + if ( registry[queue[q]] !== undefined && registry[queue[q]].state === 'registered' ) { // Prevent duplicate entries if ( $.inArray( queue[q], batch ) === -1 ) { batch[batch.length] = queue[q]; // Mark registered modules as loading - if ( registry[queue[q]] !== undefined ) { - registry[queue[q]].state = 'loading'; - } + registry[queue[q]].state = 'loading'; } } } @@ -985,7 +1017,7 @@ var mw = ( function ( $, undefined ) { throw new Error( 'module must be a string, not a ' + typeof module ); } if ( registry[module] !== undefined ) { - throw new Error( 'module already implemented: ' + module ); + throw new Error( 'module already registered: ' + module ); } // List the module as registered registry[module] = { @@ -1053,8 +1085,6 @@ var mw = ( function ( $, undefined ) { registry[module].dependencies ) ) { execute( module ); - } else { - request( module ); } }, @@ -1107,45 +1137,59 @@ var mw = ( function ( $, undefined ) { * "text/javascript"; if no type is provided, text/javascript is assumed. */ load: function ( modules, type ) { + var filtered, m; + // Validate input if ( typeof modules !== 'object' && typeof modules !== 'string' ) { throw new Error( 'modules must be a string or an array, not a ' + typeof modules ); } - // Allow calling with an external script or single dependency as a string + // Allow calling with an external url or single dependency as a string if ( typeof modules === 'string' ) { // Support adding arbitrary external scripts if ( /^(https?:)?\/\//.test( modules ) ) { if ( type === 'text/css' ) { - $( 'head' ).append( $( '', { + $( 'head' ).append( $( '', { rel: 'stylesheet', type: 'text/css', href: modules } ) ); - return true; + return; } else if ( type === 'text/javascript' || type === undefined ) { addScript( modules ); - return true; + return; } // Unknown type - return false; + throw new Error( 'invalid type for external url, must be text/css or text/javascript. not ' + type ); } // Called with single module modules = [modules]; } + + // Filter out undefined modules, otherwise resolve() will throw + // an exception for trying to load an undefined module. + // Undefined modules are acceptable here in load(), because load() takes + // an array of unrelated modules, whereas the modules passed to + // using() are related and must all be loaded. + for ( filtered = [], m = 0; m < modules.length; m += 1 ) { + if ( registry[modules[m]] !== undefined ) { + filtered[filtered.length] = modules[m]; + } + } + // Resolve entire dependency map - modules = resolve( modules ); + filtered = resolve( filtered ); // If all modules are ready, nothing dependency be done - if ( compare( filter( ['ready'], modules ), modules ) ) { - return true; + if ( compare( filter( ['ready'], filtered ), filtered ) ) { + return; } - // If any modules have errors return false - else if ( filter( ['error'], modules ).length ) { - return false; + // If any modules have errors + else if ( filter( ['error'], filtered ).length ) { + return; } // Since some modules are not yet ready, queue up a request else { - request( modules ); - return true; + request( filtered ); + return; } }, @@ -1210,6 +1254,18 @@ var mw = ( function ( $, undefined ) { return key; } ); }, + + /** + * Enable or disable blocking. If blocking is enabled and + * document ready has not yet occurred, scripts will be loaded + * in a blocking way (using document.write) rather than + * asynchronously using DOM manipulation + * + * @param b {Boolean} True to enable blocking, false to disable it + */ + setBlocking: function( b ) { + blocking = b; + }, /** * For backwards-compatibility with Squid-cached pages. Loads mw.user