Merge "Don't compute ref contents in converter preview mode"

This commit is contained in:
Jforrester 2018-04-30 16:57:03 +00:00 committed by Gerrit Code Review
commit 2e13427f3d
2 changed files with 101 additions and 96 deletions

View file

@ -9,14 +9,13 @@ QUnit.module( 've.ui.DiffElement (Cite)' );
QUnit.test( 'Diffing', function ( assert ) {
var i, len,
// spacer = '<div class="ve-ui-diffElement-spacer">⋮</div>',
ref = function ( text, num, name ) {
ref = function ( text, num ) {
var dataMw = {
name: 'ref',
body: { html: text }
// attrs doesn't get set in preview mode
};
if ( name ) {
dataMw.attrs = { name: name };
}
return '<sup typeof="mw:Extension/ref" data-mw="' + JSON.stringify( dataMw ).replace( /"/g, '&quot;' ) + '" class="mw-ref">' +
'<a style="counter-reset: mw-Ref ' + num + ';"><span class="mw-reflink-text">[' + num + ']</span></a>' +
'</sup>';
@ -37,10 +36,10 @@ QUnit.test( 'Diffing', function ( assert ) {
'<p>' +
ref( 'Foo', '1' ) +
'<span data-diff-action="change-remove">' +
ref( 'Bar', '2', ':0' ) +
ref( 'Bar', '2' ) +
'</span>' +
'<span data-diff-action="change-insert">' +
ref( 'Bar ish', '2', ':0' ) +
ref( 'Bar ish', '2' ) +
'</span>' +
ref( 'Baz', '3' ) +
'</p>' +

View file

@ -101,118 +101,124 @@ ve.dm.MWReferenceNode.static.toDataElement = function ( domElements, converter )
};
ve.dm.MWReferenceNode.static.toDomElements = function ( dataElement, doc, converter ) {
var itemNodeHtml, originalHtml, mwData, i, iLen, keyedNodes, setContents, contentsAlreadySet,
var itemNode, itemNodeRange, itemNodeHtml, originalHtml, mwData, i, iLen, keyedNodes, setContents, contentsAlreadySet,
originalMw, listKeyParts, name, group, $link,
isForClipboard = converter.isForClipboard(),
el = doc.createElement( 'sup' ),
itemNodeWrapper = doc.createElement( 'div' ),
originalHtmlWrapper = doc.createElement( 'div' ),
itemNode = converter.internalList.getItemNode( dataElement.attributes.listIndex ),
itemNodeRange = itemNode.getRange();
originalHtmlWrapper = doc.createElement( 'div' );
el.setAttribute( 'typeof', 'mw:Extension/ref' );
mwData = dataElement.attributes.mw ? ve.copy( dataElement.attributes.mw ) : {};
mwData.name = 'ref';
setContents = dataElement.attributes.contentsUsed;
if ( isForClipboard || converter.isForParser() ) {
setContents = dataElement.attributes.contentsUsed;
keyedNodes = converter.internalList
.getNodeGroup( dataElement.attributes.listGroup )
.keyedNodes[ dataElement.attributes.listKey ];
// This call rebuilds the document tree if it isn't built already (e.g. on a
// document slice), so only use when necessary (i.e. not in preview mode)
itemNode = converter.internalList.getItemNode( dataElement.attributes.listIndex );
itemNodeRange = itemNode.getRange();
if ( setContents ) {
// Check if a previous node has already set the content. If so, we don't overwrite this
// node's contents.
contentsAlreadySet = false;
if ( keyedNodes ) {
for ( i = 0, iLen = keyedNodes.length; i < iLen; i++ ) {
if ( keyedNodes[ i ].element === dataElement ) {
break;
}
if ( keyedNodes[ i ].element.attributes.contentsUsed ) {
contentsAlreadySet = true;
break;
keyedNodes = converter.internalList
.getNodeGroup( dataElement.attributes.listGroup )
.keyedNodes[ dataElement.attributes.listKey ];
if ( setContents ) {
// Check if a previous node has already set the content. If so, we don't overwrite this
// node's contents.
contentsAlreadySet = false;
if ( keyedNodes ) {
for ( i = 0, iLen = keyedNodes.length; i < iLen; i++ ) {
if ( keyedNodes[ i ].element === dataElement ) {
break;
}
if ( keyedNodes[ i ].element.attributes.contentsUsed ) {
contentsAlreadySet = true;
break;
}
}
}
}
} else {
// Check if any other nodes with this key provided content. If not
// then we attach the contents to the first reference with this key
// Check that this is the first reference with its key
if ( keyedNodes && dataElement === keyedNodes[ 0 ].element ) {
setContents = true;
// Check no other reference originally defined the contents
// As this is keyedNodes[0] we can start at 1
for ( i = 1, iLen = keyedNodes.length; i < iLen; i++ ) {
if ( keyedNodes[ i ].element.attributes.contentsUsed ) {
setContents = false;
break;
}
}
}
}
if ( setContents && !contentsAlreadySet ) {
converter.getDomSubtreeFromData(
itemNode.getDocument().getFullData( itemNodeRange, true ),
itemNodeWrapper
);
itemNodeHtml = itemNodeWrapper.innerHTML; // Returns '' if itemNodeWrapper is empty
originalHtml = ve.getProp( mwData, 'body', 'html' ) ||
( ve.getProp( mwData, 'body', 'id' ) !== undefined && itemNode.getAttribute( 'originalHtml' ) ) ||
'';
originalHtmlWrapper.innerHTML = originalHtml;
// Only set body.html if itemNodeHtml and originalHtml are actually different,
// or we are writing the clipboard for use in another VE instance
if ( isForClipboard || !originalHtmlWrapper.isEqualNode( itemNodeWrapper ) ) {
ve.setProp( mwData, 'body', 'html', itemNodeHtml );
}
}
// If we have no internal item data for this reference, don't let it get pasted into
// another VE document. T110479
if ( isForClipboard && itemNodeRange.isCollapsed() ) {
el.setAttribute( 'data-ve-ignore', 'true' );
}
// Generate name
listKeyParts = dataElement.attributes.listKey.match( this.listKeyRegex );
if ( listKeyParts[ 1 ] === 'auto' ) {
// Only render a name if this key was reused
if ( keyedNodes.length > 1 ) {
// Allocate a unique list key, then strip the 'literal/'' prefix
name = converter.internalList.getUniqueListKey(
dataElement.attributes.listGroup,
dataElement.attributes.listKey,
// Generate a name starting with ':' to distinguish it from normal names
'literal/:'
).slice( 'literal/'.length );
} else {
name = undefined;
}
} else {
// Use literal name
name = listKeyParts[ 2 ];
}
// Set name
if ( name !== undefined ) {
ve.setProp( mwData, 'attrs', 'name', name );
}
// Check if any other nodes with this key provided content. If not
// then we attach the contents to the first reference with this key
// Set or clear group
if ( dataElement.attributes.refGroup !== '' ) {
ve.setProp( mwData, 'attrs', 'group', dataElement.attributes.refGroup );
} else if ( mwData.attrs ) {
delete mwData.attrs.refGroup;
// Check that this is the first reference with its key
if ( keyedNodes && dataElement === keyedNodes[ 0 ].element ) {
setContents = true;
// Check no other reference originally defined the contents
// As this is keyedNodes[0] we can start at 1
for ( i = 1, iLen = keyedNodes.length; i < iLen; i++ ) {
if ( keyedNodes[ i ].element.attributes.contentsUsed ) {
setContents = false;
break;
}
}
}
}
// Add reference contents to data-mw.
if ( setContents && !contentsAlreadySet ) {
converter.getDomSubtreeFromData(
itemNode.getDocument().getFullData( itemNodeRange, true ),
itemNodeWrapper
);
itemNodeHtml = itemNodeWrapper.innerHTML; // Returns '' if itemNodeWrapper is empty
originalHtml = ve.getProp( mwData, 'body', 'html' ) ||
( ve.getProp( mwData, 'body', 'id' ) !== undefined && itemNode.getAttribute( 'originalHtml' ) ) ||
'';
originalHtmlWrapper.innerHTML = originalHtml;
// Only set body.html if itemNodeHtml and originalHtml are actually different,
// or we are writing the clipboard for use in another VE instance
if ( isForClipboard || !originalHtmlWrapper.isEqualNode( itemNodeWrapper ) ) {
ve.setProp( mwData, 'body', 'html', itemNodeHtml );
}
}
// If we have no internal item data for this reference, don't let it get pasted into
// another VE document. T110479
if ( isForClipboard && itemNodeRange.isCollapsed() ) {
el.setAttribute( 'data-ve-ignore', 'true' );
}
// Generate name
listKeyParts = dataElement.attributes.listKey.match( this.listKeyRegex );
if ( listKeyParts[ 1 ] === 'auto' ) {
// Only render a name if this key was reused
if ( keyedNodes.length > 1 ) {
// Allocate a unique list key, then strip the 'literal/'' prefix
name = converter.internalList.getUniqueListKey(
dataElement.attributes.listGroup,
dataElement.attributes.listKey,
// Generate a name starting with ':' to distinguish it from normal names
'literal/:'
).slice( 'literal/'.length );
} else {
name = undefined;
}
} else {
// Use literal name
name = listKeyParts[ 2 ];
}
// Set name
if ( name !== undefined ) {
ve.setProp( mwData, 'attrs', 'name', name );
}
// Set or clear group
if ( dataElement.attributes.refGroup !== '' ) {
ve.setProp( mwData, 'attrs', 'group', dataElement.attributes.refGroup );
} else if ( mwData.attrs ) {
delete mwData.attrs.refGroup;
}
}
// If mwAttr and originalMw are the same, use originalMw to prevent reserialization,
// unless we are writing the clipboard for use in another VE instance
// Reserialization has the potential to reorder keys and so change the DOM unnecessarily
originalMw = dataElement.attributes.originalMw;
if ( !isForClipboard && originalMw && ve.compare( mwData, JSON.parse( originalMw ) ) ) {
if ( converter.isForParser() && originalMw && ve.compare( mwData, JSON.parse( originalMw ) ) ) {
el.setAttribute( 'data-mw', originalMw );
// Return the original DOM elements if possible