X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;f=resources%2Fmediawiki%2Fmediawiki.jqueryMsg.js;h=d7d90f429da110804a41f03a4b155d3e1ca2536e;hb=be148901bc02f5adc6e9f04d187852478ace6d1f;hp=e286d3211005e0ed2b26a1667e8760989a1e3f05;hpb=1a7324ed643512be940aa9c59f385ce16a71cb9f;p=lhc%2Fweb%2Fwiklou.git diff --git a/resources/mediawiki/mediawiki.jqueryMsg.js b/resources/mediawiki/mediawiki.jqueryMsg.js index e286d32110..d7d90f429d 100644 --- a/resources/mediawiki/mediawiki.jqueryMsg.js +++ b/resources/mediawiki/mediawiki.jqueryMsg.js @@ -1,4 +1,4 @@ -/** +/*! * Experimental advanced wikitext parser-emitter. * See: http://www.mediawiki.org/wiki/Extension:UploadWizard/MessageParser for docs * @@ -6,6 +6,11 @@ * @author mflaschen@wikimedia.org */ ( function ( mw, $ ) { + /** + * @class mw.jqueryMsg + * @singleton + */ + var oldParser, slice = Array.prototype.slice, parserDefaults = { @@ -59,6 +64,7 @@ * * Object elements of children (jQuery, HTMLElement, TextNode, etc.) will be left as is. * + * @private * @param {jQuery} $parent Parent node wrapped by jQuery * @param {Object|string|Array} children What to append, with the same possible types as jQuery * @return {jQuery} $parent @@ -82,6 +88,7 @@ /** * Decodes the main HTML entities, those encoded by mw.html.escape. * + * @private * @param {string} encode Encoded string * @return {string} String with those entities decoded */ @@ -96,19 +103,19 @@ /** * Given parser options, return a function that parses a key and replacements, returning jQuery object + * + * Try to parse a key and optional replacements, returning a jQuery object that may be a tree of jQuery nodes. + * If there was an error parsing, return the key and the error message (wrapped in jQuery). This should put the error right into + * the interface, without causing the page to halt script execution, and it hopefully should be clearer how to fix it. + * @private * @param {Object} parser options - * @return {Function} accepting ( String message key, String replacement1, String replacement2 ... ) and returning {jQuery} + * @return {Function} + * @return {Array} return.args First element is the key, replacements may be in array in 2nd element, or remaining elements. + * @return {jQuery} return.return */ function getFailableParserFn( options ) { var parser = new mw.jqueryMsg.parser( options ); - /** - * Try to parse a key and optional replacements, returning a jQuery object that may be a tree of jQuery nodes. - * If there was an error parsing, return the key and the error message (wrapped in jQuery). This should put the error right into - * the interface, without causing the page to halt script execution, and it hopefully should be clearer how to fix it. - * - * @param {Array} first element is the key, replacements may be in array in 2nd element, or remaining elements. - * @return {jQuery} - */ + return function ( args ) { var key = args[0], argsArray = $.isArray( args[1] ) ? args[1] : slice.call( args, 1 ); @@ -123,17 +130,25 @@ mw.jqueryMsg = {}; /** - * Class method. * Returns a function suitable for use as a global, to construct strings from the message key (and optional replacements). * e.g. + * * window.gM = mediaWiki.parser.getMessageFunction( options ); * $( 'p#headline' ).html( gM( 'hello-user', username ) ); * * Like the old gM() function this returns only strings, so it destroys any bindings. If you want to preserve bindings use the * jQuery plugin version instead. This is only included for backwards compatibility with gM(). * - * @param {Array} parser options - * @return {Function} function suitable for assigning to window.gM + * N.B. replacements are variadic arguments or an array in second parameter. In other words: + * somefunction( a, b, c, d ) + * is equivalent to + * somefunction( a, [b, c, d] ) + * + * @param {Object} options parser options + * @return {Function} Function suitable for assigning to window.gM + * @return {string} return.key Message key. + * @return {Array|Mixed} return.replacements Optional variable replacements (variadically or an array). + * @return {string} return.return Rendered HTML. */ mw.jqueryMsg.getMessageFunction = function ( options ) { var failableParserFn = getFailableParserFn( options ), @@ -145,16 +160,6 @@ format = parserDefaults.format; } - /** - * N.B. replacements are variadic arguments or an array in second parameter. In other words: - * somefunction( a, b, c, d ) - * is equivalent to - * somefunction( a, [b, c, d] ) - * - * @param {string} key Message key. - * @param {Array|mixed} replacements Optional variable replacements (variadically or an array). - * @return {string} Rendered HTML. - */ return function () { var failableResult = failableParserFn( arguments ); if ( format === 'text' || format === 'escaped' ) { @@ -166,30 +171,30 @@ }; /** - * Class method. * Returns a jQuery plugin which parses the message in the message key, doing replacements optionally, and appends the nodes to * the current selector. Bindings to passed-in jquery elements are preserved. Functions become click handlers for [$1 linktext] links. * e.g. + * * $.fn.msg = mediaWiki.parser.getJqueryPlugin( options ); * var userlink = $( '' ).click( function () { alert( "hello!!" ) } ); * $( 'p#headline' ).msg( 'hello-user', userlink ); * - * @param {Array} parser options - * @return {Function} function suitable for assigning to jQuery plugin, such as $.fn.msg + * N.B. replacements are variadic arguments or an array in second parameter. In other words: + * somefunction( a, b, c, d ) + * is equivalent to + * somefunction( a, [b, c, d] ) + * + * We append to 'this', which in a jQuery plugin context will be the selected elements. + * + * @param {Object} options Parser options + * @return {Function} Function suitable for assigning to jQuery plugin, such as jQuery#msg + * @return {string} return.key Message key. + * @return {Array|Mixed} return.replacements Optional variable replacements (variadically or an array). + * @return {jQuery} return.return */ mw.jqueryMsg.getPlugin = function ( options ) { var failableParserFn = getFailableParserFn( options ); - /** - * N.B. replacements are variadic arguments or an array in second parameter. In other words: - * somefunction( a, b, c, d ) - * is equivalent to - * somefunction( a, [b, c, d] ) - * - * We append to 'this', which in a jQuery plugin context will be the selected elements. - * @param {string} key Message key. - * @param {Array|mixed} replacements Optional variable replacements (variadically or an array). - * @return {jQuery} this - */ + return function () { var $target = this.empty(); // TODO: Simply appendWithoutParsing( $target, failableParserFn( arguments ).contents() ) @@ -204,7 +209,10 @@ /** * The parser itself. * Describes an object, whose primary duty is to .parse() message keys. - * @param {Array} options + * + * @class + * @private + * @param {Object} options */ mw.jqueryMsg.parser = function ( options ) { this.settings = $.extend( {}, parserDefaults, options ); @@ -222,13 +230,18 @@ * * The two parts of the key are separated by colon. For example: * - * "message-key:true": ast + * "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: {}, @@ -236,18 +249,19 @@ * Where the magic happens. * Parses a message from the key, and swaps in replacements as necessary, wraps in jQuery * If an error is thrown, returns original key, and logs the error - * @param {String} key Message key. + * @param {string} key Message key. * @param {Array} replacements Variable replacements for $1, $2... $n * @return {jQuery} */ parse: function ( key, replacements ) { return this.emitter.emit( this.getAst( key ), replacements ); }, + /** * Fetch the message string associated with a key, return parsed structure. Memoized. * Note that we pass '[' + key + ']' back for a missing message here. - * @param {String} key - * @return {String|Array} string of '[key]' if message missing, simple string if possible, array of arrays if needs parsing + * @param {string} key + * @return {string|Array} string of '[key]' if message missing, simple string if possible, array of arrays if needs parsing */ getAst: function ( key ) { var cacheKey = [key, this.settings.onlyCurlyBraceTransform].join( ':' ), wikiText; @@ -268,7 +282,7 @@ * CAVEAT: This does not parse all wikitext. It could be more efficient, but it's pretty good already. * n.b. We want to move this functionality to the server. Nothing here is required to be on the client. * - * @param {String} message string wikitext + * @param {string} input Message string wikitext * @throws Error * @return {Mixed} abstract syntax tree */ @@ -290,7 +304,13 @@ // ========================================================= // parsing combinators - could be a library on its own // ========================================================= - // Try parsers until one works, if none work return null + + /** + * Try parsers until one works, if none work return null + * @private + * @param {Function[]} ps + * @return {string|null} + */ function choice( ps ) { return function () { var i, result; @@ -303,8 +323,14 @@ return null; }; } - // try several ps in a row, all must succeed or return null - // this is the only eager one + + /** + * Try several ps in a row, all must succeed or return null. + * This is the only eager one. + * @private + * @param {Function[]} ps + * @return {string|null} + */ function sequence( ps ) { var i, res, originalPos = pos, @@ -319,8 +345,15 @@ } return result; } - // run the same parser over and over until it fails. - // must succeed a minimum of n times or return null + + /** + * Run the same parser over and over until it fails. + * Must succeed a minimum of n times or return null. + * @private + * @param {number} n + * @param {Function} p + * @return {string|null} + */ function nOrMore( n, p ) { return function () { var originalPos = pos, @@ -337,16 +370,32 @@ return result; }; } - // There is a general pattern -- parse a thing, if that worked, apply transform, otherwise return null. - // But using this as a combinator seems to cause problems when combined with nOrMore(). - // May be some scoping issue + + /** + * There is a general pattern -- parse a thing, if that worked, apply transform, otherwise return null. + * + * TODO: But using this as a combinator seems to cause problems when combined with #nOrMore(). + * May be some scoping issue + * + * @private + * @param {Function} p + * @param {Function} fn + * @return {string|null} + */ function transform( p, fn ) { return function () { var result = p(); return result === null ? null : fn( result ); }; } - // Helpers -- just make ps out of simpler JS builtin types + + /** + * Just make parsers out of simpler JS builtin types + * @private + * @param {string} s + * @return {Function} + * @return {string} return.return + */ function makeStringParser( s ) { var len = s.length; return function () { @@ -364,6 +413,7 @@ * The regex being passed in should start with a ^ to anchor it to the start * of the string. * + * @private * @param {RegExp} regex anchored regex * @return {Function} function to parse input based on the regex */ @@ -378,11 +428,10 @@ }; } - /** - * =================================================================== - * General patterns above this line -- wikitext specific parsers below - * =================================================================== - */ + // =================================================================== + // General patterns above this line -- wikitext specific parsers below + // =================================================================== + // Parsing functions follow. All parsing functions work like this: // They don't accept any arguments. // Instead, they just operate non destructively on the string 'input' @@ -736,13 +785,20 @@ ] ); return result === null ? null : [ result[0], result[2] ]; } + function templateWithOutFirstParameter() { + var result = sequence( [ + templateName, + colon + ] ); + return result === null ? null : [ result[0], '' ]; + } colon = makeStringParser( ':' ); templateContents = choice( [ function () { var res = sequence( [ // templates can have placeholders for dynamic replacement eg: {{PLURAL:$1|one car|$1 cars}} // or no placeholders eg: {{GRAMMAR:genitive|{{SITENAME}}} - choice( [ templateWithReplacement, templateWithOutReplacement ] ), + choice( [ templateWithReplacement, templateWithOutReplacement, templateWithOutFirstParameter ] ), nOrMore( 0, templateParam ) ] ); return res === null ? null : res[0].concat( res[1] ); @@ -889,7 +945,7 @@ * Parsing has been applied depth-first we can assume that all nodes here are single nodes * Must return a single node to parents -- a jQuery with synthetic span * However, unwrap any other synthetic spans in our children and pass them upwards - * @param {Array} nodes - mixed, some single nodes, some arrays of nodes + * @param {Mixed[]} nodes Some single nodes, some arrays of nodes * @return {jQuery} */ concat: function ( nodes ) { @@ -913,8 +969,11 @@ * Note that we expect the parsed parameter to be zero-based. i.e. $1 should have become [ 0 ]. * if the specified parameter is not found return the same string * (e.g. "$99" -> parameter 98 -> not found -> return "$99" ) + * * TODO: Throw error if nodes.length > 1 ? - * @param {Array} of one element, integer, n >= 0 + * + * @param {Array} nodes List of one element, integer, n >= 0 + * @param {Array} replacements * @return {String} replacement */ replace: function ( nodes, replacements ) { @@ -970,8 +1029,9 @@ /** * Converts array of HTML element key value pairs to object * - * @param {Array} nodes array of consecutive key value pairs, with index 2 * n being a name and 2 * n + 1 the associated value - * @return {Object} object mapping attribute name to attribute value + * @param {Array} nodes Array of consecutive key value pairs, with index 2 * n being a + * name and 2 * n + 1 the associated value + * @return {Object} Object mapping attribute name to attribute value */ htmlattributes: function ( nodes ) { var i, len, mapping = {}; @@ -984,7 +1044,7 @@ /** * Handles an (already-validated) HTML element. * - * @param {Array} nodes nodes to process when creating element + * @param {Array} nodes Nodes to process when creating element * @return {jQuery|Array} jQuery node for valid HTML or array for disallowed element */ htmlelement: function ( nodes ) { @@ -1000,10 +1060,13 @@ /** * Transform parsed structure into external link * If the href is a jQuery object, treat it as "enclosing" the link text. - * ... function, treat it as the click handler - * ... string, treat it as a URI + * + * - ... function, treat it as the click handler. + * - ... string, treat it as a URI. + * * TODO: throw an error if nodes.length > 2 ? - * @param {Array} of two elements, {jQuery|Function|String} and {String} + * + * @param {Array} nodes List of two elements, {jQuery|Function|String} and {String} * @return {jQuery} */ extlink: function ( nodes ) { @@ -1028,9 +1091,11 @@ * as url), but we don't want to run the regular replace here-on: inserting a * url as href-attribute of a link will automatically escape it already, so * we don't want replace to (manually) escape it as well. - * TODO throw error if nodes.length > 1 ? - * @param {Array} of one element, integer, n >= 0 - * @return {String} replacement + * + * TODO: throw error if nodes.length > 1 ? + * + * @param {Array} nodes List of one element, integer, n >= 0 + * @return {string} replacement */ extlinkparam: function ( nodes, replacements ) { var replacement, @@ -1047,8 +1112,8 @@ * Transform parsed structure into pluralization * n.b. The first node may be a non-integer (for instance, a string representing an Arabic number). * So convert it back with the current language's convertNumber. - * @param {Array} of nodes, [ {String|Number}, {String}, {String} ... ] - * @return {String} selected pluralized form according to current language + * @param {Array} nodes List of nodes, [ {string|number}, {string}, {string} ... ] + * @return {string} selected pluralized form according to current language */ plural: function ( nodes ) { var forms, count; @@ -1059,32 +1124,42 @@ /** * Transform parsed structure according to gender. - * Usage {{gender:[ gender | mw.user object ] | masculine form|feminine form|neutral form}}. - * The first node is either a string, which can be "male" or "female", - * or a User object (not a username). * - * @param {Array} of nodes, [ {String|mw.User}, {String}, {String}, {String} ] - * @return {String} selected gender form according to current language + * Usage: {{gender:[ mw.user object | '' | 'male' | 'female' | 'unknown' ] | masculine form | feminine form | neutral form}}. + * + * The first node must be one of: + * - the mw.user object (or a compatible one) + * - an empty string - indicating the current user, same effect as passing the mw.user object + * - a gender string ('male', 'female' or 'unknown') + * + * @param {Array} nodes List of nodes, [ {string|mw.user}, {string}, {string}, {string} ] + * @return {string} Selected gender form according to current language */ gender: function ( nodes ) { - var gender, forms; + var gender, + maybeUser = nodes[0], + forms = nodes.slice( 1 ); - if ( nodes[0] && nodes[0].options instanceof mw.Map ) { - gender = nodes[0].options.get( 'gender' ); - } else { - gender = nodes[0]; + if ( maybeUser === '' ) { + maybeUser = mw.user; } - forms = nodes.slice( 1 ); + // If we are passed a mw.user-like object, check their gender. + // Otherwise, assume the gender string itself was passed . + if ( maybeUser && maybeUser.options instanceof mw.Map ) { + gender = maybeUser.options.get( 'gender' ); + } else { + gender = maybeUser; + } return this.language.gender( gender, forms ); }, /** * Transform parsed structure into grammar conversion. - * Invoked by putting {{grammar:form|word}} in a message - * @param {Array} of nodes [{Grammar case eg: genitive}, {String word}] - * @return {String} selected grammatical form according to current language + * Invoked by putting `{{grammar:form|word}}` in a message + * @param {Array} nodes List of nodes [{Grammar case eg: genitive}, {string word}] + * @return {string} selected grammatical form according to current language */ grammar: function ( nodes ) { var form = nodes[0], @@ -1094,8 +1169,8 @@ /** * Tranform parsed structure into a int: (interface language) message include - * Invoked by putting {{int:othermessage}} into a message - * @param {Array} of nodes + * Invoked by putting `{{int:othermessage}}` into a message + * @param {Array} nodes List of nodes * @return {string} Other message */ int: function ( nodes ) { @@ -1105,9 +1180,9 @@ /** * Takes an unformatted number (arab, no group separators and . as decimal separator) * and outputs it in the localized digit script and formatted with decimal - * separator, according to the current language - * @param {Array} of nodes - * @return {Number|String} formatted number + * separator, according to the current language. + * @param {Array} nodes List of nodes + * @return {number|string} Formatted number */ formatnum: function ( nodes ) { var isInteger = ( nodes[1] && nodes[1] === 'R' ) ? true : false, @@ -1116,11 +1191,19 @@ return this.language.convertNumber( number, isInteger ); } }; + // Deprecated! don't rely on gM existing. // The window.gM ought not to be required - or if required, not required here. // But moving it to extensions breaks it (?!) // Need to fix plugin so it could do attributes as well, then will be okay to remove this. - window.gM = mw.jqueryMsg.getMessageFunction(); + // @deprecated since 1.23 + mw.log.deprecate( window, 'gM', mw.jqueryMsg.getMessageFunction(), 'Use mw.message( ... ).parse() instead' ); + + /** + * @method + * @member jQuery + * @see mw.jqueryMsg#getPlugin + */ $.fn.msg = mw.jqueryMsg.getPlugin(); // Replace the default message parser with jqueryMsg