summaryrefslogtreecommitdiff
path: root/resources/mediawiki/mediawiki.jqueryMsg.js
diff options
context:
space:
mode:
Diffstat (limited to 'resources/mediawiki/mediawiki.jqueryMsg.js')
-rw-r--r--resources/mediawiki/mediawiki.jqueryMsg.js411
1 files changed, 212 insertions, 199 deletions
diff --git a/resources/mediawiki/mediawiki.jqueryMsg.js b/resources/mediawiki/mediawiki.jqueryMsg.js
index 6c00bd15..86af31ff 100644
--- a/resources/mediawiki/mediawiki.jqueryMsg.js
+++ b/resources/mediawiki/mediawiki.jqueryMsg.js
@@ -1,22 +1,27 @@
/**
- * Experimental advanced wikitext parser-emitter.
- * See: http://www.mediawiki.org/wiki/Extension:UploadWizard/MessageParser for docs
- *
- * @author neilk@wikimedia.org
- */
-
-( function( mw, $, undefined ) {
-
- mw.jqueryMsg = {};
+* Experimental advanced wikitext parser-emitter.
+* See: http://www.mediawiki.org/wiki/Extension:UploadWizard/MessageParser for docs
+*
+* @author neilk@wikimedia.org
+*/
+( function ( mw, $ ) {
+ var slice = Array.prototype.slice,
+ parserDefaults = {
+ magic : {
+ 'SITENAME' : mw.config.get( 'wgSiteName' )
+ },
+ messages : mw.messages,
+ language : mw.language
+ };
/**
* Given parser options, return a function that parses a key and replacements, returning jQuery object
* @param {Object} parser options
* @return {Function} accepting ( String message key, String replacement1, String replacement2 ... ) and returning {jQuery}
*/
- function getFailableParserFn( options ) {
- var parser = new mw.jqueryMsg.parser( options );
- /**
+ 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.
@@ -24,24 +29,23 @@
* @param {Array} first element is the key, replacements may be in array in 2nd element, or remaining elements.
* @return {jQuery}
*/
- return function( args ) {
+ return function ( args ) {
var key = args[0];
- var argsArray = $.isArray( args[1] ) ? args[1] : $.makeArray( args ).slice( 1 );
- var escapedArgsArray = $.map( argsArray, function( arg ) {
- return typeof arg === 'string' ? mw.html.escape( arg ) : arg;
- } );
+ var argsArray = $.isArray( args[1] ) ? args[1] : slice.call( args, 1 );
try {
- return parser.parse( key, escapedArgsArray );
+ return parser.parse( key, argsArray );
} catch ( e ) {
- return $( '<span></span>' ).append( key + ': ' + e.message );
+ return $( '<span>' ).append( key + ': ' + e.message );
}
};
}
+ mw.jqueryMsg = {};
+
/**
- * Class method.
+ * Class method.
* Returns a function suitable for use as a global, to construct strings from the message key (and optional replacements).
- * e.g.
+ * e.g.
* window.gM = mediaWiki.parser.getMessageFunction( options );
* $( 'p#headline' ).html( gM( 'hello-user', username ) );
*
@@ -51,75 +55,68 @@
* @param {Array} parser options
* @return {Function} function suitable for assigning to window.gM
*/
- mw.jqueryMsg.getMessageFunction = function( options ) {
+ mw.jqueryMsg.getMessageFunction = 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)
+ * is equivalent to
* somefunction(a, [b, c, d])
*
* @param {String} message key
* @param {Array} optional replacements (can also specify variadically)
* @return {String} rendered HTML as string
*/
- return function( /* key, replacements */ ) {
+ return function ( /* key, replacements */ ) {
return failableParserFn( arguments ).html();
};
};
/**
- * 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.
+ * 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 = $( '<a>' ).click( function() { alert( "hello!!") } );
+ * var userlink = $( '<a>' ).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
*/
- mw.jqueryMsg.getPlugin = function( options ) {
+ 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)
+ * 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} message key
* @param {Array} optional replacements (can also specify variadically)
* @return {jQuery} this
*/
- return function( /* key, replacements */ ) {
+ return function ( /* key, replacements */ ) {
var $target = this.empty();
- $.each( failableParserFn( arguments ).contents(), function( i, node ) {
+ $.each( failableParserFn( arguments ).contents(), function ( i, node ) {
$target.append( node );
} );
return $target;
};
};
- var parserDefaults = {
- 'magic' : {},
- 'messages' : mw.messages,
- 'language' : mw.language
- };
-
/**
* The parser itself.
* Describes an object, whose primary duty is to .parse() message keys.
* @param {Array} options
*/
- mw.jqueryMsg.parser = function( options ) {
+ mw.jqueryMsg.parser = function ( options ) {
this.settings = $.extend( {}, parserDefaults, options );
this.emitter = new mw.jqueryMsg.htmlEmitter( this.settings.language, this.settings.magic );
};
mw.jqueryMsg.parser.prototype = {
-
// cache, map of mediaWiki message key 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).
astCache: {},
@@ -132,51 +129,46 @@
* @param {Array} replacements for $1, $2... $n
* @return {jQuery}
*/
- parse: function( key, replacements ) {
+ 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
+ * 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
*/
- getAst: function( key ) {
- if ( this.astCache[ key ] === undefined ) {
+ getAst: function ( key ) {
+ if ( this.astCache[ key ] === undefined ) {
var wikiText = this.settings.messages.get( key );
if ( typeof wikiText !== 'string' ) {
wikiText = "\\[" + key + "\\]";
}
this.astCache[ key ] = this.wikiTextToAst( wikiText );
}
- return this.astCache[ key ];
+ return this.astCache[ key ];
},
-
/*
* Parses the input wikiText into an abstract syntax tree, essentially an s-expression.
*
* 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
* @throws Error
* @return {Mixed} abstract syntax tree
*/
- wikiTextToAst: function( input ) {
-
- // Indicates current position in input as we parse through it.
- // Shared among all parsing functions below.
- var pos = 0;
+ wikiTextToAst: function ( input ) {
+ // Indicates current position in input as we parse through it.
+ // Shared among all parsing functions below.
+ var pos = 0;
// =========================================================
// 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
function choice( ps ) {
- return function() {
+ return function () {
for ( var i = 0; i < ps.length; i++ ) {
var result = ps[i]();
if ( result !== null ) {
@@ -186,27 +178,25 @@
return null;
};
}
-
// try several ps in a row, all must succeed or return null
// this is the only eager one
function sequence( ps ) {
var originalPos = pos;
var result = [];
- for ( var i = 0; i < ps.length; i++ ) {
+ for ( var i = 0; i < ps.length; i++ ) {
var res = ps[i]();
if ( res === null ) {
pos = originalPos;
return null;
- }
+ }
result.push( res );
}
return result;
}
-
// run the same parser over and over until it fails.
// must succeed a minimum of n times or return null
function nOrMore( n, p ) {
- return function() {
+ return function () {
var originalPos = pos;
var result = [];
var parsed = p();
@@ -217,26 +207,23 @@
if ( result.length < n ) {
pos = originalPos;
return null;
- }
+ }
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
function transform( p, fn ) {
- return function() {
+ return function () {
var result = p();
return result === null ? null : fn( result );
};
}
-
// Helpers -- just make ps out of simpler JS builtin types
-
- function makeStringParser( s ) {
+ function makeStringParser( s ) {
var len = s.length;
- return function() {
+ return function () {
var result = null;
if ( input.substr( pos, len ) === s ) {
result = s;
@@ -245,105 +232,87 @@
return result;
};
}
-
function makeRegexParser( regex ) {
- return function() {
+ return function () {
var matches = input.substr( pos ).match( regex );
- if ( matches === null ) {
+ if ( matches === null ) {
return null;
- }
+ }
pos += matches[0].length;
return matches[0];
};
}
-
- /**
- * ===================================================================
+ /**
+ * ===================================================================
* 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'
// As they can consume parts of the string, they advance the shared variable pos,
// and return tokens (or whatever else they want to return).
-
// some things are defined as closures and other things as ordinary functions
// converting everything to a closure makes it a lot harder to debug... errors pop up
// but some debuggers can't tell you exactly where they come from. Also the mutually
// recursive functions seem not to work in all browsers then. (Tested IE6-7, Opera, Safari, FF)
// This may be because, to save code, memoization was removed
-
-
- var regularLiteral = makeRegexParser( /^[^{}[\]$\\]/ );
- var regularLiteralWithoutBar = makeRegexParser(/^[^{}[\]$\\|]/);
- var regularLiteralWithoutSpace = makeRegexParser(/^[^{}[\]$\s]/);
-
+ var regularLiteral = makeRegexParser( /^[^{}\[\]$\\]/ );
+ var regularLiteralWithoutBar = makeRegexParser(/^[^{}\[\]$\\|]/);
+ var regularLiteralWithoutSpace = makeRegexParser(/^[^{}\[\]$\s]/);
var backslash = makeStringParser( "\\" );
var anyCharacter = makeRegexParser( /^./ );
-
function escapedLiteral() {
var result = sequence( [
- backslash,
+ backslash,
anyCharacter
] );
return result === null ? null : result[1];
}
-
var escapedOrLiteralWithoutSpace = choice( [
escapedLiteral,
regularLiteralWithoutSpace
] );
-
var escapedOrLiteralWithoutBar = choice( [
escapedLiteral,
regularLiteralWithoutBar
] );
-
- var escapedOrRegularLiteral = choice( [
+ var escapedOrRegularLiteral = choice( [
escapedLiteral,
regularLiteral
] );
-
// Used to define "literals" without spaces, in space-delimited situations
function literalWithoutSpace() {
var result = nOrMore( 1, escapedOrLiteralWithoutSpace )();
return result === null ? null : result.join('');
}
-
- // Used to define "literals" within template parameters. The pipe character is the parameter delimeter, so by default
+ // Used to define "literals" within template parameters. The pipe character is the parameter delimeter, so by default
// it is not a literal in the parameter
function literalWithoutBar() {
var result = nOrMore( 1, escapedOrLiteralWithoutBar )();
return result === null ? null : result.join('');
}
-
function literal() {
var result = nOrMore( 1, escapedOrRegularLiteral )();
return result === null ? null : result.join('');
}
-
- var whitespace = makeRegexParser( /^\s+/ );
+ var whitespace = makeRegexParser( /^\s+/ );
var dollar = makeStringParser( '$' );
- var digits = makeRegexParser( /^\d+/ );
+ var digits = makeRegexParser( /^\d+/ );
function replacement() {
var result = sequence( [
dollar,
digits
] );
- if ( result === null ) {
+ if ( result === null ) {
return null;
}
return [ 'REPLACE', parseInt( result[1], 10 ) - 1 ];
}
-
-
var openExtlink = makeStringParser( '[' );
var closeExtlink = makeStringParser( ']' );
-
// this extlink MUST have inner text, e.g. [foo] not allowed; [foo bar] is allowed
function extlink() {
var result = null;
@@ -359,10 +328,23 @@
}
return result;
}
-
+ // this is the same as the above extlink, except that the url is being passed on as a parameter
+ function extLinkParam() {
+ var result = sequence( [
+ openExtlink,
+ dollar,
+ digits,
+ whitespace,
+ expression,
+ closeExtlink
+ ] );
+ if ( result === null ) {
+ return null;
+ }
+ return [ 'LINKPARAM', parseInt( result[2], 10 ) - 1, result[4] ];
+ }
var openLink = makeStringParser( '[[' );
var closeLink = makeStringParser( ']]' );
-
function link() {
var result = null;
var parsedResult = sequence( [
@@ -375,16 +357,14 @@
}
return result;
}
-
- var templateName = transform(
+ var templateName = transform(
// see $wgLegalTitleChars
// not allowing : due to the need to catch "PLURAL:$1"
- makeRegexParser( /^[ !"$&'()*,.\/0-9;=?@A-Z\^_`a-z~\x80-\xFF+-]+/ ),
- function( result ) { return result.toString(); }
+ makeRegexParser( /^[ !"$&'()*,.\/0-9;=?@A-Z\^_`a-z~\x80-\xFF+\-]+/ ),
+ function ( result ) { return result.toString(); }
);
-
function templateParam() {
- var result = sequence( [
+ var result = sequence( [
pipe,
nOrMore( 0, paramExpression )
] );
@@ -395,9 +375,7 @@
// use a "CONCAT" operator if there are multiple nodes, otherwise return the first node, raw.
return expr.length > 1 ? [ "CONCAT" ].concat( expr ) : expr[0];
}
-
var pipe = makeStringParser( '|' );
-
function templateWithReplacement() {
var result = sequence( [
templateName,
@@ -406,21 +384,29 @@
] );
return result === null ? null : [ result[0], result[2] ];
}
-
+ function templateWithOutReplacement() {
+ var result = sequence( [
+ templateName,
+ colon,
+ paramExpression
+ ] );
+ return result === null ? null : [ result[0], result[2] ];
+ }
var colon = makeStringParser(':');
-
var templateContents = choice( [
- function() {
+ function () {
var res = sequence( [
- templateWithReplacement,
+ // templates can have placeholders for dynamic replacement eg: {{PLURAL:$1|one car|$1 cars}}
+ // or no placeholders eg: {{GRAMMAR:genitive|{{SITENAME}}}
+ choice( [ templateWithReplacement, templateWithOutReplacement ] ),
nOrMore( 0, templateParam )
] );
return res === null ? null : res[0].concat( res[1] );
},
- function() {
+ function () {
var res = sequence( [
templateName,
- nOrMore( 0, templateParam )
+ nOrMore( 0, templateParam )
] );
if ( res === null ) {
return null;
@@ -428,10 +414,8 @@
return [ res[0] ].concat( res[1] );
}
] );
-
var openTemplate = makeStringParser('{{');
var closeTemplate = makeStringParser('}}');
-
function template() {
var result = sequence( [
openTemplate,
@@ -440,31 +424,30 @@
] );
return result === null ? null : result[1];
}
-
var nonWhitespaceExpression = choice( [
- template,
+ template,
link,
+ extLinkParam,
extlink,
replacement,
literalWithoutSpace
] );
-
var paramExpression = choice( [
- template,
+ template,
link,
+ extLinkParam,
extlink,
replacement,
literalWithoutBar
] );
-
- var expression = choice( [
+ var expression = choice( [
template,
link,
+ extLinkParam,
extlink,
replacement,
- literal
+ literal
] );
-
function start() {
var result = nOrMore( 0, expression )();
if ( result === null ) {
@@ -472,16 +455,13 @@
}
return [ "CONCAT" ].concat( result );
}
-
// everything above this point is supposed to be stateless/static, but
// I am deferring the work of turning it into prototypes & objects. It's quite fast enough
-
// finally let's do some actual work...
-
var result = start();
-
+
/*
- * For success, the p must have gotten to the end of the input
+ * For success, the p must have gotten to the end of the input
* and returned a non-null.
* n.b. This is part of language infrastructure, so we do not throw an internationalizable message.
*/
@@ -490,20 +470,19 @@
}
return result;
}
-
- };
+ };
/**
* htmlEmitter - object which primarily exists to emit HTML from parser ASTs
*/
- mw.jqueryMsg.htmlEmitter = function( language, magic ) {
+ mw.jqueryMsg.htmlEmitter = function ( language, magic ) {
this.language = language;
- var _this = this;
-
- $.each( magic, function( key, val ) {
- _this[ key.toLowerCase() ] = function() { return val; };
+ var jmsg = this;
+ $.each( magic, function ( key, val ) {
+ jmsg[ key.toLowerCase() ] = function () {
+ return val;
+ };
} );
-
/**
* (We put this method definition here, and not in prototype, to make sure it's not overwritten by any magic.)
* Walk entire node structure, applying replacements and template functions when appropriate
@@ -511,23 +490,23 @@
* @param {Array} replacements for $1, $2, ... $n
* @return {Mixed} single-string node or array of nodes suitable for jQuery appending
*/
- this.emit = function( node, replacements ) {
+ this.emit = function ( node, replacements ) {
var ret = null;
- var _this = this;
- switch( typeof node ) {
+ var jmsg = this;
+ switch ( typeof node ) {
case 'string':
case 'number':
ret = node;
break;
case 'object': // node is an array of nodes
- var subnodes = $.map( node.slice( 1 ), function( n ) {
- return _this.emit( n, replacements );
+ var subnodes = $.map( node.slice( 1 ), function ( n ) {
+ return jmsg.emit( n, replacements );
} );
var operation = node[0].toLowerCase();
- if ( typeof _this[operation] === 'function' ) {
- ret = _this[ operation ]( subnodes, replacements );
+ if ( typeof jmsg[operation] === 'function' ) {
+ ret = jmsg[ operation ]( subnodes, replacements );
} else {
- throw new Error( 'unknown operation "' + operation + '"' );
+ throw new Error( 'Unknown operation "' + operation + '"' );
}
break;
case 'undefined':
@@ -537,21 +516,18 @@
ret = '';
break;
default:
- throw new Error( 'unexpected type in AST: ' + typeof node );
+ throw new Error( 'Unexpected type in AST: ' + typeof node );
}
return ret;
};
-
};
-
// For everything in input that follows double-open-curly braces, there should be an equivalent parser
- // function. For instance {{PLURAL ... }} will be processed by 'plural'.
+ // function. For instance {{PLURAL ... }} will be processed by 'plural'.
// If you have 'magic words' then configure the parser to have them upon creation.
//
// An emitter method takes the parent node, the array of subnodes and the array of replacements (the values that $1, $2... should translate to).
// Note: all such functions must be pure, with the exception of referring to other pure functions via this.language (convertPlural and so on)
mw.jqueryMsg.htmlEmitter.prototype = {
-
/**
* 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
@@ -559,23 +535,23 @@
* @param {Array} nodes - mixed, some single nodes, some arrays of nodes
* @return {jQuery}
*/
- concat: function( nodes ) {
- var span = $( '<span>' ).addClass( 'mediaWiki_htmlEmitter' );
- $.each( nodes, function( i, node ) {
+ concat: function ( nodes ) {
+ var $span = $( '<span>' ).addClass( 'mediaWiki_htmlEmitter' );
+ $.each( nodes, function ( i, node ) {
if ( node instanceof jQuery && node.hasClass( 'mediaWiki_htmlEmitter' ) ) {
- $.each( node.contents(), function( j, childNode ) {
- span.append( childNode );
+ $.each( node.contents(), function ( j, childNode ) {
+ $span.append( childNode );
} );
} else {
// strings, integers, anything else
- span.append( node );
+ $span.append( node );
}
} );
- return span;
+ return $span;
},
/**
- * Return replacement of correct index, or string if unavailable.
+ * Return escaped replacement of correct index, or string if unavailable.
* 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" )
@@ -583,17 +559,29 @@
* @param {Array} of one element, integer, n >= 0
* @return {String} replacement
*/
- replace: function( nodes, replacements ) {
+ replace: function ( nodes, replacements ) {
var index = parseInt( nodes[0], 10 );
- return index < replacements.length ? replacements[index] : '$' + ( index + 1 );
+
+ if ( index < replacements.length ) {
+ if ( typeof arg === 'string' ) {
+ // replacement is a string, escape it
+ return mw.html.escape( replacements[index] );
+ } else {
+ // replacement is no string, don't touch!
+ return replacements[index];
+ }
+ } else {
+ // index not found, fallback to displaying variable
+ return '$' + ( index + 1 );
+ }
},
- /**
+ /**
* Transform wiki-link
- * TODO unimplemented
+ * TODO unimplemented
*/
- wlink: function( nodes ) {
- return "unimplemented";
+ wlink: function ( nodes ) {
+ return 'unimplemented';
},
/**
@@ -601,14 +589,14 @@
* 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
- * TODO: throw an error if nodes.length > 2 ?
+ * TODO: throw an error if nodes.length > 2 ?
* @param {Array} of two elements, {jQuery|Function|String} and {String}
* @return {jQuery}
*/
- link: function( nodes ) {
+ link: function ( nodes ) {
var arg = nodes[0];
var contents = nodes[1];
- var $el;
+ var $el;
if ( arg instanceof jQuery ) {
$el = arg;
} else {
@@ -619,19 +607,39 @@
$el.attr( 'href', arg.toString() );
}
}
- $el.append( contents );
+ $el.append( contents );
return $el;
},
/**
+ * This is basically use a combination of replace + link (link with parameter
+ * 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
+ */
+ linkparam: function ( nodes, replacements ) {
+ var replacement,
+ index = parseInt( nodes[0], 10 );
+ if ( index < replacements.length) {
+ replacement = replacements[index];
+ } else {
+ replacement = '$' + ( index + 1 );
+ }
+ return this.link( [ replacement, nodes[1] ] );
+ },
+
+ /**
* 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} ... ]
+ * @param {Array} of nodes, [ {String|Number}, {String}, {String} ... ]
* @return {String} selected pluralized form according to current language
*/
- plural: function( nodes ) {
- var count = parseInt( this.language.convertNumber( nodes[0], true ), 10 );
+ plural: function ( nodes ) {
+ var count = parseFloat( this.language.convertNumber( nodes[0], true ) );
var forms = nodes.slice(1);
return forms.length ? this.language.convertPlural( count, forms ) : '';
},
@@ -639,10 +647,10 @@
/**
* Transform parsed structure into gender
* Usage {{gender:[gender| mw.user object ] | masculine|feminine|neutral}}.
- * @param {Array} of nodes, [ {String|mw.User}, {String}, {String} , {String} ]
+ * @param {Array} of nodes, [ {String|mw.User}, {String}, {String} , {String} ]
* @return {String} selected gender form according to current language
*/
- gender: function( nodes ) {
+ gender: function ( nodes ) {
var gender;
if ( nodes[0] && nodes[0].options instanceof mw.Map ){
gender = nodes[0].options.get( 'gender' );
@@ -651,35 +659,40 @@
}
var forms = nodes.slice(1);
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
+ */
+ grammar: function ( nodes ) {
+ var form = nodes[0];
+ var word = nodes[1];
+ return word && form && this.language.convertGrammar( word, form );
+ }
};
-
- // TODO figure out a way to make magic work with common globals like wgSiteName, without requiring init from library users...
- // var options = { magic: { 'SITENAME' : mw.config.get( 'wgSiteName' ) } };
-
- // 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 (?!)
+ // 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();
-
+ window.gM = mw.jqueryMsg.getMessageFunction();
$.fn.msg = mw.jqueryMsg.getPlugin();
-
+
// Replace the default message parser with jqueryMsg
var oldParser = mw.Message.prototype.parser;
- mw.Message.prototype.parser = function() {
+ mw.Message.prototype.parser = function () {
// TODO: should we cache the message function so we don't create a new one every time? Benchmark this maybe?
// Caching is somewhat problematic, because we do need different message functions for different maps, so
// we'd have to cache the parser as a member of this.map, which sounds a bit ugly.
-
// Do not use mw.jqueryMsg unless required
if ( this.map.get( this.key ).indexOf( '{{' ) < 0 ) {
// Fall back to mw.msg's simple parser
return oldParser.apply( this );
}
-
var messageFunction = mw.jqueryMsg.getMessageFunction( { 'messages': this.map } );
return messageFunction( this.key, this.parameters );
};
-} )( mediaWiki, jQuery );
+}( mediaWiki, jQuery ) );