smartxml: first take on document fragments
[fnpeditor.git] / src / smartxml / fragments.js
1 define(function(require) {
2     
3 'use strict';
4
5 var $ = require('libs/jquery'),
6     _ = require('libs/underscore');
7
8
9 var Fragment = function(document) {
10     this.document = document;
11 };
12 $.extend(Fragment.prototype, {
13     isValid: function() {
14         return false;
15     }
16 });
17
18
19 var NodeFragment = function(document, params) {
20     Fragment.call(this, document);
21     this.node = params.node;
22 };
23 NodeFragment.prototype = Object.create(Fragment.prototype);
24 $.extend(NodeFragment.prototype, {
25     isValid: function() {
26         return this.document.containsNode(this.node);
27     }
28 });
29
30
31 var CaretFragment = function(document, params) {
32     NodeFragment.call(this, document, params);
33     this.offset = params.offset;
34
35 };
36 CaretFragment.prototype = Object.create(NodeFragment.prototype);
37 $.extend(CaretFragment.prototype, {
38     isValid: function() {
39         /* globals Node */
40         return NodeFragment.prototype.isValid.call(this) &&
41                 this.node.nodeType === Node.TEXT_NODE &&
42                 _.isNumber(this.offset);
43     }
44 });
45
46
47
48 var RangeFragment = function(document, params) {
49     Fragment.call(this, document);
50
51     if(params.node1.sameNode(params.node2)) {
52         this.startNode = this.endNode = params.node1;
53     } else {
54         /*jshint bitwise: false*/
55         /* globals Node */
56         var node1First = params.node1.nativeNode.compareDocumentPosition(params.node2.nativeNode) & Node.DOCUMENT_POSITION_FOLLOWING;
57         (node1First ? ['start', 'end'] : ['end','start']).forEach(function(prefix, idx) {
58             this[prefix + 'Node'] = params['node'+(idx+1)];
59         }.bind(this));
60     }
61 };
62 RangeFragment.prototype = Object.create(Fragment.prototype);
63 $.extend(RangeFragment.prototype, {
64     isValid: function() {
65         return this.document.containsNode(this.startNode) && this.document.containsNode(this.endNode);
66     },
67     hasSiblingBoundries: function() {
68         return this.isValid() && this.startNode.isSiblingOf(this.endNode);
69     },
70     boundriesSiblingParents: function() {
71         return this.startNode.document.getSiblingParents({
72             node1: this.startNode,
73             node2: this.endNode
74         });
75     },
76     getCommonParent: function() {
77         var siblingParents = this.boundriesSiblingParents();
78         if(siblingParents) {
79             return siblingParents.node1.parent();
80         }
81     },
82 });
83
84 var TextRangeFragment = function(document, params) {
85     var orderChanged;
86
87     RangeFragment.call(this, document, params);
88
89     if(this.startNode.sameNode(this.endNode)) {
90         this.startOffset = Math.min(params.offset1, params.offset2);
91         this.endOffset = Math.max(params.offset1, params.offset2);
92     } else {
93         orderChanged =  !params.node1.sameNode(this.startNode);
94         this.startOffset = orderChanged ? params.offset2 : params.offset1;
95         this.endOffset = orderChanged ? params.offset1 : params.offset2;
96     }
97 };
98 TextRangeFragment.prototype = Object.create(RangeFragment.prototype);
99 $.extend(TextRangeFragment.prototype, {
100     isValid: function() {
101         return RangeFragment.prototype.isValid.call(this) &&
102             _.isNumber(this.startOffset) &&
103             _.isNumber(this.endOffset);
104     }
105 });
106
107 var FragmentTypes = {
108     Fragment: Fragment,
109     NodeFragment: NodeFragment,
110     CaretFragment: CaretFragment,
111     RangeFragment: RangeFragment,
112     TextRangeFragment: TextRangeFragment
113 };
114 _.values(FragmentTypes).forEach(function(Type) {
115     $.extend(Type.prototype, FragmentTypes);
116 });
117
118 return FragmentTypes;
119
120 });