|
0
|
1 |
getWrapperAncestor = function(elt) { |
|
341
|
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 ; |
|
0
|
9 |
} |
|
|
10 |
|
|
|
11 |
hasWrapperAncestor = function(elt) { |
|
341
|
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 ;*/ |
|
0
|
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 () { |
|
341
|
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(); |
|
0
|
34 |
|
|
341
|
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 ; |
|
0
|
52 |
|
|
341
|
53 |
if (!hasWrapperAncestor(endNode) && hasWrapperAncestor(startNode)){ |
|
|
54 |
var r3 = document.createRange() ; |
|
|
55 |
r3.setStart(innerStartNode, startOffset) ; |
|
0
|
56 |
|
|
341
|
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) ; |
|
0
|
78 |
|
|
341
|
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 |
} |
|
0
|
129 |
|
|
341
|
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(); |
|
0
|
170 |
|
|
341
|
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(); |
|
0
|
174 |
|
|
341
|
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 |
} |