fcklistcommands.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.  * Implementation for the "Insert/Remove Ordered/Unordered List" commands.
  22.  */
  23. var FCKListCommand = function( name, tagName )
  24. {
  25. this.Name = name ;
  26. this.TagName = tagName ;
  27. }
  28. FCKListCommand.prototype =
  29. {
  30. GetState : function()
  31. {
  32. // Disabled if not WYSIWYG.
  33. if ( FCK.EditMode != FCK_EDITMODE_WYSIWYG || ! FCK.EditorWindow )
  34. return FCK_TRISTATE_DISABLED ;
  35. // We'll use the style system's convention to determine list state here...
  36. // If the starting block is a descendant of an <ol> or <ul> node, then we're in a list.
  37. var startContainer = FCKSelection.GetBoundaryParentElement( true ) ;
  38. var listNode = startContainer ;
  39. while ( listNode )
  40. {
  41. if ( listNode.nodeName.IEquals( [ 'ul', 'ol' ] ) )
  42. break ;
  43. listNode = listNode.parentNode ;
  44. }
  45. if ( listNode && listNode.nodeName.IEquals( this.TagName ) )
  46. return FCK_TRISTATE_ON ;
  47. else
  48. return FCK_TRISTATE_OFF ;
  49. },
  50. Execute : function()
  51. {
  52. FCKUndo.SaveUndoStep() ;
  53. var doc = FCK.EditorDocument ;
  54. var range = new FCKDomRange( FCK.EditorWindow ) ;
  55. range.MoveToSelection() ;
  56. var state = this.GetState() ;
  57. // Midas lists rule #1 says we can create a list even in an empty document.
  58. // But FCKDomRangeIterator wouldn't run if the document is really empty.
  59. // So create a paragraph if the document is empty and we're going to create a list.
  60. if ( state == FCK_TRISTATE_OFF )
  61. {
  62. FCKDomTools.TrimNode( doc.body ) ;
  63. if ( ! doc.body.firstChild )
  64. {
  65. var paragraph = doc.createElement( 'p' ) ;
  66. doc.body.appendChild( paragraph ) ;
  67. range.MoveToNodeContents( paragraph ) ;
  68. }
  69. }
  70. var bookmark = range.CreateBookmark() ;
  71. // Group the blocks up because there are many cases where multiple lists have to be created,
  72. // or multiple lists have to be cancelled.
  73. var listGroups = [] ;
  74. var markerObj = {} ;
  75. var iterator = new FCKDomRangeIterator( range ) ;
  76. var block ;
  77. iterator.ForceBrBreak = ( state == FCK_TRISTATE_OFF ) ;
  78. var nextRangeExists = true ;
  79. var rangeQueue = null ;
  80. while ( nextRangeExists )
  81. {
  82. while ( ( block = iterator.GetNextParagraph() ) )
  83. {
  84. var path = new FCKElementPath( block ) ;
  85. var listNode = null ;
  86. var processedFlag = false ;
  87. var blockLimit = path.BlockLimit ;
  88. // First, try to group by a list ancestor.
  89. for ( var i = path.Elements.length - 1 ; i >= 0 ; i-- )
  90. {
  91. var el = path.Elements[i] ;
  92. if ( el.nodeName.IEquals( ['ol', 'ul'] ) )
  93. {
  94. // If we've encountered a list inside a block limit
  95. // The last group object of the block limit element should
  96. // no longer be valid. Since paragraphs after the list
  97. // should belong to a different group of paragraphs before
  98. // the list. (Bug #1309)
  99. if ( blockLimit._FCK_ListGroupObject )
  100. blockLimit._FCK_ListGroupObject = null ;
  101. var groupObj = el._FCK_ListGroupObject ;
  102. if ( groupObj )
  103. groupObj.contents.push( block ) ;
  104. else
  105. {
  106. groupObj = { 'root' : el, 'contents' : [ block ] } ;
  107. listGroups.push( groupObj ) ;
  108. FCKDomTools.SetElementMarker( markerObj, el, '_FCK_ListGroupObject', groupObj ) ;
  109. }
  110. processedFlag = true ;
  111. break ;
  112. }
  113. }
  114. if ( processedFlag )
  115. continue ;
  116. // No list ancestor? Group by block limit.
  117. var root = blockLimit ;
  118. if ( root._FCK_ListGroupObject )
  119. root._FCK_ListGroupObject.contents.push( block ) ;
  120. else
  121. {
  122. var groupObj = { 'root' : root, 'contents' : [ block ] } ;
  123. FCKDomTools.SetElementMarker( markerObj, root, '_FCK_ListGroupObject', groupObj ) ;
  124. listGroups.push( groupObj ) ;
  125. }
  126. }
  127. if ( FCKBrowserInfo.IsIE )
  128. nextRangeExists = false ;
  129. else
  130. {
  131. if ( rangeQueue == null )
  132. {
  133. rangeQueue = [] ;
  134. var selectionObject = FCKSelection.GetSelection() ;
  135. if ( selectionObject && listGroups.length == 0 )
  136. rangeQueue.push( selectionObject.getRangeAt( 0 ) ) ;
  137. for ( var i = 1 ; selectionObject && i < selectionObject.rangeCount ; i++ )
  138. rangeQueue.push( selectionObject.getRangeAt( i ) ) ;
  139. }
  140. if ( rangeQueue.length < 1 )
  141. nextRangeExists = false ;
  142. else
  143. {
  144. var internalRange = FCKW3CRange.CreateFromRange( doc, rangeQueue.shift() ) ;
  145. range._Range = internalRange ;
  146. range._UpdateElementInfo() ;
  147. if ( range.StartNode.nodeName.IEquals( 'td' ) )
  148. range.SetStart( range.StartNode, 1 ) ;
  149. if ( range.EndNode.nodeName.IEquals( 'td' ) )
  150. range.SetEnd( range.EndNode, 2 ) ;
  151. iterator = new FCKDomRangeIterator( range ) ;
  152. iterator.ForceBrBreak = ( state == FCK_TRISTATE_OFF ) ;
  153. }
  154. }
  155. }
  156. // Now we have two kinds of list groups, groups rooted at a list, and groups rooted at a block limit element.
  157. // We either have to build lists or remove lists, for removing a list does not makes sense when we are looking
  158. // at the group that's not rooted at lists. So we have three cases to handle.
  159. var listsCreated = [] ;
  160. while ( listGroups.length > 0 )
  161. {
  162. var groupObj = listGroups.shift() ;
  163. if ( state == FCK_TRISTATE_OFF )
  164. {
  165. if ( groupObj.root.nodeName.IEquals( ['ul', 'ol'] ) )
  166. this._ChangeListType( groupObj, markerObj, listsCreated ) ;
  167. else
  168. this._CreateList( groupObj, listsCreated ) ;
  169. }
  170. else if ( state == FCK_TRISTATE_ON && groupObj.root.nodeName.IEquals( ['ul', 'ol'] ) )
  171. this._RemoveList( groupObj, markerObj ) ;
  172. }
  173. // For all new lists created, merge adjacent, same type lists.
  174. for ( var i = 0 ; i < listsCreated.length ; i++ )
  175. {
  176. var listNode = listsCreated[i] ;
  177. var stopFlag = false ;
  178. var currentNode = listNode ;
  179. while ( ! stopFlag )
  180. {
  181. currentNode = currentNode.nextSibling ;
  182. if ( currentNode && currentNode.nodeType == 3 && currentNode.nodeValue.search( /^[nrt ]*$/ ) == 0 )
  183. continue ;
  184. stopFlag = true ;
  185. }
  186. if ( currentNode && currentNode.nodeName.IEquals( this.TagName ) )
  187. {
  188. currentNode.parentNode.removeChild( currentNode ) ;
  189. while ( currentNode.firstChild )
  190. listNode.appendChild( currentNode.removeChild( currentNode.firstChild ) ) ;
  191. }
  192. stopFlag = false ;
  193. currentNode = listNode ;
  194. while ( ! stopFlag )
  195. {
  196. currentNode = currentNode.previousSibling ;
  197. if ( currentNode && currentNode.nodeType == 3 && currentNode.nodeValue.search( /^[nrt ]*$/ ) == 0 )
  198. continue ;
  199. stopFlag = true ;
  200. }
  201. if ( currentNode && currentNode.nodeName.IEquals( this.TagName ) )
  202. {
  203. currentNode.parentNode.removeChild( currentNode ) ;
  204. while ( currentNode.lastChild )
  205. listNode.insertBefore( currentNode.removeChild( currentNode.lastChild ),
  206.        listNode.firstChild ) ;
  207. }
  208. }
  209. // Clean up, restore selection and update toolbar button states.
  210. FCKDomTools.ClearAllMarkers( markerObj ) ;
  211. range.MoveToBookmark( bookmark ) ;
  212. range.Select() ;
  213. FCK.Focus() ;
  214. FCK.Events.FireEvent( 'OnSelectionChange' ) ;
  215. },
  216. _ChangeListType : function( groupObj, markerObj, listsCreated )
  217. {
  218. // This case is easy...
  219. // 1. Convert the whole list into a one-dimensional array.
  220. // 2. Change the list type by modifying the array.
  221. // 3. Recreate the whole list by converting the array to a list.
  222. // 4. Replace the original list with the recreated list.
  223. var listArray = FCKDomTools.ListToArray( groupObj.root, markerObj ) ;
  224. var selectedListItems = [] ;
  225. for ( var i = 0 ; i < groupObj.contents.length ; i++ )
  226. {
  227. var itemNode = groupObj.contents[i] ;
  228. itemNode = FCKTools.GetElementAscensor( itemNode, 'li' ) ;
  229. if ( ! itemNode || itemNode._FCK_ListItem_Processed )
  230. continue ;
  231. selectedListItems.push( itemNode ) ;
  232. FCKDomTools.SetElementMarker( markerObj, itemNode, '_FCK_ListItem_Processed', true ) ;
  233. }
  234. var fakeParent = FCKTools.GetElementDocument( groupObj.root ).createElement( this.TagName ) ;
  235. for ( var i = 0 ; i < selectedListItems.length ; i++ )
  236. {
  237. var listIndex = selectedListItems[i]._FCK_ListArray_Index ;
  238. listArray[listIndex].parent = fakeParent ;
  239. }
  240. var newList = FCKDomTools.ArrayToList( listArray, markerObj ) ;
  241. for ( var i = 0 ; i < newList.listNode.childNodes.length ; i++ )
  242. {
  243. if ( newList.listNode.childNodes[i].nodeName.IEquals( this.TagName ) )
  244. listsCreated.push( newList.listNode.childNodes[i] ) ;
  245. }
  246. groupObj.root.parentNode.replaceChild( newList.listNode, groupObj.root ) ;
  247. },
  248. _CreateList : function( groupObj, listsCreated )
  249. {
  250. var contents = groupObj.contents ;
  251. var doc = FCKTools.GetElementDocument( groupObj.root ) ;
  252. var listContents = [] ;
  253. // It is possible to have the contents returned by DomRangeIterator to be the same as the root.
  254. // e.g. when we're running into table cells.
  255. // In such a case, enclose the childNodes of contents[0] into a <div>.
  256. if ( contents.length == 1 && contents[0] == groupObj.root )
  257. {
  258. var divBlock = doc.createElement( 'div' );
  259. while ( contents[0].firstChild )
  260. divBlock.appendChild( contents[0].removeChild( contents[0].firstChild ) ) ;
  261. contents[0].appendChild( divBlock ) ;
  262. contents[0] = divBlock ;
  263. }
  264. // Calculate the common parent node of all content blocks.
  265. var commonParent = groupObj.contents[0].parentNode ;
  266. for ( var i = 0 ; i < contents.length ; i++ )
  267. commonParent = FCKDomTools.GetCommonParents( commonParent, contents[i].parentNode ).pop() ;
  268. // We want to insert things that are in the same tree level only, so calculate the contents again
  269. // by expanding the selected blocks to the same tree level.
  270. for ( var i = 0 ; i < contents.length ; i++ )
  271. {
  272. var contentNode = contents[i] ;
  273. while ( contentNode.parentNode )
  274. {
  275. if ( contentNode.parentNode == commonParent )
  276. {
  277. listContents.push( contentNode ) ;
  278. break ;
  279. }
  280. contentNode = contentNode.parentNode ;
  281. }
  282. }
  283. if ( listContents.length < 1 )
  284. return ;
  285. // Insert the list to the DOM tree.
  286. var insertAnchor = listContents[listContents.length - 1].nextSibling ;
  287. var listNode = doc.createElement( this.TagName ) ;
  288. listsCreated.push( listNode ) ;
  289. while ( listContents.length )
  290. {
  291. var contentBlock = listContents.shift() ;
  292. var docFrag = doc.createDocumentFragment() ;
  293. while ( contentBlock.firstChild )
  294. docFrag.appendChild( contentBlock.removeChild( contentBlock.firstChild ) ) ;
  295. contentBlock.parentNode.removeChild( contentBlock ) ;
  296. var listItem = doc.createElement( 'li' ) ;
  297. listItem.appendChild( docFrag ) ;
  298. listNode.appendChild( listItem ) ;
  299. }
  300. commonParent.insertBefore( listNode, insertAnchor ) ;
  301. },
  302. _RemoveList : function( groupObj, markerObj )
  303. {
  304. // This is very much like the change list type operation.
  305. // Except that we're changing the selected items' indent to -1 in the list array.
  306. var listArray = FCKDomTools.ListToArray( groupObj.root, markerObj ) ;
  307. var selectedListItems = [] ;
  308. for ( var i = 0 ; i < groupObj.contents.length ; i++ )
  309. {
  310. var itemNode = groupObj.contents[i] ;
  311. itemNode = FCKTools.GetElementAscensor( itemNode, 'li' ) ;
  312. if ( ! itemNode || itemNode._FCK_ListItem_Processed )
  313. continue ;
  314. selectedListItems.push( itemNode ) ;
  315. FCKDomTools.SetElementMarker( markerObj, itemNode, '_FCK_ListItem_Processed', true ) ;
  316. }
  317. var lastListIndex = null ;
  318. for ( var i = 0 ; i < selectedListItems.length ; i++ )
  319. {
  320. var listIndex = selectedListItems[i]._FCK_ListArray_Index ;
  321. listArray[listIndex].indent = -1 ;
  322. lastListIndex = listIndex ;
  323. }
  324. // After cutting parts of the list out with indent=-1, we still have to maintain the array list
  325. // model's nextItem.indent <= currentItem.indent + 1 invariant. Otherwise the array model of the
  326. // list cannot be converted back to a real DOM list.
  327. for ( var i = lastListIndex + 1; i < listArray.length ; i++ )
  328. {
  329. if ( listArray[i].indent > listArray[i-1].indent + 1 )
  330. {
  331. var indentOffset = listArray[i-1].indent + 1 - listArray[i].indent ;
  332. var oldIndent = listArray[i].indent ;
  333. while ( listArray[i] && listArray[i].indent >= oldIndent)
  334. {
  335. listArray[i].indent += indentOffset ;
  336. i++ ;
  337. }
  338. i-- ;
  339. }
  340. }
  341. var newList = FCKDomTools.ArrayToList( listArray, markerObj ) ;
  342. // If groupObj.root is the last element in its parent, or its nextSibling is a <br>, then we should
  343. // not add a <br> after the final item. So, check for the cases and trim the <br>.
  344. if ( groupObj.root.nextSibling == null || groupObj.root.nextSibling.nodeName.IEquals( 'br' ) )
  345. {
  346. if ( newList.listNode.lastChild.nodeName.IEquals( 'br' ) )
  347. newList.listNode.removeChild( newList.listNode.lastChild ) ;
  348. }
  349. groupObj.root.parentNode.replaceChild( newList.listNode, groupObj.root ) ;
  350. }
  351. };