Skip to content
Browse files

Rewrote using "node-proxy"

  • Loading branch information...
1 parent 31196e8 commit cb130c0e8db89b24170fbbc19e7901efa8dc817a @vgrichina committed
Showing with 107 additions and 76 deletions.
  1. +2 −0 .gitignore
  2. +57 −0 handler-maker.js
  3. +41 −74 main.js
  4. +3 −2 package.json
  5. +4 −0 test.js
View
2 .gitignore
@@ -0,0 +1,2 @@
+node_modules
+*.swp
View
57 handler-maker.js
@@ -0,0 +1,57 @@
+// Can be used with node-proxy to create forwarding proxy,
+// i.e. forward all the calls to underlying object.
+//
+// Taken from http://wiki.ecmascript.org/doku.php?id=harmony:proxies
+//
+function handlerMaker(obj) {
+ return {
+ getOwnPropertyDescriptor: function(name) {
+ var desc = Object.getOwnPropertyDescriptor(obj, name);
+ // a trapping proxy's properties must always be configurable
+ if (desc !== undefined) { desc.configurable = true; }
+ return desc;
+ },
+ getPropertyDescriptor: function(name) {
+ var desc = Object.getPropertyDescriptor(obj, name); // not in ES5
+ // a trapping proxy's properties must always be configurable
+ if (desc !== undefined) { desc.configurable = true; }
+ return desc;
+ },
+ getOwnPropertyNames: function() {
+ return Object.getOwnPropertyNames(obj);
+ },
+ getPropertyNames: function() {
+ return Object.getPropertyNames(obj); // not in ES5
+ },
+ defineProperty: function(name, desc) {
+ Object.defineProperty(obj, name, desc);
+ },
+ delete: function(name) { return delete obj[name]; },
+ fix: function() {
+ if (Object.isFrozen(obj)) {
+ var result = {};
+ Object.getOwnPropertyNames(obj).forEach(function(name) {
+ result[name] = Object.getOwnPropertyDescriptor(obj, name);
+ });
+ return result;
+ }
+ // As long as obj is not frozen, the proxy won't allow itself to be fixed
+ return undefined; // will cause a TypeError to be thrown
+ },
+
+ has: function(name) { return name in obj; },
+ hasOwn: function(name) { return ({}).hasOwnProperty.call(obj, name); },
+ get: function(receiver, name) { return obj[name]; },
+ set: function(receiver, name, val) { obj[name] = val; return true; }, // bad behavior when set fails in non-strict mode
+ enumerate: function() {
+ var result = [];
+ for (var name in obj) { result.push(name); };
+ return result;
+ },
+ keys: function() { return Object.keys(obj); }
+
+ };
+}
+
+
+module.exports = handlerMaker;
View
115 main.js
@@ -12,6 +12,8 @@
var libxml = require("libxmljs");
var util = require("util");
+var Proxy = require("node-proxy");
+var handlerMaker = require("./handler-maker");
// Usage
// -----
@@ -56,69 +58,34 @@ exports.parse = function(str) {
return convertElement(xml.root());
}
-// Merge enumerable properties from `src` object into `dst` object.
-// Note that `dst` is modified in-place.
-function merge(dst, src) {
- for (var key in src) {
- dst[key] = src[key];
- }
-}
-
-// Add properties to access tags with given names using given getter
-function addTagProperties(parent, tagNames, getter) {
- for (var tag in tagNames) {
- Object.defineProperty(parent, tag, {
- get: getter.bind(parent, tag)
- });
- }
-}
-
-// Add shorthand properties to given array of converted nodes
-function addShorthandProperties(result) {
- // Create properties for child tags
- var allTags = {};
- result.forEach(function(it) {
- merge(allTags, it.$$tagNames);
- });
- addTagProperties(result, allTags, childTagGetter);
-
- // Create properties for attributes of matched tags
- var allAttrs = {};
- result.forEach(function(it) {
- it.$.attrs().forEach(function(attr) {
- allAttrs[attr.name()] = (allAttrs[attr.name()] || "") + attr.value();
- });
- });
- for (var key in allAttrs) {
- Object.defineProperty(result, "$" + key, {
- value: allAttrs[key]
- });
- }
-}
-
-// Get child elements with given tag of elements in `this` as array
-function childTagGetter(tag) {
- // Collect child elements
- var result = [];
- this.forEach(function(it) {
- Array.prototype.push.apply(result, it[tag]);
- });
+function enhanceResults(results) {
+ var handler = handlerMaker(results);
- // Add shorthand properties for child elements / attributes
- addShorthandProperties(result);
+ handler.get = function(target, name) {
+ if (typeof results[name] != "undefined") {
+ return results[name];
+ }
- return result;
-}
+ if (name[0] == "$") {
+ var attrName = name.slice(1);
-// Get elements with given tag from `this` as array
-function tagGetter(tag) {
- // Filter matching tags
- var result = this.filter(function(it) { return it.$.name() == tag; });
+ return results.map(function(it) {
+ return it.$.attr(attrName) ? it.$.attr(attrName).value() : "";
+ }).join("");
+ }
- // Add shorthand properties for child elements / attributes
- addShorthandProperties(result);
+ return enhanceResults(
+ Array.prototype.concat.apply([],
+ results.map(function (result) {
+ return result.filter(function(it) {
+ return it.$.name() == name;
+ });
+ })
+ )
+ );
+ }
- return result;
+ return Proxy.create(handler);
}
// Convert single DOM element into "easy" representation
@@ -132,25 +99,25 @@ function convertElement(elem) {
return it;
});
- // Create properties that group child elements by names
- var tagNames = {};
- converted.forEach(function (it) {
- if (it instanceof Array) {
- var name = it.$.name();
- tagNames[name] = true;
+ // Save DOM element object
+ Object.defineProperty(converted, "$", {value: elem});
+
+ var handler = handlerMaker(converted);
+ handler.get = function(target, name) {
+ if (typeof converted[name] != "undefined") {
+ return converted[name];
}
- });
- addTagProperties(converted, tagNames, tagGetter);
- // Collect attributes
- elem.attrs().forEach(function(it) {
- converted["$" + it.name()] = it.value();
- });
+ if (name[0] == "$") {
+ var attrName = name.slice(1);
- // Save DOM element object
- Object.defineProperty(converted, "$", {value: elem});
- Object.defineProperty(converted, "$$tagNames", {value: tagNames});
+ return elem.attr(attrName) ? elem.attr(attrName).value() : "";
+ }
+ return enhanceResults(converted.filter(function(it) {
+ return it.$.name() == name;
+ }));
+ };
- return converted;
+ return Proxy.create(handler);
}
View
5 package.json
@@ -2,7 +2,7 @@
"author": "Vladimir Grichina <vgrichina@gmail.com> (http://www.componentix.com)",
"name": "libxmljs-easy",
"description": "Work with libmxmljs DOM as with plain Javascript objects.",
- "version": "0.0.1",
+ "version": "0.0.2",
"homepage": "http://github.com/vgrichina/libxmljs-easy",
"repository": {
"type": "git",
@@ -12,7 +12,8 @@
"node": "~0.6.9"
},
"dependencies": {
- "libxmljs": "~0.5.3"
+ "libxmljs": "~0.5.3",
+ "node-proxy": "~0.5.2"
},
"devDependencies": {
"mocha": "~0.11.0"
View
4 test.js
@@ -10,12 +10,16 @@ describe("easy", function() {
it("should parse XML into JS object", function() {
var xml = easy.parse(sampleXml);
+ assert.ok(xml);
+ assert.ok(xml.book);
+ assert.equal(xml.book.length, 1);
assert.equal(xml.book[0].$name, "Lord of the Rings");
assert.equal(xml.book[0].author[0].$name, "J. R. R. Tolkien");
});
it("should save original DOM element in '$' property", function() {
var xml = easy.parse(sampleXml);
+ assert.ok(xml.$);
assert.equal(xml.book.language[0].$.text(), "English");
});

0 comments on commit cb130c0

Please sign in to comment.
Something went wrong with that request. Please try again.