Cscott has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/227886

Change subject: Add Text and Comment node support to JsApi.
......................................................................

Add Text and Comment node support to JsApi.

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, 154 insertions(+), 5 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/services/parsoid 
refs/changes/86/227886/1

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 c396bac..15b2e14 100644
--- a/lib/jsapi.js
+++ b/lib/jsapi.js
@@ -13,6 +13,9 @@
 
 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,47 @@
                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;
+                       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 +177,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 +200,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 +260,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 +320,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 +598,23 @@
 };
 
 /**
+ * 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, {
+});
+
+/**
  * PExtLink represents an external link, like `[http://example.com Example]`.
  * @class PExtLink
  * @extends PNode
@@ -643,6 +728,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 1fd38a8..c4fd5a5 100644
--- a/tests/mocha/jsapi.js
+++ b/tests/mocha/jsapi.js
@@ -172,4 +172,37 @@
                        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);
+               });
+       });
+       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: newchange
Gerrit-Change-Id: I3667af918035c721d40813d32e9063bf61404f39
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/services/parsoid
Gerrit-Branch: master
Gerrit-Owner: Cscott <[email protected]>

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

Reply via email to