wikimedia/mediawiki-core

View on GitHub
resources/lib/jquery.i18n/src/jquery.i18n.emitter.js

Summary

Maintainability
A
0 mins
Test Coverage
/*!
 * jQuery Internationalization library
 *
 * Copyright (C) 2011-2013 Santhosh Thottingal, Neil Kandalgaonkar
 *
 * jquery.i18n is dual licensed GPLv2 or later and MIT. You don't have to do
 * anything special to choose one license or the other and you don't have to
 * notify anyone which license you are using. You are free to use
 * UniversalLanguageSelector in commercial projects as long as the copyright
 * header is left intact. See files GPL-LICENSE and MIT-LICENSE for details.
 *
 * @licence GNU General Public Licence 2.0 or later
 * @licence MIT License
 */

( function ( $ ) {
    'use strict';

    var MessageParserEmitter = function () {
        this.language = $.i18n.languages[ String.locale ] || $.i18n.languages[ 'default' ];
    };

    MessageParserEmitter.prototype = {
        constructor: MessageParserEmitter,

        /**
         * (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
         *
         * @param {Mixed} node abstract syntax tree (top node or subnode)
         * @param {Array} replacements for $1, $2, ... $n
         * @return {Mixed} single-string node or array of nodes suitable for
         *  jQuery appending.
         */
        emit: function ( node, replacements ) {
            var ret, subnodes, operation,
                messageParserEmitter = this;

            switch ( typeof node ) {
                case 'string':
                case 'number':
                    ret = node;
                    break;
                case 'object':
                // node is an array of nodes
                    subnodes = $.map( node.slice( 1 ), function ( n ) {
                        return messageParserEmitter.emit( n, replacements );
                    } );

                    operation = node[ 0 ].toLowerCase();

                    if ( typeof messageParserEmitter[ operation ] === 'function' ) {
                        ret = messageParserEmitter[ operation ]( subnodes, replacements );
                    } else {
                        throw new Error( 'unknown operation "' + operation + '"' );
                    }

                    break;
                case 'undefined':
                // Parsing the empty string (as an entire expression, or as a
                // paramExpression in a template) results in undefined
                // Perhaps a more clever parser can detect this, and return the
                // empty string? Or is that useful information?
                // The logical thing is probably to return the empty string here
                // when we encounter undefined.
                    ret = '';
                    break;
                default:
                    throw new Error( 'unexpected type in AST: ' + typeof node );
            }

            return ret;
        },

        /**
         * 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.
         * @return {string}
         */
        concat: function ( nodes ) {
            var result = '';

            $.each( nodes, function ( i, node ) {
                // strings, integers, anything else
                result += node;
            } );

            return result;
        },

        /**
         * 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" ) TODO throw error if
         * nodes.length > 1 ?
         *
         * @param {Array} nodes One element, integer, n >= 0
         * @param {Array} replacements for $1, $2, ... $n
         * @return {string} replacement
         */
        replace: function ( nodes, replacements ) {
            var index = parseInt( nodes[ 0 ], 10 );

            if ( index < replacements.length ) {
                // replacement is not a string, don't touch!
                return replacements[ index ];
            } else {
                // index not found, fallback to displaying variable
                return '$' + ( index + 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} nodes List [ {String|Number}, {String}, {String} ... ]
         * @return {string} selected pluralized form according to current
         *  language.
         */
        plural: function ( nodes ) {
            var count = parseFloat( this.language.convertNumber( nodes[ 0 ], 10 ) ),
                forms = nodes.slice( 1 );

            return forms.length ? this.language.convertPlural( count, forms ) : '';
        },

        /**
         * Transform parsed structure into gender Usage
         * {{gender:gender|masculine|feminine|neutral}}.
         *
         * @param {Array} nodes List [ {String}, {String}, {String} , {String} ]
         * @return {string} selected gender form according to current language
         */
        gender: function ( nodes ) {
            var gender = nodes[ 0 ],
                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} nodes List [{Grammar case eg: genitive}, {String word}]
         * @return {string} selected grammatical form according to current
         *  language.
         */
        grammar: function ( nodes ) {
            var form = nodes[ 0 ],
                word = nodes[ 1 ];

            return word && form && this.language.convertGrammar( word, form );
        }
    };

    $.extend( $.i18n.parser.emitter, new MessageParserEmitter() );
}( jQuery ) );