src/cm/media/js/client/c_selection.js
changeset 0 40c8f766c9b8
child 341 053551f213fb
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/cm/media/js/client/c_selection.js	Mon Nov 23 15:14:29 2009 +0100
@@ -0,0 +1,199 @@
+getWrapperAncestor = function(elt) {
+	var parent = elt ;
+	while (parent != null) {
+    	if (CY.DOM.hasClass(parent, 'c-s')) 
+    		return parent ;
+    	parent = parent.parentNode ;
+	}
+	return null ; 
+}
+
+hasWrapperAncestor = function(elt) {
+	return (getWrapperAncestor(elt) != null) ;
+/*	var parent = elt ;
+	while (parent != null) {
+    	if (CY.DOM.hasClass(parent, 'c-s')) 
+    		return true ;
+    	parent = parent.parentNode ;
+	}
+	return false ;*/ 
+}
+
+// returns null or :
+// {'text' : textcontent, 'start': {'elt':startNode, 'nbChar':startOffset(==number of characters to selection start in the start node},
+// 'end': ....}
+// the text attribute is informational having it empty doesn't mean selection is empty !!
+
+// when selection starts/ends in/on a non textual element (<hr/> for example) we very often have anchorNode/focusNode == body elt
+// TODO adapt this body case by considering offset ( cf. http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html)
+getSelectionInfo  = function () {
+	var startNode = null, endNode = null, startOffset = 0, endOffset = 0, text = '' ;
+	
+	if (window.getSelection) { // everything else than IE
+		var userSelection = window.getSelection();
+
+		if (userSelection.rangeCount > 0) {
+			var range = userSelection.getRangeAt(0) ;
+			text = range.toString() ;
+			if (text != "")  {
+				
+				// selection occured from right to left ? :
+				var r1 = document.createRange() ;r1.setStart(userSelection.anchorNode, userSelection.anchorOffset) ;r1.collapse(true) ;
+				var r2 = document.createRange() ;r2.setEnd(userSelection.focusNode, userSelection.focusOffset) ;r2.collapse(false) ;
+				var leftToRight = (r2.compareBoundaryPoints(2, r1) == 1) ; // 2 is for END_TO_END
+//				CY.log("leftToRight : " + leftToRight) ;
+				startNode = (leftToRight) ? userSelection.anchorNode.parentNode : userSelection.focusNode.parentNode ;  
+				innerStartNode = (leftToRight) ? userSelection.anchorNode : userSelection.focusNode ;
+				endNode = (leftToRight) ? userSelection.focusNode.parentNode : userSelection.anchorNode.parentNode;
+				innerEndNode = (leftToRight) ? userSelection.focusNode : userSelection.anchorNode;
+					
+				startOffset = (leftToRight) ? userSelection.anchorOffset : userSelection.focusOffset;
+				endOffset = (leftToRight) ? userSelection.focusOffset : userSelection.anchorOffset ;
+
+				if (!hasWrapperAncestor(endNode) && hasWrapperAncestor(startNode)){
+					var r3 = document.createRange() ;
+					r3.setStart(innerStartNode, startOffset) ;
+
+					var csStartAncestor = getWrapperAncestor(startNode) ;
+					var next = csStartAncestor ;
+					r3.setEndAfter(next) ;
+					
+					var ind = parseInt(csStartAncestor.id.substring('sv_'.length)) ;
+					while(r3.toString().length < range.toString().length) {
+							ind++ ;
+							var node = CY.get("#sv_"+ind) ;
+							if (node) {
+								next = CY.Node.getDOMNode(node) ;
+								r3.setEndAfter(next) ;
+							}
+							else 
+								break ;
+					}
+					endNode = next.lastChild ;
+					endOffset = CY.DOM.getText(endNode).length ;
+				}
+				else if (!hasWrapperAncestor(startNode) && hasWrapperAncestor(endNode)){
+					var r3 = document.createRange() ;
+					r3.setEnd(innerEndNode, endOffset) ;
+
+					var csEndAncestor = getWrapperAncestor(endNode) ;
+					var prev = csEndAncestor ;
+					r3.setStartBefore(prev) ;
+					
+					var ind = parseInt(csEndAncestor.id.substring('sv_'.length)) ;
+					while(r3.toString().length < range.toString().length) {
+							ind-- ;
+							var node = CY.get("#sv_"+ind) ;
+							if (node) {
+								prev = CY.Node.getDOMNode(node) ;
+								r3.setStartBefore(prev) ;
+							}
+							else 
+								break ;
+					}
+					startNode = prev.firstChild ;
+					startOffset = 0 ;
+				}
+				else if (!hasWrapperAncestor(startNode) && !hasWrapperAncestor(endNode)){
+					var textLength = text.length ;
+					
+					// gather nodes with id sv_xxxx as candidates for start ancestor
+					var startNodeInds = [] ;
+					for (var ind = 0 ;  ; ind++) {
+						var svNode = CY.get("#sv_"+ind) ;
+						if (svNode == null) {
+							break;
+						}
+						else {
+							var svText = svNode.get("text") ;
+							if (text.indexOf(svText) == 0) {
+								startNodeInds.push(ind) ;
+							}
+						}
+					}
+					
+					// gather nodes with id sv_xxxx as candidates for end ancestor
+					var endNodeInds = [] ;
+					for (var ind = 0 ;  ; ind++) {
+						var svNode = CY.get("#sv_"+ind) ;
+						if (svNode == null) {
+							break;
+						}
+						else {
+							var svText = svNode.get("text") ;
+							if (text.indexOf(svText) == (textLength - svText.length)) { // i.e. the selection exactly ends with svText
+								endNodeInds.push(ind) ;
+							}
+						}
+					}
+
+					var stop = false ;
+					for (var i = 0 ; i < startNodeInds.length ; i++) {
+						for (var j = 0 ; j < endNodeInds.length ; j++) {
+							var r4 = document.createRange() ;
+							
+							var s = CY.Node.getDOMNode(CY.get("#sv_"+startNodeInds[i])) ; var e = CY.Node.getDOMNode(CY.get("#sv_"+endNodeInds[j])) ;
+							
+							r4.setStartBefore(s) ; r4.setEndAfter(CY.Node.getDOMNode(e)) ;
+							
+							// does r4 starts after range start and r4 ends before range end ? 
+							if ((-1 < r4.compareBoundaryPoints(0, range)) && (1 > r4.compareBoundaryPoints(2, range))) { 
+								startNode = s.firstChild ;
+								startOffset = 0 ;
+								endNode = e.lastChild ;
+								endOffset = CY.DOM.getText(e).length ;
+								
+								stop = true ; 
+								break ;
+							}
+						}
+						if (stop)
+							break ;
+					}							
+				}				
+				
+				r1.detach() ;
+				r2.detach() ;
+			}
+			else 
+				return null ;
+		}
+		else 
+			return null ;
+		
+	}
+	else if (document.selection) { // IE case
+		var rng = document.selection.createRange();
+		if (rng.text.length == 0) 
+			return null ;
+		var el = rng.parentElement();
+
+		// duplicate the range and collapse it to its start, to ask IE the parent element of the start textNode.		
+		var rngStart = rng.duplicate();
+		var rngEnd = rng.duplicate();
+
+		rngStart.collapse(true); // collapse to start
+		rngEnd.collapse(false);  // collapse to end
+		
+		startNode = rngStart.parentElement() ;
+		while(rngStart.moveStart('character', -1) != 0) {
+			if (rngStart.parentElement() != startNode)
+				break ;
+			startOffset++ ;
+		}
+		endNode = rngEnd.parentElement() ;
+		while(rngEnd.moveEnd('character', -1) != 0) {
+			if (rngEnd.parentElement() != endNode)
+				break ;
+			endOffset++ ;
+		}
+		
+		text = rng.text ;
+	}
+	
+	if (!hasWrapperAncestor(startNode) || !hasWrapperAncestor(endNode)){
+		// CY.log('no wrapper on one end') ;
+		return null ;
+	}
+	return {'text' : text, 'start' : {'elt':startNode, 'offset':startOffset}, 'end' : {'elt':endNode, 'offset':endOffset}} ;
+}
\ No newline at end of file