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

Change subject: JsApi: Add text and comment node support; bump domino 
dependency.
......................................................................


JsApi: Add text and comment node support; bump domino dependency.

Bump domino dependency to 1.0.19 due to fixes needed for the DOM
TreeWalker interface.

Change-Id: I3667af918035c721d40813d32e9063bf61404f39
---
M .jsduck/categories.json
M lib/jsapi.js
M package.json
M tests/mocha/jsapi.js
4 files changed, 178 insertions(+), 6 deletions(-)

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



diff --git a/.jsduck/categories.json b/.jsduck/categories.json
index 56b89e7..7d8f247 100644
--- a/.jsduck/categories.json
+++ b/.jsduck/categories.json
@@ -6,6 +6,7 @@
                                "name": "Package Interface",
                                "classes": [
                                        "Parsoid",
+                                       "PComment",
                                        "PDoc",
                                        "PNodeList",
                                        "PNode",
@@ -13,6 +14,7 @@
                                        "PHeading",
                                        "PHtmlEntity",
                                        "PTemplate",
+                                       "PText",
                                        "PWikiLink"
                                ]
                        },
diff --git a/lib/jsapi.js b/lib/jsapi.js
index ece9e90..e91d88d 100644
--- a/lib/jsapi.js
+++ b/lib/jsapi.js
@@ -6,13 +6,16 @@
 require('../lib/core-upgrade.js');
 
 // TO DO:
-// comment/tag/text/figure
+// tag/figure
 // PTemplate#get should return PParameter and support mutation.
 // PExtLink#url PWikiLink#title should handle mw:ExpandedAttrs
 // make separate package?
 
 var WikitextSerializer = 
require('../lib/mediawiki.WikitextSerializer.js').WikitextSerializer;
 var DU = require('../lib/mediawiki.DOMUtils.js').DOMUtils;
+var DOMImpl = require('domino').impl;
+var Node = DOMImpl.Node;
+var NodeFilter = DOMImpl.NodeFilter;
 var util = require('util');
 
 // WTS helper
@@ -38,7 +41,7 @@
 var noop = function() { };
 
 // Forward declarations of Wrapper classes.
-var PNode, PNodeList, PExtLink, PHeading, PHtmlEntity, PTemplate, PWikiLink;
+var PNode, PNodeList, PComment, PExtLink, PHeading, PHtmlEntity, PTemplate, 
PText, PWikiLink;
 
 // HTML escape helper
 var toHtmlStr = function(node, v) {
@@ -106,7 +109,52 @@
                if (this.parent) { this.parent.update(); }
        }, },
        _querySelectorAll: { value: function(selector) {
-               return Array.from(this.container.querySelectorAll(selector));
+               var tweakedSelector = ',' + selector + ',';
+               if (!(/,(COMMENT|TEXT),/.test(tweakedSelector))) {
+                       // Use fast native querySelectorAll
+                       return 
Array.from(this.container.querySelectorAll(selector));
+               }
+               // Implement comment/text node selector the hard way
+               /* jshint bitwise: false */
+               var whatToShow = NodeFilter.SHOW_ELEMENT; // always show 
templates
+               if (/,COMMENT,/.test(tweakedSelector)) {
+                       whatToShow = whatToShow | NodeFilter.SHOW_COMMENT;
+               }
+               if (/,TEXT,/.test(tweakedSelector)) {
+                       whatToShow = whatToShow | NodeFilter.SHOW_TEXT;
+               }
+               var nodeFilter = function(node) {
+                       if (node.nodeType !== Node.ELEMENT_NODE) {
+                               return NodeFilter.FILTER_ACCEPT;
+                       }
+                       if 
(/\bmw:Transclusion\b/.test(node.getAttribute('typeof') || '')) {
+                               return NodeFilter.FILTER_ACCEPT;
+                       }
+                       return NodeFilter.FILTER_SKIP;
+               };
+               var result = [];
+               var includeTemplates =
+                       /,\[typeof~="mw:Transclusion"\],/.test(tweakedSelector);
+               var treeWalker = this.pdoc.document.createTreeWalker(
+                       this.container, whatToShow, nodeFilter, false
+               );
+               while (treeWalker.nextNode()) {
+                       var node = treeWalker.currentNode;
+                       // We don't need the extra test for ELEMENT_NODEs yet, 
since
+                       // non-template element nodes will be skipped by the 
nodeFilter
+                       // above. But if we ever extend filter() to be fully 
generic,
+                       // we might need the commented-out portion of this test.
+                       if (node.nodeType === Node.ELEMENT_NODE /* &&
+                               
/\bmw:Transclusion\b/.test(node.getAttribute('typeof') || '')*/
+                       ) {
+                               treeWalker.lastChild(); // always skip over all 
children
+                               if (!includeTemplates) {
+                                       continue; // skip template itself
+                               }
+                       }
+                       result.push(node);
+               }
+               return result;
        }, },
        _templatesForNode: { value: function(node) {
                // each Transclusion node could represent multiple templates.
@@ -134,8 +182,8 @@
                        tSelector += ',' + selector;
                }
                this._querySelectorAll(tSelector).forEach(function(node) {
-                       var ty = node.getAttribute('typeof') || '';
-                       var isTemplate = /\bmw:Transclusion\b/.test(ty);
+                       var isTemplate = node.nodeType === Node.ELEMENT_NODE &&
+                               
/\bmw:Transclusion\b/.test(node.getAttribute('typeof') || '');
                        if (isTemplate) {
                                
self._templatesForNode(node).forEach(function(t) {
                                        if (!selector) {
@@ -157,6 +205,18 @@
                        }
                });
                return result;
+       }, },
+
+       /**
+        * Return an array of {@link PComment} representing comments
+        * found in this {@link PNodeList}.
+        * @inheritdoc #_filter
+        * @return {PComment[]}
+        */
+       filterComments: { value: function(opts) {
+               return this._filter([], 'COMMENT', function(r, parent, node, 
opts) {
+                       r.push(new PComment(parent.pdoc, parent, node));
+               }, opts);
        }, },
 
        /**
@@ -205,6 +265,17 @@
                return this._filter([], null, null, opts);
        }, },
 
+       /**
+        * Return an array of {@link PText} representing plain text
+        * found in this {@link PNodeList}.
+        * @inheritdoc #_filter
+        * @return {PText[]}
+        */
+       filterText: { value: function(opts) {
+               return this._filter([], 'TEXT', function(r, parent, node, opts) 
{
+                       r.push(new PText(parent.pdoc, parent, node));
+               }, opts);
+       }, },
 
        /**
         * Return an array of {@link PWikiLink} representing wiki links
@@ -254,10 +325,12 @@
  *
  * Useful subclasses of {@link PNode} include:
  *
+ * - {@link PComment}: comments, like `<!-- example -->`
  * - {@link PExtLink}: external links, like `[http://example.com Example]`
  * - {@link PHeading}: headings, like `== Section 1 ==`
  * - {@link PHtmlEntity}: html entities, like `&nbsp;`
  * - {@link PTemplate}: templates, like `{{foo|bar}}`
+ * - {@link PText}: unformatted text, like `foo`
  * - {@link PWikiLink}: wiki links, like `[[Foo|bar]]`
  */
 /**
@@ -530,6 +603,36 @@
 };
 
 /**
+ * PComment represents a hidden HTML comment, like `<!-- fobar -->`.
+ * @class PComment
+ * @extends PNode
+ */
+/**
+ * @method constructor
+ * @private
+ * @inheritdoc PNode#constructor
+ */
+PComment = function PComment(pdoc, parent, node, opts) {
+       PNode.call(this, pdoc, parent, node, opts);
+};
+util.inherits(PComment, PNode);
+Object.defineProperties(PComment.prototype, {
+       /**
+        * The hidden text contained between `<!--` and `-->`.
+        * @property {String}
+        */
+       contents: {
+               get: function() {
+                       return DU.decodeComment(this.node.data);
+               },
+               set: function(v) {
+                       this.node.data = DU.encodeComment(v);
+                       this.update();
+               },
+       },
+});
+
+/**
  * PExtLink represents an external link, like `[http://example.com Example]`.
  * @class PExtLink
  * @extends PNode
@@ -643,6 +746,35 @@
        },
 });
 
+/**
+ * PText represents ordinary unformatted text with no special properties.
+ * @class PText
+ * @extends PNode
+ */
+/**
+ * @method constructor
+ * @private
+ * @inheritdoc PNode#constructor
+ */
+PText = function PText(pdoc, parent, node, opts) {
+       PNode.call(this, pdoc, parent, node, opts);
+};
+util.inherits(PText, PNode);
+Object.defineProperties(PText.prototype, {
+       /**
+        * The actual text itself.
+        * @property {String}
+        */
+       value: {
+               get: function() {
+                       return this.node.data;
+               },
+               set: function(v) {
+                       this.node.data = v;
+                       this.update();
+               },
+       },
+});
 
 /**
  * PWikiLink represents an internal wikilink, like `[[Foo|Bar]]`.
diff --git a/package.json b/package.json
index ba4047d..b8f73b4 100644
--- a/package.json
+++ b/package.json
@@ -10,7 +10,7 @@
     "compression": "~1.4.0",
     "core-js": "^0.8.1",
     "diff": "~1.0.7",
-    "domino": "~1.0.18",
+    "domino": "~1.0.19",
     "entities": "~1.1.1",
     "express": "~2.5.11",
     "gelf-stream": "~0.2.4",
diff --git a/tests/mocha/jsapi.js b/tests/mocha/jsapi.js
index b0c5f53..de71321 100644
--- a/tests/mocha/jsapi.js
+++ b/tests/mocha/jsapi.js
@@ -172,4 +172,42 @@
                        String(pdoc).should.equal('&#x3C;{{echo|&#x3E;}}\n');
                });
        });
+       it('filters and mutates comments', function() {
+               var text = '<!-- foo --> {{echo|<!--bar-->}}';
+               return Parsoid.parse(text, { pdoc: true }).then(function(pdoc) {
+                       var comments = pdoc.filterComments();
+                       comments.length.should.equal(2);
+                       comments[0].contents.should.equal(' foo ');
+                       comments[1].contents.should.equal('bar');
+                       comments[0].contents = '<!-- ha! -->';
+                       comments[1].contents = '--';
+                       String(pdoc).should.equal('<!--<!-- ha! --&gt;--> 
{{echo|<!------>}}');
+               });
+       });
+       it('filters and mutates text', function() {
+               var text = 'foo {{echo|bar}}';
+               return Parsoid.parse(text, { pdoc: true }).then(function(pdoc) {
+                       var texts = pdoc.filterText({ recursive: false });
+                       texts.length.should.equal(1);
+                       texts = pdoc.filterText({ recursive: true });
+                       texts.length.should.equal(2);
+                       texts[0].value.should.equal('foo ');
+                       texts[1].value.should.equal('bar');
+                       texts[0].value = 'FOO ';
+                       String(pdoc).should.equal('FOO {{echo|bar}}\n');
+                       texts[1].value = 'BAR';
+                       String(pdoc).should.equal('FOO {{echo|BAR}}\n');
+               });
+       });
+       it.skip('filters and mutates text (2)', function() {
+               var text = '{{{echo|{{!}}}}\n| foo\n|}';
+               return Parsoid.parse(text, { pdoc: true }).then(function(pdoc) {
+                       var texts = pdoc.filterText();
+                       texts.length.should.equal(1);
+                       // XXX this doesn't work yet, see note at end of
+                       // 
https://www.mediawiki.org/wiki/Parsoid/MediaWiki_DOM_spec#Transclusion_content
+                       // for details.
+                       texts[0].value.should.equal(' foo');
+               });
+       });
 });

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

Gerrit-MessageType: merged
Gerrit-Change-Id: I3667af918035c721d40813d32e9063bf61404f39
Gerrit-PatchSet: 7
Gerrit-Project: mediawiki/services/parsoid
Gerrit-Branch: master
Gerrit-Owner: Cscott <canan...@wikimedia.org>
Gerrit-Reviewer: Arlolra <abrea...@wikimedia.org>
Gerrit-Reviewer: Cscott <canan...@wikimedia.org>
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