jenkins-bot has submitted this change and it was merged. Change subject: T88495: Part 1 of 2: Handle more templated <td>-attr scenarios ......................................................................
T88495: Part 1 of 2: Handle more templated <td>-attr scenarios * Removed some artificial limitations in the handling of templated <td> attributes and simplified / documented the logic. * Re-orged some if-then-else nests to make the code more readable. * Testing: * The example in the bug report now parses and RTs correctly. * http://localhost:8000/_rt/enwiki/List_of_largest_container_ships now parses and renders properly. * Added a new test that captures the scenario seen in that page. * The more complex scenario (which actually has a failing test in parserTests.txt) of multiple cells on the same line separated by || is not implemented yet. That will be part 2. Change-Id: Ib4ad0a63b97463fbc3944d674945c12ead23f944 --- M lib/dom.t.TableFixups.js M lib/mediawiki.DOMUtils.js M tests/parserTests-blacklist.js M tests/parserTests.txt 4 files changed, 226 insertions(+), 119 deletions(-) Approvals: Arlolra: Looks good to me, approved jenkins-bot: Verified diff --git a/lib/dom.t.TableFixups.js b/lib/dom.t.TableFixups.js index 326f939..38437da 100644 --- a/lib/dom.t.TableFixups.js +++ b/lib/dom.t.TableFixups.js @@ -1,9 +1,9 @@ "use strict"; var DU = require('./mediawiki.DOMUtils.js').DOMUtils, - PegTokenizer = require('./mediawiki.tokenizer.peg.js').PegTokenizer, + PegTokenizer = require('./mediawiki.tokenizer.peg.js').PegTokenizer, Sanitizer = require('./ext.core.Sanitizer.js').Sanitizer, - defines = require('./mediawiki.parser.defines.js'); + defines = require('./mediawiki.parser.defines.js'); // define some constructor shortcuts @@ -113,9 +113,10 @@ TableFixups.prototype.collectAttributishContent = function (node) { var buf = [], nodes = [], - transclusionNode, - // Build the result. - buildRes = function () { + transclusionNode = DU.hasTypeOf(node, 'mw:Transclusion') ? node : null; + + // Build the result. + var buildRes = function () { return { txt: buf.join(''), nodes: nodes, @@ -123,37 +124,64 @@ }; }, child = node.firstChild; + + /* + * In this loop below, where we are trying to collect text content, + * it is safe to use child.textContent since textContent skips over + * comments. See this transcript of a node session: + * + * > d.body.childNodes[0].outerHTML + * '<span><!--foo-->bar</span>' + * > d.body.childNodes[0].textContent + * 'bar' + * + * PHP parser strips comments during parsing, i.e. they don't impact + * how other wikitext constructs are parsed. So, in this code below, + * we have to skip over comments. + */ while (child) { - if (!transclusionNode && - (DU.isComment(child) || - DU.isText(child) && !/[|]/.test(child.nodeValue))) - { + if (DU.isComment(child)) { /* jshint noempty:false */ + // <!--foo--> are not comments in CSS and PHP parser strips them + } else if (DU.isText(child)) { buf.push(child.nodeValue); + } else if (child.nodeName !== 'SPAN') { + // The idea here is that style attributes can only + // be text/comment nodes, and nowiki-spans at best. + // So, if we hit anything else, there is nothing more + // to do here! + return buildRes(); + } else if (transclusionNode && DU.hasTypeOf(child, 'mw:Nowiki')) { + // Nowiki span added in the template to protect otherwise + // meaningful wikitext chars used in attributes. + buf.push(child.textContent); + } else if (DU.hasTypeOf(child, 'mw:Transclusion') && + DU.allChildrenAreTextOrComments(child)) + { + // We encountered a transclusion wrapper + console.assert(!transclusionNode); + buf.push(child.textContent); + transclusionNode = child; } else if (transclusionNode && - child.nodeName === 'SPAN' && - DU.hasTypeOf(child, 'mw:Nowiki')) + child.getAttribute('typeof') === null && + child.getAttribute('about') === transclusionNode.getAttribute('about') && + DU.allChildrenAreTextOrComments(child)) { + // Continue accumulating only if we hit grouped template content buf.push(child.textContent); - } else if (child.nodeName === 'SPAN' && - child.childNodes.length === 1 && - (child.getAttribute('typeof') === null && - transclusionNode && - child.getAttribute('about') === transclusionNode.getAttribute('about') || - DU.hasTypeOf(child, 'mw:Transclusion'))) - { - buf.push(child.textContent); - if (!transclusionNode && DU.hasTypeOf(child, 'mw:Transclusion')) { - transclusionNode = child; - } } else { return buildRes(); } + nodes.push(child); - if (/[|]/.test(buf.last())) { + + // Are we done accumulating? + if (/(?:^|[^|])\|(?:[^|]|$)/.test(buf.last())) { return buildRes(); } + child = child.nextSibling; } + return buildRes(); }; @@ -183,114 +211,146 @@ * - There is only a single transclusion in the table cell content. This * limitation can be lifted with more advanced data-mw construction. */ + +// SSS FIXME: It is silly to examine every fricking <td> for possible fixup. +// We only need to examine <td>s that either have mw:Transclusion typeof or +// have a child (not descendent) with a mw:Transclusion typeof. +// +// This info is not readily available right now, but perhaps could be provided +// based on annotating nodes via a tmp attribute during tpl wrapping. +// +// Or, perhaps the tokenizer can mark <td>s that have a transclusion node +// on the same wikitext line. +// +// TO BE DONE. + TableFixups.prototype.reparseTemplatedAttributes = function (env, node) { var dp = DU.getDataParsoid( node ); - // Cheap checks first - if (!DU.isLiteralHTMLNode(node) && - // We use the dsr start tag width as a proxy for 'has no attributes - // yet'. We accept '|' and '||' (row-based syntax), so at most two - // chars. - dp.dsr && dp.dsr[2] !== null && dp.dsr[2] <= 2) + // Cheap checks first: + // * Don't bother with literal HTML nodes + // * Don't bother with <td>s that already have attrs. + // We use the dsr start tag width + // as a proxy for 'has no attributes yet'. + // We accept '|' and '||' (row-based syntax), + // so at most two chars. + if (DU.isLiteralHTMLNode(node) || + !(dp.dsr && dp.dsr[2] !== null && dp.dsr[2] <= 2)) { - // Now actually look at the content - var attributishContent = this.collectAttributishContent(node), - transclusionNode = attributishContent.transclusionNode; + return true; + } - // First of all make sure we have a transclusion that produces leading - // text content - if ( transclusionNode && - // Check for the pipe character in the attributish text. - // Also make sure that we only trigger for simple - // attribute-only cases for now. Don't handle |{{multicells}} - // where multicells expands to something like style="foo"| Bar - // || Baz - /^[^|]+[|][^|]*$/.test(attributishContent.txt) && - // And only handle a single nested transclusion for now - // TODO: Handle data-mw construction for multi-transclusion content as - // well, then relax this restriction. - this.hasOnlyOneTransclusionChild(node) - ) - { - //console.log(node.data.parsoid.dsr, JSON.stringify(attributishText)); + // Collect attribute content and examine it + var attributishContent = this.collectAttributishContent(node), + transclusionNode = attributishContent.transclusionNode; - // Try to re-parse the attributish text content - var attributishPrefix = attributishContent.txt.match(/^[^|]+\|/)[0], - // re-parse the attributish prefix - attributeTokens = this.tokenizer - .tokenizeTableCellAttributes(attributishPrefix); - if (attributeTokens) { - // Found attributes. + // First of all make sure we have a transclusion + // that produces leading text content + if (!transclusionNode + // Check for the pipe character in the attributish text. + || !/^[^|]+\|[^|]*([^|]\|\|[^|]*)*$/.test(attributishContent.txt) + // And only handle a single nested transclusion for now. + // TODO: Handle data-mw construction for multi-transclusion content + // as well, then relax this restriction. + || !this.hasOnlyOneTransclusionChild(node) + ) + { + return true; + } - // Sanitize them - var sanitizedToken = this.sanitizer - .sanitizeTokens( - [new TagTk(node.nodeName.toLowerCase(), - attributeTokens[0])])[0]; - //console.log(JSON.stringify(sanitizedToken)); + // Try to re-parse the attributish text content + var attributishPrefix = attributishContent.txt.match(/^[^|]+\|/)[0]; + // re-parse the attributish prefix + var attributeTokens = this.tokenizer + .tokenizeTableCellAttributes(attributishPrefix); - // and transfer the sanitized attributes to the td node - sanitizedToken.attribs.forEach(function(kv) { - node.setAttribute(kv.k, kv.v); - }); + // No attributes => nothing more to do! + if (!attributeTokens) { + return true; + } - // Update the template encapsulation including data-mw + // Found attributes. + // Sanitize them + var sanitizedToken = this.sanitizer + .sanitizeTokens( + [ new TagTk(node.nodeName.toLowerCase(), attributeTokens[0]) ] + )[0]; - // Lift up the about group to our td node. - node.setAttribute('typeof', transclusionNode.getAttribute('typeof')); - node.setAttribute('about', transclusionNode.getAttribute('about')); - var dataMW = DU.getDataMw(transclusionNode), - parts = dataMW.parts, - tnDP = DU.getDataParsoid( transclusionNode ); + // and transfer the sanitized attributes to the td node + sanitizedToken.attribs.forEach(function(kv) { + node.setAttribute(kv.k, kv.v); + }); - // Get the td and content source up to the transclusion start - if (dp.dsr[0] < tnDP.dsr[0]) { - parts.unshift(env.page.src.substring( dp.dsr[0], tnDP.dsr[0] )); - } + // Update the template encapsulation including data-mw - // Add wikitext for the table cell content following the - // transclusion. This is safe as we are currently only - // handling a single transclusion in the content, which is - // guaranteed to have a dsr that covers the transclusion - // itself. - if (tnDP.dsr[1] < dp.dsr[1]) { - parts.push(env.page.src.substring( tnDP.dsr[1], dp.dsr[1] )); - } + // If the transclusion node was embedded within the td node, + // lift up the about group to the td node. + if (node !== transclusionNode) { + node.setAttribute('typeof', transclusionNode.getAttribute('typeof')); + node.setAttribute('about', transclusionNode.getAttribute('about')); + var dataMW = DU.getDataMw(transclusionNode), + parts = dataMW.parts, + tnDP = DU.getDataParsoid( transclusionNode ); - // Save the new data-mw on the td node - DU.setDataMw(node, { parts: parts }); - dp.pi = tnDP.pi; + // Get the td and content source up to the transclusion start + if (dp.dsr[0] < tnDP.dsr[0]) { + parts.unshift(env.page.src.substring( dp.dsr[0], tnDP.dsr[0] )); + } - // Remove the span wrapper - var attributishNodes = attributishContent.nodes; - while(attributishNodes.length) { - var n = attributishNodes.shift(); - if (/[|]/.test(n.textContent)) { - // Remove the consumed prefix from the text node - var nValue; - if (n.nodeName === '#text') { - nValue = n.nodeValue; - } else { - nValue = n.textContent; - } - // and convert it into a simple text node - node.replaceChild(node.ownerDocument.createTextNode(nValue.replace(/^[^|]*[|]/, '')), n); - } else { - // content was consumed by attributes, so just drop it - // from the cell - node.removeChild(n); - } - } - // Remove template encapsulation from other children. The - // table cell wraps everything now. - node.childNodes.forEach(function(childNode) { - if (childNode.getAttribute && childNode.getAttribute('about')) { - childNode.removeAttribute('about'); - } - }); + // Add wikitext for the table cell content following the + // transclusion. This is safe as we are currently only + // handling a single transclusion in the content, which is + // guaranteed to have a dsr that covers the transclusion + // itself. + if (tnDP.dsr[1] < dp.dsr[1]) { + parts.push(env.page.src.substring( tnDP.dsr[1], dp.dsr[1] )); + } + + // Save the new data-mw on the td node + DU.setDataMw(node, { parts: parts }); + dp.pi = tnDP.pi; + } + + // Remove the span wrapper + var attributishNodes = attributishContent.nodes; + while(attributishNodes.length) { + var n = attributishNodes.shift(); + if (/[|]/.test(n.textContent)) { + // Remove the consumed prefix from the text node + var nValue; + if (n.nodeName === '#text') { + nValue = n.nodeValue; + } else { + nValue = n.textContent; + } + // and convert it into a simple text node + node.replaceChild(node.ownerDocument.createTextNode(nValue.replace(/^[^|]*[|]/, '')), n); + } else { + // content was consumed by attributes, so just drop it from the cell + node.removeChild(n); + } + } + + if (node !== transclusionNode) { + // Remove template encapsulation from other children. + // The td node wraps everything now. + var child = node.firstChild; + while (child) { + // Remove the span wrapper -- this simplifies the problem of + // analyzing the <td> for additional fixups (|| Boo || Baz) by + // potentially invoking 'reparseTemplatedAttributes' recursively + // with some modifications. + if (child.nodeName === 'SPAN' && child.getAttribute('about')) { + var next = child.firstChild || child.nextSibling; + DU.migrateChildren(child, node, child); + DU.deleteNode(child); + child = next; + } else { + child = child.nextSibling; } } } + return true; }; diff --git a/lib/mediawiki.DOMUtils.js b/lib/mediawiki.DOMUtils.js index 54a2d6c..67f5475 100644 --- a/lib/mediawiki.DOMUtils.js +++ b/lib/mediawiki.DOMUtils.js @@ -1190,6 +1190,23 @@ }, /** + * Are all children of this node text or comment nodes? + */ + allChildrenAreTextOrComments: function (node) { + var child = node.firstChild; + while (child) { + if (!this.isMarkerMeta(child, "mw:DiffMarker") + && !this.isText(child) + && !this.isComment(child)) + { + return false; + } + child = child.nextSibling; + } + return true; + }, + + /** * Are all children of this node text nodes? */ allChildrenAreText: function (node) { diff --git a/tests/parserTests-blacklist.js b/tests/parserTests-blacklist.js index dde5fba..fb057a3 100644 --- a/tests/parserTests-blacklist.js +++ b/tests/parserTests-blacklist.js @@ -54,7 +54,7 @@ add("wt2html", "Definition Lists: colons and tables 1", "<dl data-parsoid='{\"dsr\":[0,21,0,0]}'><dd data-parsoid='{\"dsr\":[0,10,1,0]}'><table data-parsoid='{\"dsr\":[1,10,2,2]}'>\n<tbody data-parsoid='{\"dsr\":[4,8,0,0]}'><tr data-parsoid='{\"autoInsertedEnd\":true,\"autoInsertedStart\":true,\"dsr\":[4,7,0,0]}'><td data-parsoid='{\"autoInsertedEnd\":true,\"dsr\":[4,7,1,0]}'> x</td></tr>\n</tbody></table></dd>\n<dd data-parsoid='{\"dsr\":[11,21,1,0]}'><table data-parsoid='{\"dsr\":[12,21,2,2]}'>\n<tbody data-parsoid='{\"dsr\":[15,19,0,0]}'><tr data-parsoid='{\"autoInsertedEnd\":true,\"autoInsertedStart\":true,\"dsr\":[15,18,0,0]}'><td data-parsoid='{\"autoInsertedEnd\":true,\"dsr\":[15,18,1,0]}'> y</td></tr>\n</tbody></table></dd></dl>"); add("wt2html", "Bug 2702: Mismatched <i>, <b> and <a> tags are invalid", "<p data-parsoid='{\"dsr\":[0,204,0,0]}'><i data-parsoid='{\"autoInsertedEnd\":true,\"dsr\":[0,29,2,0]}'><a rel=\"mw:ExtLink\" href=\"http://example.com\" data-parsoid='{\"targetOff\":22,\"contentOffsets\":[22,28],\"dsr\":[2,29,20,1]}'>text<i data-parsoid='{\"autoInsertedEnd\":true,\"dsr\":[26,28,2,0]}'></i></a></i>\n<a rel=\"mw:ExtLink\" href=\"http://example.com\" data-parsoid='{\"targetOff\":50,\"contentOffsets\":[50,57],\"dsr\":[30,58,20,1]}'><b data-parsoid='{\"autoInsertedEnd\":true,\"dsr\":[50,57,3,0]}'>text</b></a><b data-parsoid='{\"autoInsertedEnd\":true,\"dsr\":[58,61,3,0]}'></b>\n<i data-parsoid='{\"autoInsertedEnd\":true,\"dsr\":[62,106,2,0]}'>Something <a rel=\"mw:ExtLink\" href=\"http://example.com\" data-parsoid='{\"targetOff\":94,\"contentOffsets\":[94,105],\"dsr\":[74,106,20,1]}'>in italic<i data-parsoid='{\"autoInsertedEnd\":true,\"dsr\":[103,105,2,0]}'></i></a></i>\n<i data-parsoid='{\"dsr\":[107,164,2,2]}'>Something <a rel=\"mw:ExtLink\" href=\"http://example.com\" data-parsoid='{\"targetOff\":139,\"contentOffsets\":[139,160],\"dsr\":[119,161,20,1]}'>mixed<b data-parsoid='{\"autoInsertedEnd\":true,\"dsr\":[144,160,3,0]}'><i data-parsoid='{\"autoInsertedEnd\":true,\"dsr\":[147,160,2,0]}'>, even bold</i></b></a>'</i>\n<b data-parsoid='{\"autoInsertedEnd\":true,\"dsr\":[165,204,3,0]}'><i data-parsoid='{\"autoInsertedEnd\":true,\"dsr\":[168,204,2,0]}'>Now <a rel=\"mw:ExtLink\" href=\"http://example.com\" data-parsoid='{\"targetOff\":194,\"contentOffsets\":[194,203],\"dsr\":[174,204,20,1]}'>both<b data-parsoid='{\"autoInsertedEnd\":true,\"dsr\":[198,203,3,0]}'><i data-parsoid='{\"autoInsertedEnd\":true,\"dsr\":[201,203,2,0]}'></i></b></a></i></b></p>"); add("wt2html", "External link containing double-single-quotes in text embedded in italics (bug 4598 sanity check)", "<p data-parsoid='{\"dsr\":[0,60,0,0]}'><i data-parsoid='{\"dsr\":[0,60,2,2]}'>Some <a rel=\"mw:ExtLink\" href=\"http://example.com/\" data-parsoid='{\"targetOff\":28,\"contentOffsets\":[28,56],\"dsr\":[7,57,21,1]}'>pretty <i data-parsoid='{\"dsr\":[35,46,2,2]}'>italics</i> and stuff</a>!</i></p>"); -add("wt2html", "Template-generated table cell attributes and cell content (2)", "<table data-parsoid='{\"dsr\":[0,35,2,2]}'>\n<tbody data-parsoid='{\"dsr\":[3,33,0,0]}'><tr data-parsoid='{\"autoInsertedEnd\":true,\"autoInsertedStart\":true,\"dsr\":[3,32,0,0]}'><td align=\"center\" style=\"color: red\" typeof=\"mw:Transclusion\" about=\"#mwt1\" data-parsoid='{\"autoInsertedEnd\":true,\"dsr\":[3,32,1,0],\"pi\":[[]]}' data-mw='{\"parts\":[\"|align=center \",{\"template\":{\"target\":{\"wt\":\"table_cells\",\"href\":\"./Template:Table_cells\"},\"params\":{},\"i\":0}}]}'> Foo<span> || Bar || Baz</span></td></tr>\n</tbody></table>"); +add("wt2html", "3. Template-generated table cell attributes and cell content", "<table data-parsoid='{\"dsr\":[0,35,2,2]}'>\n<tbody data-parsoid='{\"dsr\":[3,33,0,0]}'><tr data-parsoid='{\"autoInsertedEnd\":true,\"autoInsertedStart\":true,\"dsr\":[3,32,0,0]}'><td align=\"center\" style=\"color: red\" typeof=\"mw:Transclusion\" about=\"#mwt1\" data-parsoid='{\"autoInsertedEnd\":true,\"dsr\":[3,32,1,0],\"pi\":[[]]}' data-mw='{\"parts\":[\"|align=center \",{\"template\":{\"target\":{\"wt\":\"table_cells\",\"href\":\"./Template:Table_cells\"},\"params\":{},\"i\":0}}]}'> Foo || Bar || Baz</td></tr>\n</tbody></table>"); add("wt2html", "Self-link to numeric title", "<p data-parsoid='{\"dsr\":[0,5,0,0]}'><a rel=\"mw:WikiLink\" href=\"./0\" title=\"0\" data-parsoid='{\"stx\":\"simple\",\"a\":{\"href\":\"./0\"},\"sa\":{\"href\":\"0\"},\"dsr\":[0,5,2,2]}'>0</a></p>"); add("wt2html", "<nowiki> inside a link", "<p data-parsoid='{\"dsr\":[0,96,0,0]}'><a rel=\"mw:WikiLink\" href=\"./Main_Page\" title=\"Main Page\" data-parsoid='{\"stx\":\"simple\",\"a\":{\"href\":\"./Main_Page\"},\"sa\":{\"href\":\"Main<nowiki> Page</nowiki>\"},\"dsr\":[0,30,2,2]}'>Main Page</a> <a rel=\"mw:WikiLink\" href=\"./Main_Page\" title=\"Main Page\" data-parsoid='{\"stx\":\"piped\",\"a\":{\"href\":\"./Main_Page\"},\"sa\":{\"href\":\"Main Page\"},\"dsr\":[31,96,12,2]}'>the main page <span typeof=\"mw:Nowiki\" data-parsoid='{\"dsr\":[57,94,8,9]}'>[it's not very good]</span></a></p>"); add("wt2html", "2. Lists with start-of-line-transparent tokens before bullets: Template close", "<ul about=\"#mwt1\" typeof=\"mw:Transclusion\" data-parsoid='{\"dsr\":[0,18,0,0],\"pi\":[[{\"k\":\"1\",\"spc\":[\"\",\"\",\"\",\"\"]}]]}' data-mw='{\"parts\":[\"*foo \",{\"template\":{\"target\":{\"wt\":\"echo\",\"href\":\"./Template:Echo\"},\"params\":{\"1\":{\"wt\":\"bar\\n\"}},\"i\":0}}]}'><li>foo bar</li></ul><span about=\"#mwt1\">\n</span><p data-parsoid='{\"dsr\":[18,22,0,0]}'>*baz</p>"); @@ -1055,8 +1055,8 @@ add("html2wt", "Nested table", "{| border=\"1\"\n\n| α\n\n|\n{| bgcolor=\"#ABCDEF\" border=\"2\"\n\n|nested\n\n|-\n|table\n|}\n\n|the original table again\n|}\n"); add("html2wt", "Invalid attributes in table cell (bug 1830)", "{|\n\n|broken\n|}\n"); add("html2wt", "Indented table markup mixed with indented pre content (proposed in bug 6200)", " {|\n \n |\n Text that should be rendered preformatted \n \n |}\n"); -add("html2wt", "Template-generated table cell attributes and cell content", "{|\n\n| style=\"color: red\" | Foo\n\n| style=\"color: red\" | Foo\n\n| style=\"color: red\" | Foo\n\n| align=\"center\" style=\"color: red\" | Foo\n\n| align=\"center\" style=\"color: red\" | Foo\n|}\n"); -add("html2wt", "Template-generated table cell attributes and cell content (2)", "{|\n\n| align=\"center\" style=\"color: red\" | Foo \n| Bar \n| Baz\n|}\n"); +add("html2wt", "1. Template-generated table cell attributes and cell content", "{|\n\n| style=\"color: red\" | Foo\n\n| style=\"color: red\" | Foo\n\n| style=\"color: red\" | Foo\n\n| align=\"center\" style=\"color: red\" | Foo\n\n| align=\"center\" style=\"color: red\" | Foo\n|}\n"); +add("html2wt", "3. Template-generated table cell attributes and cell content", "{|\n\n| align=\"center\" style=\"color: red\" | Foo \n| Bar \n| Baz\n|}\n"); add("html2wt", "Table with row followed by newlines and table heading", "{|\n\n! foo\n|}\n"); add("html2wt", "Table with empty line following the start tag", "{|\n\n| foo\n|}\n"); add("html2wt", "Table attributes with empty value", "{|\n\n| style=\"\" | hello\n|}\n"); diff --git a/tests/parserTests.txt b/tests/parserTests.txt index 8a0fef6..d4a8996 100644 --- a/tests/parserTests.txt +++ b/tests/parserTests.txt @@ -119,6 +119,14 @@ !! endarticle !! article +Template:table_attribs_2 +!! text +<noinclude> +|</noinclude>style="color: red"| Foo +|Bar||Baz +!! endarticle + +!! article Template:table_cells !! text {{table_attribs}} || Bar || Baz @@ -5615,7 +5623,7 @@ !! end !! test -Template-generated table cell attributes and cell content +1. Template-generated table cell attributes and cell content !! wikitext {| |{{table_attribs}} @@ -5641,7 +5649,29 @@ !! end !! test -Template-generated table cell attributes and cell content (2) +2. Template-generated table cell attributes and cell content +!! wikitext +{| +|{{table_attribs_2}} +|} +!! html/php +<table> +<tr> +<td style="color: red"> Foo +</td> +<td>Bar</td> +<td>Baz +</td></tr></table> + +!! html/parsoid +<table> +<tbody><tr><td about="#mwt1" typeof="mw:Transclusion" style="color: red" data-mw='{"parts":["|",{"template":{"target":{"wt":"table_attribs_2","href":"./Template:Table_attribs_2"},"params":{},"i":0}}]}'> Foo</td> +<td about="#mwt1">Bar</td><td about="#mwt1">Baz</td></tr> +</tbody></table> +!! end + +!! test +3. Template-generated table cell attributes and cell content !! wikitext {| |align=center {{table_cells}} -- To view, visit https://gerrit.wikimedia.org/r/189642 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: Ib4ad0a63b97463fbc3944d674945c12ead23f944 Gerrit-PatchSet: 11 Gerrit-Project: mediawiki/services/parsoid Gerrit-Branch: master Gerrit-Owner: Subramanya Sastry <[email protected]> Gerrit-Reviewer: Arlolra <[email protected]> Gerrit-Reviewer: Cscott <[email protected]> Gerrit-Reviewer: GWicke <[email protected]> Gerrit-Reviewer: Marcoil <[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
