smartxml: Allow Fragments to refresh their node references using their initial paths
[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     this.nodePath = params.node.getPath();
23 };
24 NodeFragment.prototype = Object.create(Fragment.prototype);
25 $.extend(NodeFragment.prototype, {
26     isValid: function() {
27         return this.document.containsNode(this.node);
28     },
29     restoreFromPaths: function() {
30         this.node = this.document.getNodeByPath(this.nodePath);
31     }
32 });
33
34
35 var CaretFragment = function(document, params) {
36     NodeFragment.call(this, document, params);
37     this.offset = params.offset;
38
39 };
40 CaretFragment.prototype = Object.create(NodeFragment.prototype);
41 $.extend(CaretFragment.prototype, {
42     isValid: function() {
43         /* globals Node */
44         return NodeFragment.prototype.isValid.call(this) &&
45                 this.node.nodeType === Node.TEXT_NODE &&
46                 _.isNumber(this.offset);
47     }
48 });
49
50
51
52 var RangeFragment = function(document, params) {
53     Fragment.call(this, document);
54
55     if(params.node1.sameNode(params.node2)) {
56         this.startNode = this.endNode = params.node1;
57     } else {
58         /*jshint bitwise: false*/
59         /* globals Node */
60         var node1First = params.node1.nativeNode.compareDocumentPosition(params.node2.nativeNode) & Node.DOCUMENT_POSITION_FOLLOWING;
61         (node1First ? ['start', 'end'] : ['end','start']).forEach(function(prefix, idx) {
62             this[prefix + 'Node'] = params['node'+(idx+1)];
63         }.bind(this));
64     }
65     this.startNodePath = this.startNode.getPath();
66     this.endNodePath = this.endNode.getPath();
67 };
68 RangeFragment.prototype = Object.create(Fragment.prototype);
69 $.extend(RangeFragment.prototype, {
70     isValid: function() {
71         return this.document.containsNode(this.startNode) && this.document.containsNode(this.endNode);
72     },
73     restoreFromPaths: function() {
74         this.startNode = this.document.getNodeByPath(this.startNodePath);
75         this.endNode = this.document.getNodeByPath(this.endNodePath);
76     },
77     hasSiblingBoundries: function() {
78         return this.isValid() && this.startNode.isSiblingOf(this.endNode);
79     },
80     boundriesSiblingParents: function() {
81         return this.startNode.document.getSiblingParents({
82             node1: this.startNode,
83             node2: this.endNode
84         });
85     },
86     getCommonParent: function() {
87         var siblingParents = this.boundriesSiblingParents();
88         if(siblingParents) {
89             return siblingParents.node1.parent();
90         }
91     },
92 });
93
94 var TextRangeFragment = function(document, params) {
95     var orderChanged;
96
97     RangeFragment.call(this, document, params);
98
99     if(this.startNode.sameNode(this.endNode)) {
100         this.startOffset = Math.min(params.offset1, params.offset2);
101         this.endOffset = Math.max(params.offset1, params.offset2);
102     } else {
103         orderChanged =  !params.node1.sameNode(this.startNode);
104         this.startOffset = orderChanged ? params.offset2 : params.offset1;
105         this.endOffset = orderChanged ? params.offset1 : params.offset2;
106     }
107 };
108 TextRangeFragment.prototype = Object.create(RangeFragment.prototype);
109 $.extend(TextRangeFragment.prototype, {
110     isValid: function() {
111         return RangeFragment.prototype.isValid.call(this) &&
112             _.isNumber(this.startOffset) &&
113             _.isNumber(this.endOffset);
114     }
115 });
116
117 var FragmentTypes = {
118     Fragment: Fragment,
119     NodeFragment: NodeFragment,
120     CaretFragment: CaretFragment,
121     RangeFragment: RangeFragment,
122     TextRangeFragment: TextRangeFragment
123 };
124 _.values(FragmentTypes).forEach(function(Type) {
125     $.extend(Type.prototype, FragmentTypes);
126 });
127
128 return FragmentTypes;
129
130 });