Merge "resoureloader: Consolidate styles-only queue at the top"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Tue, 27 Oct 2015 09:22:44 +0000 (09:22 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Tue, 27 Oct 2015 09:22:44 +0000 (09:22 +0000)
docs/extension.schema.json
includes/db/DatabaseError.php
languages/Language.php
resources/src/mediawiki/mediawiki.jqueryMsg.js
tests/phpunit/languages/LanguageTest.php
tests/phpunit/phpunit.php
tests/qunit/suites/resources/mediawiki/mediawiki.jqueryMsg.test.js

index 218a19c..dde4fa1 100644 (file)
                "ResourceModules": {
                        "type": "object",
                        "description": "ResourceLoader modules to register",
-                       "additionalProperties": false,
                        "patternProperties": {
                                "^[a-zA-Z0-9-\\.]+$": {
                                        "type": "object",
-                                       "description": "A single ResourceLoader module descriptor",
-                                       "properties": {
-                                               "localBasePath": {
-                                                       "type": "string",
-                                                       "description": "Base path to prepend to all local paths in $options. Defaults to $IP"
-                                               },
-                                               "remoteBasePath": {
-                                                       "type": "string",
-                                                       "description": "Base path to prepend to all remote paths in $options. Defaults to $wgScriptPath"
-                                               },
-                                               "remoteExtPath": {
-                                                       "type": "string",
-                                                       "description": "Equivalent of remoteBasePath, but relative to $wgExtensionAssetsPath"
-                                               },
-                                               "scripts": {
-                                                       "type": ["string", "array"],
-                                                       "description": "Scripts to always include (array of file paths)",
-                                                       "items": {
-                                                               "type": "string"
-                                                       }
-                                               },
-                                               "languageScripts": {
-                                                       "type": "object",
-                                                       "description": "Scripts to include in specific language contexts (mapping of language code to file path(s))",
-                                                       "patternProperties": {
-                                                               "^[a-zA-Z0-9-]{2,}$": {
-                                                                       "type": [
-                                                                               "string",
-                                                                               "array"
-                                                                       ],
+                                       "anyOf": [
+                                               {
+                                                       "description": "A ResourceLoaderFileModule definition",
+                                                       "additionalProperties": false,
+                                                       "properties": {
+                                                               "localBasePath": {
+                                                                       "type": "string",
+                                                                       "description": "Base path to prepend to all local paths in $options. Defaults to $IP"
+                                                               },
+                                                               "remoteBasePath": {
+                                                                       "type": "string",
+                                                                       "description": "Base path to prepend to all remote paths in $options. Defaults to $wgScriptPath"
+                                                               },
+                                                               "remoteExtPath": {
+                                                                       "type": "string",
+                                                                       "description": "Equivalent of remoteBasePath, but relative to $wgExtensionAssetsPath"
+                                                               },
+                                                               "scripts": {
+                                                                       "type": ["string", "array"],
+                                                                       "description": "Scripts to always include (array of file paths)",
                                                                        "items": {
                                                                                "type": "string"
                                                                        }
-                                                               }
-                                                       }
-                                               },
-                                               "skinScripts": {
-                                                       "type": "object",
-                                                       "description": "Scripts to include in specific skin contexts (mapping of skin name to script(s)",
-                                                       "patternProperties": {
-                                                               ".+": {
-                                                                       "type": [
-                                                                               "string",
-                                                                               "array"
-                                                                       ],
+                                                               },
+                                                               "languageScripts": {
+                                                                       "type": "object",
+                                                                       "description": "Scripts to include in specific language contexts (mapping of language code to file path(s))",
+                                                                       "patternProperties": {
+                                                                               "^[a-zA-Z0-9-]{2,}$": {
+                                                                                       "type": [
+                                                                                               "string",
+                                                                                               "array"
+                                                                                       ],
+                                                                                       "items": {
+                                                                                               "type": "string"
+                                                                                       }
+                                                                               }
+                                                                       }
+                                                               },
+                                                               "skinScripts": {
+                                                                       "type": "object",
+                                                                       "description": "Scripts to include in specific skin contexts (mapping of skin name to script(s)",
+                                                                       "patternProperties": {
+                                                                               ".+": {
+                                                                                       "type": [
+                                                                                               "string",
+                                                                                               "array"
+                                                                                       ],
+                                                                                       "items": {
+                                                                                               "type": "string"
+                                                                                       }
+                                                                               }
+                                                                       }
+                                                               },
+                                                               "debugScripts": {
+                                                                       "type": ["string", "array"],
+                                                                       "description": "Scripts to include in debug contexts",
                                                                        "items": {
                                                                                "type": "string"
                                                                        }
-                                                               }
-                                                       }
-                                               },
-                                               "debugScripts": {
-                                                       "type": ["string", "array"],
-                                                       "description": "Scripts to include in debug contexts",
-                                                       "items": {
-                                                               "type": "string"
-                                                       }
-                                               },
-                                               "loaderScripts": {
-                                                       "type": ["string", "array"],
-                                                       "description": "Scripts to include in the startup module",
-                                                       "items": {
-                                                               "type": "string"
-                                                       }
-                                               },
-                                               "dependencies": {
-                                                       "type": ["string", "array"],
-                                                       "description": "Modules which must be loaded before this module",
-                                                       "items": {
-                                                               "type": "string"
-                                                       }
-                                               },
-                                               "styles": {
-                                                       "type": ["string", "array", "object"],
-                                                       "description": "Styles to always load",
-                                                       "items": {
-                                                               "type": "string"
-                                                       }
-                                               },
-                                               "skinStyles": {
-                                                       "type": "object",
-                                                       "description": "Styles to include in specific skin contexts (mapping of skin name to style(s))",
-                                                       "patternProperties": {
-                                                               ".+": {
-                                                                       "type": [
-                                                                               "string",
-                                                                               "array"
-                                                                       ],
+                                                               },
+                                                               "loaderScripts": {
+                                                                       "type": ["string", "array"],
+                                                                       "description": "Scripts to include in the startup module",
+                                                                       "items": {
+                                                                               "type": "string"
+                                                                       }
+                                                               },
+                                                               "dependencies": {
+                                                                       "type": ["string", "array"],
+                                                                       "description": "Modules which must be loaded before this module",
+                                                                       "items": {
+                                                                               "type": "string"
+                                                                       }
+                                                               },
+                                                               "styles": {
+                                                                       "type": ["string", "array", "object"],
+                                                                       "description": "Styles to always load",
+                                                                       "items": {
+                                                                               "type": "string"
+                                                                       }
+                                                               },
+                                                               "skinStyles": {
+                                                                       "type": "object",
+                                                                       "description": "Styles to include in specific skin contexts (mapping of skin name to style(s))",
+                                                                       "patternProperties": {
+                                                                               ".+": {
+                                                                                       "type": [
+                                                                                               "string",
+                                                                                               "array"
+                                                                                       ],
+                                                                                       "items": {
+                                                                                               "type": "string"
+                                                                                       }
+                                                                               }
+                                                                       }
+                                                               },
+                                                               "messages": {
+                                                                       "type": ["string", "array"],
+                                                                       "description": "Messages to always load",
+                                                                       "items": {
+                                                                               "type": "string"
+                                                                       }
+                                                               },
+                                                               "group": {
+                                                                       "type": "string",
+                                                                       "description": "Group which this module should be loaded together with"
+                                                               },
+                                                               "position": {
+                                                                       "type": "string",
+                                                                       "description": "Position on the page to load this module at",
+                                                                       "enum": [
+                                                                               "bottom",
+                                                                               "top"
+                                                                       ]
+                                                               },
+                                                               "templates": {
+                                                                       "type": "object",
+                                                                       "description": "Templates to be loaded for client-side usage"
+                                                               },
+                                                               "targets": {
+                                                                       "type": ["string", "array"],
+                                                                       "description": "ResourceLoader target the module can run on",
                                                                        "items": {
                                                                                "type": "string"
                                                                        }
                                                                }
                                                        }
                                                },
-                                               "messages": {
-                                                       "type": ["string", "array"],
-                                                       "description": "Messages to always load",
-                                                       "items": {
-                                                               "type": "string"
+                                               {
+                                                       "description": "A ResourceLoaderImageModule definition",
+                                                       "additionalProperties": false,
+                                                       "properties": {
+                                                               "class": {
+                                                                       "enum": ["ResourceLoaderImageModule"]
+                                                               },
+                                                               "data": {
+                                                                       "type": "string"
+                                                               },
+                                                               "prefix": {
+                                                                       "type": "string"
+                                                               },
+                                                               "selector": {
+                                                                       "type": "string"
+                                                               },
+                                                               "selectorWithoutVariant": {
+                                                                       "type": "string"
+                                                               },
+                                                               "selectorWithVariant": {
+                                                                       "type": "string"
+                                                               },
+                                                               "variants": {
+                                                                       "type": "object"
+                                                               },
+                                                               "images": {
+                                                                       "type": "object"
+                                                               },
+                                                               "position": {
+                                                                       "enum": [
+                                                                               "top",
+                                                                               "bottom"
+                                                                       ]
+                                                               }
                                                        }
                                                },
-                                               "group": {
-                                                       "type": "string",
-                                                       "description": "Group which this module should be loaded together with"
-                                               },
-                                               "position": {
-                                                       "type": "string",
-                                                       "description": "Position on the page to load this module at",
-                                                       "enum": [
-                                                               "bottom",
-                                                               "top"
-                                                       ]
-                                               },
-                                               "templates": {
-                                                       "type": "object",
-                                                       "description": "Templates to be loaded for client-side usage"
+                                               {
+                                                       "description": "An arbitrary ResourceLoaderModule definition",
+                                                       "properties": {
+                                                               "class": {
+                                                                       "type": "string",
+                                                                       "pattern": "^((?!ResourceLoader(File|Image)Module).)*$"
+                                                               }
+                                                       },
+                                                       "required": ["class"]
                                                }
-                                       }
+                                       ]
                                }
                        }
                },
index e6c285e..78d26ae 100644 (file)
@@ -78,6 +78,10 @@ class DBExpectedError extends DBError {
                return $s;
        }
 
+       function getPageTitle() {
+               return $this->msg( 'databaseerror', 'Database error' );
+       }
+
        /**
         * @return string
         */
@@ -456,4 +460,7 @@ class DBUnexpectedError extends DBError {
  * @ingroup Database
  */
 class DBReadOnlyError extends DBExpectedError {
+       function getPageTitle() {
+               return $this->msg( 'readonly', 'Database is locked' );
+       }
 }
index 50ed513..3ea2693 100644 (file)
@@ -3691,8 +3691,9 @@ class Language {
                                # We got the first byte only of a multibyte char; remove it.
                                $string = substr( $string, 0, -1 );
                        } elseif ( $char >= 0x80 &&
+                               // Use the /s modifier (PCRE_DOTALL) so (.*) also matches newlines
                                preg_match( '/^(.*)(?:[\xe0-\xef][\x80-\xbf]|' .
-                                       '[\xf0-\xf7][\x80-\xbf]{1,2})$/', $string, $m )
+                                       '[\xf0-\xf7][\x80-\xbf]{1,2})$/s', $string, $m )
                        ) {
                                # We chopped in the middle of a character; remove it
                                $string = $m[1];
index de63a18..2e89f6b 100644 (file)
         * @return {string} return.return Rendered HTML.
         */
        mw.jqueryMsg.getMessageFunction = function ( options ) {
-               var failableParserFn = getFailableParserFn( options ),
-                       format;
+               var failableParserFn, format;
 
                if ( options && options.format !== undefined ) {
                        format = options.format;
                }
 
                return function () {
+                       if ( !failableParserFn ) {
+                               failableParserFn = getFailableParserFn( options );
+                       }
                        var failableResult = failableParserFn( arguments );
                        if ( format === 'text' || format === 'escaped' ) {
                                return failableResult.text();
         * @return {jQuery} return.return
         */
        mw.jqueryMsg.getPlugin = function ( options ) {
-               var failableParserFn = getFailableParserFn( options );
+               var failableParserFn;
 
                return function () {
+                       if ( !failableParserFn ) {
+                               failableParserFn = getFailableParserFn( options );
+                       }
                        var $target = this.empty();
                        appendWithoutParsing( $target, failableParserFn( arguments ) );
                        return $target;
        };
 
        mw.jqueryMsg.parser.prototype = {
-               /**
-                * Cache mapping MediaWiki message keys and the value onlyCurlyBraceTransform, to the AST of the message.
-                *
-                * In most cases, the message is a string so this is identical.
-                * (This is why we would like to move this functionality server-side).
-                *
-                * The two parts of the key are separated by colon.  For example:
-                *
-                *     "message-key:true": ast
-                *
-                * if they key is "message-key" and onlyCurlyBraceTransform is true.
-                *
-                * This cache is shared by all instances of mw.jqueryMsg.parser.
-                *
-                * NOTE: We promise, it's static - when you create this empty object
-                * in the prototype, each new instance of the class gets a reference
-                * to the same object.
-                *
-                * @static
-                * @property {Object}
-                */
-               astCache: {},
-
                /**
                 * Where the magic happens.
                 * Parses a message from the key, and swaps in replacements as necessary, wraps in jQuery
                 * @return {string|Array} string of '[key]' if message missing, simple string if possible, array of arrays if needs parsing
                 */
                getAst: function ( key ) {
-                       var wikiText,
-                               cacheKey = [ key, this.settings.onlyCurlyBraceTransform ].join( ':' );
-
-                       if ( this.astCache[ cacheKey ] === undefined ) {
-                               wikiText = this.settings.messages.get( key );
-                               if ( typeof wikiText !== 'string' ) {
-                                       wikiText = '\\[' + key + '\\]';
-                               }
-                               this.astCache[ cacheKey ] = this.wikiTextToAst( wikiText );
+                       var wikiText = this.settings.messages.get( key );
+                       if ( typeof wikiText !== 'string' ) {
+                               wikiText = '\\[' + key + '\\]';
                        }
-                       return this.astCache[ cacheKey ];
+                       return this.wikiTextToAst( wikiText );
                },
 
                /**
                        // I am deferring the work of turning it into prototypes & objects. It's quite fast enough
                        // finally let's do some actual work...
 
-                       // If you add another possible rootExpression, you must update the astCache key scheme.
                        result = start( this.settings.onlyCurlyBraceTransform ? curlyBraceTransformExpression : expression );
 
                        /*
index 4fca002..77c3c02 100644 (file)
@@ -261,6 +261,16 @@ class LanguageTest extends LanguageClassesTestCase {
                        $this->getLang()->truncate( "1234567890", 5, 'XXX', false ),
                        'truncate without adjustment'
                );
+               $this->assertEquals(
+                       "泰乐菌...",
+                       $this->getLang()->truncate( "泰乐菌素123456789", 11, '...', false ),
+                       'truncate does not chop Unicode characters in half'
+               );
+               $this->assertEquals(
+                       "\n泰乐菌...",
+                       $this->getLang()->truncate( "\n泰乐菌素123456789", 12, '...', false ),
+                       'truncate does not chop Unicode characters in half if there is a preceding newline'
+               );
        }
 
        /**
index 49b91c3..fab6dfb 100755 (executable)
@@ -113,6 +113,11 @@ class PHPUnitMaintClass extends Maintenance {
                );
                // xdebug's default of 100 is too low for MediaWiki
                ini_set( 'xdebug.max_nesting_level', 1000 );
+
+               // Bug T116683 serialize_precision of 100
+               // may break testing against floating point values
+               // treated with PHP's serialize()
+               ini_set( 'serialize_precision', 14 );
        }
 
        public function execute() {
index 4f273bc..24528bb 100644 (file)
                assert.equal( logSpy.callCount, 2, 'mw.log.warn calls' );
        } );
 
+       QUnit.test( 'Integration', 4, function ( assert ) {
+               var expected, logSpy;
+
+               expected = '<b><a title="Bold" href="/wiki/Bold">Bold</a>!</b>';
+               mw.messages.set( 'integration-test', '<b>[[Bold]]!</b>' );
+
+               this.suppressWarnings();
+               logSpy = this.sandbox.spy( mw.log, 'warn' );
+               assert.equal(
+                       window.gM( 'integration-test' ),
+                       expected,
+                       'Global function gM() works correctly'
+               );
+               assert.equal( logSpy.callCount, 1, 'mw.log.warn called' );
+               this.restoreWarnings();
+
+               assert.equal(
+                       mw.message( 'integration-test' ).parse(),
+                       expected,
+                       'mw.message().parse() works correctly'
+               );
+
+               assert.equal(
+                       $( '<span>' ).msg( 'integration-test' ).html(),
+                       expected,
+                       'jQuery plugin $.fn.msg() works correctly'
+               );
+       } );
+
 }( mediaWiki, jQuery ) );