smartxml: Cache DocumentNode instances in the underlying DOM structure
authorAleksander Łukasz <aleksander.lukasz@nowoczesnapolska.org.pl>
Tue, 17 Jun 2014 07:55:37 +0000 (09:55 +0200)
committerAleksander Łukasz <aleksander.lukasz@nowoczesnapolska.org.pl>
Fri, 18 Jul 2014 09:47:37 +0000 (11:47 +0200)
This allows for data persistence in the DocumentNode.object api. And,
at last, puts an end to the creation of unnecessary instances.

The side effect is that extensions needs to be registered before
acquiring references to nodes as they won't be updated with extension
behavior after they are created. This probably should be better
reflected in the api, maybe by setting extension via document constructor.

src/smartxml/smartxml.js
src/smartxml/smartxml.test.js

index a5a1abc..9cff5ec 100644 (file)
@@ -12,6 +12,8 @@ define([
 /* globals Node */
 
 
+var privateKey = '_smartxml';
+
 var DocumentNode = function(nativeNode, document) {
     if(!document) {
         throw new Error('undefined document for a node');
@@ -48,7 +50,7 @@ $.extend(DocumentNode.prototype, {
         clone.find('*').addBack().each(function() {
             var el = this,
                 clonedData = $(this).data();
-
+            $(el).removeData(privateKey);
             _.pairs(clonedData).forEach(function(pair) {
                 var key = pair[0],
                     value = pair[1];
@@ -180,6 +182,7 @@ $.extend(DocumentNode.prototype, {
 
 var ElementNode = function(nativeNode, document) {
     DocumentNode.call(this, nativeNode, document);
+    $(nativeNode).data(privateKey, {node: this});
 };
 ElementNode.prototype = Object.create(DocumentNode.prototype);
 
@@ -203,7 +206,9 @@ $.extend(ElementNode.prototype, {
         if(key) {
             return this._$.data(key);
         }
-        return this._$.data();
+        var toret = _.clone(this._$.data());
+        delete toret[privateKey];
+        return toret;
     },
 
     getTagName: function() {
@@ -352,7 +357,14 @@ $.extend(Document.prototype, Backbone.Events, fragments, {
     TextNodeFactory: TextNode,
 
     createDocumentNode: function(from) {
-        if(!(from instanceof Node)) {
+        var cached;
+
+        if(from instanceof Node) {
+            cached = ($(from).data(privateKey) || {}).node;
+            if(cached instanceof DocumentNode) {
+                return cached;
+            }
+        } else {
             if(typeof from === 'string') {
                 from = parseXML(from);
                 this.normalizeXML(from);
index 524c976..93c401b 100644 (file)
@@ -620,9 +620,7 @@ describe('smartxml', function() {
         });
 
         it('removes parent-describing sibling nodes of unwrapped node', function() {
-            var doc = getDocumentFromXML('<root><div><a></a><x></x><a></a></div></root>'),
-                div = doc.root.contents()[0],
-                x = div.contents()[1];
+            var doc = getDocumentFromXML('<root><div><a></a><x></x><a></a></div></root>');
 
             doc.registerExtension({documentNode: {methods: {
                 object: {
@@ -632,6 +630,9 @@ describe('smartxml', function() {
                 }
             }}});
 
+            var div = doc.root.contents()[0],
+                x = div.contents()[1];
+
             div.unwrapContent();
             expect(doc.root.contents().length).to.equal(2);
             expect(x.isInDocument()).to.be.false;
@@ -691,10 +692,7 @@ describe('smartxml', function() {
             });
 
             it('keeps parent-describing nodes in place', function() {
-                var doc = getDocumentFromXML('<root>Alice <x></x> probably <y></y> has a cat</root>'),
-                    root = doc.root,
-                    x = root.contents()[1],
-                    y = root.contents()[3];
+                var doc = getDocumentFromXML('<root>Alice <x></x> probably <y></y> has a cat</root>');
 
                 doc.registerExtension({documentNode: {methods: {
                     object: {
@@ -705,6 +703,10 @@ describe('smartxml', function() {
                     }
                 }}});
 
+                var root = doc.root,
+                    x = root.contents()[1],
+                    y = root.contents()[3];
+
                 root.wrapText({
                     _with: {tagName: 'span', attrs: {'attr1': 'value1'}},
                     offsetStart: 1,
@@ -1348,19 +1350,6 @@ describe('smartxml', function() {
 
         beforeEach(function() {
             doc = getDocumentFromXML('<section>Alice<div class="test_class"></div></section>');
-            elementNode = doc.root;
-            textNode = doc.root.contents()[0];
-            extension = {};
-            
-            expect(elementNode.testTransformation).to.be.undefined;
-            expect(textNode.testTransformation).to.be.undefined;
-            expect(doc.testTransformation).to.be.undefined;
-            
-            expect(doc.testMethod).to.be.undefined;
-            expect(elementNode.testMethod).to.be.undefined;
-            expect(textNode.testMethod).to.be.undefined;
-            expect(elementNode.elementTestMethod).to.be.undefined;
-            expect(textNode.textTestMethod).to.be.undefined;
         });
 
         it('allows adding method to a document', function() {
@@ -1404,7 +1393,6 @@ describe('smartxml', function() {
 
             doc.registerExtension(extension);
 
-            /* refresh */
             elementNode = doc.root;
             textNode = doc.root.contents()[0];
 
@@ -1440,7 +1428,6 @@ describe('smartxml', function() {
             
             doc.registerExtension(extension);
 
-            /* refresh */
             elementNode = doc.root;
             textNode = doc.root.contents()[0];