fckdomrange.js
上传用户:ah_jiwei
上传日期:2022-07-24
资源大小:54044k
文件大小:23k
源码类别:

数据库编程

开发平台:

Visual C++

  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.  * Class for working with a selection range, much like the W3C DOM Range, but
  22.  * it is not intended to be an implementation of the W3C interface.
  23.  */
  24. var FCKDomRange = function( sourceWindow )
  25. {
  26. this.Window = sourceWindow ;
  27. this._Cache = {} ;
  28. }
  29. FCKDomRange.prototype =
  30. {
  31. _UpdateElementInfo : function()
  32. {
  33. var innerRange = this._Range ;
  34. if ( !innerRange )
  35. this.Release( true ) ;
  36. else
  37. {
  38. // For text nodes, the node itself is the StartNode.
  39. var eStart = innerRange.startContainer ;
  40. var eEnd = innerRange.endContainer ;
  41. var oElementPath = new FCKElementPath( eStart ) ;
  42. this.StartNode = eStart.nodeType == 3 ? eStart : eStart.childNodes[ innerRange.startOffset ] ;
  43. this.StartContainer = eStart ;
  44. this.StartBlock = oElementPath.Block ;
  45. this.StartBlockLimit = oElementPath.BlockLimit ;
  46. if ( eStart != eEnd )
  47. oElementPath = new FCKElementPath( eEnd ) ;
  48. // The innerRange.endContainer[ innerRange.endOffset ] is not
  49. // usually part of the range, but the marker for the range end. So,
  50. // let's get the previous available node as the real end.
  51. var eEndNode = eEnd ;
  52. if ( innerRange.endOffset == 0 )
  53. {
  54. while ( eEndNode && !eEndNode.previousSibling )
  55. eEndNode = eEndNode.parentNode ;
  56. if ( eEndNode )
  57. eEndNode = eEndNode.previousSibling ;
  58. }
  59. else if ( eEndNode.nodeType == 1 )
  60. eEndNode = eEndNode.childNodes[ innerRange.endOffset - 1 ] ;
  61. this.EndNode = eEndNode ;
  62. this.EndContainer = eEnd ;
  63. this.EndBlock = oElementPath.Block ;
  64. this.EndBlockLimit = oElementPath.BlockLimit ;
  65. }
  66. this._Cache = {} ;
  67. },
  68. CreateRange : function()
  69. {
  70. return new FCKW3CRange( this.Window.document ) ;
  71. },
  72. DeleteContents : function()
  73. {
  74. if ( this._Range )
  75. {
  76. this._Range.deleteContents() ;
  77. this._UpdateElementInfo() ;
  78. }
  79. },
  80. ExtractContents : function()
  81. {
  82. if ( this._Range )
  83. {
  84. var docFrag = this._Range.extractContents() ;
  85. this._UpdateElementInfo() ;
  86. return docFrag ;
  87. }
  88. },
  89. CheckIsCollapsed : function()
  90. {
  91. if ( this._Range )
  92. return this._Range.collapsed ;
  93. },
  94. Collapse : function( toStart )
  95. {
  96. if ( this._Range )
  97. this._Range.collapse( toStart ) ;
  98. this._UpdateElementInfo() ;
  99. },
  100. Clone : function()
  101. {
  102. var oClone = FCKTools.CloneObject( this ) ;
  103. if ( this._Range )
  104. oClone._Range = this._Range.cloneRange() ;
  105. return oClone ;
  106. },
  107. MoveToNodeContents : function( targetNode )
  108. {
  109. if ( !this._Range )
  110. this._Range = this.CreateRange() ;
  111. this._Range.selectNodeContents( targetNode ) ;
  112. this._UpdateElementInfo() ;
  113. },
  114. MoveToElementStart : function( targetElement )
  115. {
  116. this.SetStart(targetElement,1) ;
  117. this.SetEnd(targetElement,1) ;
  118. },
  119. // Moves to the first editing point inside a element. For example, in a
  120. // element tree like "<p><b><i></i></b> Text</p>", the start editing point
  121. // is "<p><b><i>^</i></b> Text</p>" (inside <i>).
  122. MoveToElementEditStart : function( targetElement )
  123. {
  124. var child ;
  125. while ( ( child = targetElement.firstChild ) && child.nodeType == 1 && FCKListsLib.EmptyElements[ child.nodeName.toLowerCase() ] == null )
  126. targetElement = child ;
  127. this.MoveToElementStart( targetElement ) ;
  128. },
  129. InsertNode : function( node )
  130. {
  131. if ( this._Range )
  132. this._Range.insertNode( node ) ;
  133. },
  134. CheckIsEmpty : function()
  135. {
  136. if ( this.CheckIsCollapsed() )
  137. return true ;
  138. // Inserts the contents of the range in a div tag.
  139. var eToolDiv = this.Window.document.createElement( 'div' ) ;
  140. this._Range.cloneContents().AppendTo( eToolDiv ) ;
  141. FCKDomTools.TrimNode( eToolDiv ) ;
  142. return ( eToolDiv.innerHTML.length == 0 ) ;
  143. },
  144. CheckStartOfBlock : function()
  145. {
  146. var bIsStartOfBlock = this._Cache.IsStartOfBlock ;
  147. if ( bIsStartOfBlock != undefined )
  148. return bIsStartOfBlock ;
  149. // Create a clone of the current range.
  150. var oTestRange = this.Clone() ;
  151. // Collapse it to its start point.
  152. oTestRange.Collapse( true ) ;
  153. // Move the start boundary to the start of the block.
  154. oTestRange.SetStart( oTestRange.StartBlock || oTestRange.StartBlockLimit, 1 ) ;
  155. if ( oTestRange.CheckIsCollapsed() )
  156. bIsStartOfBlock = true ;
  157. else
  158. {
  159. // Inserts the contents of the range in a div tag.
  160. var eToolDiv = oTestRange.Window.document.createElement( 'div' ) ;
  161. oTestRange._Range.cloneContents().AppendTo( eToolDiv ) ;
  162. // This line is why we don't use CheckIsEmpty() here...
  163. // Because using RTrimNode() or TrimNode() would be incorrect - 
  164. // TrimNode() and RTrimNode() would delete <br> nodes at the end of the div node,
  165. // but for checking start of block they are actually meaningful. (Bug #1350)
  166. FCKDomTools.LTrimNode( eToolDiv ) ;
  167. bIsStartOfBlock = ( eToolDiv.innerHTML.length == 0 ) ;
  168. }
  169. oTestRange.Release() ;
  170. return ( this._Cache.IsStartOfBlock = bIsStartOfBlock ) ;
  171. },
  172. CheckEndOfBlock : function( refreshSelection )
  173. {
  174. var bIsEndOfBlock = this._Cache.IsEndOfBlock ;
  175. if ( bIsEndOfBlock != undefined )
  176. return bIsEndOfBlock ;
  177. // Create a clone of the current range.
  178. var oTestRange = this.Clone() ;
  179. // Collapse it to its end point.
  180. oTestRange.Collapse( false ) ;
  181. // Move the end boundary to the end of the block.
  182. oTestRange.SetEnd( oTestRange.EndBlock || oTestRange.EndBlockLimit, 2 ) ;
  183. bIsEndOfBlock = oTestRange.CheckIsCollapsed() ;
  184. if ( !bIsEndOfBlock )
  185. {
  186. // Inserts the contents of the range in a div tag.
  187. var eToolDiv = this.Window.document.createElement( 'div' ) ;
  188. oTestRange._Range.cloneContents().AppendTo( eToolDiv ) ;
  189. FCKDomTools.TrimNode( eToolDiv ) ;
  190. // Find out if we are in an empty tree of inline elements, like <b><i><span></span></i></b>
  191. bIsEndOfBlock = true ;
  192. var eLastChild = eToolDiv ;
  193. while ( ( eLastChild = eLastChild.lastChild ) )
  194. {
  195. // Check the following:
  196. // 1. Is there more than one node in the parents children?
  197. // 2. Is the node not an element node?
  198. // 3. Is it not a inline element.
  199. if ( eLastChild.previousSibling || eLastChild.nodeType != 1 || FCKListsLib.InlineChildReqElements[ eLastChild.nodeName.toLowerCase() ] == null )
  200. {
  201. // So we are not in the end of the range.
  202. bIsEndOfBlock = false ;
  203. break ;
  204. }
  205. }
  206. }
  207. oTestRange.Release() ;
  208. if ( refreshSelection )
  209. this.Select() ;
  210. return this._Cache.IsEndOfBlock = bIsEndOfBlock ;
  211. },
  212. // This is an "intrusive" way to create a bookmark. It includes <span> tags
  213. // in the range boundaries. The advantage of it is that it is possible to
  214. // handle DOM mutations when moving back to the bookmark.
  215. // Attention: the inclusion of nodes in the DOM is a design choice and
  216. // should not be changed as there are other points in the code that may be
  217. // using those nodes to perform operations. See GetBookmarkNode.
  218. // For performance, includeNodes=true if intended to SelectBookmark.
  219. CreateBookmark : function( includeNodes )
  220. {
  221. // Create the bookmark info (random IDs).
  222. var oBookmark =
  223. {
  224. StartId : (new Date()).valueOf() + Math.floor(Math.random()*1000) + 'S',
  225. EndId : (new Date()).valueOf() + Math.floor(Math.random()*1000) + 'E'
  226. } ;
  227. var oDoc = this.Window.document ;
  228. var eStartSpan ;
  229. var eEndSpan ;
  230. var oClone ;
  231. // For collapsed ranges, add just the start marker.
  232. if ( !this.CheckIsCollapsed() )
  233. {
  234. eEndSpan = oDoc.createElement( 'span' ) ;
  235. eEndSpan.style.display = 'none' ;
  236. eEndSpan.id = oBookmark.EndId ;
  237. eEndSpan.setAttribute( '_fck_bookmark', true ) ;
  238. // For IE, it must have something inside, otherwise it may be
  239. // removed during DOM operations.
  240. // if ( FCKBrowserInfo.IsIE )
  241. eEndSpan.innerHTML = '&nbsp;' ;
  242. oClone = this.Clone() ;
  243. oClone.Collapse( false ) ;
  244. oClone.InsertNode( eEndSpan ) ;
  245. }
  246. eStartSpan = oDoc.createElement( 'span' ) ;
  247. eStartSpan.style.display = 'none' ;
  248. eStartSpan.id = oBookmark.StartId ;
  249. eStartSpan.setAttribute( '_fck_bookmark', true ) ;
  250. // For IE, it must have something inside, otherwise it may be removed
  251. // during DOM operations.
  252. // if ( FCKBrowserInfo.IsIE )
  253. eStartSpan.innerHTML = '&nbsp;' ;
  254. oClone = this.Clone() ;
  255. oClone.Collapse( true ) ;
  256. oClone.InsertNode( eStartSpan ) ;
  257. if ( includeNodes )
  258. {
  259. oBookmark.StartNode = eStartSpan ;
  260. oBookmark.EndNode = eEndSpan ;
  261. }
  262. // Update the range position.
  263. if ( eEndSpan )
  264. {
  265. this.SetStart( eStartSpan, 4 ) ;
  266. this.SetEnd( eEndSpan, 3 ) ;
  267. }
  268. else
  269. this.MoveToPosition( eStartSpan, 4 ) ;
  270. return oBookmark ;
  271. },
  272. // This one should be a part of a hypothetic "bookmark" object.
  273. GetBookmarkNode : function( bookmark, start )
  274. {
  275. var doc = this.Window.document ;
  276. if ( start )
  277. return bookmark.StartNode || doc.getElementById( bookmark.StartId ) ;
  278. else
  279. return bookmark.EndNode || doc.getElementById( bookmark.EndId ) ;
  280. },
  281. MoveToBookmark : function( bookmark, preserveBookmark )
  282. {
  283. var eStartSpan = this.GetBookmarkNode( bookmark, true ) ;
  284. var eEndSpan = this.GetBookmarkNode( bookmark, false ) ;
  285. this.SetStart( eStartSpan, 3 ) ;
  286. if ( !preserveBookmark )
  287. FCKDomTools.RemoveNode( eStartSpan ) ;
  288. // If collapsed, the end span will not be available.
  289. if ( eEndSpan )
  290. {
  291. this.SetEnd( eEndSpan, 3 ) ;
  292. if ( !preserveBookmark )
  293. FCKDomTools.RemoveNode( eEndSpan ) ;
  294. }
  295. else
  296. this.Collapse( true ) ;
  297. this._UpdateElementInfo() ;
  298. },
  299. // Non-intrusive bookmark algorithm
  300. CreateBookmark2 : function()
  301. {
  302. // If there is no range then get out of here.
  303. // It happens on initial load in Safari #962 and if the editor it's hidden also in Firefox
  304. if ( ! this._Range )
  305. return { "Start" : 0, "End" : 0 } ;
  306. // First, we record down the offset values
  307. var bookmark =
  308. {
  309. "Start" : [ this._Range.startOffset ],
  310. "End" : [ this._Range.endOffset ]
  311. } ;
  312. var curStart = this._Range.startContainer.previousSibling ;
  313. var curEnd = this._Range.endContainer.previousSibling ;
  314. while ( curStart && curStart.nodeType == 3 )
  315. {
  316. bookmark.Start[0] += curStart.length ;
  317. curStart = curStart.previousSibling ;
  318. }
  319. while ( curEnd && curEnd.nodeType == 3 )
  320. {
  321. bookmark.End[0] += curEnd.length ;
  322. curEnd = curEnd.previousSibling ;
  323. }
  324. // Then, we record down the precise position of the container nodes
  325. // by walking up the DOM tree and counting their childNode index
  326. bookmark.Start = FCKDomTools.GetNodeAddress( this._Range.startContainer, true ).concat( bookmark.Start ) ;
  327. bookmark.End = FCKDomTools.GetNodeAddress( this._Range.endContainer, true ).concat( bookmark.End ) ;
  328. return bookmark;
  329. },
  330. MoveToBookmark2 : function( bookmark )
  331. {
  332. // Reverse the childNode counting algorithm in CreateBookmark2()
  333. var curStart = FCKDomTools.GetNodeFromAddress( this.Window.document, bookmark.Start.slice( 0, -1 ), true ) ;
  334. var curEnd = FCKDomTools.GetNodeFromAddress( this.Window.document, bookmark.End.slice( 0, -1 ), true ) ;
  335. // Generate the W3C Range object and update relevant data
  336. this.Release( true ) ;
  337. this._Range = new FCKW3CRange( this.Window.document ) ;
  338. var startOffset = bookmark.Start[ bookmark.Start.length - 1 ] ;
  339. var endOffset = bookmark.End[ bookmark.End.length - 1 ] ;
  340. while ( curStart.nodeType == 3 && startOffset > curStart.length )
  341. {
  342. if ( ! curStart.nextSibling || curStart.nextSibling.nodeType != 3 )
  343. break ;
  344. startOffset -= curStart.length ;
  345. curStart = curStart.nextSibling ;
  346. }
  347. while ( curEnd.nodeType == 3 && endOffset > curEnd.length )
  348. {
  349. if ( ! curEnd.nextSibling || curEnd.nextSibling.nodeType != 3 )
  350. break ;
  351. endOffset -= curEnd.length ;
  352. curEnd = curEnd.nextSibling ;
  353. }
  354. this._Range.setStart( curStart, startOffset ) ;
  355. this._Range.setEnd( curEnd, endOffset ) ;
  356. this._UpdateElementInfo() ;
  357. },
  358. MoveToPosition : function( targetElement, position )
  359. {
  360. this.SetStart( targetElement, position ) ;
  361. this.Collapse( true ) ;
  362. },
  363. /*
  364.  * Moves the position of the start boundary of the range to a specific position
  365.  * relatively to a element.
  366.  * @position:
  367.  * 1 = After Start <target>^contents</target>
  368.  * 2 = Before End <target>contents^</target>
  369.  * 3 = Before Start ^<target>contents</target>
  370.  * 4 = After End <target>contents</target>^
  371.  */
  372. SetStart : function( targetElement, position, noInfoUpdate )
  373. {
  374. var oRange = this._Range ;
  375. if ( !oRange )
  376. oRange = this._Range = this.CreateRange() ;
  377. switch( position )
  378. {
  379. case 1 : // After Start <target>^contents</target>
  380. oRange.setStart( targetElement, 0 ) ;
  381. break ;
  382. case 2 : // Before End <target>contents^</target>
  383. oRange.setStart( targetElement, targetElement.childNodes.length ) ;
  384. break ;
  385. case 3 : // Before Start ^<target>contents</target>
  386. oRange.setStartBefore( targetElement ) ;
  387. break ;
  388. case 4 : // After End <target>contents</target>^
  389. oRange.setStartAfter( targetElement ) ;
  390. }
  391. if ( !noInfoUpdate )
  392. this._UpdateElementInfo() ;
  393. },
  394. /*
  395.  * Moves the position of the start boundary of the range to a specific position
  396.  * relatively to a element.
  397.  * @position:
  398.  * 1 = After Start <target>^contents</target>
  399.  * 2 = Before End <target>contents^</target>
  400.  * 3 = Before Start ^<target>contents</target>
  401.  * 4 = After End <target>contents</target>^
  402.  */
  403. SetEnd : function( targetElement, position, noInfoUpdate )
  404. {
  405. var oRange = this._Range ;
  406. if ( !oRange )
  407. oRange = this._Range = this.CreateRange() ;
  408. switch( position )
  409. {
  410. case 1 : // After Start <target>^contents</target>
  411. oRange.setEnd( targetElement, 0 ) ;
  412. break ;
  413. case 2 : // Before End <target>contents^</target>
  414. oRange.setEnd( targetElement, targetElement.childNodes.length ) ;
  415. break ;
  416. case 3 : // Before Start ^<target>contents</target>
  417. oRange.setEndBefore( targetElement ) ;
  418. break ;
  419. case 4 : // After End <target>contents</target>^
  420. oRange.setEndAfter( targetElement ) ;
  421. }
  422. if ( !noInfoUpdate )
  423. this._UpdateElementInfo() ;
  424. },
  425. Expand : function( unit )
  426. {
  427. var oNode, oSibling ;
  428. switch ( unit )
  429. {
  430. // Expand the range to include all inline parent elements if we are
  431. // are in their boundary limits.
  432. // For example (where [ ] are the range limits):
  433. // Before => Some <b>[<i>Some sample text]</i></b>.
  434. // After => Some [<b><i>Some sample text</i></b>].
  435. case 'inline_elements' :
  436. // Expand the start boundary.
  437. if ( this._Range.startOffset == 0 )
  438. {
  439. oNode = this._Range.startContainer ;
  440. if ( oNode.nodeType != 1 )
  441. oNode = oNode.previousSibling ? null : oNode.parentNode ;
  442. if ( oNode )
  443. {
  444. while ( FCKListsLib.InlineNonEmptyElements[ oNode.nodeName.toLowerCase() ] )
  445. {
  446. this._Range.setStartBefore( oNode ) ;
  447. if ( oNode != oNode.parentNode.firstChild )
  448. break ;
  449. oNode = oNode.parentNode ;
  450. }
  451. }
  452. }
  453. // Expand the end boundary.
  454. oNode = this._Range.endContainer ;
  455. var offset = this._Range.endOffset ;
  456. if ( ( oNode.nodeType == 3 && offset >= oNode.nodeValue.length ) || ( oNode.nodeType == 1 && offset >= oNode.childNodes.length ) || ( oNode.nodeType != 1 && oNode.nodeType != 3 ) )
  457. {
  458. if ( oNode.nodeType != 1 )
  459. oNode = oNode.nextSibling ? null : oNode.parentNode ;
  460. if ( oNode )
  461. {
  462. while ( FCKListsLib.InlineNonEmptyElements[ oNode.nodeName.toLowerCase() ] )
  463. {
  464. this._Range.setEndAfter( oNode ) ;
  465. if ( oNode != oNode.parentNode.lastChild )
  466. break ;
  467. oNode = oNode.parentNode ;
  468. }
  469. }
  470. }
  471. break ;
  472. case 'block_contents' :
  473. case 'list_contents' :
  474. var boundarySet = FCKListsLib.BlockBoundaries ;
  475. if ( unit == 'list_contents' || FCKConfig.EnterMode == 'br' )
  476. boundarySet = FCKListsLib.ListBoundaries ;
  477. if ( this.StartBlock && FCKConfig.EnterMode != 'br' && unit == 'block_contents' )
  478. this.SetStart( this.StartBlock, 1 ) ;
  479. else
  480. {
  481. // Get the start node for the current range.
  482. oNode = this._Range.startContainer ;
  483. // If it is an element, get the node right before of it (in source order).
  484. if ( oNode.nodeType == 1 )
  485. {
  486. var lastNode = oNode.childNodes[ this._Range.startOffset ] ;
  487. if ( lastNode )
  488. oNode = FCKDomTools.GetPreviousSourceNode( lastNode, true ) ;
  489. else
  490. oNode = oNode.lastChild || oNode ;
  491. }
  492. // We must look for the left boundary, relative to the range
  493. // start, which is limited by a block element.
  494. while ( oNode
  495. && ( oNode.nodeType != 1
  496. || ( oNode != this.StartBlockLimit
  497. && !boundarySet[ oNode.nodeName.toLowerCase() ] ) ) )
  498. {
  499. this._Range.setStartBefore( oNode ) ;
  500. oNode = oNode.previousSibling || oNode.parentNode ;
  501. }
  502. }
  503. if ( this.EndBlock && FCKConfig.EnterMode != 'br' && unit == 'block_contents' && this.EndBlock.nodeName.toLowerCase() != 'li' )
  504. this.SetEnd( this.EndBlock, 2 ) ;
  505. else
  506. {
  507. oNode = this._Range.endContainer ;
  508. if ( oNode.nodeType == 1 )
  509. oNode = oNode.childNodes[ this._Range.endOffset ] || oNode.lastChild ;
  510. // We must look for the right boundary, relative to the range
  511. // end, which is limited by a block element.
  512. while ( oNode
  513. && ( oNode.nodeType != 1
  514. || ( oNode != this.StartBlockLimit
  515. && !boundarySet[ oNode.nodeName.toLowerCase() ] ) ) )
  516. {
  517. this._Range.setEndAfter( oNode ) ;
  518. oNode = oNode.nextSibling || oNode.parentNode ;
  519. }
  520. // In EnterMode='br', the end <br> boundary element must
  521. // be included in the expanded range.
  522. if ( oNode && oNode.nodeName.toLowerCase() == 'br' )
  523. this._Range.setEndAfter( oNode ) ;
  524. }
  525. this._UpdateElementInfo() ;
  526. }
  527. },
  528. /**
  529.  * Split the block element for the current range. It deletes the contents
  530.  * of the range and splits the block in the collapsed position, resulting
  531.  * in two sucessive blocks. The range is then positioned in the middle of
  532.  * them.
  533.  *
  534.  * It returns and object with the following properties:
  535.  * - PreviousBlock : a reference to the block element that preceeds
  536.  *   the range after the split.
  537.  * - NextBlock : a reference to the block element that preceeds the
  538.  *   range after the split.
  539.  * - WasStartOfBlock : a boolean indicating that the range was
  540.  *   originaly at the start of the block.
  541.  * - WasEndOfBlock : a boolean indicating that the range was originaly
  542.  *   at the end of the block.
  543.  *
  544.  * If the range was originaly at the start of the block, no split will happen
  545.  * and the PreviousBlock value will be null. The same is valid for the
  546.  * NextBlock value if the range was at the end of the block.
  547.  */
  548. SplitBlock : function()
  549. {
  550. if ( !this._Range )
  551. this.MoveToSelection() ;
  552. // The range boundaries must be in the same "block limit" element.
  553. if ( this.StartBlockLimit == this.EndBlockLimit )
  554. {
  555. // Get the current blocks.
  556. var eStartBlock = this.StartBlock ;
  557. var eEndBlock = this.EndBlock ;
  558. if ( FCKConfig.EnterMode != 'br' )
  559. {
  560. if ( !eStartBlock )
  561. {
  562. eStartBlock = this.FixBlock( true ) ;
  563. eEndBlock = this.EndBlock ; // FixBlock may have fixed the EndBlock too.
  564. }
  565. if ( !eEndBlock )
  566. eEndBlock = this.FixBlock( false ) ;
  567. }
  568. // Get the range position.
  569. var bIsStartOfBlock = ( eStartBlock != null && this.CheckStartOfBlock() ) ;
  570. var bIsEndOfBlock = ( eEndBlock != null && this.CheckEndOfBlock() ) ;
  571. // Delete the current contents.
  572. if ( !this.CheckIsEmpty() )
  573. this.DeleteContents() ;
  574. if ( eStartBlock && eEndBlock && eStartBlock == eEndBlock )
  575. {
  576. if ( bIsEndOfBlock )
  577. {
  578. this.MoveToPosition( eEndBlock, 4 ) ;
  579. eEndBlock = null ;
  580. }
  581. else if ( bIsStartOfBlock )
  582. {
  583. this.MoveToPosition( eStartBlock, 3 ) ;
  584. eStartBlock = null ;
  585. }
  586. else
  587. {
  588. // Extract the contents of the block from the selection point to the end of its contents.
  589. this.SetEnd( eStartBlock, 2 ) ;
  590. var eDocFrag = this.ExtractContents() ;
  591. // Duplicate the block element after it.
  592. eEndBlock = eStartBlock.cloneNode( false ) ;
  593. eEndBlock.removeAttribute( 'id', false ) ;
  594. // Place the extracted contents in the duplicated block.
  595. eDocFrag.AppendTo( eEndBlock ) ;
  596. FCKDomTools.InsertAfterNode( eStartBlock, eEndBlock ) ;
  597. this.MoveToPosition( eStartBlock, 4 ) ;
  598. // In Gecko, the last child node must be a bogus <br>.
  599. // Note: bogus <br> added under <ul> or <ol> would cause lists to be incorrectly rendered.
  600. if ( FCKBrowserInfo.IsGecko &&
  601. ! eStartBlock.nodeName.IEquals( ['ul', 'ol'] ) )
  602. FCKTools.AppendBogusBr( eStartBlock ) ;
  603. }
  604. }
  605. return {
  606. PreviousBlock : eStartBlock,
  607. NextBlock : eEndBlock,
  608. WasStartOfBlock : bIsStartOfBlock,
  609. WasEndOfBlock : bIsEndOfBlock
  610. } ;
  611. }
  612. return null ;
  613. },
  614. // Transform a block without a block tag in a valid block (orphan text in the body or td, usually).
  615. FixBlock : function( isStart )
  616. {
  617. // Bookmark the range so we can restore it later.
  618. var oBookmark = this.CreateBookmark() ;
  619. // Collapse the range to the requested ending boundary.
  620. this.Collapse( isStart ) ;
  621. // Expands it to the block contents.
  622. this.Expand( 'block_contents' ) ;
  623. // Create the fixed block.
  624. var oFixedBlock = this.Window.document.createElement( FCKConfig.EnterMode ) ;
  625. // Move the contents of the temporary range to the fixed block.
  626. this.ExtractContents().AppendTo( oFixedBlock ) ;
  627. FCKDomTools.TrimNode( oFixedBlock ) ;
  628. // Insert the fixed block into the DOM.
  629. this.InsertNode( oFixedBlock ) ;
  630. // Move the range back to the bookmarked place.
  631. this.MoveToBookmark( oBookmark ) ;
  632. return oFixedBlock ;
  633. },
  634. Release : function( preserveWindow )
  635. {
  636. if ( !preserveWindow )
  637. this.Window = null ;
  638. this.StartNode = null ;
  639. this.StartContainer = null ;
  640. this.StartBlock = null ;
  641. this.StartBlockLimit = null ;
  642. this.EndNode = null ;
  643. this.EndContainer = null ;
  644. this.EndBlock = null ;
  645. this.EndBlockLimit = null ;
  646. this._Range = null ;
  647. this._Cache = null ;
  648. },
  649. CheckHasRange : function()
  650. {
  651. return !!this._Range ;
  652. },
  653. GetTouchedStartNode : function()
  654. {
  655. var range = this._Range ;
  656. var container = range.startContainer ;
  657. if ( range.collapsed || container.nodeType != 1 )
  658. return container ;
  659. return container.childNodes[ range.startOffset ] || container ;
  660. },
  661. GetTouchedEndNode : function()
  662. {
  663. var range = this._Range ;
  664. var container = range.endContainer ;
  665. if ( range.collapsed || container.nodeType != 1 )
  666. return container ;
  667. return container.childNodes[ range.endOffset - 1 ] || container ;
  668. }
  669. } ;