wikimedia/mediawiki-extensions-VisualEditor

View on GitHub
modules/ve-mw/init/apiresponsecache/ve.init.mw.LinkCache.js

Summary

Maintainability
A
1 hr
Test Coverage
/*!
 * VisualEditor MediaWiki Initialization LinkCache class.
 *
 * @copyright See AUTHORS.txt
 * @license The MIT License (MIT); see LICENSE.txt
 */

/**
 * Caches information about titles.
 *
 * @class
 * @extends ve.init.mw.ApiResponseCache
 * @constructor
 * @param {mw.Api} [api]
 */
ve.init.mw.LinkCache = function VeInitMwLinkCache() {
    // Parent constructor
    ve.init.mw.LinkCache.super.apply( this, arguments );
};

/* Inheritance */

OO.inheritClass( ve.init.mw.LinkCache, ve.init.mw.ApiResponseCache );

/* Static methods */

/**
 * Get the icon name to use for a particular link type
 *
 * @param {Object} linkData Link data
 * @return {string} Icon name
 */
ve.init.mw.LinkCache.static.getIconForLink = function ( linkData ) {
    if ( linkData.missing ) {
        return 'articleNotFound';
    }
    if ( linkData.redirect ) {
        return 'articleRedirect';
    }
    if ( linkData.disambiguation ) {
        return 'articleDisambiguation';
    }
    return 'article';
};

/**
 * @inheritdoc
 */
ve.init.mw.LinkCache.static.processPage = function ( page ) {
    return {
        missing: page.missing !== undefined,
        known: page.known !== undefined,
        redirect: page.redirect !== undefined,
        disambiguation: ve.getProp( page, 'pageprops', 'disambiguation' ) !== undefined,
        hidden: ve.getProp( page, 'pageprops', 'hiddencat' ) !== undefined,
        imageUrl: ve.getProp( page, 'thumbnail', 'source' ),
        description: page.description
    };
};

/* Methods */

/**
 * Requests information about the title, then adds classes to the provided element as appropriate.
 *
 * @param {string} title
 * @param {jQuery} $element Element to style
 * @param {boolean} [hasFragment=false] Whether the link goes to a fragment
 */
ve.init.mw.LinkCache.prototype.styleElement = function ( title, $element, hasFragment ) {
    const cachedMissingData = this.getCached( '_missing/' + title );

    let promise;
    // Use the synchronous missing link cache data if it exists
    if ( cachedMissingData ) {
        promise = ve.createDeferred().resolve( cachedMissingData ).promise();
    } else {
        promise = this.get( title );
    }

    promise.done( ( data ) => {
        if ( data.missing && !data.known ) {
            $element.addClass( 'new' );
        } else {
            // Provided by core MediaWiki, styled like a <strong> element by default.
            if ( !hasFragment && this.constructor.static.normalizeTitle( title ) === this.constructor.static.normalizeTitle( mw.config.get( 'wgRelevantPageName' ) ) ) {
                $element.addClass( 'mw-selflink' );
            }
            // Provided by core MediaWiki, no styles by default.
            if ( data.redirect ) {
                $element.addClass( 'mw-redirect' );
            }
            // Provided by the Disambiguator extension, no styles by default.
            if ( data.disambiguation ) {
                $element.addClass( 'mw-disambig' );
            }
        }
    } );
};

/**
 * Given a chunk of Parsoid HTML, applies style transformations.
 *
 * Previously this was used for applying red-link styles, but that
 * has since been upstreamed to Parsoid.
 *
 * TODO: Evaluate if this method should be renamed/removed as it
 * now has nothing to do with the link cache.
 *
 * @param {jQuery} $elements Elements to style
 * @param {HTMLDocument} doc Base document to use for normalisation
 */
ve.init.mw.LinkCache.prototype.styleParsoidElements = function ( $elements ) {
    if ( ve.dm.MWLanguageVariantNode ) {
        // Render the user's preferred variant in language converter markup
        $elements.each( ( i, element ) => {
            ve.dm.MWLanguageVariantNode.static.processVariants( element );
        } );
    }
};

/**
 * Enable or disable automatic assumption of existence.
 *
 * While enabled, any get() for a title that's not already in the cache will return
 * { missing: false } and write that to the cache.
 *
 * @param {boolean} assume Assume all uncached titles exist
 */
ve.init.mw.LinkCache.prototype.setAssumeExistence = function ( assume ) {
    this.assumeExistence = !!assume;
};

/**
 * Set link missing data
 *
 * Stored separately from the full link data cache
 *
 * @param {Object} entries Object keyed by page title, with the values being data objects
 */
ve.init.mw.LinkCache.prototype.setMissing = function ( entries ) {
    const missingEntries = {};
    for ( const name in entries ) {
        missingEntries[ '_missing/' + name ] = entries[ name ];
    }
    this.set( missingEntries );
};

/**
 * @inheritdoc
 */
ve.init.mw.LinkCache.prototype.get = function ( title ) {
    const data = {};
    if ( this.assumeExistence ) {
        data[ this.constructor.static.normalizeTitle( title ) ] = { missing: false };
        this.setMissing( data );
    }

    // Parent method
    return ve.init.mw.LinkCache.super.prototype.get.call( this, title );
};

/**
 * @inheritdoc
 */
ve.init.mw.LinkCache.prototype.getRequestPromise = function ( subqueue ) {
    return this.api.get( {
        action: 'query',
        prop: 'info|pageprops|pageimages|description',
        pithumbsize: 80,
        pilimit: subqueue.length,
        ppprop: 'disambiguation|hiddencat',
        titles: subqueue,
        continue: ''
    } );
};