[ Index ] |
|
Code source de FCKeditor 2.4 |
1 /* 2 * FCKeditor - The text editor for Internet - http://www.fckeditor.net 3 * Copyright (C) 2003-2007 Frederico Caldeira Knabben 4 * 5 * == BEGIN LICENSE == 6 * 7 * Licensed under the terms of any of the following licenses at your 8 * choice: 9 * 10 * - GNU General Public License Version 2 or later (the "GPL") 11 * http://www.gnu.org/licenses/gpl.html 12 * 13 * - GNU Lesser General Public License Version 2.1 or later (the "LGPL") 14 * http://www.gnu.org/licenses/lgpl.html 15 * 16 * - Mozilla Public License Version 1.1 or later (the "MPL") 17 * http://www.mozilla.org/MPL/MPL-1.1.html 18 * 19 * == END LICENSE == 20 * 21 * File Name: fckw3crange.js 22 * This class partially implements the W3C DOM Range for browser that don't 23 * support the standards (like IE): 24 * http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html 25 * 26 * File Authors: 27 * Frederico Caldeira Knabben (www.fckeditor.net) 28 */ 29 30 var FCKW3CRange = function( parentDocument ) 31 { 32 this._Document = parentDocument ; 33 34 this.startContainer = null ; 35 this.startOffset = null ; 36 this.endContainer = null ; 37 this.endOffset = null ; 38 this.collapsed = true ; 39 } 40 41 FCKW3CRange.CreateRange = function( parentDocument ) 42 { 43 // We could opt to use the Range implentation of the browsers. The problem 44 // is that every browser have different bugs on their implementations, 45 // mostly related to different interpretations of the W3C specifications. 46 // So, for now, let's use our implementation and pray for browsers fixings 47 // soon. Otherwise will go crazy on trying to find out workarounds. 48 /* 49 // Get the browser implementation of the range, if available. 50 if ( parentDocument.createRange ) 51 { 52 var range = parentDocument.createRange() ; 53 if ( typeof( range.startContainer ) != 'undefined' ) 54 return range ; 55 } 56 */ 57 return new FCKW3CRange( parentDocument ) ; 58 } 59 60 FCKW3CRange.CreateFromRange = function( parentDocument, sourceRange ) 61 { 62 var range = FCKW3CRange.CreateRange( parentDocument ) ; 63 range.setStart( sourceRange.startContainer, sourceRange.startOffset ) ; 64 range.setEnd( sourceRange.endContainer, sourceRange.endOffset ) ; 65 return range ; 66 } 67 68 FCKW3CRange.prototype = 69 { 70 71 _UpdateCollapsed : function() 72 { 73 this.collapsed = ( this.startContainer == this.endContainer && this.startOffset == this.endOffset ) ; 74 }, 75 76 // W3C requires a check for the new position. If it is after the end 77 // boundary, the range should be collapsed to the new start. It seams we 78 // will not need this check for our use of this class so we can ignore it for now. 79 setStart : function( refNode, offset ) 80 { 81 this.startContainer = refNode ; 82 this.startOffset = offset ; 83 84 if ( !this.endContainer ) 85 { 86 this.endContainer = refNode ; 87 this.endOffset = offset ; 88 } 89 90 this._UpdateCollapsed() ; 91 }, 92 93 // W3C requires a check for the new position. If it is before the start 94 // boundary, the range should be collapsed to the new end. It seams we 95 // will not need this check for our use of this class so we can ignore it for now. 96 setEnd : function( refNode, offset ) 97 { 98 this.endContainer = refNode ; 99 this.endOffset = offset ; 100 101 if ( !this.startContainer ) 102 { 103 this.startContainer = refNode ; 104 this.startOffset = offset ; 105 } 106 107 this._UpdateCollapsed() ; 108 }, 109 110 setStartAfter : function( refNode ) 111 { 112 this.setStart( refNode.parentNode, FCKDomTools.GetIndexOf( refNode ) + 1 ) ; 113 }, 114 115 setStartBefore : function( refNode ) 116 { 117 this.setStart( refNode.parentNode, FCKDomTools.GetIndexOf( refNode ) ) ; 118 }, 119 120 setEndAfter : function( refNode ) 121 { 122 this.setEnd( refNode.parentNode, FCKDomTools.GetIndexOf( refNode ) + 1 ) ; 123 }, 124 125 setEndBefore : function( refNode ) 126 { 127 this.setEnd( refNode.parentNode, FCKDomTools.GetIndexOf( refNode ) ) ; 128 }, 129 130 collapse : function( toStart ) 131 { 132 if ( toStart ) 133 { 134 this.endContainer = this.startContainer ; 135 this.endOffset = this.startOffset ; 136 } 137 else 138 { 139 this.startContainer = this.endContainer ; 140 this.startOffset = this.endOffset ; 141 } 142 143 this.collapsed = true ; 144 }, 145 146 selectNodeContents : function( refNode ) 147 { 148 this.setStart( refNode, 0 ) ; 149 this.setEnd( refNode, refNode.nodeType == 3 ? refNode.data.length : refNode.childNodes.length ) ; 150 }, 151 152 insertNode : function( newNode ) 153 { 154 var startContainer = this.startContainer ; 155 var startOffset = this.startOffset ; 156 157 // If we are in a text node. 158 if ( startContainer.nodeType == 3 ) 159 { 160 startContainer.splitText( startOffset ) ; 161 162 // Check if it is necessary to update the end boundary. 163 if ( startContainer == this.endContainer ) 164 this.setEnd( startContainer.nextSibling, this.endOffset - this.startOffset ) ; 165 166 // Insert the new node it after the text node. 167 FCKDomTools.InsertAfterNode( startContainer, newNode ) ; 168 169 return ; 170 } 171 else 172 { 173 // Simply insert the new node before the current start node. 174 startContainer.insertBefore( newNode, startContainer.childNodes[ startOffset ] || null ) ; 175 176 // Check if it is necessary to update the end boundary. 177 if ( startContainer == this.endContainer ) 178 { 179 this.endOffset++ ; 180 this.collapsed = false ; 181 } 182 } 183 }, 184 185 deleteContents : function() 186 { 187 if ( this.collapsed ) 188 return ; 189 190 this._ExecContentsAction( 0 ) ; 191 }, 192 193 extractContents : function() 194 { 195 var docFrag = new FCKDocumentFragment( this._Document ) ; 196 197 if ( !this.collapsed ) 198 this._ExecContentsAction( 1, docFrag ) ; 199 200 return docFrag ; 201 }, 202 203 // The selection may be lost when clonning (due to the splitText() call). 204 cloneContents : function() 205 { 206 var docFrag = new FCKDocumentFragment( this._Document ) ; 207 208 if ( !this.collapsed ) 209 this._ExecContentsAction( 2, docFrag ) ; 210 211 return docFrag ; 212 }, 213 214 _ExecContentsAction : function( action, docFrag ) 215 { 216 var startNode = this.startContainer ; 217 var endNode = this.endContainer ; 218 219 var startOffset = this.startOffset ; 220 var endOffset = this.endOffset ; 221 222 var removeStartNode = false ; 223 var removeEndNode = false ; 224 225 // Check the start and end nodes and make the necessary removals or changes. 226 227 // Start from the end, otherwise DOM mutations (splitText) made in the 228 // start boundary may interfere on the results here. 229 230 // For text containers, we must simply split the node and point to the 231 // second part. The removal will be handled by the rest of the code . 232 if ( endNode.nodeType == 3 ) 233 endNode = endNode.splitText( endOffset ) ; 234 else 235 { 236 // If the end container has children and the offset is pointing 237 // to a child, then we should start from it. 238 if ( endNode.childNodes.length > 0 ) 239 { 240 // If the offset points after the last node. 241 if ( endOffset > endNode.childNodes.length - 1 ) 242 { 243 // Let's create a temporary node and mark it for removal. 244 endNode = FCKDomTools.InsertAfterNode( endNode.lastChild, this._Document.createTextNode('') ) ; 245 removeEndNode = true ; 246 } 247 else 248 endNode = endNode.childNodes[ endOffset ] ; 249 } 250 } 251 252 // For text containers, we must simply split the node. The removal will 253 // be handled by the rest of the code . 254 if ( startNode.nodeType == 3 ) 255 { 256 startNode.splitText( startOffset ) ; 257 258 // In cases the end node is the same as the start node, the above 259 // splitting will also split the end, so me must move the end to 260 // the second part of the split. 261 if ( startNode == endNode ) 262 endNode = startNode.nextSibling ; 263 } 264 else 265 { 266 // If the start container has children and the offset is pointing 267 // to a child, then we should start from its previous sibling. 268 if ( startNode.childNodes.length > 0 && startOffset <= startNode.childNodes.length - 1 ) 269 { 270 // If the offset points to the first node, we don't have a 271 // sibling, so let's use the first one, but mark it for removal. 272 if ( startOffset == 0 ) 273 { 274 // Let's create a temporary node and mark it for removal. 275 startNode = startNode.insertBefore( this._Document.createTextNode(''), startNode.firstChild ) ; 276 removeStartNode = true ; 277 } 278 else 279 startNode = startNode.childNodes[ startOffset ].previousSibling ; 280 } 281 } 282 283 // Get the parent nodes tree for the start and end boundaries. 284 var startParents = FCKDomTools.GetParents( startNode ) ; 285 var endParents = FCKDomTools.GetParents( endNode ) ; 286 287 // Compare them, to find the top most siblings. 288 var i, topStart, topEnd ; 289 290 for ( i = 0 ; i < startParents.length ; i++ ) 291 { 292 topStart = startParents[i] ; 293 topEnd = endParents[i] ; 294 295 // The compared nodes will match until we find the top most 296 // siblings (different nodes that have the same parent). 297 // "i" will hold the index in the parants array for the top 298 // most element. 299 if ( topStart != topEnd ) 300 break ; 301 } 302 303 var clone, levelStartNode, levelClone, currentNode, currentSibling ; 304 305 if ( docFrag ) 306 clone = docFrag.RootNode ; 307 308 // Remove all successive sibling nodes for every node in the 309 // startParents tree. 310 for ( var j = i ; j < startParents.length ; j++ ) 311 { 312 levelStartNode = startParents[j] ; 313 314 // For Extract and Clone, we must clone this level. 315 if ( clone && levelStartNode != startNode ) // action = 0 = Delete 316 levelClone = clone.appendChild( levelStartNode.cloneNode( levelStartNode == startNode ) ) ; 317 318 currentNode = levelStartNode.nextSibling ; 319 320 while( currentNode ) 321 { 322 // Stop processing when the current node matches a node in the 323 // endParents tree or if it is the endNode. 324 if ( currentNode == endParents[j] || currentNode == endNode ) 325 break ; 326 327 // Cache the next sibling. 328 currentSibling = currentNode.nextSibling ; 329 330 // If clonning, just clone it. 331 if ( action == 2 ) // 2 = Clone 332 clone.appendChild( currentNode.cloneNode( true ) ) ; 333 else 334 { 335 // Both Delete and Extract will remove the node. 336 currentNode.parentNode.removeChild( currentNode ) ; 337 338 // When Extracting, move the removed node to the docFrag. 339 if ( action == 1 ) // 1 = Extract 340 clone.appendChild( currentNode ) ; 341 } 342 343 currentNode = currentSibling ; 344 } 345 346 if ( clone ) 347 clone = levelClone ; 348 } 349 350 if ( docFrag ) 351 clone = docFrag.RootNode ; 352 353 // Remove all previous sibling nodes for every node in the 354 // endParents tree. 355 for ( var k = i ; k < endParents.length ; k++ ) 356 { 357 levelStartNode = endParents[k] ; 358 359 // For Extract and Clone, we must clone this level. 360 if ( action > 0 && levelStartNode != endNode ) // action = 0 = Delete 361 levelClone = clone.appendChild( levelStartNode.cloneNode( levelStartNode == endNode ) ) ; 362 363 // The processing of siblings may have already been done by the parent. 364 if ( !startParents[k] || levelStartNode.parentNode != startParents[k].parentNode ) 365 { 366 currentNode = levelStartNode.previousSibling ; 367 368 while( currentNode ) 369 { 370 // Stop processing when the current node matches a node in the 371 // startParents tree or if it is the startNode. 372 if ( currentNode == startParents[k] || currentNode == startNode ) 373 break ; 374 375 // Cache the next sibling. 376 currentSibling = currentNode.previousSibling ; 377 378 // If clonning, just clone it. 379 if ( action == 2 ) // 2 = Clone 380 clone.insertBefore( currentNode.cloneNode( true ), clone.firstChild ) ; 381 else 382 { 383 // Both Delete and Extract will remove the node. 384 currentNode.parentNode.removeChild( currentNode ) ; 385 386 // When Extracting, mode the removed node to the docFrag. 387 if ( action == 1 ) // 1 = Extract 388 clone.insertBefore( currentNode, clone.firstChild ) ; 389 } 390 391 currentNode = currentSibling ; 392 } 393 } 394 395 if ( clone ) 396 clone = levelClone ; 397 } 398 399 if ( action == 2 ) // 2 = Clone. 400 { 401 // No changes in the DOM should be done, so fix the split text (if any). 402 403 var startTextNode = this.startContainer ; 404 if ( startTextNode.nodeType == 3 ) 405 { 406 startTextNode.data += startTextNode.nextSibling.data ; 407 startTextNode.parentNode.removeChild( startTextNode.nextSibling ) ; 408 } 409 410 var endTextNode = this.endContainer ; 411 if ( endTextNode.nodeType == 3 && endTextNode.nextSibling ) 412 { 413 endTextNode.data += endTextNode.nextSibling.data ; 414 endTextNode.parentNode.removeChild( endTextNode.nextSibling ) ; 415 } 416 } 417 else 418 { 419 // Collapse the range. 420 421 // If a node has been partially selected, collapse the range between 422 // topStart and topEnd. Otherwise, simply collapse it to the start. (W3C specs). 423 if ( topStart && topEnd && ( startNode.parentNode != topStart.parentNode || endNode.parentNode != topEnd.parentNode ) ) 424 this.setStart( topEnd.parentNode, FCKDomTools.GetIndexOf( topEnd ) ) ; 425 426 // Collapse it to the start. 427 this.collapse( true ) ; 428 } 429 430 // Cleanup any marked node. 431 if( removeStartNode ) 432 startNode.parentNode.removeChild( startNode ) ; 433 434 if( removeEndNode && endNode.parentNode ) 435 endNode.parentNode.removeChild( endNode ) ; 436 }, 437 438 cloneRange : function() 439 { 440 return FCKW3CRange.CreateFromRange( this._Document, this ) ; 441 }, 442 443 toString : function() 444 { 445 var docFrag = this.cloneContents() ; 446 447 var tmpDiv = this._Document.createElement( 'div' ) ; 448 docFrag.AppendTo( tmpDiv ) ; 449 450 return tmpDiv.textContent || tmpDiv.innerText ; 451 } 452 } ;
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
Généré le : Sun Feb 25 15:28:05 2007 | par Balluche grâce à PHPXref 0.7 |