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 ` `
* - {@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('<{{echo|>}}\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