editor: plugins/core - restore selection after editing/deleting link via a link tool box
[fnpeditor.git] / src / smartxml / core.js
index 44be8d6..419b175 100644 (file)
@@ -13,7 +13,7 @@ var INSERTION = function(implementation) {
             nodeParent,
             returned;
         options = options || {};
-        if(!(this.document.containsNode(this))) {
+        if(!(this.document.containsNode(this)) || !insertion.isNew) {
             nodeParent = insertion.ofNode.parent();
         }
         returned = implementation.call(this, insertion.ofNode);
@@ -32,6 +32,10 @@ var documentNodeTransformations = {
         this._$.detach();
         if(existed) {
             this.triggerChangeEvent('nodeDetached', {parent: parent});
+            if(!parent) {
+                // This was the root of the document
+                this.document._defineDocumentProperties(null);
+            }
         }
         return this;
     },
@@ -41,9 +45,12 @@ var documentNodeTransformations = {
         if(this.isRoot()) {
             return this.document.replaceRoot(node);
         }
-        toret = this.after(node);
-        this.detach();
-        return toret;
+        if(this.parent()) {
+            toret = this.after(node);
+            this.detach();
+            return toret;
+        }
+        throw new Error('Cannot replace node without a parent.');
     },
 
     after: INSERTION(function(node) {
@@ -70,8 +77,9 @@ var documentNodeTransformations = {
 
     wrapWith: function(node) {
         var insertion = this.getNodeInsertion(node);
-        if(this.parent()) {
-            this.before(insertion.ofNode);
+
+        if(this.parent() || this.isRoot()) {
+            this.replaceWith(insertion.ofNode);
         }
         insertion.ofNode.append(this);
         return insertion.ofNode;
@@ -98,7 +106,7 @@ var elementNodeTransformations = {
 
     detach: function() {
         var next;
-        if(this.parent() && this.isSurroundedByTextElements()) {
+        if(this.parent() && this.isSurroundedByTextNodes()) {
             next = this.next();
             this.prev().appendText(next.getText());
             next.detach();
@@ -107,26 +115,21 @@ var elementNodeTransformations = {
     },
 
     setTag: function(tagName) {
-        var node = this.document.createDocumentNode({tagName: tagName}),
-            oldTagName = this.getTagName(),
-            myContents = this._$.contents();
+        var node = this.document.createDocumentNode({tagName: tagName});
 
         this.getAttrs().forEach(function(attribute) {
-            node.setAttr(attribute.name, attribute.value, true);
+            node.setAttr(attribute.name, attribute.value);
         });
-        node.setData(this.getData());
 
-        if(this.sameNode(this.document.root)) {
-            this.document._defineDocumentProperties(node._$);
-        }
+        this.contents().forEach(function(child) {
+            node.append(child);
+        });
 
-        /* TODO: This invalidates old references to this node. Caching instances on nodes would fix this. */
-        this._$.replaceWith(node._$);
-        this._setNativeNode(node._$[0]);
-        this._$.append(myContents);
-        this.triggerChangeEvent('nodeTagChange', {oldTagName: oldTagName, newTagName: this.getTagName()});
-    },
+        node.setData(this.getData());
 
+        this.replaceWith(node);
+        return node;
+    },
 
     setAttr: function(name, value, silent) {
         var oldVal = this.getAttr(name);
@@ -433,7 +436,7 @@ var documentTransformations = {
         return insertion.ofNode;
     },
     deleteText: function(params) {
-        var ptr, prev, next, toDetach, middle, text;
+        var ptr, next, toDetach, middle, text;
 
         if(params.from.node.sameNode(params.to.node)) {
             ptr = params.from.node;
@@ -442,12 +445,20 @@ var documentTransformations = {
             return;
         }
 
+        // Both edge text nodes need to be edited before anything else happen in case that
+        // they get merged when detaching content between them.
+        params.from.node.setText(params.from.node.getText().substr(0, params.from.offset));
+        params.to.node.setText(params.to.node.getText().substr(params.to.offset));
+
         ptr = params.from.node;
-        ptr.setText(ptr.getText().substr(0, params.from.offset));
         next = ptr.next();
+
         while(next || ptr.parent()) {
             if(next) {
-                if(next.sameNode(params.to.node) || (next.nodeType === Node.ELEMENT_NODE && next.containsNode(params.to.node))) {
+                if(next.sameNode(params.to.node)) {
+                    return;
+                }
+                else if(next.nodeType === Node.ELEMENT_NODE && next.containsNode(params.to.node)) {
                     middle = next;
                     break;
                 } else {
@@ -461,20 +472,19 @@ var documentTransformations = {
             }
         }
 
-        ptr = params.to.node;
-        ptr.setText(ptr.getText().substr(params.to.offset));
-        prev = ptr.prev();
-        while(prev || ptr.parent()) {
-            if(ptr.sameNode(middle)) {
-                break;
-            }
-            if(prev) {
-                toDetach = prev;
-                prev = prev.prev();
-                toDetach.detach();
+        if(!this.containsNode(params.to.node)) {
+            // The end node was merged during detaching nodes above - there is nothing more left to do.
+            return;
+        }
+
+        ptr = middle.contents()[0];
+        while(ptr && !ptr.sameNode(params.to.node)) {
+            if(ptr.nodeType === Node.ELEMENT_NODE && ptr.containsNode(params.to.node)) {
+                ptr = ptr.contents()[0];
+                continue;
             } else {
-                ptr = ptr.parent();
-                prev = ptr.prev();
+                ptr = ptr.next();
+                ptr.prev().detach();
             }
         }
     }