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

Change subject: Bug 50243: Preserve transclusion parameter order
......................................................................


Bug 50243: Preserve transclusion parameter order

* Record the order of keys in an internal array inside data-parsoid, so that
  we can preserve the order on edit.
* Use it in the serializer to preserve the order

Change-Id: I541abfba5edd274f43c0eda9ddef8d61c0d544e2
---
M js/lib/ext.core.ExtensionHandler.js
M js/lib/ext.core.TemplateHandler.js
M js/lib/mediawiki.DOMPostProcessor.js
M js/lib/mediawiki.WikitextSerializer.js
4 files changed, 84 insertions(+), 32 deletions(-)

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



diff --git a/js/lib/ext.core.ExtensionHandler.js 
b/js/lib/ext.core.ExtensionHandler.js
index 6f509f6..1a82a09 100644
--- a/js/lib/ext.core.ExtensionHandler.js
+++ b/js/lib/ext.core.ExtensionHandler.js
@@ -42,15 +42,17 @@
 /**
  * Get the public data-mw structure that exposes the extension name, args, and 
body
  */
-ExtensionHandler.prototype.getArgDict = function (state) {
+ExtensionHandler.prototype.getArgInfo = function (state) {
        var extToken = state.token,
                extName = state.token.getAttribute("name"),
                extSrc = state.token.getAttribute("source");
 
        return {
-               name: extName,
-               attrs: Util.KVtoHash(extToken.getAttribute("options")),
-               body: { extsrc: Util.extractExtBody(extName, extSrc) }
+               dict: {
+                       name: extName,
+                       attrs: Util.KVtoHash(extToken.getAttribute("options")),
+                       body: { extsrc: Util.extractExtBody(extName, extSrc) }
+               }
        };
 };
 
diff --git a/js/lib/ext.core.TemplateHandler.js 
b/js/lib/ext.core.TemplateHandler.js
index c44865e..eb21a64 100644
--- a/js/lib/ext.core.TemplateHandler.js
+++ b/js/lib/ext.core.TemplateHandler.js
@@ -667,7 +667,8 @@
 
        if (state.recordArgDict) {
                // Get the arg dict
-               var argDict = this.getArgDict(state);
+               var argInfo = this.getArgInfo(state),
+                       argDict = argInfo.dict;
 
                // Add in tpl-target/pf-name info
                // Only one of these will be set.
@@ -676,7 +677,7 @@
 
                // Use a data-attribute to prevent the sanitizer from stripping 
this
                // attribute before it reaches the DOM pass where it is needed.
-               attrs.push(new KV("data-mw-arginfo", JSON.stringify(argDict)));
+               attrs.push(new KV("data-mw-arginfo", JSON.stringify(argInfo)));
        }
 
        if ( chunk.length ) {
@@ -812,13 +813,14 @@
 
        // Add the wrapper attributes to the first element
        firstNode.setAttribute('typeof', state.wrapperType);
+       var argInfo = this.getArgInfo(state);
+       firstNode.setAttribute('data-mw', JSON.stringify(argInfo.dict));
        firstNode.setAttribute('data-parsoid', JSON.stringify(
                {
                        tsr: Util.clone(state.token.dataAttribs.tsr),
                        src: state.token.dataAttribs.src
                }
        ));
-       firstNode.setAttribute('data-mw', 
JSON.stringify(this.getArgDict(state)));
 
        function outerHTML (n) {
                return n.outerHTML;
@@ -836,14 +838,16 @@
        cb({ tokens: toks });
 };
 
+
 /**
  * Get the public data-mw structure that exposes the template name and 
parameters
- * ExtensionHandler provides its own getArgDict function
+ * ExtensionHandler provides its own getArgInfo function
  */
-TemplateHandler.prototype.getArgDict = function (state) {
+TemplateHandler.prototype.getArgInfo = function (state) {
        var src = this.manager.env.page.src,
                params = state.token.attribs,
                dict = {},
+               keys = [],
                argIndex = 1;
 
        // Use source offsets to extract arg-name and arg-value wikitext
@@ -861,9 +865,16 @@
                                name = src.substring(srcOffsets[0], 
srcOffsets[1]);
                        }
 
+                       if (dict[name] === undefined) {
+                               keys.push(name);
+                       }
                        dict[name] = { wt: src.substring(srcOffsets[2], 
srcOffsets[3]) };
                } else {
-                       dict[params[i].k] = params[i].v;
+                       name = params[i].k;
+                       if (dict[name] === undefined) {
+                               keys.push(name);
+                       }
+                       dict[name] = params[i].v;
                }
        }
 
@@ -871,13 +882,11 @@
        if (tplTgtSrcOffsets) {
                var tplTgtWT = src.substring(tplTgtSrcOffsets[0], 
tplTgtSrcOffsets[1]);
                return {
-                       // gwicke: Removed the non-deterministic id member for 
now as this
-                       // makes the data-mw attribute non-deterministic across 
reparses.
-                       // That in turn triggers diffs. See
-                       // https://bugzilla.wikimedia.org/show_bug.cgi?id=47426.
-                       //id: state.wrappedObjectId,
-                       target: { wt: tplTgtWT },
-                       params: dict
+                       dict: {
+                               target: { wt: tplTgtWT },
+                               params: dict
+                       },
+                       keys: keys
                };
        }
 };
diff --git a/js/lib/mediawiki.DOMPostProcessor.js 
b/js/lib/mediawiki.DOMPostProcessor.js
index e54a0b2..b591bfe 100644
--- a/js/lib/mediawiki.DOMPostProcessor.js
+++ b/js/lib/mediawiki.DOMPostProcessor.js
@@ -1108,7 +1108,7 @@
                return rId;
        }
 
-       function recordTemplateInfo(compoundTpls, compoundTplId, tpl, tplArgs) {
+       function recordTemplateInfo(compoundTpls, compoundTplId, tpl, argInfo) {
                // Record template args info alongwith any intervening wikitext
                // between templates part of the same compound structure
                var tplArray = compoundTpls[compoundTplId],
@@ -1120,7 +1120,7 @@
                                tplArray.push({ wt: 
env.page.src.substring(prevTplInfo.dsr[1], dsr[0]) });
                        }
                }
-               tplArray.push({ dsr: dsr, args: tplArgs });
+               tplArray.push({ dsr: dsr, args: argInfo.dict, keys: 
argInfo.keys });
        }
 
        var i, r, n, e;
@@ -1444,13 +1444,36 @@
                                        tplArray.push({ wt: 
env.page.src.substring(lastTplInfo.dsr[1], dp1.dsr[1]) });
                                }
 
+                               // Extract the key orders for the templates
+                               /* jshint loopfunc: true */ // yes, this 
function is in a loop
+                               var keyArrays = [],
+                                       pushKey = function(a) {
+                                               if(a.keys) {
+                                                       keyArrays.push(a.keys);
+                                               }
+                                       };
+                               tplArray.forEach(pushKey);
+
                                // Map the array of { dsr: .. , args: .. } 
objects to just the args property
                                /* jshint loopfunc: true */ // yes, this 
function is in a loop
-                               tplArray = tplArray.map(function(a) { return 
a.wt ? a.wt : {template: a.args }; });
+                               var infoIndex = 0;
+                               tplArray = tplArray.map(function(a) {
+                                       if (a.wt) {
+                                               return a.wt;
+                                       } else {
+                                               // Remember the position of the 
transclusion relative
+                                               // to other transclusions. 
Should match the index of
+                                               // the corresponding private 
metadata in keyArrays
+                                               // above.
+                                               a.args.i = infoIndex++;
+                                               return {template: a.args};
+                                       }
+                               });
 
                                // Output the data-mw obj.
                                var datamw = (tplArray.length === 1) ? 
tplArray[0].template : { parts: tplArray };
                                range.start.setAttribute("data-mw", 
JSON.stringify(datamw));
+                               range.start.data.parsoid.keys = keyArrays;
                        }
                } else {
                        console.warn("ERROR: Do not have necessary info. to RT 
Tpl: " + i);
diff --git a/js/lib/mediawiki.WikitextSerializer.js 
b/js/lib/mediawiki.WikitextSerializer.js
index 63c893e..19cf7a0 100644
--- a/js/lib/mediawiki.WikitextSerializer.js
+++ b/js/lib/mediawiki.WikitextSerializer.js
@@ -2624,7 +2624,7 @@
                        node, state, cb);
 };
 
-WSP._buildTemplateWT = function(state, srcParts) {
+WSP._buildTemplateWT = function(state, srcParts, dp) {
        var buf = [],
                serializer = this;
        srcParts.map(function(part) {
@@ -2639,19 +2639,37 @@
                        // tpl args
                        var argBuf = [],
                                keys = Object.keys(tpl.params),
+                               // the original keys in order
+                               origKeys = tpl.i !== undefined ? dp.keys[tpl.i] 
: [],
                                n = keys.length;
                        if (n > 0) {
-                               for (var i = 0; i < n; i++) {
-                                       var k = keys[i],
-                                               v = 
serializer.escapeTplArgWT(state, tpl.params[k].wt);
-                                       if (k === (i+1).toString()) {
-                                               argBuf.push(v);
-                                       } else {
-                                               var kStr = k + (isTpl && 
!k.match(/\s$/) ? ' ' : ''),
-                                                       vStr = (isTpl && 
!v.match(/^\s/) ? ' ' : '') + v;
-                                               argBuf.push(kStr + "=" + vStr);
+                               var numericIndex = 1,
+                                       pushArg = function (k) {
+                                               var v = 
serializer.escapeTplArgWT(state, tpl.params[k].wt);
+                                               if (k === 
numericIndex.toString()) {
+                                                       numericIndex++;
+                                                       argBuf.push(v);
+                                               } else {
+                                                       var kStr = k + (isTpl 
&& !k.match(/\s$/) ? ' ' : ''),
+                                                               vStr = (isTpl 
&& !v.match(/^\s/) ? ' ' : '') + v;
+                                                       argBuf.push(kStr + "=" 
+ vStr);
+                                               }
+                                       };
+
+                               // first serialize out old parameters in order
+                               origKeys.forEach(function(k) {
+                                       if (tpl.params[k] !== undefined) {
+                                               pushArg(k);
                                        }
-                               }
+                               });
+                               // then push out remaining parameters
+                               keys.forEach(function(k) {
+                                       if (origKeys.indexOf(k) === -1) {
+                                               pushArg(k);
+                                       }
+                               });
+
+                               // Now append the parameters joined by pipes
                                buf.push("|");
                                buf.push(argBuf.join("|"));
                        }
@@ -2752,7 +2770,7 @@
                                if 
(/\bmw:(Transclusion\b|Param\b)/.test(typeOf)) {
                                        dataMW = 
JSON.parse(node.getAttribute("data-mw"));
                                        if (dataMW) {
-                                               src = 
state.serializer._buildTemplateWT(state, dataMW.parts || [{ template: dataMW 
}]);
+                                               src = 
state.serializer._buildTemplateWT(state, dataMW.parts || [{ template: dataMW 
}], dp);
                                        } else {
                                                console.error("ERROR: No 
data-mw for: " + node.outerHTML);
                                                src = dp.src;

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

Gerrit-MessageType: merged
Gerrit-Change-Id: I541abfba5edd274f43c0eda9ddef8d61c0d544e2
Gerrit-PatchSet: 8
Gerrit-Project: mediawiki/extensions/Parsoid
Gerrit-Branch: master
Gerrit-Owner: GWicke <[email protected]>
Gerrit-Reviewer: Subramanya Sastry <[email protected]>
Gerrit-Reviewer: jenkins-bot

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to