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