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

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 partially implements the W3C DOM Range for browser that don't
  22.  * support the standards (like IE):
  23.  * http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html
  24.  */
  25. var FCKW3CRange = function( parentDocument )
  26. {
  27. this._Document = parentDocument ;
  28. this.startContainer = null ;
  29. this.startOffset = null ;
  30. this.endContainer = null ;
  31. this.endOffset = null ;
  32. this.collapsed = true ;
  33. }
  34. FCKW3CRange.CreateRange = function( parentDocument )
  35. {
  36. // We could opt to use the Range implementation of the browsers. The problem
  37. // is that every browser have different bugs on their implementations,
  38. // mostly related to different interpretations of the W3C specifications.
  39. // So, for now, let's use our implementation and pray for browsers fixings
  40. // soon. Otherwise will go crazy on trying to find out workarounds.
  41. /*
  42. // Get the browser implementation of the range, if available.
  43. if ( parentDocument.createRange )
  44. {
  45. var range = parentDocument.createRange() ;
  46. if ( typeof( range.startContainer ) != 'undefined' )
  47. return range ;
  48. }
  49. */
  50. return new FCKW3CRange( parentDocument ) ;
  51. }
  52. FCKW3CRange.CreateFromRange = function( parentDocument, sourceRange )
  53. {
  54. var range = FCKW3CRange.CreateRange( parentDocument ) ;
  55. range.setStart( sourceRange.startContainer, sourceRange.startOffset ) ;
  56. range.setEnd( sourceRange.endContainer, sourceRange.endOffset ) ;
  57. return range ;
  58. }
  59. FCKW3CRange.prototype =
  60. {
  61. _UpdateCollapsed : function()
  62. {
  63.       this.collapsed = ( this.startContainer == this.endContainer && this.startOffset == this.endOffset ) ;
  64. },
  65. // W3C requires a check for the new position. If it is after the end
  66. // boundary, the range should be collapsed to the new start. It seams we
  67. // will not need this check for our use of this class so we can ignore it for now.
  68. setStart : function( refNode, offset )
  69. {
  70. this.startContainer = refNode ;
  71. this.startOffset = offset ;
  72. if ( !this.endContainer )
  73. {
  74. this.endContainer = refNode ;
  75. this.endOffset = offset ;
  76. }
  77. this._UpdateCollapsed() ;
  78. },
  79. // W3C requires a check for the new position. If it is before the start
  80. // boundary, the range should be collapsed to the new end. It seams we
  81. // will not need this check for our use of this class so we can ignore it for now.
  82. setEnd : function( refNode, offset )
  83. {
  84. this.endContainer = refNode ;
  85. this.endOffset = offset ;
  86. if ( !this.startContainer )
  87. {
  88. this.startContainer = refNode ;
  89. this.startOffset = offset ;
  90. }
  91. this._UpdateCollapsed() ;
  92. },
  93. setStartAfter : function( refNode )
  94. {
  95. this.setStart( refNode.parentNode, FCKDomTools.GetIndexOf( refNode ) + 1 ) ;
  96. },
  97. setStartBefore : function( refNode )
  98. {
  99. this.setStart( refNode.parentNode, FCKDomTools.GetIndexOf( refNode ) ) ;
  100. },
  101. setEndAfter : function( refNode )
  102. {
  103. this.setEnd( refNode.parentNode, FCKDomTools.GetIndexOf( refNode ) + 1 ) ;
  104. },
  105. setEndBefore : function( refNode )
  106. {
  107. this.setEnd( refNode.parentNode, FCKDomTools.GetIndexOf( refNode ) ) ;
  108. },
  109. collapse : function( toStart )
  110. {
  111. if ( toStart )
  112. {
  113. this.endContainer = this.startContainer ;
  114. this.endOffset = this.startOffset ;
  115. }
  116. else
  117. {
  118. this.startContainer = this.endContainer ;
  119. this.startOffset = this.endOffset ;
  120. }
  121. this.collapsed = true ;
  122. },
  123. selectNodeContents : function( refNode )
  124. {
  125. this.setStart( refNode, 0 ) ;
  126. this.setEnd( refNode, refNode.nodeType == 3 ? refNode.data.length : refNode.childNodes.length ) ;
  127. },
  128. insertNode : function( newNode )
  129. {
  130. var startContainer = this.startContainer ;
  131. var startOffset = this.startOffset ;
  132. // If we are in a text node.
  133. if ( startContainer.nodeType == 3 )
  134. {
  135. startContainer.splitText( startOffset ) ;
  136. // Check if it is necessary to update the end boundary.
  137. if ( startContainer == this.endContainer )
  138. this.setEnd( startContainer.nextSibling, this.endOffset - this.startOffset ) ;
  139. // Insert the new node it after the text node.
  140. FCKDomTools.InsertAfterNode( startContainer, newNode ) ;
  141. return ;
  142. }
  143. else
  144. {
  145. // Simply insert the new node before the current start node.
  146. startContainer.insertBefore( newNode, startContainer.childNodes[ startOffset ] || null ) ;
  147. // Check if it is necessary to update the end boundary.
  148. if ( startContainer == this.endContainer )
  149. {
  150. this.endOffset++ ;
  151. this.collapsed = false ;
  152. }
  153. }
  154. },
  155. deleteContents : function()
  156. {
  157. if ( this.collapsed )
  158. return ;
  159. this._ExecContentsAction( 0 ) ;
  160. },
  161. extractContents : function()
  162. {
  163. var docFrag = new FCKDocumentFragment( this._Document ) ;
  164. if ( !this.collapsed )
  165. this._ExecContentsAction( 1, docFrag ) ;
  166. return docFrag ;
  167. },
  168. // The selection may be lost when cloning (due to the splitText() call).
  169. cloneContents : function()
  170. {
  171. var docFrag = new FCKDocumentFragment( this._Document ) ;
  172. if ( !this.collapsed )
  173. this._ExecContentsAction( 2, docFrag ) ;
  174. return docFrag ;
  175. },
  176. _ExecContentsAction : function( action, docFrag )
  177. {
  178. var startNode = this.startContainer ;
  179. var endNode = this.endContainer ;
  180. var startOffset = this.startOffset ;
  181. var endOffset = this.endOffset ;
  182. var removeStartNode = false ;
  183. var removeEndNode = false ;
  184. // Check the start and end nodes and make the necessary removals or changes.
  185. // Start from the end, otherwise DOM mutations (splitText) made in the
  186. // start boundary may interfere on the results here.
  187. // For text containers, we must simply split the node and point to the
  188. // second part. The removal will be handled by the rest of the code .
  189. if ( endNode.nodeType == 3 )
  190. endNode = endNode.splitText( endOffset ) ;
  191. else
  192. {
  193. // If the end container has children and the offset is pointing
  194. // to a child, then we should start from it.
  195. if ( endNode.childNodes.length > 0 )
  196. {
  197. // If the offset points after the last node.
  198. if ( endOffset > endNode.childNodes.length - 1 )
  199. {
  200. // Let's create a temporary node and mark it for removal.
  201. endNode = FCKDomTools.InsertAfterNode( endNode.lastChild, this._Document.createTextNode('') ) ;
  202. removeEndNode = true ;
  203. }
  204. else
  205. endNode = endNode.childNodes[ endOffset ] ;
  206. }
  207. }
  208. // For text containers, we must simply split the node. The removal will
  209. // be handled by the rest of the code .
  210. if ( startNode.nodeType == 3 )
  211. {
  212. startNode.splitText( startOffset ) ;
  213. // In cases the end node is the same as the start node, the above
  214. // splitting will also split the end, so me must move the end to
  215. // the second part of the split.
  216. if ( startNode == endNode )
  217. endNode = startNode.nextSibling ;
  218. }
  219. else
  220. {
  221. // If the start container has children and the offset is pointing
  222. // to a child, then we should start from its previous sibling.
  223. // If the offset points to the first node, we don't have a
  224. // sibling, so let's use the first one, but mark it for removal.
  225. if ( startOffset == 0 )
  226. {
  227. // Let's create a temporary node and mark it for removal.
  228. startNode = startNode.insertBefore( this._Document.createTextNode(''), startNode.firstChild ) ;
  229. removeStartNode = true ;
  230. }
  231. else if ( startOffset > startNode.childNodes.length - 1 )
  232. {
  233. // Let's create a temporary node and mark it for removal.
  234. startNode = startNode.appendChild( this._Document.createTextNode('') ) ;
  235. removeStartNode = true ;
  236. }
  237. else
  238. startNode = startNode.childNodes[ startOffset ].previousSibling ;
  239. }
  240. // Get the parent nodes tree for the start and end boundaries.
  241. var startParents = FCKDomTools.GetParents( startNode ) ;
  242. var endParents = FCKDomTools.GetParents( endNode ) ;
  243. // Compare them, to find the top most siblings.
  244. var i, topStart, topEnd ;
  245. for ( i = 0 ; i < startParents.length ; i++ )
  246. {
  247. topStart = startParents[i] ;
  248. topEnd = endParents[i] ;
  249. // The compared nodes will match until we find the top most
  250. // siblings (different nodes that have the same parent).
  251. // "i" will hold the index in the parents array for the top
  252. // most element.
  253. if ( topStart != topEnd )
  254. break ;
  255. }
  256. var clone, levelStartNode, levelClone, currentNode, currentSibling ;
  257. if ( docFrag )
  258. clone = docFrag.RootNode ;
  259. // Remove all successive sibling nodes for every node in the
  260. // startParents tree.
  261. for ( var j = i ; j < startParents.length ; j++ )
  262. {
  263. levelStartNode = startParents[j] ;
  264. // For Extract and Clone, we must clone this level.
  265. if ( clone && levelStartNode != startNode ) // action = 0 = Delete
  266. levelClone = clone.appendChild( levelStartNode.cloneNode( levelStartNode == startNode ) ) ;
  267. currentNode = levelStartNode.nextSibling ;
  268. while( currentNode )
  269. {
  270. // Stop processing when the current node matches a node in the
  271. // endParents tree or if it is the endNode.
  272. if ( currentNode == endParents[j] || currentNode == endNode )
  273. break ;
  274. // Cache the next sibling.
  275. currentSibling = currentNode.nextSibling ;
  276. // If cloning, just clone it.
  277. if ( action == 2 ) // 2 = Clone
  278. clone.appendChild( currentNode.cloneNode( true ) ) ;
  279. else
  280. {
  281. // Both Delete and Extract will remove the node.
  282. currentNode.parentNode.removeChild( currentNode ) ;
  283. // When Extracting, move the removed node to the docFrag.
  284. if ( action == 1 ) // 1 = Extract
  285. clone.appendChild( currentNode ) ;
  286. }
  287. currentNode = currentSibling ;
  288. }
  289. if ( clone )
  290. clone = levelClone ;
  291. }
  292. if ( docFrag )
  293. clone = docFrag.RootNode ;
  294. // Remove all previous sibling nodes for every node in the
  295. // endParents tree.
  296. for ( var k = i ; k < endParents.length ; k++ )
  297. {
  298. levelStartNode = endParents[k] ;
  299. // For Extract and Clone, we must clone this level.
  300. if ( action > 0 && levelStartNode != endNode ) // action = 0 = Delete
  301. levelClone = clone.appendChild( levelStartNode.cloneNode( levelStartNode == endNode ) ) ;
  302. // The processing of siblings may have already been done by the parent.
  303. if ( !startParents[k] || levelStartNode.parentNode != startParents[k].parentNode )
  304. {
  305. currentNode = levelStartNode.previousSibling ;
  306. while( currentNode )
  307. {
  308. // Stop processing when the current node matches a node in the
  309. // startParents tree or if it is the startNode.
  310. if ( currentNode == startParents[k] || currentNode == startNode )
  311. break ;
  312. // Cache the next sibling.
  313. currentSibling = currentNode.previousSibling ;
  314. // If cloning, just clone it.
  315. if ( action == 2 ) // 2 = Clone
  316. clone.insertBefore( currentNode.cloneNode( true ), clone.firstChild ) ;
  317. else
  318. {
  319. // Both Delete and Extract will remove the node.
  320. currentNode.parentNode.removeChild( currentNode ) ;
  321. // When Extracting, mode the removed node to the docFrag.
  322. if ( action == 1 ) // 1 = Extract
  323. clone.insertBefore( currentNode, clone.firstChild ) ;
  324. }
  325. currentNode = currentSibling ;
  326. }
  327. }
  328. if ( clone )
  329. clone = levelClone ;
  330. }
  331. if ( action == 2 ) // 2 = Clone.
  332. {
  333. // No changes in the DOM should be done, so fix the split text (if any).
  334. var startTextNode = this.startContainer ;
  335. if ( startTextNode.nodeType == 3 )
  336. {
  337. startTextNode.data += startTextNode.nextSibling.data ;
  338. startTextNode.parentNode.removeChild( startTextNode.nextSibling ) ;
  339. }
  340. var endTextNode = this.endContainer ;
  341. if ( endTextNode.nodeType == 3 && endTextNode.nextSibling )
  342. {
  343. endTextNode.data += endTextNode.nextSibling.data ;
  344. endTextNode.parentNode.removeChild( endTextNode.nextSibling ) ;
  345. }
  346. }
  347. else
  348. {
  349. // Collapse the range.
  350. // If a node has been partially selected, collapse the range between
  351. // topStart and topEnd. Otherwise, simply collapse it to the start. (W3C specs).
  352. if ( topStart && topEnd && ( startNode.parentNode != topStart.parentNode || endNode.parentNode != topEnd.parentNode ) )
  353. {
  354. var endIndex = FCKDomTools.GetIndexOf( topEnd ) ;
  355. // If the start node is to be removed, we must correct the
  356. // index to reflect the removal.
  357. if ( removeStartNode && topEnd.parentNode == startNode.parentNode )
  358. endIndex-- ;
  359. this.setStart( topEnd.parentNode, endIndex ) ;
  360. }
  361. // Collapse it to the start.
  362. this.collapse( true ) ;
  363. }
  364. // Cleanup any marked node.
  365. if( removeStartNode )
  366. startNode.parentNode.removeChild( startNode ) ;
  367. if( removeEndNode && endNode.parentNode )
  368. endNode.parentNode.removeChild( endNode ) ;
  369. },
  370. cloneRange : function()
  371. {
  372. return FCKW3CRange.CreateFromRange( this._Document, this ) ;
  373. }
  374. } ;