Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/third_party/closure/goog/events/inputhandler.js
2868 views
1
// Copyright 2006 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 An object that encapsulates text changed events for textareas
17
* and input element of type text and password. The event occurs after the value
18
* has been changed. The event does not occur if value was changed
19
* programmatically.<br>
20
* <br>
21
* Note: this does not guarantee the correctness of {@code keyCode} or
22
* {@code charCode}, or attempt to unify them across browsers. See
23
* {@code goog.events.KeyHandler} for that functionality<br>
24
* <br>
25
* Known issues:
26
* <ul>
27
* <li>IE doesn't have native support for input event. WebKit before version 531
28
* doesn't have support for textareas. For those browsers an emulation mode
29
* based on key, clipboard and drop events is used. Thus this event won't
30
* trigger in emulation mode if text was modified by context menu commands
31
* such as 'Undo' and 'Delete'.
32
* </ul>
33
* @author [email protected] (Erik Arvidsson)
34
* @see ../demos/inputhandler.html
35
*/
36
37
goog.provide('goog.events.InputHandler');
38
goog.provide('goog.events.InputHandler.EventType');
39
40
goog.require('goog.Timer');
41
goog.require('goog.dom.TagName');
42
goog.require('goog.events.BrowserEvent');
43
goog.require('goog.events.EventHandler');
44
goog.require('goog.events.EventTarget');
45
goog.require('goog.events.KeyCodes');
46
goog.require('goog.userAgent');
47
48
49
50
/**
51
* This event handler will dispatch events when the user types into a text
52
* input, password input or a textarea
53
* @param {Element} element The element that you want to listen for input
54
* events on.
55
* @constructor
56
* @extends {goog.events.EventTarget}
57
*/
58
goog.events.InputHandler = function(element) {
59
goog.events.InputHandler.base(this, 'constructor');
60
61
/**
62
* Id of a timer used to postpone firing input event in emulation mode.
63
* @type {?number}
64
* @private
65
*/
66
this.timer_ = null;
67
68
/**
69
* The element that you want to listen for input events on.
70
* @type {Element}
71
* @private
72
*/
73
this.element_ = element;
74
75
// Determine whether input event should be emulated.
76
// IE8 doesn't support input events. We could use property change events but
77
// they are broken in many ways:
78
// - Fire even if value was changed programmatically.
79
// - Aren't always delivered. For example, if you change value or even width
80
// of input programmatically, next value change made by user won't fire an
81
// event.
82
// IE9 supports input events when characters are inserted, but not deleted.
83
// WebKit before version 531 did not support input events for textareas.
84
var emulateInputEvents = goog.userAgent.IE || goog.userAgent.EDGE ||
85
(goog.userAgent.WEBKIT && !goog.userAgent.isVersionOrHigher('531') &&
86
element.tagName == goog.dom.TagName.TEXTAREA);
87
88
/**
89
* @type {goog.events.EventHandler<!goog.events.InputHandler>}
90
* @private
91
*/
92
this.eventHandler_ = new goog.events.EventHandler(this);
93
94
// Even if input event emulation is enabled, still listen for input events
95
// since they may be partially supported by the browser (such as IE9).
96
// If the input event does fire, we will be able to dispatch synchronously.
97
// (InputHandler events being asynchronous for IE is a common issue for
98
// cases like auto-grow textareas where they result in a quick flash of
99
// scrollbars between the textarea content growing and it being resized to
100
// fit.)
101
this.eventHandler_.listen(
102
this.element_,
103
emulateInputEvents ? ['keydown', 'paste', 'cut', 'drop', 'input'] :
104
'input',
105
this);
106
};
107
goog.inherits(goog.events.InputHandler, goog.events.EventTarget);
108
109
110
/**
111
* Enum type for the events fired by the input handler
112
* @enum {string}
113
*/
114
goog.events.InputHandler.EventType = {
115
INPUT: 'input'
116
};
117
118
119
/**
120
* This handles the underlying events and dispatches a new event as needed.
121
* @param {goog.events.BrowserEvent} e The underlying browser event.
122
*/
123
goog.events.InputHandler.prototype.handleEvent = function(e) {
124
if (e.type == 'input') {
125
// http://stackoverflow.com/questions/18389732/changing-placeholder-triggers-input-event-in-ie-10
126
// IE 10+ fires an input event when there are inputs with placeholders.
127
// It fires the event with keycode 0, so if we detect it we don't
128
// propagate the input event.
129
if (goog.userAgent.IE && goog.userAgent.isVersionOrHigher(10) &&
130
e.keyCode == 0 && e.charCode == 0) {
131
return;
132
}
133
// This event happens after all the other events we listen to, so cancel
134
// an asynchronous event dispatch if we have it queued up. Otherwise, we
135
// will end up firing an extra event.
136
this.cancelTimerIfSet_();
137
138
this.dispatchEvent(this.createInputEvent_(e));
139
} else {
140
// Filter out key events that don't modify text.
141
if (e.type == 'keydown' &&
142
!goog.events.KeyCodes.isTextModifyingKeyEvent(e)) {
143
return;
144
}
145
146
// It is still possible that pressed key won't modify the value of an
147
// element. Storing old value will help us to detect modification but is
148
// also a little bit dangerous. If value is changed programmatically in
149
// another key down handler, we will detect it as user-initiated change.
150
var valueBeforeKey = e.type == 'keydown' ? this.element_.value : null;
151
152
// In IE on XP, IME the element's value has already changed when we get
153
// keydown events when the user is using an IME. In this case, we can't
154
// check the current value normally, so we assume that it's a modifying key
155
// event. This means that ENTER when used to commit will fire a spurious
156
// input event, but it's better to have a false positive than let some input
157
// slip through the cracks.
158
if (goog.userAgent.IE && e.keyCode == goog.events.KeyCodes.WIN_IME) {
159
valueBeforeKey = null;
160
}
161
162
// Create an input event now, because when we fire it on timer, the
163
// underlying event will already be disposed.
164
var inputEvent = this.createInputEvent_(e);
165
166
// Since key down, paste, cut and drop events are fired before actual value
167
// of the element has changed, we need to postpone dispatching input event
168
// until value is updated.
169
this.cancelTimerIfSet_();
170
this.timer_ = goog.Timer.callOnce(function() {
171
this.timer_ = null;
172
if (this.element_.value != valueBeforeKey) {
173
this.dispatchEvent(inputEvent);
174
}
175
}, 0, this);
176
}
177
};
178
179
180
/**
181
* Cancels timer if it is set, does nothing otherwise.
182
* @private
183
*/
184
goog.events.InputHandler.prototype.cancelTimerIfSet_ = function() {
185
if (this.timer_ != null) {
186
goog.Timer.clear(this.timer_);
187
this.timer_ = null;
188
}
189
};
190
191
192
/**
193
* Creates an input event from the browser event.
194
* @param {goog.events.BrowserEvent} be A browser event.
195
* @return {!goog.events.BrowserEvent} An input event.
196
* @private
197
*/
198
goog.events.InputHandler.prototype.createInputEvent_ = function(be) {
199
var e = new goog.events.BrowserEvent(be.getBrowserEvent());
200
e.type = goog.events.InputHandler.EventType.INPUT;
201
return e;
202
};
203
204
205
/** @override */
206
goog.events.InputHandler.prototype.disposeInternal = function() {
207
goog.events.InputHandler.base(this, 'disposeInternal');
208
this.eventHandler_.dispose();
209
this.cancelTimerIfSet_();
210
delete this.element_;
211
};
212
213