fckdomrangeiterator.js
上传用户:dbstep
上传日期:2022-08-06
资源大小:2803k
文件大小:10k
源码类别:

WEB源码(ASP,PHP,...)

开发平台:

ASP/ASPX

  1. /*
  2.  * FCKeditor - The text editor for Internet - http://www.fckeditor.net
  3.  * Copyright (C) 2003-2009 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.  * This class can be used to interate through nodes inside a range.
  22.  *
  23.  * During interation, the provided range can become invalid, due to document
  24.  * mutations, so CreateBookmark() used to restore it after processing, if
  25.  * needed.
  26.  */
  27. var FCKDomRangeIterator = function( range )
  28. {
  29. /**
  30.  * The FCKDomRange object that marks the interation boundaries.
  31.  */
  32. this.Range = range ;
  33. /**
  34.  * Indicates that <br> elements must be used as paragraph boundaries.
  35.  */
  36. this.ForceBrBreak = false ;
  37. /**
  38.  * Guarantees that the iterator will always return "real" block elements.
  39.  * If "false", elements like <li>, <th> and <td> are returned. If "true", a
  40.  * dedicated block element block element will be created inside those
  41.  * elements to hold the selected content.
  42.  */
  43. this.EnforceRealBlocks = false ;
  44. }
  45. FCKDomRangeIterator.CreateFromSelection = function( targetWindow )
  46. {
  47. var range = new FCKDomRange( targetWindow ) ;
  48. range.MoveToSelection() ;
  49. return new FCKDomRangeIterator( range ) ;
  50. }
  51. FCKDomRangeIterator.prototype =
  52. {
  53. /**
  54.  * Get the next paragraph element. It automatically breaks the document
  55.  * when necessary to generate block elements for the paragraphs.
  56.  */
  57. GetNextParagraph : function()
  58. {
  59. // The block element to be returned.
  60. var block ;
  61. // The range object used to identify the paragraph contents.
  62. var range ;
  63. // Indicated that the current element in the loop is the last one.
  64. var isLast ;
  65. // Instructs to cleanup remaining BRs.
  66. var removePreviousBr ;
  67. var removeLastBr ;
  68. var boundarySet = this.ForceBrBreak ? FCKListsLib.ListBoundaries : FCKListsLib.BlockBoundaries ;
  69. // This is the first iteration. Let's initialize it.
  70. if ( !this._LastNode )
  71. {
  72. var range = this.Range.Clone() ;
  73. range.Expand( this.ForceBrBreak ? 'list_contents' : 'block_contents' ) ;
  74. this._NextNode = range.GetTouchedStartNode() ;
  75. this._LastNode = range.GetTouchedEndNode() ;
  76. // Let's reuse this variable.
  77. range = null ;
  78. }
  79. var currentNode = this._NextNode ;
  80. var lastNode = this._LastNode ;
  81. this._NextNode = null ;
  82. while ( currentNode )
  83. {
  84. // closeRange indicates that a paragraph boundary has been found,
  85. // so the range can be closed.
  86. var closeRange = false ;
  87. // includeNode indicates that the current node is good to be part
  88. // of the range. By default, any non-element node is ok for it.
  89. var includeNode = ( currentNode.nodeType != 1 ) ;
  90. var continueFromSibling = false ;
  91. // If it is an element node, let's check if it can be part of the
  92. // range.
  93. if ( !includeNode )
  94. {
  95. var nodeName = currentNode.nodeName.toLowerCase() ;
  96. if ( boundarySet[ nodeName ] && ( !FCKBrowserInfo.IsIE || currentNode.scopeName == 'HTML' ) )
  97. {
  98. // <br> boundaries must be part of the range. It will
  99. // happen only if ForceBrBreak.
  100. if ( nodeName == 'br' )
  101. includeNode = true ;
  102. else if ( !range && currentNode.childNodes.length == 0 && nodeName != 'hr' )
  103. {
  104. // If we have found an empty block, and haven't started
  105. // the range yet, it means we must return this block.
  106. block = currentNode ;
  107. isLast = currentNode == lastNode ;
  108. break ;
  109. }
  110. // The range must finish right before the boundary,
  111. // including possibly skipped empty spaces. (#1603)
  112. if ( range )
  113. {
  114. range.SetEnd( currentNode, 3, true ) ;
  115. // The found boundary must be set as the next one at this
  116. // point. (#1717)
  117. if ( nodeName != 'br' )
  118. this._NextNode = FCKDomTools.GetNextSourceNode( currentNode, true, null, lastNode ) || currentNode ;
  119. }
  120. closeRange = true ;
  121. }
  122. else
  123. {
  124. // If we have child nodes, let's check them.
  125. if ( currentNode.firstChild )
  126. {
  127. // If we don't have a range yet, let's start it.
  128. if ( !range )
  129. {
  130. range = new FCKDomRange( this.Range.Window ) ;
  131. range.SetStart( currentNode, 3, true ) ;
  132. }
  133. currentNode = currentNode.firstChild ;
  134. continue ;
  135. }
  136. includeNode = true ;
  137. }
  138. }
  139. else if ( currentNode.nodeType == 3 )
  140. {
  141. // Ignore normal whitespaces (i.e. not including &nbsp; or
  142. // other unicode whitespaces) before/after a block node.
  143. if ( /^[rnt ]+$/.test( currentNode.nodeValue ) )
  144. includeNode = false ;
  145. }
  146. // The current node is good to be part of the range and we are
  147. // starting a new range, initialize it first.
  148. if ( includeNode && !range )
  149. {
  150. range = new FCKDomRange( this.Range.Window ) ;
  151. range.SetStart( currentNode, 3, true ) ;
  152. }
  153. // The last node has been found.
  154. isLast = ( ( !closeRange || includeNode ) && currentNode == lastNode ) ;
  155. // isLast = ( currentNode == lastNode && ( currentNode.nodeType != 1 || currentNode.childNodes.length == 0 ) ) ;
  156. // If we are in an element boundary, let's check if it is time
  157. // to close the range, otherwise we include the parent within it.
  158. if ( range && !closeRange )
  159. {
  160. while ( !currentNode.nextSibling && !isLast )
  161. {
  162. var parentNode = currentNode.parentNode ;
  163. if ( boundarySet[ parentNode.nodeName.toLowerCase() ] )
  164. {
  165. closeRange = true ;
  166. isLast = isLast || ( parentNode == lastNode ) ;
  167. break ;
  168. }
  169. currentNode = parentNode ;
  170. includeNode = true ;
  171. isLast = ( currentNode == lastNode ) ;
  172. continueFromSibling = true ;
  173. }
  174. }
  175. // Now finally include the node.
  176. if ( includeNode )
  177. range.SetEnd( currentNode, 4, true ) ;
  178. // We have found a block boundary. Let's close the range and move out of the
  179. // loop.
  180. if ( ( closeRange || isLast ) && range )
  181. {
  182. range._UpdateElementInfo() ;
  183. if ( range.StartNode == range.EndNode
  184. && range.StartNode.parentNode == range.StartBlockLimit
  185. && range.StartNode.getAttribute && range.StartNode.getAttribute( '_fck_bookmark' ) )
  186. range = null ;
  187. else
  188. break ;
  189. }
  190. if ( isLast )
  191. break ;
  192. currentNode = FCKDomTools.GetNextSourceNode( currentNode, continueFromSibling, null, lastNode ) ;
  193. }
  194. // Now, based on the processed range, look for (or create) the block to be returned.
  195. if ( !block )
  196. {
  197. // If no range has been found, this is the end.
  198. if ( !range )
  199. {
  200. this._NextNode = null ;
  201. return null ;
  202. }
  203. block = range.StartBlock ;
  204. if ( !block
  205. && !this.EnforceRealBlocks
  206. && range.StartBlockLimit.nodeName.IEquals( 'DIV', 'TH', 'TD' )
  207. && range.CheckStartOfBlock()
  208. && range.CheckEndOfBlock() )
  209. {
  210. block = range.StartBlockLimit ;
  211. }
  212. else if ( !block || ( this.EnforceRealBlocks && block.nodeName.toLowerCase() == 'li' ) )
  213. {
  214. // Create the fixed block.
  215. block = this.Range.Window.document.createElement( FCKConfig.EnterMode == 'p' ? 'p' : 'div' ) ;
  216. // Move the contents of the temporary range to the fixed block.
  217. range.ExtractContents().AppendTo( block ) ;
  218. FCKDomTools.TrimNode( block ) ;
  219. // Insert the fixed block into the DOM.
  220. range.InsertNode( block ) ;
  221. removePreviousBr = true ;
  222. removeLastBr = true ;
  223. }
  224. else if ( block.nodeName.toLowerCase() != 'li' )
  225. {
  226. // If the range doesn't includes the entire contents of the
  227. // block, we must split it, isolating the range in a dedicated
  228. // block.
  229. if ( !range.CheckStartOfBlock() || !range.CheckEndOfBlock() )
  230. {
  231. // The resulting block will be a clone of the current one.
  232. block = block.cloneNode( false ) ;
  233. // Extract the range contents, moving it to the new block.
  234. range.ExtractContents().AppendTo( block ) ;
  235. FCKDomTools.TrimNode( block ) ;
  236. // Split the block. At this point, the range will be in the
  237. // right position for our intents.
  238. var splitInfo = range.SplitBlock() ;
  239. removePreviousBr = !splitInfo.WasStartOfBlock ;
  240. removeLastBr = !splitInfo.WasEndOfBlock ;
  241. // Insert the new block into the DOM.
  242. range.InsertNode( block ) ;
  243. }
  244. }
  245. else if ( !isLast )
  246. {
  247. // LIs are returned as is, with all their children (due to the
  248. // nested lists). But, the next node is the node right after
  249. // the current range, which could be an <li> child (nested
  250. // lists) or the next sibling <li>.
  251. this._NextNode = block == lastNode ? null : FCKDomTools.GetNextSourceNode( range.EndNode, true, null, lastNode ) ;
  252. return block ;
  253. }
  254. }
  255. if ( removePreviousBr )
  256. {
  257. var previousSibling = block.previousSibling ;
  258. if ( previousSibling && previousSibling.nodeType == 1 )
  259. {
  260. if ( previousSibling.nodeName.toLowerCase() == 'br' )
  261. previousSibling.parentNode.removeChild( previousSibling ) ;
  262. else if ( previousSibling.lastChild && previousSibling.lastChild.nodeName.IEquals( 'br' ) )
  263. previousSibling.removeChild( previousSibling.lastChild ) ;
  264. }
  265. }
  266. if ( removeLastBr )
  267. {
  268. var lastChild = block.lastChild ;
  269. if ( lastChild && lastChild.nodeType == 1 && lastChild.nodeName.toLowerCase() == 'br' )
  270. block.removeChild( lastChild ) ;
  271. }
  272. // Get a reference for the next element. This is important because the
  273. // above block can be removed or changed, so we can rely on it for the
  274. // next interation.
  275. if ( !this._NextNode )
  276. this._NextNode = ( isLast || block == lastNode ) ? null : FCKDomTools.GetNextSourceNode( block, true, null, lastNode ) ;
  277. return block ;
  278. }
  279. } ;