Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/third_party/closure/goog/events/imehandler.js
2868 views
1
// Copyright 2010 The Closure Library Authors. All Rights Reserved.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
// http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS-IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
/**
16
* @fileoverview Input Method Editors (IMEs) are OS-level widgets that make
17
* it easier to type non-ascii characters on ascii keyboards (in particular,
18
* characters that require more than one keystroke).
19
*
20
* When the user wants to type such a character, a modal menu pops up and
21
* suggests possible "next" characters in the IME character sequence. After
22
* typing N characters, the user hits "enter" to commit the IME to the field.
23
* N differs from language to language.
24
*
25
* This class offers high-level events for how the user is interacting with the
26
* IME in editable regions.
27
*
28
* Known Issues:
29
*
30
* Firefox always fires an extra pair of compositionstart/compositionend events.
31
* We do not normalize for this.
32
*
33
* Opera does not fire any IME events.
34
*
35
* Spurious UPDATE events are common on all browsers.
36
*
37
* We currently do a bad job detecting when the IME closes on IE, and
38
* make a "best effort" guess on when we know it's closed.
39
*
40
* @author [email protected] (Nick Santos) (Ported to Closure)
41
*/
42
43
goog.provide('goog.events.ImeHandler');
44
goog.provide('goog.events.ImeHandler.Event');
45
goog.provide('goog.events.ImeHandler.EventType');
46
47
goog.require('goog.events.Event');
48
goog.require('goog.events.EventHandler');
49
goog.require('goog.events.EventTarget');
50
goog.require('goog.events.EventType');
51
goog.require('goog.events.KeyCodes');
52
goog.require('goog.userAgent');
53
54
55
56
/**
57
* Dispatches high-level events for IMEs.
58
* @param {Element} el The element to listen on.
59
* @extends {goog.events.EventTarget}
60
* @constructor
61
* @final
62
*/
63
goog.events.ImeHandler = function(el) {
64
goog.events.ImeHandler.base(this, 'constructor');
65
66
/**
67
* The element to listen on.
68
* @type {Element}
69
* @private
70
*/
71
this.el_ = el;
72
73
/**
74
* Tracks the keyup event only, because it has a different life-cycle from
75
* other events.
76
* @type {goog.events.EventHandler<!goog.events.ImeHandler>}
77
* @private
78
*/
79
this.keyUpHandler_ = new goog.events.EventHandler(this);
80
81
/**
82
* Tracks all the browser events.
83
* @type {goog.events.EventHandler<!goog.events.ImeHandler>}
84
* @private
85
*/
86
this.handler_ = new goog.events.EventHandler(this);
87
88
if (goog.events.ImeHandler.USES_COMPOSITION_EVENTS) {
89
this.handler_
90
.listen(
91
el, goog.events.EventType.COMPOSITIONSTART,
92
this.handleCompositionStart_)
93
.listen(
94
el, goog.events.EventType.COMPOSITIONEND,
95
this.handleCompositionEnd_)
96
.listen(
97
el, goog.events.EventType.COMPOSITIONUPDATE,
98
this.handleTextModifyingInput_);
99
}
100
101
this.handler_
102
.listen(el, goog.events.EventType.TEXTINPUT, this.handleTextInput_)
103
.listen(el, goog.events.EventType.TEXT, this.handleTextModifyingInput_)
104
.listen(el, goog.events.EventType.KEYDOWN, this.handleKeyDown_);
105
};
106
goog.inherits(goog.events.ImeHandler, goog.events.EventTarget);
107
108
109
/**
110
* Event types fired by ImeHandler. These events do not make any guarantees
111
* about whether they were fired before or after the event in question.
112
* @enum {string}
113
*/
114
goog.events.ImeHandler.EventType = {
115
// After the IME opens.
116
START: 'startIme',
117
118
// An update to the state of the IME. An 'update' does not necessarily mean
119
// that the text contents of the field were modified in any way.
120
UPDATE: 'updateIme',
121
122
// After the IME closes.
123
END: 'endIme'
124
};
125
126
127
128
/**
129
* An event fired by ImeHandler.
130
* @param {goog.events.ImeHandler.EventType} type The type.
131
* @param {goog.events.BrowserEvent} reason The trigger for this event.
132
* @constructor
133
* @extends {goog.events.Event}
134
* @final
135
*/
136
goog.events.ImeHandler.Event = function(type, reason) {
137
goog.events.ImeHandler.Event.base(this, 'constructor', type);
138
139
/**
140
* The event that triggered this.
141
* @type {goog.events.BrowserEvent}
142
*/
143
this.reason = reason;
144
};
145
goog.inherits(goog.events.ImeHandler.Event, goog.events.Event);
146
147
148
/**
149
* Whether to use the composition events.
150
* @type {boolean}
151
*/
152
goog.events.ImeHandler.USES_COMPOSITION_EVENTS = goog.userAgent.GECKO ||
153
(goog.userAgent.WEBKIT && goog.userAgent.isVersionOrHigher(532));
154
155
156
/**
157
* Stores whether IME mode is active.
158
* @type {boolean}
159
* @private
160
*/
161
goog.events.ImeHandler.prototype.imeMode_ = false;
162
163
164
/**
165
* The keyCode value of the last keyDown event. This value is used for
166
* identiying whether or not a textInput event is sent by an IME.
167
* @type {number}
168
* @private
169
*/
170
goog.events.ImeHandler.prototype.lastKeyCode_ = 0;
171
172
173
/**
174
* @return {boolean} Whether an IME is active.
175
*/
176
goog.events.ImeHandler.prototype.isImeMode = function() {
177
return this.imeMode_;
178
};
179
180
181
/**
182
* Handles the compositionstart event.
183
* @param {goog.events.BrowserEvent} e The event.
184
* @private
185
*/
186
goog.events.ImeHandler.prototype.handleCompositionStart_ = function(e) {
187
this.handleImeActivate_(e);
188
};
189
190
191
/**
192
* Handles the compositionend event.
193
* @param {goog.events.BrowserEvent} e The event.
194
* @private
195
*/
196
goog.events.ImeHandler.prototype.handleCompositionEnd_ = function(e) {
197
this.handleImeDeactivate_(e);
198
};
199
200
201
/**
202
* Handles the compositionupdate and text events.
203
* @param {goog.events.BrowserEvent} e The event.
204
* @private
205
*/
206
goog.events.ImeHandler.prototype.handleTextModifyingInput_ = function(e) {
207
if (this.isImeMode()) {
208
this.processImeComposition_(e);
209
}
210
};
211
212
213
/**
214
* Handles IME activation.
215
* @param {goog.events.BrowserEvent} e The event.
216
* @private
217
*/
218
goog.events.ImeHandler.prototype.handleImeActivate_ = function(e) {
219
if (this.imeMode_) {
220
return;
221
}
222
223
// Listens for keyup events to handle unexpected IME keydown events on older
224
// versions of webkit.
225
//
226
// In those versions, we currently use textInput events deactivate IME
227
// (see handleTextInput_() for the reason). However,
228
// Safari fires a keydown event (as a result of pressing keys to commit IME
229
// text) with keyCode == WIN_IME after textInput event. This activates IME
230
// mode again unnecessarily. To prevent this problem, listens keyup events
231
// which can use to determine whether IME text has been committed.
232
if (goog.userAgent.WEBKIT &&
233
!goog.events.ImeHandler.USES_COMPOSITION_EVENTS) {
234
this.keyUpHandler_.listen(
235
this.el_, goog.events.EventType.KEYUP, this.handleKeyUpSafari4_);
236
}
237
238
this.imeMode_ = true;
239
this.dispatchEvent(
240
new goog.events.ImeHandler.Event(
241
goog.events.ImeHandler.EventType.START, e));
242
};
243
244
245
/**
246
* Handles the IME compose changes.
247
* @param {goog.events.BrowserEvent} e The event.
248
* @private
249
*/
250
goog.events.ImeHandler.prototype.processImeComposition_ = function(e) {
251
this.dispatchEvent(
252
new goog.events.ImeHandler.Event(
253
goog.events.ImeHandler.EventType.UPDATE, e));
254
};
255
256
257
/**
258
* Handles IME deactivation.
259
* @param {goog.events.BrowserEvent} e The event.
260
* @private
261
*/
262
goog.events.ImeHandler.prototype.handleImeDeactivate_ = function(e) {
263
this.imeMode_ = false;
264
this.keyUpHandler_.removeAll();
265
this.dispatchEvent(
266
new goog.events.ImeHandler.Event(
267
goog.events.ImeHandler.EventType.END, e));
268
};
269
270
271
/**
272
* Handles a key down event.
273
* @param {!goog.events.BrowserEvent} e The event.
274
* @private
275
*/
276
goog.events.ImeHandler.prototype.handleKeyDown_ = function(e) {
277
// Firefox and Chrome have a separate event for IME composition ('text'
278
// and 'compositionupdate', respectively), other browsers do not.
279
if (!goog.events.ImeHandler.USES_COMPOSITION_EVENTS) {
280
var imeMode = this.isImeMode();
281
// If we're in IE and we detect an IME input on keyDown then activate
282
// the IME, otherwise if the imeMode was previously active, deactivate.
283
if (!imeMode && e.keyCode == goog.events.KeyCodes.WIN_IME) {
284
this.handleImeActivate_(e);
285
} else if (imeMode && e.keyCode != goog.events.KeyCodes.WIN_IME) {
286
if (goog.events.ImeHandler.isImeDeactivateKeyEvent_(e)) {
287
this.handleImeDeactivate_(e);
288
}
289
} else if (imeMode) {
290
this.processImeComposition_(e);
291
}
292
}
293
294
// Safari on Mac doesn't send IME events in the right order so that we must
295
// ignore some modifier key events to insert IME text correctly.
296
if (goog.events.ImeHandler.isImeDeactivateKeyEvent_(e)) {
297
this.lastKeyCode_ = e.keyCode;
298
}
299
};
300
301
302
/**
303
* Handles a textInput event.
304
* @param {!goog.events.BrowserEvent} e The event.
305
* @private
306
*/
307
goog.events.ImeHandler.prototype.handleTextInput_ = function(e) {
308
// Some WebKit-based browsers including Safari 4 don't send composition
309
// events. So, we turn down IME mode when it's still there.
310
if (!goog.events.ImeHandler.USES_COMPOSITION_EVENTS &&
311
goog.userAgent.WEBKIT &&
312
this.lastKeyCode_ == goog.events.KeyCodes.WIN_IME && this.isImeMode()) {
313
this.handleImeDeactivate_(e);
314
}
315
};
316
317
318
/**
319
* Handles the key up event for any IME activity. This handler is just used to
320
* prevent activating IME unnecessary in Safari at this time.
321
* @param {!goog.events.BrowserEvent} e The event.
322
* @private
323
*/
324
goog.events.ImeHandler.prototype.handleKeyUpSafari4_ = function(e) {
325
if (this.isImeMode()) {
326
switch (e.keyCode) {
327
// These keyup events indicates that IME text has been committed or
328
// cancelled. We should turn off IME mode when these keyup events
329
// received.
330
case goog.events.KeyCodes.ENTER:
331
case goog.events.KeyCodes.TAB:
332
case goog.events.KeyCodes.ESC:
333
this.handleImeDeactivate_(e);
334
break;
335
}
336
}
337
};
338
339
340
/**
341
* Returns whether the given event should be treated as an IME
342
* deactivation trigger.
343
* @param {!goog.events.Event} e The event.
344
* @return {boolean} Whether the given event is an IME deactivate trigger.
345
* @private
346
*/
347
goog.events.ImeHandler.isImeDeactivateKeyEvent_ = function(e) {
348
// Which key events involve IME deactivation depends on the user's
349
// environment (i.e. browsers, platforms, and IMEs). Usually Shift key
350
// and Ctrl key does not involve IME deactivation, so we currently assume
351
// that these keys are not IME deactivation trigger.
352
switch (e.keyCode) {
353
case goog.events.KeyCodes.SHIFT:
354
case goog.events.KeyCodes.CTRL:
355
return false;
356
default:
357
return true;
358
}
359
};
360
361
362
/** @override */
363
goog.events.ImeHandler.prototype.disposeInternal = function() {
364
this.handler_.dispose();
365
this.keyUpHandler_.dispose();
366
this.el_ = null;
367
goog.events.ImeHandler.base(this, 'disposeInternal');
368
};
369
370