mediawiki-extensions-Visual.../modules/ve-mw/ce/nodes/ve.ce.MWTransclusionNode.js
Thiemo Kreuz 4367235dcc Inline many var declarations in the code below
This makes the code more readable and easier to reason about.
The ESLint rule responsible for this code style was removed
just recently.

Notes:
* I focus on classes that are relevant for what the WMDE team
  does right now.
* I merge multiple `var` keywords only when the variables are
  strongly connected.
* Caching the length in a for loop makes the code hard to
  read, but not really faster when it's a trivial property
  access anyway.

Bug: T284895
Change-Id: I621fed61d894a83dc95f58129bbe679d82b0f5f5
2021-06-23 09:02:24 +00:00

232 lines
6.8 KiB
JavaScript

/*!
* VisualEditor ContentEditable MWTransclusionNode class.
*
* @copyright 2011-2020 VisualEditor Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
/**
* ContentEditable MediaWiki transclusion node.
*
* @class
* @abstract
* @extends ve.ce.LeafNode
* @mixins ve.ce.GeneratedContentNode
* @mixins ve.ce.FocusableNode
*
* @constructor
* @param {ve.dm.MWTransclusionNode} model Model to observe
* @param {Object} [config] Configuration options
*/
ve.ce.MWTransclusionNode = function VeCeMWTransclusionNode( model, config ) {
// Parent constructor
ve.ce.MWTransclusionNode.super.call( this, model, config );
// Mixin constructors
ve.ce.GeneratedContentNode.call( this );
ve.ce.FocusableNode.call( this );
};
/* Inheritance */
OO.inheritClass( ve.ce.MWTransclusionNode, ve.ce.LeafNode );
OO.mixinClass( ve.ce.MWTransclusionNode, ve.ce.GeneratedContentNode );
OO.mixinClass( ve.ce.MWTransclusionNode, ve.ce.FocusableNode );
/* Static Properties */
ve.ce.MWTransclusionNode.static.name = 'mwTransclusion';
ve.ce.MWTransclusionNode.static.primaryCommandName = 'transclusion';
ve.ce.MWTransclusionNode.static.iconWhenInvisible = 'puzzle';
/* Static Methods */
/**
* Get a list of descriptions of template parts in a transclusion node
*
* @static
* @param {ve.dm.MWTransclusionNode} model Node model
* @return {string[]} List of template part descriptions
*/
ve.ce.MWTransclusionNode.static.getTemplatePartDescriptions = function ( model ) {
return model.getPartsList().map( this.getTemplatePartDescription );
};
/**
* Get a description of a template part in a transclusion node
*
* @static
* @param {Object} part Template part
* @return {string} Template part description
*/
ve.ce.MWTransclusionNode.static.getTemplatePartDescription = function ( part ) {
// Ignore parts that are just content
if ( part.templatePage ) {
return mw.Title.newFromText( part.templatePage )
.getRelativeText( mw.config.get( 'wgNamespaceIds' ).template );
} else if ( part.template ) {
// Not actually a template, but e.g. a parser function
return part.template;
}
};
/**
* @inheritdoc
*/
ve.ce.MWTransclusionNode.static.getDescription = function ( model ) {
return this.getTemplatePartDescriptions( model )
.filter( function ( desc ) {
return desc;
} )
.join( ve.msg( 'comma-separator' ) );
};
/**
* Filter rendering to remove auto-generated content and wrappers
*
* @static
* @param {Node[]} contentNodes Rendered nodes
* @return {Node[]} Filtered rendered nodes
*/
ve.ce.MWTransclusionNode.static.filterRendering = function ( contentNodes ) {
if ( !contentNodes.length ) {
return;
}
var whitespaceRegex = new RegExp( '^[' + ve.dm.Converter.static.whitespaceList + ']+$' );
// Filter out auto-generated items, e.g. reference lists
contentNodes = contentNodes.filter( function ( node ) {
var dataMw = node &&
node.nodeType === Node.ELEMENT_NODE &&
node.hasAttribute( 'data-mw' ) &&
JSON.parse( node.getAttribute( 'data-mw' ) );
return !dataMw || !dataMw.autoGenerated;
} );
contentNodes.forEach( function ( node ) {
if ( node.nodeType === Node.ELEMENT_NODE ) {
mw.libs.ve.stripParsoidFallbackIds( node );
}
} );
function isWhitespaceNode( node ) {
return node && node.nodeType === Node.TEXT_NODE && !!node.data.match( whitespaceRegex );
}
while ( isWhitespaceNode( contentNodes[ 0 ] ) ) {
contentNodes.shift();
}
while ( isWhitespaceNode( contentNodes[ contentNodes.length - 1 ] ) ) {
contentNodes.pop();
}
// HACK: if $content consists of a single paragraph, unwrap it.
// We have to do this because the parser wraps everything in <p>s, and inline templates
// will render strangely when wrapped in <p>s.
if ( contentNodes.length === 1 && contentNodes[ 0 ].nodeName.toLowerCase() === 'p' ) {
contentNodes = Array.prototype.slice.call( contentNodes[ 0 ].childNodes );
}
return contentNodes;
};
/* Methods */
/**
* @inheritdoc
*/
ve.ce.MWTransclusionNode.prototype.generateContents = function ( config ) {
var xhr, deferred = ve.createDeferred();
xhr = ve.init.target.parseWikitextFragment(
( config && config.wikitext ) || this.model.getWikitext(),
true,
this.getModel().getDocument()
)
.done( this.onParseSuccess.bind( this, deferred ) )
.fail( this.onParseError.bind( this, deferred ) );
return deferred.promise( { abort: xhr.abort } );
};
/**
* Handle a successful response from the parser for the wikitext fragment.
*
* @param {jQuery.Deferred} deferred The Deferred object created by #generateContents
* @param {Object} response Response data
*/
ve.ce.MWTransclusionNode.prototype.onParseSuccess = function ( deferred, response ) {
if ( ve.getProp( response, 'visualeditor', 'result' ) !== 'success' ) {
this.onParseError( deferred );
return;
}
// Work around https://github.com/jquery/jquery/issues/1997
var contentNodes = $.parseHTML( response.visualeditor.content, this.model && this.getModelHtmlDocument() ) || [];
deferred.resolve( this.constructor.static.filterRendering( contentNodes ) );
};
/**
* Extend the ve.ce.GeneratedContentNode render method to check for hidden templates.
*
* Check if the final result of the imported template is empty.
*
* @inheritdoc ve.ce.GeneratedContentNode
*/
ve.ce.MWTransclusionNode.prototype.render = function ( generatedContents ) {
// Call parent mixin
ve.ce.GeneratedContentNode.prototype.render.call( this, generatedContents );
};
/**
* @inheritdoc
*/
ve.ce.MWTransclusionNode.prototype.onSetup = function () {
// Parent method
ve.ce.MWTransclusionNode.super.prototype.onSetup.apply( this, arguments );
// Render replaces this.$element with a new node so re-add classes
this.$element.addClass( 've-ce-mwTransclusionNode' );
};
/**
* @inheritdoc
*/
ve.ce.MWTransclusionNode.prototype.getRenderedDomElements = function () {
// Parent method
var elements = ve.ce.GeneratedContentNode.prototype.getRenderedDomElements.apply( this, arguments );
if ( this.model && this.getModelHtmlDocument() ) {
ve.init.platform.linkCache.styleParsoidElements(
$( elements ),
this.getModelHtmlDocument()
);
}
return elements;
};
/**
* @inheritdoc
*/
ve.ce.MWTransclusionNode.prototype.filterRenderedDomElements = function ( domElements ) {
// We want to remove all styles and links which aren't from TemplateStyles.
var selector = 'style:not([data-mw-deduplicate^="TemplateStyles:"]), link:not([rel="mw-deduplicated-inline-style"][href^="mw-data:TemplateStyles:"])';
return $( domElements ).find( selector ).addBack( selector ).remove().end().end().toArray();
};
/**
* Handle an unsuccessful response from the parser for the wikitext fragment.
*
* @param {jQuery.Deferred} deferred The promise object created by #generateContents
* @param {Object} response Response data
*/
ve.ce.MWTransclusionNode.prototype.onParseError = function ( deferred ) {
deferred.reject();
};
/* Registration */
ve.ce.nodeFactory.register( ve.ce.MWTransclusionNode );