diff options
Diffstat (limited to 'sources/core/focusmanager.js')
-rw-r--r-- | sources/core/focusmanager.js | 281 |
1 files changed, 281 insertions, 0 deletions
diff --git a/sources/core/focusmanager.js b/sources/core/focusmanager.js new file mode 100644 index 0000000..6fc9969 --- /dev/null +++ b/sources/core/focusmanager.js | |||
@@ -0,0 +1,281 @@ | |||
1 | /** | ||
2 | * @license Copyright (c) 2003-2017, 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 | var editor = this._.editor; | ||
155 | |||
156 | if ( this.hasFocus ) { | ||
157 | this.hasFocus = false; | ||
158 | |||
159 | // Blink browsers leave selection in `[contenteditable=true]` | ||
160 | // when it's blurred and it's neccessary to remove it manually for inline editor. (#13446) | ||
161 | if ( CKEDITOR.env.chrome && editor.editable().isInline() ) { | ||
162 | editor.window.$.getSelection().removeAllRanges(); | ||
163 | } | ||
164 | |||
165 | var ct = this._.editor.container; | ||
166 | ct && ct.removeClass( 'cke_focus' ); | ||
167 | this._.editor.fire( 'blur' ); | ||
168 | } | ||
169 | } | ||
170 | |||
171 | if ( this._.timer ) | ||
172 | clearTimeout( this._.timer ); | ||
173 | |||
174 | var delay = CKEDITOR.focusManager._.blurDelay; | ||
175 | if ( noDelay || !delay ) | ||
176 | doBlur.call( this ); | ||
177 | else { | ||
178 | this._.timer = CKEDITOR.tools.setTimeout( function() { | ||
179 | delete this._.timer; | ||
180 | doBlur.call( this ); | ||
181 | }, delay, this ); | ||
182 | } | ||
183 | }, | ||
184 | |||
185 | /** | ||
186 | * Registers a UI DOM element to the focus manager, which will make the focus manager "hasFocus" | ||
187 | * once the input focus is relieved on the element. | ||
188 | * This method is designed to be used by plugins to expand the jurisdiction of the editor focus. | ||
189 | * | ||
190 | * @param {CKEDITOR.dom.element} element The container (topmost) element of one UI part. | ||
191 | * @param {Boolean} isCapture If specified, {@link CKEDITOR.event#useCapture} will be used when listening to the focus event. | ||
192 | * @member CKEDITOR.focusManager | ||
193 | */ | ||
194 | add: function( element, isCapture ) { | ||
195 | var fm = element.getCustomData( SLOT_NAME ); | ||
196 | if ( !fm || fm != this ) { | ||
197 | // If this element is already taken by another instance, dismiss it first. | ||
198 | fm && fm.remove( element ); | ||
199 | |||
200 | var focusEvent = 'focus', | ||
201 | blurEvent = 'blur'; | ||
202 | |||
203 | // Bypass the element's internal DOM focus change. | ||
204 | if ( isCapture ) { | ||
205 | |||
206 | // Use "focusin/focusout" events instead of capture phase in IEs, | ||
207 | // which fires synchronously. | ||
208 | if ( CKEDITOR.env.ie ) { | ||
209 | focusEvent = 'focusin'; | ||
210 | blurEvent = 'focusout'; | ||
211 | } else { | ||
212 | CKEDITOR.event.useCapture = 1; | ||
213 | } | ||
214 | } | ||
215 | |||
216 | var listeners = { | ||
217 | blur: function() { | ||
218 | if ( element.equals( this.currentActive ) ) | ||
219 | this.blur(); | ||
220 | }, | ||
221 | focus: function() { | ||
222 | this.focus( element ); | ||
223 | } | ||
224 | }; | ||
225 | |||
226 | element.on( focusEvent, listeners.focus, this ); | ||
227 | element.on( blurEvent, listeners.blur, this ); | ||
228 | |||
229 | if ( isCapture ) | ||
230 | CKEDITOR.event.useCapture = 0; | ||
231 | |||
232 | element.setCustomData( SLOT_NAME, this ); | ||
233 | element.setCustomData( SLOT_NAME_LISTENERS, listeners ); | ||
234 | } | ||
235 | }, | ||
236 | |||
237 | /** | ||
238 | * Dismisses an element from the focus manager delegations added by {@link #add}. | ||
239 | * | ||
240 | * @param {CKEDITOR.dom.element} element The element to be removed from the focus manager. | ||
241 | * @member CKEDITOR.focusManager | ||
242 | */ | ||
243 | remove: function( element ) { | ||
244 | element.removeCustomData( SLOT_NAME ); | ||
245 | var listeners = element.removeCustomData( SLOT_NAME_LISTENERS ); | ||
246 | element.removeListener( 'blur', listeners.blur ); | ||
247 | element.removeListener( 'focus', listeners.focus ); | ||
248 | } | ||
249 | |||
250 | }; | ||
251 | |||
252 | } )(); | ||
253 | |||
254 | /** | ||
255 | * Fired when the editor instance receives the input focus. | ||
256 | * | ||
257 | * editor.on( 'focus', function( e ) { | ||
258 | * alert( 'The editor named ' + e.editor.name + ' is now focused' ); | ||
259 | * } ); | ||
260 | * | ||
261 | * @event focus | ||
262 | * @member CKEDITOR.editor | ||
263 | * @param {CKEDITOR.editor} editor The editor instance. | ||
264 | */ | ||
265 | |||
266 | /** | ||
267 | * Fired when the editor instance loses the input focus. | ||
268 | * | ||
269 | * **Note:** This event will **NOT** be triggered when focus is moved internally, e.g. from | ||
270 | * an editable to another part of the editor UI like a dialog window. | ||
271 | * If you are interested only in the focus state of the editable, listen to the `focus` | ||
272 | * and `blur` events of the {@link CKEDITOR.editable} instead. | ||
273 | * | ||
274 | * editor.on( 'blur', function( e ) { | ||
275 | * alert( 'The editor named ' + e.editor.name + ' lost the focus' ); | ||
276 | * } ); | ||
277 | * | ||
278 | * @event blur | ||
279 | * @member CKEDITOR.editor | ||
280 | * @param {CKEDITOR.editor} editor The editor instance. | ||
281 | */ | ||