wikimedia/mediawiki-extensions-VisualEditor

View on GitHub
modules/ve-mw/ui/contextitems/ve.ui.MWDefinedTransclusionContextItem.js

Summary

Maintainability
A
45 mins
Test Coverage
/*!
 * VisualEditor MWDefinedTransclusionContextItem class.
 *
 * @copyright See AUTHORS.txt
 */

/**
 * Context item for a defined MWTransclusion.
 *
 * Templates are defined on-wiki using the message page
 * [[MediaWiki:Visualeditor-template-tools-definition.json]]
 *
 * Example:
 * {
 *   // This key is the static name of the ve.ui.ContextItem
 *   "citationNeeded": [
 *     {
 *       // Normalized title string, or list of strings (for redirects)
 *       "title": [ "Citation needed", "cn" ],
 *       // Extra params. This whole object can be accessed
 *       // via #getMatchedTool
 *       "params": {
 *         "reason": "reason",
 *         "date": "date"
 *       }
 *     },
 *     {
 *       "title": "Cite quote",
 *       "params": {
 *         "date": "date"
 *       }
 *     }
 *   ]
 * }
 *
 * @class
 * @abstract
 * @extends ve.ui.MWTransclusionContextItem
 *
 * @constructor
 * @param {ve.ui.LinearContext} context Context the item is in
 * @param {ve.dm.Model} model Model the item is related to
 * @param {Object} [config]
 */
ve.ui.MWDefinedTransclusionContextItem = function VeUiMWDefinedTransclusionContextItem() {
    // Parent constructor
    ve.ui.MWDefinedTransclusionContextItem.super.apply( this, arguments );

    this.tool = this.constructor.static.getMatchedTool( this.model );
};

/* Inheritance */

OO.inheritClass( ve.ui.MWDefinedTransclusionContextItem, ve.ui.MWTransclusionContextItem );

/* Static Properties */

ve.ui.MWDefinedTransclusionContextItem.static.name = null;

ve.ui.MWDefinedTransclusionContextItem.static.toolDefinitions = ( function () {
    let tools;

    try {
        // Must use mw.message to avoid JSON being parsed as Wikitext
        tools = JSON.parse( mw.message( 'visualeditor-template-tools-definition.json' ).plain() );
    } catch ( e ) {}

    return tools || {};
}() );

/**
 * Only display item for single-template transclusions of these templates.
 *
 * @property {string|string[]|null}
 * @static
 * @inheritable
 */
ve.ui.MWDefinedTransclusionContextItem.static.template = null;

/* Static Methods */

/**
 * @inheritdoc
 */
ve.ui.MWDefinedTransclusionContextItem.static.isCompatibleWith = function ( model ) {
    // Parent method
    return ve.ui.MWDefinedTransclusionContextItem.super.static.isCompatibleWith.apply( this, arguments ) &&
        !!this.getMatchedTool( model );
};

/**
 * Get tool definitions, indexed by normalized title
 *
 * @return {Object} Collection of tool definitions
 */
ve.ui.MWDefinedTransclusionContextItem.static.getToolsByTitle = function () {
    if ( !this.toolsByTitle ) {
        let toolsByTitle;
        this.toolsByTitle = toolsByTitle = {};
        ( this.toolDefinitions[ this.name ] || [] ).forEach( ( template ) => {
            const titles = Array.isArray( template.title ) ? template.title : [ template.title ];
            // 'title' can be a single title, or list of titles (including redirects)
            titles.forEach( ( title ) => {
                toolsByTitle[ mw.Title.newFromText( title, mw.config.get( 'wgNamespaceIds' ).template ).getPrefixedText() ] = template;
            } );
        } );
    }
    return this.toolsByTitle;
};

/**
 * Get the tool definition that matches a specific model, if any
 *
 * @param {ve.dm.Model} model
 * @return {Object|null} Tool definition, or null if no match
 */
ve.ui.MWDefinedTransclusionContextItem.static.getMatchedTool = function ( model ) {
    const resource = ve.getProp( model.getAttribute( 'mw' ), 'parts', 0, 'template', 'target', 'href' );
    if ( resource ) {
        const title = mw.Title.newFromText( mw.libs.ve.normalizeParsoidResourceName( resource ) ).getPrefixedText();
        return this.getToolsByTitle()[ title ] || null;
    }
    return null;
};

/**
 * Get a template param using its canonical name
 *
 * @param {string} name Canonical parameter name
 * @return {string|null} Param wikitext, null if not found
 */
ve.ui.MWDefinedTransclusionContextItem.prototype.getCanonicalParam = function ( name ) {
    const params = this.tool.params || {};

    if ( Object.prototype.hasOwnProperty.call( params, name ) ) {
        const aliases = Array.isArray( params[ name ] ) ? params[ name ] : [ params[ name ] ];
        // Find the first non-empty value from the alias list
        for ( let i = 0; i < aliases.length; i++ ) {
            const value = ve.getProp( this.model.getAttribute( 'mw' ), 'parts', 0, 'template', 'params', aliases[ i ], 'wt' );
            if ( value ) {
                return value;
            }
        }
    }
    return null;
};