editor: adding gap class to ui
[fnpeditor.git] / src / smartxml / core.js
index ae6d20e..665f9d5 100644 (file)
@@ -3,8 +3,7 @@ define(function(require) {
 'use strict';
 /* globals Node */
 
-var _ = require('libs/underscore'),
-    TEXT_NODE = Node.TEXT_NODE;
+var _ = require('libs/underscore');
 
 
 var INSERTION = function(implementation) {
@@ -28,9 +27,12 @@ var INSERTION = function(implementation) {
 
 var documentNodeTransformations = {
     detach: function() {
-        var parent = this.parent();
+        var parent = this.parent(),
+            existed = this.document.containsNode(this);
         this._$.detach();
-        this.triggerChangeEvent('nodeDetached', {parent: parent});
+        if(existed) {
+            this.triggerChangeEvent('nodeDetached', {parent: parent});
+        }
         return this;
     },
 
@@ -48,6 +50,7 @@ var documentNodeTransformations = {
         var next = this.next();
         if(next && next.nodeType === Node.TEXT_NODE && node.nodeType === Node.TEXT_NODE) {
             next.setText(node.getText() + next.getText());
+            node.detach();
             return next;
         }
         this._$.after(node.nativeNode);
@@ -58,6 +61,7 @@ var documentNodeTransformations = {
         var prev = this.prev();
         if(prev && prev.nodeType === Node.TEXT_NODE && node.nodeType === Node.TEXT_NODE) {
             prev.setText(prev.getText() + node.getText());
+            node.detach();
             return prev;
         }
         this._$.before(node.nativeNode);
@@ -136,6 +140,7 @@ var elementNodeTransformations = {
         var last = _.last(this.contents());
         if(last && last.nodeType === Node.TEXT_NODE && node.nodeType === Node.TEXT_NODE) {
             last.setText(last.getText() + node.getText());
+            node.detach();
             return last;
         } else {
             this._$.append(node.nativeNode);
@@ -147,6 +152,7 @@ var elementNodeTransformations = {
         var first = this.contents()[0];
         if(first && first.nodeType === Node.TEXT_NODE && node.nodeType === Node.TEXT_NODE) {
             first.setText(node.getText() + first.getText());
+            node.detach();
             return first;
         } else {
             this._$.prepend(node.nativeNode);
@@ -177,38 +183,23 @@ var elementNodeTransformations = {
             return this.detach();
         }
 
-        var prev = this.prev(),
-            next = this.next(),
-            moveLeftRange, moveRightRange, leftMerged;
-
-        if(prev && (prev.nodeType === TEXT_NODE) && (myContents[0].nodeType === TEXT_NODE)) {
-            prev.appendText(myContents[0].getText());
-            myContents[0].detach();
-            moveLeftRange = true;
-            leftMerged = true;
-        } else {
-            leftMerged = false;
-        }
-
-        if(!(leftMerged && myContents.length === 1)) {
-            var lastContents = _.last(myContents);
-            if(next && (next.nodeType === TEXT_NODE) && (lastContents.nodeType === TEXT_NODE)) {
-                next.prependText(lastContents.getText());
-                lastContents.detach();
-                moveRightRange = true;
-            }
-        }
 
-        var childrenLength = this.contents().length;
+        var childrenLength = this.contents().length,
+            first = true,
+            shiftRange = false;
         this.contents().forEach(function(child) {
-            this.before(child);
+            var returned = this.before(child);
+            if(first && !(returned.sameNode(child))) {
+                shiftRange = true;
+                first = false;
+            }
         }.bind(this));
 
         this.detach();
 
         return {
-            element1: parent.contents()[myIdx + (moveLeftRange ? -1 : 0)],
-            element2: parent.contents()[myIdx + childrenLength-1 + (moveRightRange ? 1 : 0)]
+            element1: parent.contents()[myIdx + (shiftRange ? -1 : 0)],
+            element2: parent.contents()[myIdx + childrenLength-1 + (shiftRange ? -1 : 0)]
         };
     },
 
@@ -232,6 +223,7 @@ var textNodeTransformations = {
     before: INSERTION(function(node) {
         if(node.nodeType === Node.TEXT_NODE) {
             this.prependText(node.getText());
+            node.detach();
             return this;
         } else {
             return this.__super__.before(node, {silent:true});
@@ -241,12 +233,28 @@ var textNodeTransformations = {
     after: INSERTION(function(node) {
         if(node.nodeType === Node.TEXT_NODE) {
             this.appendText(node.getText());
+            node.detach();
             return this;
         } else {
             return this.__super__.after(node, {silent:true});
         }
     }),
 
+    append: function(node) {
+        if(node.nodeType === Node.TEXT_NODE) {
+            this.appendText(node.getText());
+            node.detach();
+            return this;
+        }
+    },
+    prepend: function(node) {
+        if(node.nodeType === Node.TEXT_NODE) {
+            this.prependText(node.getText());
+            node.detach();
+            return this;
+        }
+    },
+
     appendText: function(text) {
         this.nativeNode.data = this.nativeNode.data + text;
         this.triggerTextChangeEvent();
@@ -423,6 +431,59 @@ var documentTransformations = {
         this._defineDocumentProperties(insertion.ofNode._$);
         insertion.ofNode.triggerChangeEvent('nodeAdded');
         return insertion.ofNode;
+    },
+    deleteText: function(params) {
+        var ptr, next, toDetach, middle, text;
+
+        if(params.from.node.sameNode(params.to.node)) {
+            ptr = params.from.node;
+            text = ptr.getText();
+            ptr.setText(text.substr(0, params.from.offset) + text.substr(params.to.offset));
+            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;
+        next = ptr.next();
+
+        while(next || ptr.parent()) {
+            if(next) {
+                if(next.sameNode(params.to.node)) {
+                    return;
+                }
+                else if(next.nodeType === Node.ELEMENT_NODE && next.containsNode(params.to.node)) {
+                    middle = next;
+                    break;
+                } else {
+                    toDetach = next;
+                    next = next.next();
+                    toDetach.detach();
+                }
+            } else {
+                ptr = ptr.parent();
+                next = ptr.next();
+            }
+        }
+
+        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.next();
+                ptr.prev().detach();
+            }
+        }
     }
 };