jenkins-bot has submitted this change and it was merged.

Change subject: MWTemplateNode should serialise original HTML if unchanged
......................................................................


MWTemplateNode should serialise original HTML if unchanged

To help the selective serialiser we can return the original
HTML for generated content if it is unmodified.

As the output of toDomElements now depends on changes
to the dataElement we now have a 'modify' function in
the some test cases.

We also now have 'storeItems' to assert that the index-value
store is correctly populated and for loading values back
into the store for toDomElements tests.

Also make 'mw' an attribute and remove 'about' property.

Bug: 47394
Change-Id: I2bbb5d2d6a90c4eb87fa129671112c92a9b931e7
---
M modules/ve/dm/nodes/ve.dm.MWTemplateNode.js
M modules/ve/dm/ve.dm.Converter.js
M modules/ve/test/dm/ve.dm.Converter.test.js
M modules/ve/test/dm/ve.dm.example.js
4 files changed, 169 insertions(+), 67 deletions(-)

Approvals:
  Catrope: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/modules/ve/dm/nodes/ve.dm.MWTemplateNode.js 
b/modules/ve/dm/nodes/ve.dm.MWTemplateNode.js
index 0da0b13..823ebdd 100644
--- a/modules/ve/dm/nodes/ve.dm.MWTemplateNode.js
+++ b/modules/ve/dm/nodes/ve.dm.MWTemplateNode.js
@@ -35,34 +35,45 @@
 ve.dm.MWTemplateNode.static.getHashObject = function ( dataElement ) {
        return {
                type: dataElement.type,
-               mw: dataElement.mw
+               mw: dataElement.attributes.mw
        };
 };
 
 ve.dm.MWTemplateNode.static.toDataElement = function ( domElements, converter 
) {
        var dataElement,
-               about = domElements[0].getAttribute( 'about' ),
                mw = JSON.parse( domElements[0].getAttribute( 'data-mw' ) ),
                isInline = this.isHybridInline( domElements, converter ),
                type = isInline ? 'MWtemplateInline' : 'MWtemplateBlock';
 
        dataElement = {
                'type': type,
-               'mw': mw,
-               'about': about
+               'attributes': {
+                       'mw': mw,
+                       'mwOriginal': ve.copyObject( mw )
+               }
        };
        this.storeHtml( dataElement, domElements, converter.getStore() );
        return dataElement;
 };
 
-ve.dm.MWTemplateNode.static.toDomElements = function ( dataElement, doc ) {
-       var span = doc.createElement( 'span' );
-       // All we need to send back to Parsoid is the original template marker,
-       // with a reconstructed data-mw property.
-       span.setAttribute( 'about', dataElement.about );
-       span.setAttribute( 'typeof', 'mw:Object/Template' );
-       span.setAttribute( 'data-mw', JSON.stringify( dataElement.mw ) );
-       return [ span ];
+ve.dm.MWTemplateNode.static.toDomElements = function ( dataElement, doc, 
converter ) {
+       var wrapper, span, index, html;
+       if ( ve.compareObjects( dataElement.attributes.mw, 
dataElement.attributes.mwOriginal ) ) {
+               // If the template is unchanged just send back the original 
html so selser can skip over it
+               index = converter.getStore().indexOfHash( ve.getHash( 
this.getHashObject( dataElement ) ) );
+               html = converter.getStore().value( index );
+               wrapper = doc.createElement( 'div' );
+               $( wrapper ).html( html );
+               // Convert wrapper.children to an array
+               return Array.prototype.slice.call( wrapper.childNodes, 0 );
+       } else {
+               span = doc.createElement( 'span' );
+               // All we need to send back to Parsoid is the original template 
marker,
+               // with a reconstructed data-mw property.
+               span.setAttribute( 'typeof', 'mw:Object/Template' );
+               span.setAttribute( 'data-mw', JSON.stringify( 
dataElement.attributes.mw ) );
+               return [ span ];
+       }
 };
 
 /* Concrete subclasses */
diff --git a/modules/ve/dm/ve.dm.Converter.js b/modules/ve/dm/ve.dm.Converter.js
index 66191be..e5e1701 100644
--- a/modules/ve/dm/ve.dm.Converter.js
+++ b/modules/ve/dm/ve.dm.Converter.js
@@ -770,26 +770,27 @@
  */
 ve.dm.Converter.prototype.getDomFromData = function ( store, data ) {
        var doc = ve.createDocumentFromHTML( '' );
-       this.getDomSubtreeFromData( store, data, doc.body );
+       this.store = store;
+       this.getDomSubtreeFromData( data, doc.body );
+       this.store = null;
        return doc;
 };
 
 /**
  * Convert linear model data to an HTML DOM subtree and add it to a container 
element.
  *
- * @param {ve.dm.IndexValueStore} store Index-value store
  * @param {Array} data Linear model data
  * @param {HTMLElement} container DOM element to add the generated elements 
to. Should be empty.
  * @throws Unbalanced data: looking for closing /type
  */
-ve.dm.Converter.prototype.getDomSubtreeFromData = function ( store, data, 
container ) {
+ve.dm.Converter.prototype.getDomSubtreeFromData = function ( data, container ) 
{
        var text, i, j, k, annotations, annotationElement, dataElement, 
dataElementOrSlice,
                childDomElements, pre, ours, theirs, parentDomElement, 
lastChild,
                isContentNode, changed, parentChanged, sibling, 
previousSiblings, doUnwrap, textNode,
                conv = this,
                doc = container.ownerDocument,
                domElement = container,
-               annotationStack = new ve.dm.AnnotationSet( store );
+               annotationStack = new ve.dm.AnnotationSet( this.store );
 
        function openAnnotation( annotation ) {
                // Add text if needed
@@ -873,7 +874,7 @@
                                )
                        ) {
                                annotations = new ve.dm.AnnotationSet(
-                                       store, data[i].annotations || data[i][1]
+                                       this.store, data[i].annotations || 
data[i][1]
                                );
                                ve.dm.Converter.openAndCloseAnnotations( 
annotationStack, annotations,
                                        openAnnotation, closeAnnotation
@@ -917,7 +918,7 @@
                                domElement = domElement.parentNode;
                        }
                        // Clear annotationStack
-                       annotationStack = new ve.dm.AnnotationSet( store );
+                       annotationStack = new ve.dm.AnnotationSet( this.store );
                } else if ( data[i].type !== undefined ) {
                        dataElement = data[i];
                        // Element
diff --git a/modules/ve/test/dm/ve.dm.Converter.test.js 
b/modules/ve/test/dm/ve.dm.Converter.test.js
index 687a104..bec9131 100644
--- a/modules/ve/test/dm/ve.dm.Converter.test.js
+++ b/modules/ve/test/dm/ve.dm.Converter.test.js
@@ -40,8 +40,7 @@
 } );
 
 QUnit.test( 'getDataFromDom', function ( assert ) {
-       var msg, n = 0,
-               store = new ve.dm.IndexValueStore(),
+       var msg, store, i, length, hash, n = 0,
                cases = ve.copyObject( ve.dm.example.domToDataCases );
 
        // TODO: this is a hack to make normal heading/preformatted
@@ -52,25 +51,39 @@
        for ( msg in cases ) {
                if ( cases[msg].html !== null ) {
                        n++;
+                       if ( cases[msg].storeItems ) {
+                               n += cases[msg].storeItems.length;
+                       }
                }
        }
        QUnit.expect( n );
 
        for ( msg in cases ) {
                if ( cases[msg].html !== null ) {
+                       store = new ve.dm.IndexValueStore();
                        ve.dm.example.preprocessAnnotations( cases[msg].data, 
store );
                        assert.deepEqual(
                                ve.dm.converter.getDataFromDom( store, 
ve.createDocumentFromHTML( cases[msg].html ) ).getData(),
                                cases[msg].data,
                                msg
                        );
+                       // check storeItems have been added to store
+                       if ( cases[msg].storeItems ) {
+                               for ( i = 0, length = 
cases[msg].storeItems.length; i < length; i++ ) {
+                                       hash = cases[msg].storeItems[i].hash || 
ve.getHash( cases[msg].storeItems[i].value );
+                                       assert.deepEqual(
+                                               store.value( store.indexOfHash( 
hash ) ),
+                                               cases[msg].storeItems[i].value,
+                                               msg + ': store item ' + i + ' 
found'
+                                       );
+                               }
+                       }
                }
        }
 } );
 
 QUnit.test( 'getDomFromData', function ( assert ) {
-       var msg, originalData, n = 0,
-               store = new ve.dm.IndexValueStore(),
+       var msg, originalData, store, i, length, n = 0,
                cases = ve.copyObject( ve.dm.example.domToDataCases );
 
        for ( msg in cases ) {
@@ -79,6 +92,17 @@
        QUnit.expect( 2*n );
 
        for ( msg in cases ) {
+               store = new ve.dm.IndexValueStore();
+               // load storeItems into store
+               if ( cases[msg].storeItems ) {
+                       for ( i = 0, length = cases[msg].storeItems.length; i < 
length; i++ ) {
+                               store.index( cases[msg].storeItems[i].value, 
cases[msg].storeItems[i].hash );
+                       }
+               }
+               // functions won't be copied by ve.copyObject
+               if( ve.dm.example.domToDataCases[msg].modify ) {
+                       ve.dm.example.domToDataCases[msg].modify( 
cases[msg].data );
+               }
                ve.dm.example.preprocessAnnotations( cases[msg].data, store );
                originalData = ve.copyArray( cases[msg].data );
                assert.equalDomElement(
diff --git a/modules/ve/test/dm/ve.dm.example.js 
b/modules/ve/test/dm/ve.dm.example.js
index fbf9baa..cd3e1cb 100644
--- a/modules/ve/test/dm/ve.dm.example.js
+++ b/modules/ve/test/dm/ve.dm.example.js
@@ -715,11 +715,75 @@
 };
 
 ve.dm.example.MWImageHtml = '<a rel="mw:Image" href="./File:Wiki.png" 
data-parsoid="{&quot;tsr&quot;:[158,216],&quot;src&quot;:&quot;[[Image:Wiki.png|500px|thumb|center|Example
 wiki 
file]]&quot;,&quot;optNames&quot;:{&quot;width&quot;:&quot;$1px&quot;},&quot;dsr&quot;:[158,216,null,null]}"><img
 height="" width="500" 
src="/index.php?title=Special:FilePath/Wiki.png&amp;width=500" 
alt="Wiki.png"></a>';
-ve.dm.example.MWBlockTemplateSpan = '<span about="#mwt1" 
typeof="mw:Object/Template" 
data-mw="{&quot;id&quot;:&quot;mwt1&quot;,&quot;target&quot;:{&quot;wt&quot;:&quot;Test&quot;},&quot;params&quot;:{&quot;1&quot;:{&quot;wt&quot;:&quot;Hello,
 world!&quot;}}}" 
data-parsoid="{&quot;tsr&quot;:[18,40],&quot;src&quot;:&quot;{{Test|Hello, 
world!}}&quot;,&quot;dsr&quot;:[18,40,null,null]}"></span>';
-ve.dm.example.MWBlockTemplateContent = '<p about="#mwt1" 
data-parsoid="{}">Hello, world!</p>';
-ve.dm.example.MWInlineTemplateOpen = '<span about="#mwt1" 
typeof="mw:Object/Template" 
data-mw="{&quot;id&quot;:&quot;mwt1&quot;,&quot;target&quot;:{&quot;wt&quot;:&quot;Inline&quot;},&quot;params&quot;:{&quot;1&quot;:{&quot;wt&quot;:&quot;1,234&quot;}}}"
 
data-parsoid="{&quot;tsr&quot;:[18,34],&quot;src&quot;:&quot;{{Inline|1,234}}&quot;,&quot;dsr&quot;:[18,34,null,null]}">';
-ve.dm.example.MWInlineTemplateContent = '$1,234.00';
-ve.dm.example.MWInlineTemplateClose = '</span>';
+ve.dm.example.MWTemplate = {
+       'blockSpan':         '<span about="#mwt1" typeof="mw:Object/Template" 
data-mw="{&quot;id&quot;:&quot;mwt1&quot;,&quot;target&quot;:{&quot;wt&quot;:&quot;Test&quot;},&quot;params&quot;:{&quot;1&quot;:{&quot;wt&quot;:&quot;Hello,
 world!&quot;}}}" 
data-parsoid="{&quot;tsr&quot;:[18,40],&quot;src&quot;:&quot;{{Test|Hello, 
world!}}&quot;,&quot;dsr&quot;:[18,40,null,null]}"></span>',
+       'blockSpanModified': '<span about="#mwt1" typeof="mw:Object/Template" 
data-mw="{&quot;id&quot;:&quot;mwt1&quot;,&quot;target&quot;:{&quot;wt&quot;:&quot;Test&quot;},&quot;params&quot;:{&quot;1&quot;:{&quot;wt&quot;:&quot;Hello,
 globe!&quot;}}}" 
data-parsoid="{&quot;tsr&quot;:[18,40],&quot;src&quot;:&quot;{{Test|Hello, 
world!}}&quot;,&quot;dsr&quot;:[18,40,null,null]}"></span>',
+       'blockContent': '<p about="#mwt1" data-parsoid="{}">Hello, world!</p>',
+       'blockData': {
+               'type': 'MWtemplateBlock',
+               'attributes': {
+                       'mw': {
+                               'id': 'mwt1',
+                               'target': { 'wt' : 'Test' },
+                               'params': {
+                                       '1': { 'wt': 'Hello, world!' }
+                               }
+                       },
+                       'mwOriginal': {
+                               'id': 'mwt1',
+                               'target': { 'wt' : 'Test' },
+                               'params': {
+                                       '1': { 'wt': 'Hello, world!' }
+                               }
+                       },
+                       'html/0/about': '#mwt1',
+                       'html/0/data-mw': 
'{\"id\":\"mwt1\",\"target\":{\"wt\":\"Test\"},\"params\":{\"1\":{\"wt\":\"Hello,
 world!\"}}}',
+                       'html/0/data-parsoid': 
'{\"tsr\":[18,40],\"src\":\"{{Test|Hello, 
world!}}\",\"dsr\":[18,40,null,null]}',
+                       'html/0/typeof': 'mw:Object/Template',
+                       'html/1/about': '#mwt1',
+                       'html/1/data-parsoid': '{}'
+               },
+       },
+       'inlineOpen':         '<span about="#mwt1" typeof="mw:Object/Template" 
data-mw="{&quot;id&quot;:&quot;mwt1&quot;,&quot;target&quot;:{&quot;wt&quot;:&quot;Inline&quot;},&quot;params&quot;:{&quot;1&quot;:{&quot;wt&quot;:&quot;1,234&quot;}}}"
 
data-parsoid="{&quot;tsr&quot;:[18,34],&quot;src&quot;:&quot;{{Inline|1,234}}&quot;,&quot;dsr&quot;:[18,34,null,null]}">',
+       'inlineOpenModified': '<span about="#mwt1" typeof="mw:Object/Template" 
data-mw="{&quot;id&quot;:&quot;mwt1&quot;,&quot;target&quot;:{&quot;wt&quot;:&quot;Inline&quot;},&quot;params&quot;:{&quot;1&quot;:{&quot;wt&quot;:&quot;5,678&quot;}}}"
 
data-parsoid="{&quot;tsr&quot;:[18,34],&quot;src&quot;:&quot;{{Inline|1,234}}&quot;,&quot;dsr&quot;:[18,34,null,null]}">',
+       'inlineContent': '$1,234.00',
+       'inlineClose': '</span>',
+       'inlineData': {
+               'type': 'MWtemplateInline',
+               'attributes': {
+                       'mw': {
+                               'id': 'mwt1',
+                               'target': { 'wt' : 'Inline' },
+                               'params': {
+                                       '1': { 'wt': '1,234' }
+                               }
+                       },
+                       'mwOriginal': {
+                               'id': 'mwt1',
+                               'target': { 'wt' : 'Inline' },
+                               'params': {
+                                       '1': { 'wt': '1,234' }
+                               }
+                       },
+                       'html/0/about': '#mwt1',
+                       'html/0/data-mw': 
'{\"id\":\"mwt1\",\"target\":{\"wt\":\"Inline\"},\"params\":{\"1\":{\"wt\":\"1,234\"}}}',
+                       'html/0/data-parsoid': 
'{\"tsr\":[18,34],\"src\":\"{{Inline|1,234}}\",\"dsr\":[18,34,null,null]}',
+                       'html/0/typeof': 'mw:Object/Template'
+               },
+       }
+};
+
+ve.dm.example.MWTemplate.blockParamsHash = ve.getHash( 
ve.dm.MWTemplateNode.static.getHashObject( ve.dm.example.MWTemplate.blockData ) 
);
+ve.dm.example.MWTemplate.blockStoreItems = {
+       'hash': ve.dm.example.MWTemplate.blockParamsHash,
+       'value': ve.dm.example.MWTemplate.blockSpan + 
ve.dm.example.MWTemplate.blockContent
+};
+
+ve.dm.example.MWTemplate.inlineParamsHash = ve.getHash( 
ve.dm.MWTemplateNode.static.getHashObject( ve.dm.example.MWTemplate.inlineData 
) );
+ve.dm.example.MWTemplate.inlineStoreItems = {
+       'hash': ve.dm.example.MWTemplate.inlineParamsHash,
+       'value': ve.dm.example.MWTemplate.inlineOpen + 
ve.dm.example.MWTemplate.inlineContent + ve.dm.example.MWTemplate.inlineClose
+};
 
 ve.dm.example.domToDataCases = {
        'paragraph with plain text': {
@@ -781,56 +845,58 @@
                ]
        },
        'mw:Template (block level)': {
-               'html': '<body>' + ve.dm.example.MWBlockTemplateSpan + 
ve.dm.example.MWBlockTemplateContent + '</body>',
+               'html': '<body>' + ve.dm.example.MWTemplate.blockSpan + 
ve.dm.example.MWTemplate.blockContent + '</body>',
                'data': [
-                       {
-                               'type': 'MWtemplateBlock',
-                               'mw': {
-                                       'id': 'mwt1',
-                                       'target': { 'wt' : 'Test' },
-                                       'params': {
-                                               '1': { 'wt': 'Hello, world!' }
-                                       }
-                               },
-                               'about': '#mwt1',
-                               'attributes': {
-                                       'html/0/about': '#mwt1',
-                                       'html/0/data-mw': 
'{\"id\":\"mwt1\",\"target\":{\"wt\":\"Test\"},\"params\":{\"1\":{\"wt\":\"Hello,
 world!\"}}}',
-                                       'html/0/data-parsoid': 
'{\"tsr\":[18,40],\"src\":\"{{Test|Hello, 
world!}}\",\"dsr\":[18,40,null,null]}',
-                                       'html/0/typeof': 'mw:Object/Template',
-                                       'html/1/about': '#mwt1',
-                                       'html/1/data-parsoid': '{}'
-                               },
-                       },
+                       ve.dm.example.MWTemplate.blockData,
                        { 'type': '/MWtemplateBlock' },
                ],
-               'normalizedHtml': ve.dm.example.MWBlockTemplateSpan
+               'storeItems': [
+                       ve.dm.example.MWTemplate.blockStoreItems
+               ],
+               'normalizedHtml': ve.dm.example.MWTemplate.blockSpan + 
ve.dm.example.MWTemplate.blockContent
+       },
+       'mw:Template (block level - modified)': {
+               'html': '<body>' + ve.dm.example.MWTemplate.blockSpan + 
ve.dm.example.MWTemplate.blockContent + '</body>',
+               'data': [
+                       ve.dm.example.MWTemplate.blockData,
+                       { 'type': '/MWtemplateBlock' },
+               ],
+               'storeItems': [
+                       ve.dm.example.MWTemplate.blockStoreItems
+               ],
+               'modify': function( data ) {
+                       data[0].attributes.mw.params['1'].wt = 'Hello, globe!';
+               },
+               'normalizedHtml': ve.dm.example.MWTemplate.blockSpanModified
        },
        'mw:Template (inline)': {
-               'html': '<body>' + ve.dm.example.MWInlineTemplateOpen + 
ve.dm.example.MWInlineTemplateContent + ve.dm.example.MWInlineTemplateClose + 
'</body>',
+               'html': '<body>' + ve.dm.example.MWTemplate.inlineOpen + 
ve.dm.example.MWTemplate.inlineContent + ve.dm.example.MWTemplate.inlineClose + 
'</body>',
                'data': [
                        { 'type': 'paragraph', 'internal': { 'generated': 
'wrapper' } },
-                       {
-                               'type': 'MWtemplateInline',
-                               'mw': {
-                                       'id': 'mwt1',
-                                       'target': { 'wt' : 'Inline' },
-                                       'params': {
-                                               '1': { 'wt': '1,234' }
-                                       }
-                               },
-                               'about': '#mwt1',
-                               'attributes': {
-                                       'html/0/about': '#mwt1',
-                                       'html/0/data-mw': 
'{\"id\":\"mwt1\",\"target\":{\"wt\":\"Inline\"},\"params\":{\"1\":{\"wt\":\"1,234\"}}}',
-                                       'html/0/data-parsoid': 
'{\"tsr\":[18,34],\"src\":\"{{Inline|1,234}}\",\"dsr\":[18,34,null,null]}',
-                                       'html/0/typeof': 'mw:Object/Template'
-                               },
-                       },
+                       ve.dm.example.MWTemplate.inlineData,
                        { 'type': '/MWtemplateInline' },
                        { 'type': '/paragraph' }
                ],
-               'normalizedHtml': ve.dm.example.MWInlineTemplateOpen + 
ve.dm.example.MWInlineTemplateClose
+               'storeItems': [
+                       ve.dm.example.MWTemplate.inlineStoreItems
+               ],
+               'normalizedHtml': ve.dm.example.MWTemplate.inlineOpen + 
ve.dm.example.MWTemplate.inlineContent + ve.dm.example.MWTemplate.inlineClose
+       },
+       'mw:Template (inline - modified)': {
+               'html': '<body>' + ve.dm.example.MWTemplate.inlineOpen + 
ve.dm.example.MWTemplate.inlineContent + ve.dm.example.MWTemplate.inlineClose + 
'</body>',
+               'data': [
+                       { 'type': 'paragraph', 'internal': { 'generated': 
'wrapper' } },
+                       ve.dm.example.MWTemplate.inlineData,
+                       { 'type': '/MWtemplateInline' },
+                       { 'type': '/paragraph' }
+               ],
+               'storeItems': [
+                       ve.dm.example.MWTemplate.inlineStoreItems
+               ],
+               'modify': function( data ) {
+                       data[1].attributes.mw.params['1'].wt = '5,678';
+               },
+               'normalizedHtml': ve.dm.example.MWTemplate.inlineOpenModified + 
ve.dm.example.MWTemplate.inlineClose
        },
        'paragraph with alienInline inside': {
                'html': '<body><p>a<tt class="foo">b</tt>c</p></body>',

-- 
To view, visit https://gerrit.wikimedia.org/r/60110
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I2bbb5d2d6a90c4eb87fa129671112c92a9b931e7
Gerrit-PatchSet: 2
Gerrit-Project: mediawiki/extensions/VisualEditor
Gerrit-Branch: master
Gerrit-Owner: Esanders <esand...@wikimedia.org>
Gerrit-Reviewer: Catrope <roan.katt...@gmail.com>
Gerrit-Reviewer: jenkins-bot

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to