(bug 42119) Handle alienation in wrapping mode properly

When alienating in wrapping mode, we need to look at the type of tag to
decide whether to create a wrapped alienInline, or to interrupt the
paragraph for an alienBlock.

This was being done just fine for the general alienation case
(unrecognized tag), but not for the special cases (mw:unrecognized,
about groups).

* Centralize the logic for ending a wrapper in stopWrapping()
* Move the wrapping-contingent block/inline detection logic into
  createAlien()
* Simplify the terrible if statement to decide whether a future decision
  requires us to stop wrapping. Instead, detect the cases in each code
  path separately and call stopWrapping() as appropriate
* Add tests

Change-Id: I4054584ae05e7d5daa71edead3e6a6588cf5d3bb
This commit is contained in:
Catrope 2012-11-19 20:26:54 -08:00 committed by Trevor Parscal
parent f71247608b
commit 662880605c
2 changed files with 82 additions and 41 deletions

View file

@ -207,8 +207,11 @@ ve.dm.Converter.prototype.getDomElementFromDataAnnotation = function ( dataAnnot
* @returns {Array} Linear model data
*/
ve.dm.Converter.prototype.getDataFromDom = function ( domElement, annotations, dataElement, path, alreadyWrapped ) {
function createAlien( domElement, isInline, isWrapper ) {
var type = isInline ? 'alienInline' : 'alienBlock', html;
function createAlien( domElement, branchIsContent, isWrapper ) {
// If we're in wrapping mode, we don't know if this alien is supposed to be block
// or inline, so detect it based on the HTML tag name.
var isInline = wrapping ? !ve.isBlockElement( domElement ) : branchIsContent,
type = isInline ? 'alienInline' : 'alienBlock', html;
if ( isWrapper ) {
html = $( domElement ).html();
} else {
@ -248,6 +251,18 @@ ve.dm.Converter.prototype.getDataFromDom = function ( domElement, annotations, d
nextWhitespace = '';
}
}
function stopWrapping() {
if ( wrappedWhitespace !== '' ) {
// Remove wrappedWhitespace from data
data.splice( -wrappedWhitespace.length, wrappedWhitespace.length );
addWhitespace( wrappingParagraph, 3, wrappedWhitespace );
nextWhitespace = wrappedWhitespace;
}
data.push( { 'type': '/paragraph' } );
wrappingParagraph = undefined;
wrapping = false;
wrappingIsOurs = false;
}
/**
* Helper function to group adjacent child elements with the same about attribute together.
@ -334,6 +349,9 @@ ve.dm.Converter.prototype.getDataFromDom = function ( domElement, annotations, d
// Alienate about groups
if ( childDomElement.hasAttribute( 'data-ve-aboutgroup' ) ) {
alien = createAlien( childDomElement, branchIsContent, true );
if ( wrapping && alien[0].type === 'alienBlock' ) {
stopWrapping();
}
data = data.concat( alien );
processNextWhitespace( alien[0] );
prevElement = alien[0];
@ -391,6 +409,9 @@ ve.dm.Converter.prototype.getDataFromDom = function ( domElement, annotations, d
!rdfaType.match( /^mw:Entity/ )
) {
alien = createAlien( childDomElement, branchIsContent );
if ( wrapping && alien[0].type === 'alienBlock' ) {
stopWrapping();
}
data = data.concat( alien );
processNextWhitespace( alien[0] );
prevElement = alien[0];
@ -426,32 +447,12 @@ ve.dm.Converter.prototype.getDataFromDom = function ( domElement, annotations, d
// Look up child element type
childDataElement = this.getDataElementFromDomElement( childDomElement );
// End auto-wrapping of bare content from a previously processed node
// but only if childDataElement is a non-content element or if
// we are about to produce a block alien
if (
wrapping && (
(
childDataElement &&
!ve.dm.nodeFactory.isNodeContent( childDataElement.type )
) || (
!childDataElement &&
ve.isBlockElement( childDomElement )
)
)
) {
if ( wrappedWhitespace !== '' ) {
// Remove wrappedWhitespace from data
data.splice( -wrappedWhitespace.length, wrappedWhitespace.length );
addWhitespace( wrappingParagraph, 3, wrappedWhitespace );
nextWhitespace = wrappedWhitespace;
}
data.push( { 'type': '/paragraph' } );
wrappingParagraph = undefined;
wrapping = false;
wrappingIsOurs = false;
}
if ( childDataElement ) {
// End auto-wrapping of bare content from a previously processed node
// but only if childDataElement is a non-content element
if ( wrapping && !ve.dm.nodeFactory.isNodeContent( childDataElement.type ) ) {
stopWrapping();
}
if ( ve.dm.nodeFactory.canNodeHaveChildren( childDataElement.type ) ) {
// Append child element data
data = data.concat(
@ -473,12 +474,12 @@ ve.dm.Converter.prototype.getDataFromDom = function ( domElement, annotations, d
break;
}
// We don't know what this is, fall back to alien.
// If we're in wrapping mode, we don't know if this alien is
// supposed to be block or inline, so detect it based on the HTML
// tag name.
alien = createAlien( childDomElement, wrapping ?
!ve.isBlockElement( childDomElement ) : branchIsContent
);
alien = createAlien( childDomElement, branchIsContent );
// End auto-wrapping of bare content from a previously processed node
// but only if we produced an alienBlock
if ( wrapping && alien[0].type === 'alienBlock' ) {
stopWrapping();
}
data = data.concat( alien );
processNextWhitespace( alien[0] );
prevElement = alien[0];
@ -627,15 +628,9 @@ ve.dm.Converter.prototype.getDataFromDom = function ( domElement, annotations, d
}
// End auto-wrapping of bare content
if ( wrapping && wrappingIsOurs ) {
if ( wrappedWhitespace !== '' ) {
// Remove wrappedWhitespace from data
data.splice( -wrappedWhitespace.length, wrappedWhitespace.length );
addWhitespace( wrappingParagraph, 3, wrappedWhitespace );
nextWhitespace = wrappedWhitespace;
}
data.push( { 'type': '/paragraph' } );
stopWrapping();
// Don't set wrapping = false here because it's checked below
wrappingParagraph = undefined;
wrapping = true;
}
// If we're closing a node that doesn't have any children, but could contain a paragraph,

View file

@ -694,6 +694,52 @@ ve.dm.example.domToDataCases = {
{ 'type': '/paragraph' }
]
},
'wrapping of bare content with mw:unrecognized inline alien': {
'html': '1<span typeof="mw:Placeholder">baz</span>2',
'data': [
{ 'type': 'paragraph', 'internal': { 'generated': 'wrapper' } },
'1',
{
'type': 'alienInline',
'attributes': { 'html': '<span typeof="mw:Placeholder">baz</span>' }
},
{ 'type': '/alienInline' },
'2',
{ 'type': '/paragraph' }
]
},
'wrapping of bare content with mw:unrecognized block alien': {
'html': '1<div typeof="mw:Placeholder">baz</div>2',
'data': [
{ 'type': 'paragraph', 'internal': { 'generated': 'wrapper' } },
'1',
{ 'type': '/paragraph' },
{
'type': 'alienBlock',
'attributes': { 'html': '<div typeof="mw:Placeholder">baz</div>' }
},
{ 'type': '/alienBlock' },
{ 'type': 'paragraph', 'internal': { 'generated': 'wrapper' } },
'2',
{ 'type': '/paragraph' }
]
},
'wrapping of bare content with about group': {
'html': '1<tt about="#mwt1">foo</tt><tt about="#mwt1">bar</tt>2',
'data': [
{ 'type': 'paragraph', 'internal': { 'generated': 'wrapper' } },
'1',
{ 'type': '/paragraph' },
{
'type': 'alienBlock',
'attributes': { 'html': '<tt about="#mwt1">foo</tt><tt about="#mwt1">bar</tt>' }
},
{ 'type': '/alienBlock' },
{ 'type': 'paragraph', 'internal': { 'generated': 'wrapper' } },
'2',
{ 'type': '/paragraph' }
]
},
'wrapping of bare content between structural nodes': {
'html': '<table></table>abc<table></table>',
'data': [