/** * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md or http://ckeditor.com/license */ /** * @fileOverview Defines the {@link CKEDITOR.focusManager} class, which is used * to handle the focus in editor instances. */ ( function() { /** * Manages the focus activity in an editor instance. This class is to be * used mainly by UI element coders when adding interface elements that need * to set the focus state of the editor. * * var focusManager = new CKEDITOR.focusManager( editor ); * focusManager.focus(); * * @class * @constructor Creates a focusManager class instance. * @param {CKEDITOR.editor} editor The editor instance. */ CKEDITOR.focusManager = function( editor ) { if ( editor.focusManager ) return editor.focusManager; /** * Indicates that the editor instance has focus. * * alert( CKEDITOR.instances.editor1.focusManager.hasFocus ); // e.g. true */ this.hasFocus = false; /** * Indicates the currently focused DOM element that makes the editor activated. * * @property {CKEDITOR.dom.domObject} */ this.currentActive = null; /** * Object used to store private stuff. * * @private */ this._ = { editor: editor }; return this; }; var SLOT_NAME = 'focusmanager', SLOT_NAME_LISTENERS = 'focusmanager_handlers'; /** * Object used to store private stuff. * * @private * @class * @singleton */ CKEDITOR.focusManager._ = { /** * The delay (in milliseconds) to deactivate the editor when a UI DOM element has lost focus. * * @private * @property {Number} [blurDelay=200] * @member CKEDITOR.focusManager._ */ blurDelay: 200 }; CKEDITOR.focusManager.prototype = { /** * Indicates that this editor instance is activated (due to a DOM focus change). * The `activated` state is a symbolic indicator of an active user * interaction session. * * **Note:** This method will not introduce UI focus * impact on DOM, it is here to record the editor UI focus state internally. * If you want to make the cursor blink inside the editable, use * {@link CKEDITOR.editor#method-focus} instead. * * var editor = CKEDITOR.instances.editor1; * editor.focusManage.focus( editor.editable() ); * * @param {CKEDITOR.dom.element} [currentActive] The new value of the {@link #currentActive} property. * @member CKEDITOR.focusManager */ focus: function( currentActive ) { if ( this._.timer ) clearTimeout( this._.timer ); if ( currentActive ) this.currentActive = currentActive; if ( !( this.hasFocus || this._.locked ) ) { // If another editor has the current focus, we first "blur" it. In // this way the events happen in a more logical sequence, like: // "focus 1" > "blur 1" > "focus 2" // ... instead of: // "focus 1" > "focus 2" > "blur 1" var current = CKEDITOR.currentInstance; current && current.focusManager.blur( 1 ); this.hasFocus = true; var ct = this._.editor.container; ct && ct.addClass( 'cke_focus' ); this._.editor.fire( 'focus' ); } }, /** * Prevents from changing the focus manager state until the next {@link #unlock} is called. * * @member CKEDITOR.focusManager */ lock: function() { this._.locked = 1; }, /** * Restores the automatic focus management if {@link #lock} is called. * * @member CKEDITOR.focusManager */ unlock: function() { delete this._.locked; }, /** * Used to indicate that the editor instance has been deactivated by the specified * element which has just lost focus. * * **Note:** This function acts asynchronously with a delay of 100ms to * avoid temporary deactivation. Use the `noDelay` parameter instead * to deactivate immediately. * * var editor = CKEDITOR.instances.editor1; * editor.focusManager.blur(); * * @param {Boolean} [noDelay=false] Immediately deactivate the editor instance synchronously. * @member CKEDITOR.focusManager */ blur: function( noDelay ) { if ( this._.locked ) return; function doBlur() { if ( this.hasFocus ) { this.hasFocus = false; var ct = this._.editor.container; ct && ct.removeClass( 'cke_focus' ); this._.editor.fire( 'blur' ); } } if ( this._.timer ) clearTimeout( this._.timer ); var delay = CKEDITOR.focusManager._.blurDelay; if ( noDelay || !delay ) doBlur.call( this ); else { this._.timer = CKEDITOR.tools.setTimeout( function() { delete this._.timer; doBlur.call( this ); }, delay, this ); } }, /** * Registers a UI DOM element to the focus manager, which will make the focus manager "hasFocus" * once the input focus is relieved on the element. * This method is designed to be used by plugins to expand the jurisdiction of the editor focus. * * @param {CKEDITOR.dom.element} element The container (topmost) element of one UI part. * @param {Boolean} isCapture If specified, {@link CKEDITOR.event#useCapture} will be used when listening to the focus event. * @member CKEDITOR.focusManager */ add: function( element, isCapture ) { var fm = element.getCustomData( SLOT_NAME ); if ( !fm || fm != this ) { // If this element is already taken by another instance, dismiss it first. fm && fm.remove( element ); var focusEvent = 'focus', blurEvent = 'blur'; // Bypass the element's internal DOM focus change. if ( isCapture ) { // Use "focusin/focusout" events instead of capture phase in IEs, // which fires synchronously. if ( CKEDITOR.env.ie ) { focusEvent = 'focusin'; blurEvent = 'focusout'; } else { CKEDITOR.event.useCapture = 1; } } var listeners = { blur: function() { if ( element.equals( this.currentActive ) ) this.blur(); }, focus: function() { this.focus( element ); } }; element.on( focusEvent, listeners.focus, this ); element.on( blurEvent, listeners.blur, this ); if ( isCapture ) CKEDITOR.event.useCapture = 0; element.setCustomData( SLOT_NAME, this ); element.setCustomData( SLOT_NAME_LISTENERS, listeners ); } }, /** * Dismisses an element from the focus manager delegations added by {@link #add}. * * @param {CKEDITOR.dom.element} element The element to be removed from the focus manager. * @member CKEDITOR.focusManager */ remove: function( element ) { element.removeCustomData( SLOT_NAME ); var listeners = element.removeCustomData( SLOT_NAME_LISTENERS ); element.removeListener( 'blur', listeners.blur ); element.removeListener( 'focus', listeners.focus ); } }; } )(); /** * Fired when the editor instance receives the input focus. * * editor.on( 'focus', function( e ) { * alert( 'The editor named ' + e.editor.name + ' is now focused' ); * } ); * * @event focus * @member CKEDITOR.editor * @param {CKEDITOR.editor} editor The editor instance. */ /** * Fired when the editor instance loses the input focus. * * **Note:** This event will **NOT** be triggered when focus is moved internally, e.g. from * an editable to another part of the editor UI like a dialog window. * If you are interested only in the focus state of the editable, listen to the `focus` * and `blur` events of the {@link CKEDITOR.editable} instead. * * editor.on( 'blur', function( e ) { * alert( 'The editor named ' + e.editor.name + ' lost the focus' ); * } ); * * @event blur * @member CKEDITOR.editor * @param {CKEDITOR.editor} editor The editor instance. */