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

数据库编程

开发平台:

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.  * Controls the [Enter] keystroke behavior in a document.
  22.  */
  23. /*
  24.  * Constructor.
  25.  * @targetDocument : the target document.
  26.  * @enterMode : the behavior for the <Enter> keystroke.
  27.  * May be "p", "div", "br". Default is "p".
  28.  * @shiftEnterMode : the behavior for the <Shift>+<Enter> keystroke.
  29.  * May be "p", "div", "br". Defaults to "br".
  30.  */
  31. var FCKEnterKey = function( targetWindow, enterMode, shiftEnterMode, tabSpaces )
  32. {
  33. this.Window = targetWindow ;
  34. this.EnterMode = enterMode || 'p' ;
  35. this.ShiftEnterMode = shiftEnterMode || 'br' ;
  36. // Setup the Keystroke Handler.
  37. var oKeystrokeHandler = new FCKKeystrokeHandler( false ) ;
  38. oKeystrokeHandler._EnterKey = this ;
  39. oKeystrokeHandler.OnKeystroke = FCKEnterKey_OnKeystroke ;
  40. oKeystrokeHandler.SetKeystrokes( [
  41. [ 13 , 'Enter' ],
  42. [ SHIFT + 13, 'ShiftEnter' ],
  43. [ 9  , 'Tab' ],
  44. [ 8 , 'Backspace' ],
  45. [ CTRL + 8 , 'CtrlBackspace' ],
  46. [ 46 , 'Delete' ]
  47. ] ) ;
  48. if ( tabSpaces > 0 )
  49. {
  50. this.TabText = '' ;
  51. while ( tabSpaces-- > 0 )
  52. this.TabText += 'xa0' ;
  53. }
  54. oKeystrokeHandler.AttachToElement( targetWindow.document ) ;
  55. }
  56. function FCKEnterKey_OnKeystroke(  keyCombination, keystrokeValue )
  57. {
  58. var oEnterKey = this._EnterKey ;
  59. try
  60. {
  61. switch ( keystrokeValue )
  62. {
  63. case 'Enter' :
  64. return oEnterKey.DoEnter() ;
  65. break ;
  66. case 'ShiftEnter' :
  67. return oEnterKey.DoShiftEnter() ;
  68. break ;
  69. case 'Backspace' :
  70. return oEnterKey.DoBackspace() ;
  71. break ;
  72. case 'Delete' :
  73. return oEnterKey.DoDelete() ;
  74. break ;
  75. case 'Tab' :
  76. return oEnterKey.DoTab() ;
  77. break ;
  78. case 'CtrlBackspace' :
  79. return oEnterKey.DoCtrlBackspace() ;
  80. break ;
  81. }
  82. }
  83. catch (e)
  84. {
  85. // If for any reason we are not able to handle it, go
  86. // ahead with the browser default behavior.
  87. }
  88. return false ;
  89. }
  90. /*
  91.  * Executes the <Enter> key behavior.
  92.  */
  93. FCKEnterKey.prototype.DoEnter = function( mode, hasShift )
  94. {
  95. // Save an undo snapshot before doing anything
  96. FCKUndo.SaveUndoStep() ;
  97. this._HasShift = ( hasShift === true ) ;
  98. var parentElement = FCKSelection.GetParentElement() ;
  99. var parentPath = new FCKElementPath( parentElement ) ;
  100. var sMode = mode || this.EnterMode ;
  101. if ( sMode == 'br' || parentPath.Block && parentPath.Block.tagName.toLowerCase() == 'pre' )
  102. return this._ExecuteEnterBr() ;
  103. else
  104. return this._ExecuteEnterBlock( sMode ) ;
  105. }
  106. /*
  107.  * Executes the <Shift>+<Enter> key behavior.
  108.  */
  109. FCKEnterKey.prototype.DoShiftEnter = function()
  110. {
  111. return this.DoEnter( this.ShiftEnterMode, true ) ;
  112. }
  113. /*
  114.  * Executes the <Backspace> key behavior.
  115.  */
  116. FCKEnterKey.prototype.DoBackspace = function()
  117. {
  118. var bCustom = false ;
  119. // Get the current selection.
  120. var oRange = new FCKDomRange( this.Window ) ;
  121. oRange.MoveToSelection() ;
  122. // Kludge for #247
  123. if ( FCKBrowserInfo.IsIE && this._CheckIsAllContentsIncluded( oRange, this.Window.document.body ) )
  124. {
  125. this._FixIESelectAllBug( oRange ) ;
  126. return true ;
  127. }
  128. var isCollapsed = oRange.CheckIsCollapsed() ;
  129. if ( !isCollapsed )
  130. {
  131. // Bug #327, Backspace with an img selection would activate the default action in IE.
  132. // Let's override that with our logic here.
  133. if ( FCKBrowserInfo.IsIE && this.Window.document.selection.type.toLowerCase() == "control" )
  134. {
  135. var controls = this.Window.document.selection.createRange() ;
  136. for ( var i = controls.length - 1 ; i >= 0 ; i-- )
  137. {
  138. var el = controls.item( i ) ;
  139. el.parentNode.removeChild( el ) ;
  140. }
  141. return true ;
  142. }
  143. return false ;
  144. }
  145. var oStartBlock = oRange.StartBlock ;
  146. var oEndBlock = oRange.EndBlock ;
  147. // The selection boundaries must be in the same "block limit" element
  148. if ( oRange.StartBlockLimit == oRange.EndBlockLimit && oStartBlock && oEndBlock )
  149. {
  150. if ( !isCollapsed )
  151. {
  152. var bEndOfBlock = oRange.CheckEndOfBlock() ;
  153. oRange.DeleteContents() ;
  154. if ( oStartBlock != oEndBlock )
  155. {
  156. oRange.SetStart(oEndBlock,1) ;
  157. oRange.SetEnd(oEndBlock,1) ;
  158. // if ( bEndOfBlock )
  159. // oEndBlock.parentNode.removeChild( oEndBlock ) ;
  160. }
  161. oRange.Select() ;
  162. bCustom = ( oStartBlock == oEndBlock ) ;
  163. }
  164. if ( oRange.CheckStartOfBlock() )
  165. {
  166. var oCurrentBlock = oRange.StartBlock ;
  167. var ePrevious = FCKDomTools.GetPreviousSourceElement( oCurrentBlock, true, [ 'BODY', oRange.StartBlockLimit.nodeName ], ['UL','OL'] ) ;
  168. bCustom = this._ExecuteBackspace( oRange, ePrevious, oCurrentBlock ) ;
  169. }
  170. else if ( FCKBrowserInfo.IsGeckoLike )
  171. {
  172. // Firefox and Opera (#1095) loose the selection when executing
  173. // CheckStartOfBlock, so we must reselect.
  174. oRange.Select() ;
  175. }
  176. }
  177. oRange.Release() ;
  178. return bCustom ;
  179. }
  180. FCKEnterKey.prototype.DoCtrlBackspace = function()
  181. {
  182. FCKUndo.SaveUndoStep() ;
  183. var oRange = new FCKDomRange( this.Window ) ;
  184. oRange.MoveToSelection() ;
  185. if ( FCKBrowserInfo.IsIE && this._CheckIsAllContentsIncluded( oRange, this.Window.document.body ) )
  186. {
  187. this._FixIESelectAllBug( oRange ) ;
  188. return true ;
  189. }
  190. return false ;
  191. }
  192. FCKEnterKey.prototype._ExecuteBackspace = function( range, previous, currentBlock )
  193. {
  194. var bCustom = false ;
  195. // We could be in a nested LI.
  196. if ( !previous && currentBlock && currentBlock.nodeName.IEquals( 'LI' ) && currentBlock.parentNode.parentNode.nodeName.IEquals( 'LI' ) )
  197. {
  198. this._OutdentWithSelection( currentBlock, range ) ;
  199. return true ;
  200. }
  201. if ( previous && previous.nodeName.IEquals( 'LI' ) )
  202. {
  203. var oNestedList = FCKDomTools.GetLastChild( previous, ['UL','OL'] ) ;
  204. while ( oNestedList )
  205. {
  206. previous = FCKDomTools.GetLastChild( oNestedList, 'LI' ) ;
  207. oNestedList = FCKDomTools.GetLastChild( previous, ['UL','OL'] ) ;
  208. }
  209. }
  210. if ( previous && currentBlock )
  211. {
  212. // If we are in a LI, and the previous block is not an LI, we must outdent it.
  213. if ( currentBlock.nodeName.IEquals( 'LI' ) && !previous.nodeName.IEquals( 'LI' ) )
  214. {
  215. this._OutdentWithSelection( currentBlock, range ) ;
  216. return true ;
  217. }
  218. // Take a reference to the parent for post processing cleanup.
  219. var oCurrentParent = currentBlock.parentNode ;
  220. var sPreviousName = previous.nodeName.toLowerCase() ;
  221. if ( FCKListsLib.EmptyElements[ sPreviousName ] != null || sPreviousName == 'table' )
  222. {
  223. FCKDomTools.RemoveNode( previous ) ;
  224. bCustom = true ;
  225. }
  226. else
  227. {
  228. // Remove the current block.
  229. FCKDomTools.RemoveNode( currentBlock ) ;
  230. // Remove any empty tag left by the block removal.
  231. while ( oCurrentParent.innerHTML.Trim().length == 0 )
  232. {
  233. var oParent = oCurrentParent.parentNode ;
  234. oParent.removeChild( oCurrentParent ) ;
  235. oCurrentParent = oParent ;
  236. }
  237. // Cleanup the previous and the current elements.
  238. FCKDomTools.LTrimNode( currentBlock ) ;
  239. FCKDomTools.RTrimNode( previous ) ;
  240. // Append a space to the previous.
  241. // Maybe it is not always desirable...
  242. // previous.appendChild( this.Window.document.createTextNode( ' ' ) ) ;
  243. // Set the range to the end of the previous element and bookmark it.
  244. range.SetStart( previous, 2, true ) ;
  245. range.Collapse( true ) ;
  246. var oBookmark = range.CreateBookmark() ;
  247. // Move the contents of the block to the previous element and delete it.
  248. // But for some block types (e.g. table), moving the children to the previous block makes no sense.
  249. // So a check is needed. (See #1081)
  250. if ( ! currentBlock.tagName.IEquals( [ 'TABLE' ] ) )
  251. FCKDomTools.MoveChildren( currentBlock, previous ) ;
  252. // Place the selection at the bookmark.
  253. range.MoveToBookmark( oBookmark ) ;
  254. range.Select() ;
  255. bCustom = true ;
  256. }
  257. }
  258. return bCustom ;
  259. }
  260. /*
  261.  * Executes the <Delete> key behavior.
  262.  */
  263. FCKEnterKey.prototype.DoDelete = function()
  264. {
  265. // Save an undo snapshot before doing anything
  266. // This is to conform with the behavior seen in MS Word
  267. FCKUndo.SaveUndoStep() ;
  268. // The <Delete> has the same effect as the <Backspace>, so we have the same
  269. // results if we just move to the next block and apply the same <Backspace> logic.
  270. var bCustom = false ;
  271. // Get the current selection.
  272. var oRange = new FCKDomRange( this.Window ) ;
  273. oRange.MoveToSelection() ;
  274. // Kludge for #247
  275. if ( FCKBrowserInfo.IsIE && this._CheckIsAllContentsIncluded( oRange, this.Window.document.body ) )
  276. {
  277. this._FixIESelectAllBug( oRange ) ;
  278. return true ;
  279. }
  280. // There is just one special case for collapsed selections at the end of a block.
  281. if ( oRange.CheckIsCollapsed() && oRange.CheckEndOfBlock( FCKBrowserInfo.IsGeckoLike ) )
  282. {
  283. var oCurrentBlock = oRange.StartBlock ;
  284. var eCurrentCell = FCKTools.GetElementAscensor( oCurrentBlock, 'td' );
  285. var eNext = FCKDomTools.GetNextSourceElement( oCurrentBlock, true, [ oRange.StartBlockLimit.nodeName ], 
  286. ['UL','OL','TR'] ) ;
  287. // Bug #1323 : if we're in a table cell, and the next node belongs to a different cell, then don't
  288. // delete anything.
  289. if ( eCurrentCell )
  290. {
  291. var eNextCell = FCKTools.GetElementAscensor( eNext, 'td' );
  292. if ( eNextCell != eCurrentCell )
  293. return true ;
  294. }
  295. bCustom = this._ExecuteBackspace( oRange, oCurrentBlock, eNext ) ;
  296. }
  297. oRange.Release() ;
  298. return bCustom ;
  299. }
  300. /*
  301.  * Executes the <Tab> key behavior.
  302.  */
  303. FCKEnterKey.prototype.DoTab = function()
  304. {
  305. var oRange = new FCKDomRange( this.Window );
  306. oRange.MoveToSelection() ;
  307. // If the user pressed <tab> inside a table, we should give him the default behavior ( moving between cells )
  308. // instead of giving him more non-breaking spaces. (Bug #973)
  309. var node = oRange._Range.startContainer ;
  310. while ( node )
  311. {
  312. if ( node.nodeType == 1 )
  313. {
  314. var tagName = node.tagName.toLowerCase() ;
  315. if ( tagName == "tr" || tagName == "td" || tagName == "th" || tagName == "tbody" || tagName == "table" )
  316. return false ;
  317. else
  318. break ;
  319. }
  320. node = node.parentNode ;
  321. }
  322. if ( this.TabText )
  323. {
  324. oRange.DeleteContents() ;
  325. oRange.InsertNode( this.Window.document.createTextNode( this.TabText ) ) ;
  326. oRange.Collapse( false ) ;
  327. oRange.Select() ;
  328. }
  329. return true ;
  330. }
  331. FCKEnterKey.prototype._ExecuteEnterBlock = function( blockTag, range )
  332. {
  333. // Get the current selection.
  334. var oRange = range || new FCKDomRange( this.Window ) ;
  335. var oSplitInfo = oRange.SplitBlock() ;
  336. if ( oSplitInfo )
  337. {
  338. // Get the current blocks.
  339. var ePreviousBlock = oSplitInfo.PreviousBlock ;
  340. var eNextBlock = oSplitInfo.NextBlock ;
  341. var bIsStartOfBlock = oSplitInfo.WasStartOfBlock ;
  342. var bIsEndOfBlock = oSplitInfo.WasEndOfBlock ;
  343. // If we have both the previous and next blocks, it means that the
  344. // boundaries were on separated blocks, or none of them where on the
  345. // block limits (start/end).
  346. if ( !oSplitInfo.WasStartOfBlock && !oSplitInfo.WasEndOfBlock )
  347. {
  348. // Move the selection to the end block.
  349. if ( eNextBlock )
  350. oRange.MoveToElementEditStart( eNextBlock ) ;
  351. }
  352. else
  353. {
  354. if ( bIsStartOfBlock && bIsEndOfBlock && ePreviousBlock.tagName.toUpperCase() == 'LI' )
  355. {
  356. oRange.MoveToElementStart( ePreviousBlock ) ;
  357. this._OutdentWithSelection( ePreviousBlock, oRange ) ;
  358. oRange.Release() ;
  359. return true ;
  360. }
  361. var eNewBlock ;
  362. if ( ePreviousBlock )
  363. {
  364. var sPreviousBlockTag = ePreviousBlock.tagName.toUpperCase() ;
  365. // If is a header tag, or we are in a Shift+Enter (#77),
  366. // create a new block element.
  367. if ( this._HasShift || (/^H[1-6]$/).test( sPreviousBlockTag ) )
  368. eNewBlock = this.Window.document.createElement( blockTag ) ;
  369. else
  370. {
  371. // Otherwise, duplicate the previous block.
  372. eNewBlock = FCKDomTools.CloneElement( ePreviousBlock ) ;
  373. this._RecreateEndingTree( ePreviousBlock, eNewBlock ) ;
  374. }
  375. }
  376. else if ( eNextBlock )
  377. {
  378. eNewBlock = FCKDomTools.CloneElement( eNextBlock ) ;
  379. }
  380. else
  381. eNewBlock = this.Window.document.createElement( blockTag ) ;
  382. if ( FCKBrowserInfo.IsGeckoLike )
  383. FCKTools.AppendBogusBr( eNewBlock ) ;
  384. oRange.InsertNode( eNewBlock ) ;
  385. // This is tricky, but to make the new block visible correctly
  386. // we must select it.
  387. if ( FCKBrowserInfo.IsIE )
  388. {
  389. // Move the selection to the new block.
  390. oRange.MoveToNodeContents( eNewBlock ) ;
  391. oRange.Select() ;
  392. }
  393. oRange.MoveToElementEditStart( bIsStartOfBlock && !bIsEndOfBlock ? eNextBlock : eNewBlock ) ;
  394. if ( FCKBrowserInfo.IsGeckoLike )
  395. eNewBlock.scrollIntoView( false ) ;
  396. }
  397. oRange.Select() ;
  398. }
  399. // Release the resources used by the range.
  400. oRange.Release() ;
  401. return true ;
  402. }
  403. FCKEnterKey.prototype._ExecuteEnterBr = function( blockTag )
  404. {
  405. // Get the current selection.
  406. var oRange = new FCKDomRange( this.Window ) ;
  407. oRange.MoveToSelection() ;
  408. // The selection boundaries must be in the same "block limit" element.
  409. if ( oRange.StartBlockLimit == oRange.EndBlockLimit )
  410. {
  411. oRange.DeleteContents() ;
  412. // Get the new selection (it is collapsed at this point).
  413. oRange.MoveToSelection() ;
  414. var bIsStartOfBlock = oRange.CheckStartOfBlock() ;
  415. var bIsEndOfBlock = oRange.CheckEndOfBlock() ;
  416. var sStartBlockTag = oRange.StartBlock ? oRange.StartBlock.tagName.toUpperCase() : '' ;
  417. var bHasShift = this._HasShift ;
  418. if ( !bHasShift && sStartBlockTag == 'LI' )
  419. return this._ExecuteEnterBlock( null, oRange ) ;
  420. // If we are at the end of a header block.
  421. if ( !bHasShift && bIsEndOfBlock && (/^H[1-6]$/).test( sStartBlockTag ) )
  422. {
  423. // Insert a BR after the current paragraph.
  424. FCKDomTools.InsertAfterNode( oRange.StartBlock, this.Window.document.createElement( 'br' ) ) ;
  425. // The space is required by Gecko only to make the cursor blink.
  426. if ( FCKBrowserInfo.IsGecko )
  427. FCKDomTools.InsertAfterNode( oRange.StartBlock, this.Window.document.createTextNode( '' ) ) ;
  428. // IE and Gecko have different behaviors regarding the position.
  429. oRange.SetStart( oRange.StartBlock.nextSibling, FCKBrowserInfo.IsIE ? 3 : 1 ) ;
  430. }
  431. else
  432. {
  433. var eLineBreak = null ;
  434. if ( sStartBlockTag.IEquals( 'pre' ) )
  435. eLineBreak = this.Window.document.createTextNode( FCKBrowserInfo.IsIE ? 'r' : 'n' ) ;
  436. else
  437. eLineBreak = this.Window.document.createElement( 'br' ) ;
  438. oRange.InsertNode( eLineBreak ) ;
  439. // The space is required by Gecko only to make the cursor blink.
  440. if ( FCKBrowserInfo.IsGecko )
  441. FCKDomTools.InsertAfterNode( eLineBreak, this.Window.document.createTextNode( '' ) ) ;
  442. // If we are at the end of a block, we must be sure the bogus node is available in that block.
  443. if ( bIsEndOfBlock && FCKBrowserInfo.IsGeckoLike )
  444. FCKTools.AppendBogusBr( eLineBreak.parentNode ) ;
  445. if ( FCKBrowserInfo.IsIE )
  446. oRange.SetStart( eLineBreak, 4 ) ;
  447. else
  448. oRange.SetStart( eLineBreak.nextSibling, 1 ) ;
  449. if ( ! FCKBrowserInfo.IsIE )
  450. {
  451. var dummy = null ;
  452. if ( FCKBrowserInfo.IsOpera )
  453. dummy = this.Window.document.createElement( 'span' ) ;
  454. else
  455. dummy = this.Window.document.createElement( 'br' ) ;
  456. eLineBreak.parentNode.insertBefore( dummy, eLineBreak.nextSibling ) ;
  457. dummy.scrollIntoView( false ) ;
  458. dummy.parentNode.removeChild( dummy ) ;
  459. }
  460. }
  461. // This collapse guarantees the cursor will be blinking.
  462. oRange.Collapse( true ) ;
  463. oRange.Select() ;
  464. }
  465. // Release the resources used by the range.
  466. oRange.Release() ;
  467. return true ;
  468. }
  469. // Recreate the elements tree at the end of the source block, at the beginning
  470. // of the target block. Eg.:
  471. // If source = <p><u>Some</u> sample <b><i>text</i></b></p> then target = <p><b><i></i></b></p>
  472. // If source = <p><u>Some</u> sample text</p> then target = <p></p>
  473. FCKEnterKey.prototype._RecreateEndingTree = function( source, target )
  474. {
  475. while ( ( source = source.lastChild ) && source.nodeType == 1 && FCKListsLib.InlineChildReqElements[ source.nodeName.toLowerCase() ] != null )
  476. target = target.insertBefore( FCKDomTools.CloneElement( source ), target.firstChild ) ;
  477. }
  478. // Outdents a LI, maintaining the selection defined on a range.
  479. FCKEnterKey.prototype._OutdentWithSelection = function( li, range )
  480. {
  481. var oBookmark = range.CreateBookmark() ;
  482. FCKListHandler.OutdentListItem( li ) ;
  483. range.MoveToBookmark( oBookmark ) ;
  484. range.Select() ;
  485. }
  486. // Is all the contents under a node included by a range?
  487. FCKEnterKey.prototype._CheckIsAllContentsIncluded = function( range, node )
  488. {
  489. var startOk = false ;
  490. var endOk = false ;
  491. /*
  492. FCKDebug.Output( 'sc='+range.StartContainer.nodeName+
  493. ',so='+range._Range.startOffset+
  494. ',ec='+range.EndContainer.nodeName+
  495. ',eo='+range._Range.endOffset ) ;
  496. */
  497. if ( range.StartContainer == node || range.StartContainer == node.firstChild )
  498. startOk = ( range._Range.startOffset == 0 ) ;
  499. if ( range.EndContainer == node || range.EndContainer == node.lastChild )
  500. {
  501. var nodeLength = range.EndContainer.nodeType == 3 ? range.EndContainer.length : range.EndContainer.childNodes.length ;
  502. endOk = ( range._Range.endOffset == nodeLength ) ;
  503. }
  504. return startOk && endOk ;
  505. }
  506. // Kludge for #247
  507. FCKEnterKey.prototype._FixIESelectAllBug = function( range )
  508. {
  509. var doc = this.Window.document ;
  510. doc.body.innerHTML = '' ;
  511. var editBlock ;
  512. if ( FCKConfig.EnterMode.IEquals( ['div', 'p'] ) )
  513. {
  514. editBlock = doc.createElement( FCKConfig.EnterMode ) ;
  515. doc.body.appendChild( editBlock ) ;
  516. }
  517. else
  518. editBlock = doc.body ;
  519. range.MoveToNodeContents( editBlock ) ;
  520. range.Collapse( true ) ;
  521. range.Select() ;
  522. range.Release() ;
  523. }