wikimedia/mediawiki-extensions-VisualEditor

View on GitHub
modules/ve-mw/preinit/ve.init.mw.DiffLoader.js

Summary

Maintainability
A
35 mins
Test Coverage
/*!
 * VisualEditor MediaWiki DiffLoader.
 *
 * @copyright See AUTHORS.txt
 * @license The MIT License (MIT); see LICENSE.txt
 */

/* global ve */

/**
 * Diff loader.
 *
 * @class mw.libs.ve.diffLoader
 * @singleton
 * @hideconstructor
 */
( function () {
    const revCache = {};

    mw.libs.ve = mw.libs.ve || {};

    mw.libs.ve.diffLoader = {
        /**
         * Get a ve.dm.Document model from a Parsoid response
         *
         * @param {Object} response Parsoid response from the VisualEditor API
         * @param {string|null} section Section. Null for the whole document.
         * @return {ve.dm.Document|null} Document, or null if an invalid response
         */
        getModelFromResponse: function ( response, section ) {
            // This method is only called after actually loading these, see `parseDocumentModulePromise`
            const targetClass = ve.init.mw.ArticleTarget,
                data = response ? ( response.visualeditor || response.visualeditoredit ) : null;
            if ( data && typeof data.content === 'string' ) {
                const doc = targetClass.static.parseDocument( data.content, 'visual', section, section !== null );
                mw.libs.ve.stripRestbaseIds( doc );
                return targetClass.static.createModelFromDom( doc, 'visual' );
            }
            return null;
        },

        /**
         * Fetch a specific revision from Parsoid as a DM document, and cache in memory
         *
         * @param {number} revId Revision ID
         * @param {string} [pageName] Page name, defaults to wgRelevantPageName
         * @param {string|null} [section=null] Section. Null for the whole document.
         * @param {jQuery.Promise} [parseDocumentModulePromise] Promise which resolves when Target#parseDocument is available
         * @return {jQuery.Promise} Promise which resolves with a document model
         */
        fetchRevision: function ( revId, pageName, section, parseDocumentModulePromise ) {
            pageName = pageName || mw.config.get( 'wgRelevantPageName' );
            parseDocumentModulePromise = parseDocumentModulePromise || $.Deferred().resolve().promise();
            section = section !== undefined ? section : null;

            const cacheKey = revId + ( section !== null ? '/' + section : '' );

            revCache[ cacheKey ] = revCache[ cacheKey ] ||
                mw.libs.ve.targetLoader.requestParsoidData(
                    pageName,
                    { oldId: revId, targetName: 'diff' },
                    false,
                    // noMetadata, we only use `content` in getModelFromResponse
                    true
                ).then(
                    ( response ) => parseDocumentModulePromise.then( () => mw.libs.ve.diffLoader.getModelFromResponse( response, section ) ),
                    () => {
                    // Clear promise. Do not cache errors.
                        delete revCache[ cacheKey ];
                        // Let caller handle the error code
                        return $.Deferred().rejectWith( this, arguments );
                    }
                );

            return revCache[ cacheKey ];
        },

        /**
         * Get a visual diff generator promise
         *
         * @param {number|jQuery.Promise} oldIdOrPromise Old revision ID, or document model promise
         * @param {number|jQuery.Promise} newIdOrPromise New revision ID, or document model promise
         * @param {jQuery.Promise} [parseDocumentModulePromise] Promise which resolves when Target#parseDocument is available
         * @param {string} [oldPageName] Old revision's page name, defaults to wgRelevantPageName
         * @param {string} [newPageName] New revision's page name, defaults to oldPageName
         * @return {jQuery.Promise} Promise which resolves with a ve.dm.VisualDiff generator function
         */
        getVisualDiffGeneratorPromise: function ( oldIdOrPromise, newIdOrPromise, parseDocumentModulePromise, oldPageName, newPageName ) {
            parseDocumentModulePromise = parseDocumentModulePromise || $.Deferred().resolve().promise();
            oldPageName = oldPageName || mw.config.get( 'wgRelevantPageName' );

            const oldRevPromise = typeof oldIdOrPromise === 'number' ? this.fetchRevision( oldIdOrPromise, oldPageName, null, parseDocumentModulePromise ) : oldIdOrPromise;
            const newRevPromise = typeof newIdOrPromise === 'number' ? this.fetchRevision( newIdOrPromise, newPageName, null, parseDocumentModulePromise ) : newIdOrPromise;

            return $.when( oldRevPromise, newRevPromise, parseDocumentModulePromise ).then( ( oldDoc, newDoc ) => {
                // TODO: Differ expects newDoc to be derived from oldDoc and contain all its store data.
                // We may want to remove that assumption from the differ?
                newDoc.getStore().merge( oldDoc.getStore() );
                return () => new ve.dm.VisualDiff( oldDoc, newDoc );
            } );
        }

    };
}() );