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

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.  * Creation and initialization of the "FCK" object. This is the main
  22.  * object that represents an editor instance.
  23.  * (Gecko specific implementations)
  24.  */
  25. FCK.Description = "FCKeditor for Gecko Browsers" ;
  26. FCK.InitializeBehaviors = function()
  27. {
  28. // When calling "SetData", the editing area IFRAME gets a fixed height. So we must recalculate it.
  29. if ( window.onresize ) // Not for Safari/Opera.
  30. window.onresize() ;
  31. FCKFocusManager.AddWindow( this.EditorWindow ) ;
  32. this.ExecOnSelectionChange = function()
  33. {
  34. FCK.Events.FireEvent( "OnSelectionChange" ) ;
  35. }
  36. this._ExecDrop = function( evt )
  37. {
  38. if ( FCK.MouseDownFlag )
  39. {
  40. FCK.MouseDownFlag = false ;
  41. return ;
  42. }
  43. if ( FCKConfig.ForcePasteAsPlainText )
  44. {
  45. if ( evt.dataTransfer )
  46. {
  47. var text = evt.dataTransfer.getData( 'Text' ) ;
  48. text = FCKTools.HTMLEncode( text ) ;
  49. text = FCKTools.ProcessLineBreaks( window, FCKConfig, text ) ;
  50. FCK.InsertHtml( text ) ;
  51. }
  52. else if ( FCKConfig.ShowDropDialog )
  53. FCK.PasteAsPlainText() ;
  54. evt.preventDefault() ;
  55. evt.stopPropagation() ;
  56. }
  57. }
  58. this._ExecCheckCaret = function( evt )
  59. {
  60. if ( FCK.EditMode != FCK_EDITMODE_WYSIWYG )
  61. return ;
  62. if ( evt.type == 'keypress' )
  63. {
  64. var keyCode = evt.keyCode ;
  65. // ignore if positioning key is not pressed.
  66. // left or up arrow keys need to be processed as well, since <a> links can be expanded in Gecko's editor
  67. // when the caret moved left or up from another block element below.
  68. if ( keyCode < 33 || keyCode > 40 )
  69. return ;
  70. }
  71. var blockEmptyStop = function( node )
  72. {
  73. if ( node.nodeType != 1 )
  74. return false ;
  75. var tag = node.tagName.toLowerCase() ;
  76. return ( FCKListsLib.BlockElements[tag] || FCKListsLib.EmptyElements[tag] ) ;
  77. }
  78. var moveCursor = function()
  79. {
  80. var selection = FCKSelection.GetSelection() ;
  81. var range = selection.getRangeAt(0) ;
  82. if ( ! range || ! range.collapsed )
  83. return ;
  84. var node = range.endContainer ;
  85. // only perform the patched behavior if we're at the end of a text node.
  86. if ( node.nodeType != 3 )
  87. return ;
  88. if ( node.nodeValue.length != range.endOffset )
  89. return ;
  90. // only perform the patched behavior if we're in an <a> tag, or the End key is pressed.
  91. var parentTag = node.parentNode.tagName.toLowerCase() ;
  92. if ( ! (  parentTag == 'a' || ( !FCKBrowserInfo.IsOpera && String(node.parentNode.contentEditable) == 'false' ) ||
  93. ( ! ( FCKListsLib.BlockElements[parentTag] || FCKListsLib.NonEmptyBlockElements[parentTag] )
  94.   && keyCode == 35 ) ) )
  95. return ;
  96. // our caret has moved to just after the last character of a text node under an unknown tag, how to proceed?
  97. // first, see if there are other text nodes by DFS walking from this text node.
  98. //  - if the DFS has scanned all nodes under my parent, then go the next step.
  99. // - if there is a text node after me but still under my parent, then do nothing and return.
  100. var nextTextNode = FCKTools.GetNextTextNode( node, node.parentNode, blockEmptyStop ) ;
  101. if ( nextTextNode )
  102. return ;
  103. // we're pretty sure we need to move the caret forcefully from here.
  104. range = FCK.EditorDocument.createRange() ;
  105. nextTextNode = FCKTools.GetNextTextNode( node, node.parentNode.parentNode, blockEmptyStop ) ;
  106. if ( nextTextNode )
  107. {
  108. // Opera thinks the dummy empty text node we append beyond the end of <a> nodes occupies a caret
  109. // position. So if the user presses the left key and we reset the caret position here, the user
  110. // wouldn't be able to go back.
  111. if ( FCKBrowserInfo.IsOpera && keyCode == 37 )
  112. return ;
  113. // now we want to get out of our current parent node, adopt the next parent, and move the caret to
  114. // the appropriate text node under our new parent.
  115. // our new parent might be our current parent's siblings if we are lucky.
  116. range.setStart( nextTextNode, 0 ) ;
  117. range.setEnd( nextTextNode, 0 ) ;
  118. }
  119. else
  120. {
  121. // no suitable next siblings under our grandparent! what to do next?
  122. while ( node.parentNode
  123. && node.parentNode != FCK.EditorDocument.body
  124. && node.parentNode != FCK.EditorDocument.documentElement
  125. && node == node.parentNode.lastChild
  126. && ( ! FCKListsLib.BlockElements[node.parentNode.tagName.toLowerCase()]
  127.   && ! FCKListsLib.NonEmptyBlockElements[node.parentNode.tagName.toLowerCase()] ) )
  128. node = node.parentNode ;
  129. if ( FCKListsLib.BlockElements[ parentTag ]
  130. || FCKListsLib.EmptyElements[ parentTag ]
  131. || node == FCK.EditorDocument.body )
  132. {
  133. // if our parent is a block node, move to the end of our parent.
  134. range.setStart( node, node.childNodes.length ) ;
  135. range.setEnd( node, node.childNodes.length ) ;
  136. }
  137. else
  138. {
  139. // things are a little bit more interesting if our parent is not a block node
  140. // due to the weired ways how Gecko's caret acts...
  141. var stopNode = node.nextSibling ;
  142. // find out the next block/empty element at our grandparent, we'll
  143. // move the caret just before it.
  144. while ( stopNode )
  145. {
  146. if ( stopNode.nodeType != 1 )
  147. {
  148. stopNode = stopNode.nextSibling ;
  149. continue ;
  150. }
  151. var stopTag = stopNode.tagName.toLowerCase() ;
  152. if ( FCKListsLib.BlockElements[stopTag] || FCKListsLib.EmptyElements[stopTag]
  153. || FCKListsLib.NonEmptyBlockElements[stopTag] )
  154. break ;
  155. stopNode = stopNode.nextSibling ;
  156. }
  157. // note that the dummy marker below is NEEDED, otherwise the caret's behavior will
  158. // be broken in Gecko.
  159. var marker = FCK.EditorDocument.createTextNode( '' ) ;
  160. if ( stopNode )
  161. node.parentNode.insertBefore( marker, stopNode ) ;
  162. else
  163. node.parentNode.appendChild( marker ) ;
  164. range.setStart( marker, 0 ) ;
  165. range.setEnd( marker, 0 ) ;
  166. }
  167. }
  168. selection.removeAllRanges() ;
  169. selection.addRange( range ) ;
  170. FCK.Events.FireEvent( "OnSelectionChange" ) ;
  171. }
  172. setTimeout( moveCursor, 1 ) ;
  173. }
  174. this.ExecOnSelectionChangeTimer = function()
  175. {
  176. if ( FCK.LastOnChangeTimer )
  177. window.clearTimeout( FCK.LastOnChangeTimer ) ;
  178. FCK.LastOnChangeTimer = window.setTimeout( FCK.ExecOnSelectionChange, 100 ) ;
  179. }
  180. this.EditorDocument.addEventListener( 'mouseup', this.ExecOnSelectionChange, false ) ;
  181. // On Gecko, firing the "OnSelectionChange" event on every key press started to be too much
  182. // slow. So, a timer has been implemented to solve performance issues when typing to quickly.
  183. this.EditorDocument.addEventListener( 'keyup', this.ExecOnSelectionChangeTimer, false ) ;
  184. this._DblClickListener = function( e )
  185. {
  186. FCK.OnDoubleClick( e.target ) ;
  187. e.stopPropagation() ;
  188. }
  189. this.EditorDocument.addEventListener( 'dblclick', this._DblClickListener, true ) ;
  190. // Record changes for the undo system when there are key down events.
  191. this.EditorDocument.addEventListener( 'keydown', this._KeyDownListener, false ) ;
  192. // Hooks for data object drops
  193. if ( FCKBrowserInfo.IsGecko )
  194. {
  195. this.EditorWindow.addEventListener( 'dragdrop', this._ExecDrop, true ) ;
  196. }
  197. else if ( FCKBrowserInfo.IsSafari )
  198. {
  199. this.EditorDocument.addEventListener( 'dragover', function ( evt )
  200. { if ( !FCK.MouseDownFlag && FCK.Config.ForcePasteAsPlainText ) evt.returnValue = false ; }, true ) ;
  201. this.EditorDocument.addEventListener( 'drop', this._ExecDrop, true ) ;
  202. this.EditorDocument.addEventListener( 'mousedown',
  203. function( ev )
  204. {
  205. var element = ev.srcElement ;
  206. if ( element.nodeName.IEquals( 'IMG', 'HR', 'INPUT', 'TEXTAREA', 'SELECT' ) )
  207. {
  208. FCKSelection.SelectNode( element ) ;
  209. }
  210. }, true ) ;
  211. this.EditorDocument.addEventListener( 'mouseup',
  212. function( ev )
  213. {
  214. if ( ev.srcElement.nodeName.IEquals( 'INPUT', 'TEXTAREA', 'SELECT' ) )
  215. ev.preventDefault()
  216. }, true ) ;
  217. this.EditorDocument.addEventListener( 'click',
  218. function( ev )
  219. {
  220. if ( ev.srcElement.nodeName.IEquals( 'INPUT', 'TEXTAREA', 'SELECT' ) )
  221. ev.preventDefault()
  222. }, true ) ;
  223. }
  224. // Kludge for buggy Gecko caret positioning logic (Bug #393 and #1056)
  225. if ( FCKBrowserInfo.IsGecko || FCKBrowserInfo.IsOpera )
  226. {
  227. this.EditorDocument.addEventListener( 'keypress', this._ExecCheckCaret, false ) ;
  228. this.EditorDocument.addEventListener( 'click', this._ExecCheckCaret, false ) ;
  229. }
  230. // Reset the context menu.
  231. FCK.ContextMenu._InnerContextMenu.SetMouseClickWindow( FCK.EditorWindow ) ;
  232. FCK.ContextMenu._InnerContextMenu.AttachToElement( FCK.EditorDocument ) ;
  233. }
  234. FCK.MakeEditable = function()
  235. {
  236. this.EditingArea.MakeEditable() ;
  237. }
  238. // Disable the context menu in the editor (outside the editing area).
  239. function Document_OnContextMenu( e )
  240. {
  241. if ( !e.target._FCKShowContextMenu )
  242. e.preventDefault() ;
  243. }
  244. document.oncontextmenu = Document_OnContextMenu ;
  245. // GetNamedCommandState overload for Gecko.
  246. FCK._BaseGetNamedCommandState = FCK.GetNamedCommandState ;
  247. FCK.GetNamedCommandState = function( commandName )
  248. {
  249. switch ( commandName )
  250. {
  251. case 'Unlink' :
  252. return FCKSelection.HasAncestorNode('A') ? FCK_TRISTATE_OFF : FCK_TRISTATE_DISABLED ;
  253. default :
  254. return FCK._BaseGetNamedCommandState( commandName ) ;
  255. }
  256. }
  257. // Named commands to be handled by this browsers specific implementation.
  258. FCK.RedirectNamedCommands =
  259. {
  260. Print : true,
  261. Paste : true
  262. } ;
  263. // ExecuteNamedCommand overload for Gecko.
  264. FCK.ExecuteRedirectedNamedCommand = function( commandName, commandParameter )
  265. {
  266. switch ( commandName )
  267. {
  268. case 'Print' :
  269. FCK.EditorWindow.print() ;
  270. break ;
  271. case 'Paste' :
  272. try
  273. {
  274. // Force the paste dialog for Safari (#50).
  275. if ( FCKBrowserInfo.IsSafari )
  276. throw '' ;
  277. if ( FCK.Paste() )
  278. FCK.ExecuteNamedCommand( 'Paste', null, true ) ;
  279. }
  280. catch (e) {
  281. if ( FCKConfig.ForcePasteAsPlainText )
  282. FCK.PasteAsPlainText() ;
  283. else
  284. FCKDialog.OpenDialog( 'FCKDialog_Paste', FCKLang.Paste, 'dialog/fck_paste.html', 400, 330, 'Security' ) ;
  285. }
  286. break ;
  287. default :
  288. FCK.ExecuteNamedCommand( commandName, commandParameter ) ;
  289. }
  290. }
  291. FCK._ExecPaste = function()
  292. {
  293. // Save a snapshot for undo before actually paste the text
  294. FCKUndo.SaveUndoStep() ;
  295. if ( FCKConfig.ForcePasteAsPlainText )
  296. {
  297. FCK.PasteAsPlainText() ;
  298. return false ;
  299. }
  300. /* For now, the AutoDetectPasteFromWord feature is IE only. */
  301. return true ;
  302. }
  303. //**
  304. // FCK.InsertHtml: Inserts HTML at the current cursor location. Deletes the
  305. // selected content if any.
  306. FCK.InsertHtml = function( html )
  307. {
  308. var doc = FCK.EditorDocument,
  309. range;
  310. html = FCKConfig.ProtectedSource.Protect( html ) ;
  311. html = FCK.ProtectEvents( html ) ;
  312. html = FCK.ProtectUrls( html ) ;
  313. html = FCK.ProtectTags( html ) ;
  314. // Save an undo snapshot first.
  315. FCKUndo.SaveUndoStep() ;
  316. if ( FCKBrowserInfo.IsGecko )
  317. {
  318. html = html.replace( /&nbsp;$/, '$&<span _fcktemp="1"/>' ) ;
  319. var docFrag = new FCKDocumentFragment( this.EditorDocument ) ;
  320. docFrag.AppendHtml( html ) ;
  321. var lastNode = docFrag.RootNode.lastChild ;
  322. range = new FCKDomRange( this.EditorWindow ) ;
  323. range.MoveToSelection() ;
  324. range.DeleteContents() ;
  325. range.InsertNode( docFrag.RootNode ) ;
  326. range.MoveToPosition( lastNode, 4 ) ;
  327. }
  328. else
  329. doc.execCommand( 'inserthtml', false, html ) ;
  330. this.Focus() ;
  331. // Save the caret position before calling document processor.
  332. if ( !range )
  333. {
  334. range = new FCKDomRange( this.EditorWindow ) ;
  335. range.MoveToSelection() ;
  336. }
  337. var bookmark = range.CreateBookmark() ;
  338. FCKDocumentProcessor.Process( doc ) ;
  339. // Restore caret position, ignore any errors in case the document
  340. // processor removed the bookmark <span>s for some reason.
  341. try
  342. {
  343. range.MoveToBookmark( bookmark ) ;
  344. range.Select() ;
  345. }
  346. catch ( e ) {}
  347. // For some strange reason the SaveUndoStep() call doesn't activate the undo button at the first InsertHtml() call.
  348. this.Events.FireEvent( "OnSelectionChange" ) ;
  349. }
  350. FCK.PasteAsPlainText = function()
  351. {
  352. // TODO: Implement the "Paste as Plain Text" code.
  353. // If the function is called immediately Firefox 2 does automatically paste the contents as soon as the new dialog is created
  354. // so we run it in a Timeout and the paste event can be cancelled
  355. FCKTools.RunFunction( FCKDialog.OpenDialog, FCKDialog, ['FCKDialog_Paste', FCKLang.PasteAsText, 'dialog/fck_paste.html', 400, 330, 'PlainText'] ) ;
  356. /*
  357. var sText = FCKTools.HTMLEncode( clipboardData.getData("Text") ) ;
  358. sText = sText.replace( /n/g, '<BR>' ) ;
  359. this.InsertHtml( sText ) ;
  360. */
  361. }
  362. /*
  363. FCK.PasteFromWord = function()
  364. {
  365. // TODO: Implement the "Paste as Plain Text" code.
  366. FCKDialog.OpenDialog( 'FCKDialog_Paste', FCKLang.PasteFromWord, 'dialog/fck_paste.html', 400, 330, 'Word' ) ;
  367. // FCK.CleanAndPaste( FCK.GetClipboardHTML() ) ;
  368. }
  369. */
  370. FCK.GetClipboardHTML = function()
  371. {
  372. return '' ;
  373. }
  374. FCK.CreateLink = function( url, noUndo )
  375. {
  376. // Creates the array that will be returned. It contains one or more created links (see #220).
  377. var aCreatedLinks = new Array() ;
  378. // Only for Safari, a collapsed selection may create a link. All other
  379. // browser will have no links created. So, we check it here and return
  380. // immediatelly, having the same cross browser behavior.
  381. if ( FCKSelection.GetSelection().isCollapsed )
  382. return aCreatedLinks ;
  383. FCK.ExecuteNamedCommand( 'Unlink', null, false, !!noUndo ) ;
  384. if ( url.length > 0 )
  385. {
  386. // Generate a temporary name for the link.
  387. var sTempUrl = 'javascript:void(0);/*' + ( new Date().getTime() ) + '*/' ;
  388. // Use the internal "CreateLink" command to create the link.
  389. FCK.ExecuteNamedCommand( 'CreateLink', sTempUrl, false, !!noUndo ) ;
  390. // Retrieve the just created links using XPath.
  391. var oLinksInteractor = this.EditorDocument.evaluate("//a[@href='" + sTempUrl + "']", this.EditorDocument.body, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null) ;
  392. // Add all links to the returning array.
  393. for ( var i = 0 ; i < oLinksInteractor.snapshotLength ; i++ )
  394. {
  395. var oLink = oLinksInteractor.snapshotItem( i ) ;
  396. oLink.href = url ;
  397. aCreatedLinks.push( oLink ) ;
  398. }
  399. }
  400. return aCreatedLinks ;
  401. }
  402. FCK._FillEmptyBlock = function( emptyBlockNode )
  403. {
  404. if ( ! emptyBlockNode || emptyBlockNode.nodeType != 1 )
  405. return ;
  406. var nodeTag = emptyBlockNode.tagName.toLowerCase() ;
  407. if ( nodeTag != 'p' && nodeTag != 'div' )
  408. return ;
  409. if ( emptyBlockNode.firstChild )
  410. return ;
  411. FCKTools.AppendBogusBr( emptyBlockNode ) ;
  412. }
  413. FCK._ExecCheckEmptyBlock = function()
  414. {
  415. FCK._FillEmptyBlock( FCK.EditorDocument.body.firstChild ) ;
  416. var sel = FCKSelection.GetSelection() ;
  417. if ( !sel || sel.rangeCount < 1 )
  418. return ;
  419. var range = sel.getRangeAt( 0 );
  420. FCK._FillEmptyBlock( range.startContainer ) ;
  421. }