focusmanager.js 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. /**
  2. * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md or http://ckeditor.com/license
  4. */
  5. /**
  6. * @fileOverview Defines the {@link CKEDITOR.focusManager} class, which is used
  7. * to handle the focus in editor instances.
  8. */
  9. ( function() {
  10. /**
  11. * Manages the focus activity in an editor instance. This class is to be
  12. * used mainly by UI element coders when adding interface elements that need
  13. * to set the focus state of the editor.
  14. *
  15. * var focusManager = new CKEDITOR.focusManager( editor );
  16. * focusManager.focus();
  17. *
  18. * @class
  19. * @constructor Creates a focusManager class instance.
  20. * @param {CKEDITOR.editor} editor The editor instance.
  21. */
  22. CKEDITOR.focusManager = function( editor ) {
  23. if ( editor.focusManager )
  24. return editor.focusManager;
  25. /**
  26. * Indicates that the editor instance has focus.
  27. *
  28. * alert( CKEDITOR.instances.editor1.focusManager.hasFocus ); // e.g. true
  29. */
  30. this.hasFocus = false;
  31. /**
  32. * Indicates the currently focused DOM element that makes the editor activated.
  33. *
  34. * @property {CKEDITOR.dom.domObject}
  35. */
  36. this.currentActive = null;
  37. /**
  38. * Object used to store private stuff.
  39. *
  40. * @private
  41. */
  42. this._ = {
  43. editor: editor
  44. };
  45. return this;
  46. };
  47. var SLOT_NAME = 'focusmanager',
  48. SLOT_NAME_LISTENERS = 'focusmanager_handlers';
  49. /**
  50. * Object used to store private stuff.
  51. *
  52. * @private
  53. * @class
  54. * @singleton
  55. */
  56. CKEDITOR.focusManager._ = {
  57. /**
  58. * The delay (in milliseconds) to deactivate the editor when a UI DOM element has lost focus.
  59. *
  60. * @private
  61. * @property {Number} [blurDelay=200]
  62. * @member CKEDITOR.focusManager._
  63. */
  64. blurDelay: 200
  65. };
  66. CKEDITOR.focusManager.prototype = {
  67. /**
  68. * Indicates that this editor instance is activated (due to a DOM focus change).
  69. * The `activated` state is a symbolic indicator of an active user
  70. * interaction session.
  71. *
  72. * **Note:** This method will not introduce UI focus
  73. * impact on DOM, it is here to record the editor UI focus state internally.
  74. * If you want to make the cursor blink inside the editable, use
  75. * {@link CKEDITOR.editor#method-focus} instead.
  76. *
  77. * var editor = CKEDITOR.instances.editor1;
  78. * editor.focusManage.focus( editor.editable() );
  79. *
  80. * @param {CKEDITOR.dom.element} [currentActive] The new value of the {@link #currentActive} property.
  81. * @member CKEDITOR.focusManager
  82. */
  83. focus: function( currentActive ) {
  84. if ( this._.timer )
  85. clearTimeout( this._.timer );
  86. if ( currentActive )
  87. this.currentActive = currentActive;
  88. if ( !( this.hasFocus || this._.locked ) ) {
  89. // If another editor has the current focus, we first "blur" it. In
  90. // this way the events happen in a more logical sequence, like:
  91. // "focus 1" > "blur 1" > "focus 2"
  92. // ... instead of:
  93. // "focus 1" > "focus 2" > "blur 1"
  94. var current = CKEDITOR.currentInstance;
  95. current && current.focusManager.blur( 1 );
  96. this.hasFocus = true;
  97. var ct = this._.editor.container;
  98. ct && ct.addClass( 'cke_focus' );
  99. this._.editor.fire( 'focus' );
  100. }
  101. },
  102. /**
  103. * Prevents from changing the focus manager state until the next {@link #unlock} is called.
  104. *
  105. * @member CKEDITOR.focusManager
  106. */
  107. lock: function() {
  108. this._.locked = 1;
  109. },
  110. /**
  111. * Restores the automatic focus management if {@link #lock} is called.
  112. *
  113. * @member CKEDITOR.focusManager
  114. */
  115. unlock: function() {
  116. delete this._.locked;
  117. },
  118. /**
  119. * Used to indicate that the editor instance has been deactivated by the specified
  120. * element which has just lost focus.
  121. *
  122. * **Note:** This function acts asynchronously with a delay of 100ms to
  123. * avoid temporary deactivation. Use the `noDelay` parameter instead
  124. * to deactivate immediately.
  125. *
  126. * var editor = CKEDITOR.instances.editor1;
  127. * editor.focusManager.blur();
  128. *
  129. * @param {Boolean} [noDelay=false] Immediately deactivate the editor instance synchronously.
  130. * @member CKEDITOR.focusManager
  131. */
  132. blur: function( noDelay ) {
  133. if ( this._.locked )
  134. return;
  135. function doBlur() {
  136. if ( this.hasFocus ) {
  137. this.hasFocus = false;
  138. var ct = this._.editor.container;
  139. ct && ct.removeClass( 'cke_focus' );
  140. this._.editor.fire( 'blur' );
  141. }
  142. }
  143. if ( this._.timer )
  144. clearTimeout( this._.timer );
  145. var delay = CKEDITOR.focusManager._.blurDelay;
  146. if ( noDelay || !delay )
  147. doBlur.call( this );
  148. else {
  149. this._.timer = CKEDITOR.tools.setTimeout( function() {
  150. delete this._.timer;
  151. doBlur.call( this );
  152. }, delay, this );
  153. }
  154. },
  155. /**
  156. * Registers a UI DOM element to the focus manager, which will make the focus manager "hasFocus"
  157. * once the input focus is relieved on the element.
  158. * This method is designed to be used by plugins to expand the jurisdiction of the editor focus.
  159. *
  160. * @param {CKEDITOR.dom.element} element The container (topmost) element of one UI part.
  161. * @param {Boolean} isCapture If specified, {@link CKEDITOR.event#useCapture} will be used when listening to the focus event.
  162. * @member CKEDITOR.focusManager
  163. */
  164. add: function( element, isCapture ) {
  165. var fm = element.getCustomData( SLOT_NAME );
  166. if ( !fm || fm != this ) {
  167. // If this element is already taken by another instance, dismiss it first.
  168. fm && fm.remove( element );
  169. var focusEvent = 'focus',
  170. blurEvent = 'blur';
  171. // Bypass the element's internal DOM focus change.
  172. if ( isCapture ) {
  173. // Use "focusin/focusout" events instead of capture phase in IEs,
  174. // which fires synchronously.
  175. if ( CKEDITOR.env.ie ) {
  176. focusEvent = 'focusin';
  177. blurEvent = 'focusout';
  178. } else {
  179. CKEDITOR.event.useCapture = 1;
  180. }
  181. }
  182. var listeners = {
  183. blur: function() {
  184. if ( element.equals( this.currentActive ) )
  185. this.blur();
  186. },
  187. focus: function() {
  188. this.focus( element );
  189. }
  190. };
  191. element.on( focusEvent, listeners.focus, this );
  192. element.on( blurEvent, listeners.blur, this );
  193. if ( isCapture )
  194. CKEDITOR.event.useCapture = 0;
  195. element.setCustomData( SLOT_NAME, this );
  196. element.setCustomData( SLOT_NAME_LISTENERS, listeners );
  197. }
  198. },
  199. /**
  200. * Dismisses an element from the focus manager delegations added by {@link #add}.
  201. *
  202. * @param {CKEDITOR.dom.element} element The element to be removed from the focus manager.
  203. * @member CKEDITOR.focusManager
  204. */
  205. remove: function( element ) {
  206. element.removeCustomData( SLOT_NAME );
  207. var listeners = element.removeCustomData( SLOT_NAME_LISTENERS );
  208. element.removeListener( 'blur', listeners.blur );
  209. element.removeListener( 'focus', listeners.focus );
  210. }
  211. };
  212. } )();
  213. /**
  214. * Fired when the editor instance receives the input focus.
  215. *
  216. * editor.on( 'focus', function( e ) {
  217. * alert( 'The editor named ' + e.editor.name + ' is now focused' );
  218. * } );
  219. *
  220. * @event focus
  221. * @member CKEDITOR.editor
  222. * @param {CKEDITOR.editor} editor The editor instance.
  223. */
  224. /**
  225. * Fired when the editor instance loses the input focus.
  226. *
  227. * **Note:** This event will **NOT** be triggered when focus is moved internally, e.g. from
  228. * an editable to another part of the editor UI like a dialog window.
  229. * If you are interested only in the focus state of the editable, listen to the `focus`
  230. * and `blur` events of the {@link CKEDITOR.editable} instead.
  231. *
  232. * editor.on( 'blur', function( e ) {
  233. * alert( 'The editor named ' + e.editor.name + ' lost the focus' );
  234. * } );
  235. *
  236. * @event blur
  237. * @member CKEDITOR.editor
  238. * @param {CKEDITOR.editor} editor The editor instance.
  239. */