|
1 getWrapperAncestor = function(elt) { |
|
2 var parent = elt ; |
|
3 while (parent != null) { |
|
4 if (CY.DOM.hasClass(parent, 'c-s')) |
|
5 return parent ; |
|
6 parent = parent.parentNode ; |
|
7 } |
|
8 return null ; |
|
9 } |
|
10 |
|
11 hasWrapperAncestor = function(elt) { |
|
12 return (getWrapperAncestor(elt) != null) ; |
|
13 /* var parent = elt ; |
|
14 while (parent != null) { |
|
15 if (CY.DOM.hasClass(parent, 'c-s')) |
|
16 return true ; |
|
17 parent = parent.parentNode ; |
|
18 } |
|
19 return false ;*/ |
|
20 } |
|
21 |
|
22 // returns null or : |
|
23 // {'text' : textcontent, 'start': {'elt':startNode, 'nbChar':startOffset(==number of characters to selection start in the start node}, |
|
24 // 'end': ....} |
|
25 // the text attribute is informational having it empty doesn't mean selection is empty !! |
|
26 |
|
27 // when selection starts/ends in/on a non textual element (<hr/> for example) we very often have anchorNode/focusNode == body elt |
|
28 // TODO adapt this body case by considering offset ( cf. http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html) |
|
29 getSelectionInfo = function () { |
|
30 var startNode = null, endNode = null, startOffset = 0, endOffset = 0, text = '' ; |
|
31 |
|
32 if (window.getSelection) { // everything else than IE |
|
33 var userSelection = window.getSelection(); |
|
34 |
|
35 if (userSelection.rangeCount > 0) { |
|
36 var range = userSelection.getRangeAt(0) ; |
|
37 text = range.toString() ; |
|
38 if (text != "") { |
|
39 |
|
40 // selection occured from right to left ? : |
|
41 var r1 = document.createRange() ;r1.setStart(userSelection.anchorNode, userSelection.anchorOffset) ;r1.collapse(true) ; |
|
42 var r2 = document.createRange() ;r2.setEnd(userSelection.focusNode, userSelection.focusOffset) ;r2.collapse(false) ; |
|
43 var leftToRight = (r2.compareBoundaryPoints(2, r1) == 1) ; // 2 is for END_TO_END |
|
44 // CY.log("leftToRight : " + leftToRight) ; |
|
45 startNode = (leftToRight) ? userSelection.anchorNode.parentNode : userSelection.focusNode.parentNode ; |
|
46 innerStartNode = (leftToRight) ? userSelection.anchorNode : userSelection.focusNode ; |
|
47 endNode = (leftToRight) ? userSelection.focusNode.parentNode : userSelection.anchorNode.parentNode; |
|
48 innerEndNode = (leftToRight) ? userSelection.focusNode : userSelection.anchorNode; |
|
49 |
|
50 startOffset = (leftToRight) ? userSelection.anchorOffset : userSelection.focusOffset; |
|
51 endOffset = (leftToRight) ? userSelection.focusOffset : userSelection.anchorOffset ; |
|
52 |
|
53 if (!hasWrapperAncestor(endNode) && hasWrapperAncestor(startNode)){ |
|
54 var r3 = document.createRange() ; |
|
55 r3.setStart(innerStartNode, startOffset) ; |
|
56 |
|
57 var csStartAncestor = getWrapperAncestor(startNode) ; |
|
58 var next = csStartAncestor ; |
|
59 r3.setEndAfter(next) ; |
|
60 |
|
61 var ind = parseInt(csStartAncestor.id.substring('sv_'.length)) ; |
|
62 while(r3.toString().length < range.toString().length) { |
|
63 ind++ ; |
|
64 var node = CY.get("#sv_"+ind) ; |
|
65 if (node) { |
|
66 next = CY.Node.getDOMNode(node) ; |
|
67 r3.setEndAfter(next) ; |
|
68 } |
|
69 else |
|
70 break ; |
|
71 } |
|
72 endNode = next.lastChild ; |
|
73 endOffset = CY.DOM.getText(endNode).length ; |
|
74 } |
|
75 else if (!hasWrapperAncestor(startNode) && hasWrapperAncestor(endNode)){ |
|
76 var r3 = document.createRange() ; |
|
77 r3.setEnd(innerEndNode, endOffset) ; |
|
78 |
|
79 var csEndAncestor = getWrapperAncestor(endNode) ; |
|
80 var prev = csEndAncestor ; |
|
81 r3.setStartBefore(prev) ; |
|
82 |
|
83 var ind = parseInt(csEndAncestor.id.substring('sv_'.length)) ; |
|
84 while(r3.toString().length < range.toString().length) { |
|
85 ind-- ; |
|
86 var node = CY.get("#sv_"+ind) ; |
|
87 if (node) { |
|
88 prev = CY.Node.getDOMNode(node) ; |
|
89 r3.setStartBefore(prev) ; |
|
90 } |
|
91 else |
|
92 break ; |
|
93 } |
|
94 startNode = prev.firstChild ; |
|
95 startOffset = 0 ; |
|
96 } |
|
97 else if (!hasWrapperAncestor(startNode) && !hasWrapperAncestor(endNode)){ |
|
98 var textLength = text.length ; |
|
99 |
|
100 // gather nodes with id sv_xxxx as candidates for start ancestor |
|
101 var startNodeInds = [] ; |
|
102 for (var ind = 0 ; ; ind++) { |
|
103 var svNode = CY.get("#sv_"+ind) ; |
|
104 if (svNode == null) { |
|
105 break; |
|
106 } |
|
107 else { |
|
108 var svText = svNode.get("text") ; |
|
109 if (text.indexOf(svText) == 0) { |
|
110 startNodeInds.push(ind) ; |
|
111 } |
|
112 } |
|
113 } |
|
114 |
|
115 // gather nodes with id sv_xxxx as candidates for end ancestor |
|
116 var endNodeInds = [] ; |
|
117 for (var ind = 0 ; ; ind++) { |
|
118 var svNode = CY.get("#sv_"+ind) ; |
|
119 if (svNode == null) { |
|
120 break; |
|
121 } |
|
122 else { |
|
123 var svText = svNode.get("text") ; |
|
124 if (text.indexOf(svText) == (textLength - svText.length)) { // i.e. the selection exactly ends with svText |
|
125 endNodeInds.push(ind) ; |
|
126 } |
|
127 } |
|
128 } |
|
129 |
|
130 var stop = false ; |
|
131 for (var i = 0 ; i < startNodeInds.length ; i++) { |
|
132 for (var j = 0 ; j < endNodeInds.length ; j++) { |
|
133 var r4 = document.createRange() ; |
|
134 |
|
135 var s = CY.Node.getDOMNode(CY.get("#sv_"+startNodeInds[i])) ; var e = CY.Node.getDOMNode(CY.get("#sv_"+endNodeInds[j])) ; |
|
136 |
|
137 r4.setStartBefore(s) ; r4.setEndAfter(CY.Node.getDOMNode(e)) ; |
|
138 |
|
139 // does r4 starts after range start and r4 ends before range end ? |
|
140 if ((-1 < r4.compareBoundaryPoints(0, range)) && (1 > r4.compareBoundaryPoints(2, range))) { |
|
141 startNode = s.firstChild ; |
|
142 startOffset = 0 ; |
|
143 endNode = e.lastChild ; |
|
144 endOffset = CY.DOM.getText(e).length ; |
|
145 |
|
146 stop = true ; |
|
147 break ; |
|
148 } |
|
149 } |
|
150 if (stop) |
|
151 break ; |
|
152 } |
|
153 } |
|
154 |
|
155 r1.detach() ; |
|
156 r2.detach() ; |
|
157 } |
|
158 else |
|
159 return null ; |
|
160 } |
|
161 else |
|
162 return null ; |
|
163 |
|
164 } |
|
165 else if (document.selection) { // IE case |
|
166 var rng = document.selection.createRange(); |
|
167 if (rng.text.length == 0) |
|
168 return null ; |
|
169 var el = rng.parentElement(); |
|
170 |
|
171 // duplicate the range and collapse it to its start, to ask IE the parent element of the start textNode. |
|
172 var rngStart = rng.duplicate(); |
|
173 var rngEnd = rng.duplicate(); |
|
174 |
|
175 rngStart.collapse(true); // collapse to start |
|
176 rngEnd.collapse(false); // collapse to end |
|
177 |
|
178 startNode = rngStart.parentElement() ; |
|
179 while(rngStart.moveStart('character', -1) != 0) { |
|
180 if (rngStart.parentElement() != startNode) |
|
181 break ; |
|
182 startOffset++ ; |
|
183 } |
|
184 endNode = rngEnd.parentElement() ; |
|
185 while(rngEnd.moveEnd('character', -1) != 0) { |
|
186 if (rngEnd.parentElement() != endNode) |
|
187 break ; |
|
188 endOffset++ ; |
|
189 } |
|
190 |
|
191 text = rng.text ; |
|
192 } |
|
193 |
|
194 if (!hasWrapperAncestor(startNode) || !hasWrapperAncestor(endNode)){ |
|
195 // CY.log('no wrapper on one end') ; |
|
196 return null ; |
|
197 } |
|
198 return {'text' : text, 'start' : {'elt':startNode, 'offset':startOffset}, 'end' : {'elt':endNode, 'offset':endOffset}} ; |
|
199 } |