mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
synced 2024-09-27 12:16:51 +00:00
Retain attributes when reopening closed nodes
* Push entire elements onto openingStack rather than type strings * When closing an element, build a clone of the opening and push it onto closedElements, then insert that clone when reopening the element Change-Id: I8b0fb44394aed6c471dc6dacaab03e44c2333733
This commit is contained in:
parent
b167453f0d
commit
f7445b37b8
|
@ -848,7 +848,8 @@ ve.dm.Document.prototype.fixupInsertion = function( data, offset ) {
|
|||
// TODO document what all these variables are for
|
||||
var newData = [], i, j, openingStack = [], closingStack = [], fixupStack = [], node, index,
|
||||
parentNode, parentType, childType, allowedParents, allowedChildren,
|
||||
parentsOK, childrenOK, openings, closings, expectedType, popped, paragraphOpened;
|
||||
parentsOK, childrenOK, openings, closings, closedElements, expectedType, popped,
|
||||
clonedElement, paragraphOpened;
|
||||
node = this.getNodeFromOffset( offset );
|
||||
|
||||
/**
|
||||
|
@ -873,7 +874,7 @@ ve.dm.Document.prototype.fixupInsertion = function( data, offset ) {
|
|||
closingStack.pop();
|
||||
} else {
|
||||
// This opens something new, put it on openingStack
|
||||
openingStack.push( element.type );
|
||||
openingStack.push( element );
|
||||
}
|
||||
} else {
|
||||
// Closing
|
||||
|
@ -882,7 +883,7 @@ ve.dm.Document.prototype.fixupInsertion = function( data, offset ) {
|
|||
// The opening was on openingStack, so we're closing
|
||||
// a node that was opened within data. Don't track
|
||||
// that on closingStack
|
||||
expectedType = openingStack.pop();
|
||||
expectedType = openingStack.pop().type;
|
||||
} else {
|
||||
// openingStack is empty, so we're closing a node that
|
||||
// was already in the document. This means we have to
|
||||
|
@ -911,6 +912,7 @@ ve.dm.Document.prototype.fixupInsertion = function( data, offset ) {
|
|||
childType = data[i].type || 'text';
|
||||
openings = [];
|
||||
closings = [];
|
||||
closedElements = [];
|
||||
// Opening or content
|
||||
// Make sure that opening this element here does not violate the
|
||||
// parent/children/content rules. If it does, insert stuff to fix it
|
||||
|
@ -961,7 +963,9 @@ ve.dm.Document.prototype.fixupInsertion = function( data, offset ) {
|
|||
// Close the parent and try one level up
|
||||
closings.push( { 'type': '/' + parentType } );
|
||||
if ( openingStack.length > 0 ) {
|
||||
parentType = openingStack.pop();
|
||||
popped = openingStack.pop();
|
||||
parentType = popped.type;
|
||||
closedElements.push( ve.copyObject( popped ) );
|
||||
// The opening was on openingStack, so we're closing
|
||||
// a node that was opened within data. Don't track
|
||||
// that on closingStack
|
||||
|
@ -970,6 +974,11 @@ ve.dm.Document.prototype.fixupInsertion = function( data, offset ) {
|
|||
// was already in the document. This means we have to
|
||||
// reopen it later, so track this on closingStack
|
||||
closingStack.push( parentType );
|
||||
clonedElement = { 'type': parentType };
|
||||
if ( !$.isEmptyObject( node.getAttributes() ) ) {
|
||||
clonedElement.attributes = ve.copyObject( node.getAttributes() );
|
||||
}
|
||||
closedElements.push( clonedElement );
|
||||
parentNode = parentNode.getParent();
|
||||
if ( !parentNode ) {
|
||||
throw 'Cannot insert ' + childType + ' even ' +
|
||||
|
@ -999,7 +1008,7 @@ ve.dm.Document.prototype.fixupInsertion = function( data, offset ) {
|
|||
parentType = childType;
|
||||
}
|
||||
} else {
|
||||
fixupStack.push( { 'expectedType': '/' + data[i].type, 'openings': openings, 'closings': closings } );
|
||||
fixupStack.push( { 'expectedType': '/' + data[i].type, 'openings': openings, 'closedElements': closedElements } );
|
||||
parentType = data[i].type;
|
||||
}
|
||||
} else {
|
||||
|
@ -1013,9 +1022,8 @@ ve.dm.Document.prototype.fixupInsertion = function( data, offset ) {
|
|||
for ( j = popped.openings.length - 1; j >= 0; j-- ) {
|
||||
writeElement( { 'type': '/' + popped.openings[j].type }, i );
|
||||
}
|
||||
for ( j = popped.closings.length - 1; j >= 0; j-- ) {
|
||||
// TODO keep actual elements around so attributes are preserved
|
||||
writeElement( { 'type': popped.closings[j].type.substr( 1 ) }, i );
|
||||
for ( j = popped.closedElements.length - 1; j >= 0; j-- ) {
|
||||
writeElement( popped.closedElements[j], i );
|
||||
}
|
||||
}
|
||||
parentType = openingStack.length > 0 ? openingStack[openingStack.length - 1] : node.getType();
|
||||
|
@ -1027,7 +1035,7 @@ ve.dm.Document.prototype.fixupInsertion = function( data, offset ) {
|
|||
popped = openingStack[openingStack.length - 1];
|
||||
// writeElement() will perform the actual pop() that removes
|
||||
// popped from openingStack
|
||||
writeElement( { 'type': '/' + popped }, i );
|
||||
writeElement( { 'type': '/' + popped.type }, i );
|
||||
}
|
||||
// Re-open closed nodes
|
||||
while ( closingStack.length > 0 ) {
|
||||
|
|
|
@ -77,8 +77,7 @@ test( 'newFromInsertion', function() {
|
|||
{
|
||||
'type': 'replace',
|
||||
'remove': [],
|
||||
// FIXME should retain attributes in heading opening
|
||||
'insert': [{'type': '/heading' }, { 'type': 'paragraph' } , 'F', 'O', 'O', { 'type': '/paragraph' }, { 'type': 'heading' }]
|
||||
'insert': [{'type': '/heading' }, { 'type': 'paragraph' } , 'F', 'O', 'O', { 'type': '/paragraph' }, { 'type': 'heading', 'attributes': { 'level': 1 } }]
|
||||
},
|
||||
{ 'type': 'retain', 'length': 57 }
|
||||
]
|
||||
|
@ -90,7 +89,7 @@ test( 'newFromInsertion', function() {
|
|||
{
|
||||
'type': 'replace',
|
||||
'remove': [],
|
||||
'insert': [{'type': '/list' }, { 'type': 'paragraph' } , 'F', 'O', 'O', { 'type': '/paragraph' }, { 'type': 'list' }]
|
||||
'insert': [{'type': '/list' }, { 'type': 'paragraph' } , 'F', 'O', 'O', { 'type': '/paragraph' }, { 'type': 'list', 'attributes': { 'style': 'bullet' } }]
|
||||
},
|
||||
{ 'type': 'retain', 'length': 47 }
|
||||
]
|
||||
|
|
Loading…
Reference in a new issue