smartxml: first take on document fragments
[fnpeditor.git] / src / smartxml / fragments.js
diff --git a/src/smartxml/fragments.js b/src/smartxml/fragments.js
new file mode 100644 (file)
index 0000000..9bb92cc
--- /dev/null
@@ -0,0 +1,120 @@
+define(function(require) {
+    
+'use strict';
+
+var $ = require('libs/jquery'),
+    _ = require('libs/underscore');
+
+
+var Fragment = function(document) {
+    this.document = document;
+};
+$.extend(Fragment.prototype, {
+    isValid: function() {
+        return false;
+    }
+});
+
+
+var NodeFragment = function(document, params) {
+    Fragment.call(this, document);
+    this.node = params.node;
+};
+NodeFragment.prototype = Object.create(Fragment.prototype);
+$.extend(NodeFragment.prototype, {
+    isValid: function() {
+        return this.document.containsNode(this.node);
+    }
+});
+
+
+var CaretFragment = function(document, params) {
+    NodeFragment.call(this, document, params);
+    this.offset = params.offset;
+
+};
+CaretFragment.prototype = Object.create(NodeFragment.prototype);
+$.extend(CaretFragment.prototype, {
+    isValid: function() {
+        /* globals Node */
+        return NodeFragment.prototype.isValid.call(this) &&
+                this.node.nodeType === Node.TEXT_NODE &&
+                _.isNumber(this.offset);
+    }
+});
+
+
+
+var RangeFragment = function(document, params) {
+    Fragment.call(this, document);
+
+    if(params.node1.sameNode(params.node2)) {
+        this.startNode = this.endNode = params.node1;
+    } else {
+        /*jshint bitwise: false*/
+        /* globals Node */
+        var node1First = params.node1.nativeNode.compareDocumentPosition(params.node2.nativeNode) & Node.DOCUMENT_POSITION_FOLLOWING;
+        (node1First ? ['start', 'end'] : ['end','start']).forEach(function(prefix, idx) {
+            this[prefix + 'Node'] = params['node'+(idx+1)];
+        }.bind(this));
+    }
+};
+RangeFragment.prototype = Object.create(Fragment.prototype);
+$.extend(RangeFragment.prototype, {
+    isValid: function() {
+        return this.document.containsNode(this.startNode) && this.document.containsNode(this.endNode);
+    },
+    hasSiblingBoundries: function() {
+        return this.isValid() && this.startNode.isSiblingOf(this.endNode);
+    },
+    boundriesSiblingParents: function() {
+        return this.startNode.document.getSiblingParents({
+            node1: this.startNode,
+            node2: this.endNode
+        });
+    },
+    getCommonParent: function() {
+        var siblingParents = this.boundriesSiblingParents();
+        if(siblingParents) {
+            return siblingParents.node1.parent();
+        }
+    },
+});
+
+var TextRangeFragment = function(document, params) {
+    var orderChanged;
+
+    RangeFragment.call(this, document, params);
+
+    if(this.startNode.sameNode(this.endNode)) {
+        this.startOffset = Math.min(params.offset1, params.offset2);
+        this.endOffset = Math.max(params.offset1, params.offset2);
+    } else {
+        orderChanged =  !params.node1.sameNode(this.startNode);
+        this.startOffset = orderChanged ? params.offset2 : params.offset1;
+        this.endOffset = orderChanged ? params.offset1 : params.offset2;
+    }
+};
+TextRangeFragment.prototype = Object.create(RangeFragment.prototype);
+$.extend(TextRangeFragment.prototype, {
+    isValid: function() {
+        return RangeFragment.prototype.isValid.call(this) &&
+            _.isNumber(this.startOffset) &&
+            _.isNumber(this.endOffset);
+    }
+});
+
+var FragmentTypes = {
+    Fragment: Fragment,
+    NodeFragment: NodeFragment,
+    CaretFragment: CaretFragment,
+    RangeFragment: RangeFragment,
+    TextRangeFragment: TextRangeFragment
+};
+_.values(FragmentTypes).forEach(function(Type) {
+    $.extend(Type.prototype, FragmentTypes);
+});
+
+return FragmentTypes;
+
+});
\ No newline at end of file