smartxml: refactoring
[fnpeditor.git] / src / smartxml / smartxml.js
index a357977..05583c5 100644 (file)
@@ -36,6 +36,10 @@ $.extend(DocumentNode.prototype, {
         this._$ = $(nativeNode);
     },
 
+    isRoot: function() {
+        return this.document.root.sameNode(this);
+    },
+
     detach: function() {
         var parent = this.parent();
         this._$.detach();
@@ -64,6 +68,20 @@ $.extend(DocumentNode.prototype, {
         return parents;
     },
 
+    prev: function() {
+        var myIdx = this.getIndex();
+        return myIdx > 0 ? this.parent().contents()[myIdx-1] : null;
+    },
+
+    next: function() {
+        if(this.isRoot()) {
+            return null;
+        }
+        var myIdx = this.getIndex(),
+            parentContents = this.parent().contents();
+        return myIdx < parentContents.length - 1 ? parentContents[myIdx+1] : null;
+    },
+
     after: INSERTION(function(nativeNode) {
         return this._$.after(nativeNode);
     }),
@@ -73,18 +91,35 @@ $.extend(DocumentNode.prototype, {
     }),
 
     wrapWith: function(node) {
-        node = node instanceof ElementNode ? node : this.document.createElementNode(node);
-
+        var insertion = this.getNodeInsertion(node);
         if(this.parent()) {
-            this.before(node);
+            this.before(insertion.ofNode);
+        }
+        insertion.ofNode.append(this);
+        return insertion.ofNode;
+    },
+
+    /**
+    * Removes parent of a node if node has no siblings.
+    */
+    unwrap: function() {
+        if(this.isRoot()) {
+            return;
+        }
+        var parent = this.parent(),
+            grandParent;
+        if(parent.contents().length === 1) {
+            grandParent = parent.parent();
+            parent.unwrapContent();
+            return grandParent;
         }
-        node.append(this);
-        return node;
     },
 
     triggerChangeEvent: function(type, metaData) {
         var event = new events.ChangeEvent(type, $.extend({node: this}, metaData || {}));
-        this.document.trigger('change', event);
+        if(type === 'nodeDetached' || this.document.containsNode(event.meta.node)) {
+            this.document.trigger('change', event);
+        }
     },
     
     getNodeInsertion: function(node) {
@@ -100,6 +135,9 @@ $.extend(DocumentNode.prototype, {
     },
 
     getIndex: function() {
+        if(this.isRoot()) {
+            return 0;
+        }
         return this.parent().indexOf(this);
     }
 });
@@ -112,6 +150,18 @@ ElementNode.prototype = Object.create(DocumentNode.prototype);
 $.extend(ElementNode.prototype, {
     nodeType: Node.ELEMENT_NODE,
 
+    detach: function() {
+        var prev = this.prev(),
+            next = this.next();
+        if(parent) {
+            if(prev && prev.nodeType === Node.TEXT_NODE && next && next.nodeType === Node.TEXT_NODE) {
+                prev.appendText(next.getText());
+                next.detach();
+            }
+        }
+        return DocumentNode.prototype.detach.call(this);
+    },
+
     setData: function(key, value) {
         if(value !== undefined) {
             this._$.data(key, value);
@@ -207,6 +257,7 @@ $.extend(ElementNode.prototype, {
             myContents = this.contents(),
             myIdx = parent.indexOf(this);
 
+
         if(myContents.length === 0) {
             return this.detach();
         }
@@ -358,7 +409,7 @@ $.extend(Document.prototype, Backbone.Events, {
     },
 
     containsNode: function(node) {
-        return node._$.parents().index(this.root._$) !== -1;
+        return this.root && (node.nativeNode === this.root.nativeNode || node._$.parents().index(this.root._$) !== -1);
     },
 
     wrapNodes: function(params) {