src/cm/media/js/client/c_selection.js
changeset 341 053551f213fb
parent 0 40c8f766c9b8
child 397 eba2e55eb437
equal deleted inserted replaced
340:9e2b9e568e42 341:053551f213fb
     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 }