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