mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
synced 2024-11-24 14:33:59 +00:00
Merge "Add basic support for about groups"
This commit is contained in:
commit
5f56694b75
|
@ -206,13 +206,18 @@ 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 ) {
|
||||
var type = isInline ? 'alienInline' : 'alienBlock';
|
||||
function createAlien( domElement, isInline, isWrapper ) {
|
||||
var type = isInline ? 'alienInline' : 'alienBlock', html;
|
||||
if ( isWrapper ) {
|
||||
html = $( domElement ).html();
|
||||
} else {
|
||||
html = $( '<div>' ).append( $( domElement ).clone() ).html();
|
||||
}
|
||||
return [
|
||||
{
|
||||
'type': type,
|
||||
'attributes': {
|
||||
'html': $( '<div>' ).append( $( domElement ).clone() ).html()
|
||||
'html': html
|
||||
}
|
||||
},
|
||||
{ 'type': '/' + type }
|
||||
|
@ -243,6 +248,64 @@ ve.dm.Converter.prototype.getDataFromDom = function ( domElement, annotations, d
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to group adjacent child elements with the same about attribute together.
|
||||
* If there are multiple adjacent child nodes with the same about attribute, they are
|
||||
* wrapped in a <div> with the data-ve-aboutgroup attribute set.
|
||||
*
|
||||
* This function does not wrap single-element about groups, and does not descend into the
|
||||
* child elements.
|
||||
*
|
||||
* @param element {HTMLElement} Element to process
|
||||
*/
|
||||
function doAboutGrouping( element ) {
|
||||
var child = element.firstChild, textNodes = [],
|
||||
prevChild, aboutGroup, aboutWrapper, childAbout, nextChild, i;
|
||||
while ( child ) {
|
||||
nextChild = child.nextSibling;
|
||||
if ( !child.getAttribute ) {
|
||||
// Text nodes don't have a getAttribute() method. Thanks HTML DOM,
|
||||
// that's really helpful ^^
|
||||
textNodes.push( child );
|
||||
child = nextChild;
|
||||
continue;
|
||||
}
|
||||
childAbout = child.getAttribute( 'about' );
|
||||
if ( childAbout && !aboutGroup ) {
|
||||
// Start of a new about group
|
||||
aboutGroup = childAbout;
|
||||
} else if ( childAbout && childAbout === aboutGroup ) {
|
||||
// Continuation of the current about group
|
||||
if ( !aboutWrapper ) {
|
||||
// This is the second child in this group, so the
|
||||
// previous child is the first child in this group.
|
||||
// Wrap the previous child
|
||||
aboutWrapper = document.createElement( 'div' );
|
||||
aboutWrapper.setAttribute( 'data-ve-aboutgroup', aboutGroup );
|
||||
element.insertBefore( aboutWrapper, prevChild );
|
||||
aboutWrapper.appendChild( prevChild );
|
||||
}
|
||||
// Append any outstanding text nodes to the wrapper
|
||||
for ( i = 0; i < textNodes.length; i++ ) {
|
||||
aboutWrapper.appendChild( textNodes[i] );
|
||||
}
|
||||
// Append this child to the wrapper
|
||||
aboutWrapper.appendChild( child );
|
||||
} else if ( aboutGroup ) {
|
||||
// This child isn't in the current about group
|
||||
aboutGroup = undefined;
|
||||
aboutWrapper = undefined;
|
||||
if ( childAbout ) {
|
||||
// Start of a new about group
|
||||
aboutGroup = childAbout;
|
||||
}
|
||||
}
|
||||
prevChild = child;
|
||||
child = nextChild;
|
||||
textNodes = [];
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to defaults
|
||||
annotations = annotations || [];
|
||||
path = path || ['document'];
|
||||
|
@ -259,11 +322,23 @@ ve.dm.Converter.prototype.getDataFromDom = function ( domElement, annotations, d
|
|||
if ( dataElement ) {
|
||||
data.push( dataElement );
|
||||
}
|
||||
// Do about grouping
|
||||
// FIXME this assumes every about group is an alien
|
||||
doAboutGrouping( domElement );
|
||||
// Add contents
|
||||
for ( i = 0; i < domElement.childNodes.length; i++ ) {
|
||||
childDomElement = domElement.childNodes[i];
|
||||
switch ( childDomElement.nodeType ) {
|
||||
case Node.ELEMENT_NODE:
|
||||
// Alienate about groups
|
||||
if ( childDomElement.hasAttribute( 'data-ve-aboutgroup' ) ) {
|
||||
alien = createAlien( childDomElement, branchIsContent, true );
|
||||
data = data.concat( alien );
|
||||
processNextWhitespace( alien[0] );
|
||||
prevElement = alien[0];
|
||||
break;
|
||||
}
|
||||
|
||||
// HACK handle <meta>/<link> separately because of the
|
||||
// metaInline/metaBlock distinction
|
||||
if (
|
||||
|
@ -795,13 +870,18 @@ ve.dm.Converter.prototype.getDomFromData = function ( data ) {
|
|||
// Create nodes from source
|
||||
wrapper = document.createElement( 'div' );
|
||||
wrapper.innerHTML = dataElement.attributes.html;
|
||||
// Add element - adds first child element, any other
|
||||
// children are ignored but shouldn't exist
|
||||
//parentDomElement = domElement;
|
||||
childDomElement = wrapper.firstChild;
|
||||
//parentDomElement.appendChild( domElement );
|
||||
// Make sure the alien closing is skipped
|
||||
//parentDomElement = domElement; domElement = wrapper; //i++;
|
||||
if ( wrapper.childNodes.length > 1 ) {
|
||||
// Wrap the HTML in a single element, this makes
|
||||
// it much easier to deal with. It'll be unwrapped
|
||||
// at the end of this function.
|
||||
childDomElement = document.createElement( 'div' );
|
||||
childDomElement.setAttribute( 'data-ve-multi-child-alien-wrapper', 'true' );
|
||||
while ( wrapper.firstChild ) {
|
||||
childDomElement.appendChild( wrapper.firstChild );
|
||||
}
|
||||
} else {
|
||||
childDomElement = wrapper.firstChild;
|
||||
}
|
||||
} else {
|
||||
// Create node from data
|
||||
childDomElement = this.getDomElementFromDataElement( dataElement );
|
||||
|
@ -870,6 +950,11 @@ ve.dm.Converter.prototype.getDomFromData = function ( data ) {
|
|||
}
|
||||
delete container.lastOuterPost;
|
||||
}
|
||||
|
||||
// Unwrap multi-child alien wrappers
|
||||
$( container ).find( '[data-ve-multi-child-alien-wrapper]' ) .each( function() {
|
||||
$( this ).replaceWith( $( this ).contents() );
|
||||
} );
|
||||
return container;
|
||||
};
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ QUnit.test( 'getDomElementFromDataElement', 20, function ( assert ) {
|
|||
}
|
||||
} );
|
||||
|
||||
QUnit.test( 'getDataFromDom', 31, function ( assert ) {
|
||||
QUnit.test( 'getDataFromDom', 33, function ( assert ) {
|
||||
var msg,
|
||||
cases = ve.dm.example.domToDataCases;
|
||||
|
||||
|
@ -51,7 +51,7 @@ QUnit.test( 'getDataFromDom', 31, function ( assert ) {
|
|||
}
|
||||
} );
|
||||
|
||||
QUnit.test( 'getDomFromData', 33, function ( assert ) {
|
||||
QUnit.test( 'getDomFromData', 35, function ( assert ) {
|
||||
var msg,
|
||||
cases = ve.dm.example.domToDataCases;
|
||||
|
||||
|
|
|
@ -1507,5 +1507,70 @@ ve.dm.example.domToDataCases = {
|
|||
'normalizedHtml': '<p data-ve-changed="{"content":1}">' +
|
||||
'Foo<img data-ve-changed="{"attributes":2}" />' +
|
||||
'</p><p data-ve-changed="{"created":1}">Bar</p>'
|
||||
},
|
||||
'about grouping': {
|
||||
'html': '<div typeof="mw:Placeholder" about="#mwt1">Foo</div>' +
|
||||
'<figure typeof="mw:Placeholder" about="#mwt1">Bar</figure>' +
|
||||
'<figure typeof="mw:Placeholder" about="#mwt2">Baz</figure>' +
|
||||
'<span typeof="mw:Placeholder" about="#mwt2">Quux</span>' +
|
||||
'<p>Whee</p><span typeof="mw:Placeholder" about="#mwt2">Yay</span>' +
|
||||
'<div typeof="mw:Placeholder" about="#mwt2">Blah</div>' +
|
||||
'<span typeof="mw:Placeholder" about="#mwt3">Meh</span>',
|
||||
'data': [
|
||||
{
|
||||
'type': 'alienBlock',
|
||||
'attributes': {
|
||||
'html': '<div typeof="mw:Placeholder" about="#mwt1">Foo</div>' +
|
||||
'<figure typeof="mw:Placeholder" about="#mwt1">Bar</figure>'
|
||||
}
|
||||
},
|
||||
{ 'type': '/alienBlock' },
|
||||
{
|
||||
'type': 'alienBlock',
|
||||
'attributes': {
|
||||
'html': '<figure typeof="mw:Placeholder" about="#mwt2">Baz</figure>' +
|
||||
'<span typeof="mw:Placeholder" about="#mwt2">Quux</span>'
|
||||
}
|
||||
},
|
||||
{ 'type': '/alienBlock' },
|
||||
{ 'type': 'paragraph' },
|
||||
'W',
|
||||
'h',
|
||||
'e',
|
||||
'e',
|
||||
{ 'type': '/paragraph' },
|
||||
{
|
||||
'type': 'alienBlock',
|
||||
'attributes': {
|
||||
'html': '<span typeof="mw:Placeholder" about="#mwt2">Yay</span>' +
|
||||
'<div typeof="mw:Placeholder" about="#mwt2">Blah</div>'
|
||||
}
|
||||
},
|
||||
{ 'type': '/alienBlock' },
|
||||
{
|
||||
'type': 'alienBlock',
|
||||
'attributes': {
|
||||
'html': '<span typeof="mw:Placeholder" about="#mwt3">Meh</span>'
|
||||
}
|
||||
},
|
||||
{ 'type': '/alienBlock' }
|
||||
]
|
||||
},
|
||||
'whitespace preservation with an about group': {
|
||||
'html': ' <div typeof="mw:Placeholder" about="#mwt1">\tFoo\t\t</div>\t\t\t' +
|
||||
'<div typeof="mw:Placeholder" about="#mwt1"> Bar </div> ',
|
||||
'data': [
|
||||
{
|
||||
'type': 'alienBlock',
|
||||
'attributes': {
|
||||
'html': '<div typeof="mw:Placeholder" about="#mwt1">\tFoo\t\t</div>\t\t\t' +
|
||||
'<div typeof="mw:Placeholder" about="#mwt1"> Bar </div>'
|
||||
},
|
||||
'internal': {
|
||||
'whitespace': [ ' ', undefined, undefined, ' ' ]
|
||||
}
|
||||
},
|
||||
{ 'type': '/alienBlock' }
|
||||
]
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue