mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
synced 2024-11-24 14:33:59 +00:00
Merge "[BREAKING CHANGE] Evalute block/inline state when inserting a transclusion node"
This commit is contained in:
commit
7a69ae68ab
|
@ -87,6 +87,33 @@ ve.ce.MWTransclusionNode.static.getDescription = function ( model ) {
|
|||
.join( ve.msg( 'comma-separator' ) );
|
||||
};
|
||||
|
||||
/**
|
||||
* Filter rendering to remove auto-generated content and wrappers
|
||||
*
|
||||
* @static
|
||||
* @param {HTMLElement[]} contentNodes Rendered nodes
|
||||
* @return {HTMLElement[]} Filtered rendered nodes
|
||||
*/
|
||||
ve.ce.MWTransclusionNode.static.filterRendering = function ( contentNodes ) {
|
||||
// Filter out auto-generated items, e.g. reference lists
|
||||
contentNodes = contentNodes.filter( function ( node ) {
|
||||
var dataMw = node.nodeType === Node.ELEMENT_NODE &&
|
||||
node.hasAttribute( 'data-mw' ) &&
|
||||
JSON.parse( node.getAttribute( 'data-mw' ) );
|
||||
if ( dataMw && dataMw.autoGenerated ) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} );
|
||||
// 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.apply( contentNodes[ 0 ].childNodes );
|
||||
}
|
||||
return contentNodes;
|
||||
};
|
||||
|
||||
/* Methods */
|
||||
|
||||
/**
|
||||
|
@ -122,24 +149,7 @@ ve.ce.MWTransclusionNode.prototype.onParseSuccess = function ( deferred, respons
|
|||
|
||||
// Work around https://github.com/jquery/jquery/issues/1997
|
||||
contentNodes = $.parseHTML( response.visualeditor.content, this.getModelHtmlDocument() ) || [];
|
||||
// Filter out auto-generated items, e.g. reference lists
|
||||
contentNodes = contentNodes.filter( function ( node ) {
|
||||
var dataMw = node.nodeType === Node.ELEMENT_NODE &&
|
||||
node.hasAttribute( 'data-mw' ) &&
|
||||
JSON.parse( node.getAttribute( 'data-mw' ) );
|
||||
if ( dataMw && dataMw.autoGenerated ) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} );
|
||||
// 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.apply( contentNodes[ 0 ].childNodes );
|
||||
}
|
||||
|
||||
deferred.resolve( contentNodes );
|
||||
deferred.resolve( this.constructor.static.filterRendering( contentNodes ) );
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -50,19 +50,63 @@
|
|||
/**
|
||||
* Insert transclusion at the end of a surface fragment.
|
||||
*
|
||||
* @param {ve.dm.SurfaceFragment} surfaceFragment Surface fragment to insert at
|
||||
* If forceType is not specified and this is used in async mode, users of this method
|
||||
* should ensure the surface is not accessible while the type is being evaluated.
|
||||
*
|
||||
* @param {ve.dm.SurfaceFragment} surfaceFragment Surface fragment after which to insert.
|
||||
* @param {boolean|undefined} [forceType] Force the type to 'inline' or 'block'. If not
|
||||
* specified it will be evaluated asynchronously.
|
||||
* @return {jQuery.Promise} Promise which resolves when the node has been inserted. If
|
||||
* forceType was specified this will be instant.
|
||||
*/
|
||||
ve.dm.MWTransclusionModel.prototype.insertTransclusionNode = function ( surfaceFragment ) {
|
||||
surfaceFragment
|
||||
.insertContent( [
|
||||
{
|
||||
type: 'mwTransclusionInline',
|
||||
attributes: {
|
||||
mw: this.getPlainObject()
|
||||
ve.dm.MWTransclusionModel.prototype.insertTransclusionNode = function ( surfaceFragment, forceType ) {
|
||||
var model = this,
|
||||
deferred = $.Deferred(),
|
||||
nodeClass = ve.dm.MWTransclusionNode;
|
||||
|
||||
function insertNode( isInline ) {
|
||||
var type = isInline ? nodeClass.static.inlineType : nodeClass.static.blockType;
|
||||
surfaceFragment
|
||||
.insertContent( [
|
||||
{
|
||||
type: type,
|
||||
attributes: {
|
||||
mw: model.getPlainObject()
|
||||
}
|
||||
},
|
||||
{ type: '/' + type }
|
||||
] );
|
||||
deferred.resolve();
|
||||
}
|
||||
|
||||
if ( forceType ) {
|
||||
insertNode( forceType === 'inline' );
|
||||
} else {
|
||||
new mw.Api().post( {
|
||||
action: 'visualeditor',
|
||||
paction: 'parsefragment',
|
||||
page: mw.config.get( 'wgRelevantPageName' ),
|
||||
wikitext: nodeClass.static.getWikitext( this.getPlainObject() ),
|
||||
pst: 1
|
||||
} )
|
||||
.then( function ( response ) {
|
||||
var contentNodes;
|
||||
|
||||
if ( ve.getProp( response, 'visualeditor', 'result' ) === 'success' ) {
|
||||
contentNodes = $.parseHTML( response.visualeditor.content, surfaceFragment.getDocument().getHtmlDocument() ) || [];
|
||||
contentNodes = ve.ce.MWTransclusionNode.static.filterRendering( contentNodes );
|
||||
insertNode(
|
||||
nodeClass.static.isHybridInline( contentNodes, ve.dm.converter )
|
||||
);
|
||||
} else {
|
||||
// Request failed - just assume inline
|
||||
insertNode( true );
|
||||
}
|
||||
},
|
||||
{ type: '/mwTransclusionInline' }
|
||||
] );
|
||||
}, function () {
|
||||
insertNode( true );
|
||||
} );
|
||||
}
|
||||
return deferred.promise();
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -268,6 +268,41 @@ ve.dm.MWTransclusionNode.static.escapeParameter = function ( param ) {
|
|||
return output;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the wikitext for this transclusion.
|
||||
*
|
||||
* @static
|
||||
* @param {Object} content MW data content
|
||||
* @return {string} Wikitext like `{{foo|1=bar|baz=quux}}`
|
||||
*/
|
||||
ve.dm.MWTransclusionNode.static.getWikitext = function ( content ) {
|
||||
var i, len, part, template, param,
|
||||
wikitext = '';
|
||||
|
||||
// Normalize to multi template format
|
||||
if ( content.params ) {
|
||||
content = { parts: [ { template: content } ] };
|
||||
}
|
||||
// Build wikitext from content
|
||||
for ( i = 0, len = content.parts.length; i < len; i++ ) {
|
||||
part = content.parts[ i ];
|
||||
if ( part.template ) {
|
||||
// Template
|
||||
template = part.template;
|
||||
wikitext += '{{' + template.target.wt;
|
||||
for ( param in template.params ) {
|
||||
wikitext += '|' + param + '=' +
|
||||
this.escapeParameter( template.params[ param ].wt );
|
||||
}
|
||||
wikitext += '}}';
|
||||
} else {
|
||||
// Plain wikitext
|
||||
wikitext += part;
|
||||
}
|
||||
}
|
||||
return wikitext;
|
||||
};
|
||||
|
||||
/* Methods */
|
||||
|
||||
/**
|
||||
|
@ -350,38 +385,13 @@ ve.dm.MWTransclusionNode.prototype.getPartsList = function () {
|
|||
};
|
||||
|
||||
/**
|
||||
* Get the wikitext for this transclusion.
|
||||
* Wrapper for static method
|
||||
*
|
||||
* @method
|
||||
* @return {string} Wikitext like `{{foo|1=bar|baz=quux}}`
|
||||
*/
|
||||
ve.dm.MWTransclusionNode.prototype.getWikitext = function () {
|
||||
var i, len, part, template, param,
|
||||
content = this.getAttribute( 'mw' ),
|
||||
wikitext = '';
|
||||
|
||||
// Normalize to multi template format
|
||||
if ( content.params ) {
|
||||
content = { parts: [ { template: content } ] };
|
||||
}
|
||||
// Build wikitext from content
|
||||
for ( i = 0, len = content.parts.length; i < len; i++ ) {
|
||||
part = content.parts[ i ];
|
||||
if ( part.template ) {
|
||||
// Template
|
||||
template = part.template;
|
||||
wikitext += '{{' + template.target.wt;
|
||||
for ( param in template.params ) {
|
||||
wikitext += '|' + param + '=' +
|
||||
this.constructor.static.escapeParameter( template.params[ param ].wt );
|
||||
}
|
||||
wikitext += '}}';
|
||||
} else {
|
||||
// Plain wikitext
|
||||
wikitext += part;
|
||||
}
|
||||
}
|
||||
return wikitext;
|
||||
return this.constructor.static.getWikitext( this.getAttribute( 'mw' ) );
|
||||
};
|
||||
|
||||
/* Concrete subclasses */
|
||||
|
|
|
@ -414,19 +414,25 @@ ve.ui.MWTemplateDialog.prototype.getActionProcess = function ( action ) {
|
|||
return new OO.ui.Process( function () {
|
||||
var deferred = $.Deferred();
|
||||
dialog.checkRequiredParameters().done( function () {
|
||||
var surfaceModel = dialog.getFragment().getSurface(),
|
||||
var modelPromise,
|
||||
surfaceModel = dialog.getFragment().getSurface(),
|
||||
obj = dialog.transclusionModel.getPlainObject();
|
||||
|
||||
dialog.pushPending();
|
||||
|
||||
if ( dialog.selectedNode instanceof ve.dm.MWTransclusionNode ) {
|
||||
dialog.transclusionModel.updateTransclusionNode( surfaceModel, dialog.selectedNode );
|
||||
// TODO: updating the node could result in the inline/block state change
|
||||
modelPromise = $.Deferred().resolve().promise();
|
||||
} else if ( obj !== null ) {
|
||||
// Collapse returns a new fragment, so update dialog.fragment
|
||||
dialog.fragment = dialog.getFragment().collapseToEnd();
|
||||
dialog.transclusionModel.insertTransclusionNode( dialog.getFragment() );
|
||||
modelPromise = dialog.transclusionModel.insertTransclusionNode( dialog.getFragment() );
|
||||
}
|
||||
|
||||
dialog.pushPending();
|
||||
dialog.close( { action: action } ).always( dialog.popPending.bind( dialog ) );
|
||||
return modelPromise.then( function () {
|
||||
dialog.close( { action: action } ).always( dialog.popPending.bind( dialog ) );
|
||||
} );
|
||||
} ).always( deferred.resolve );
|
||||
|
||||
return deferred;
|
||||
|
|
Loading…
Reference in a new issue