]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - jssource/src_files/include/javascript/yui3/build/event-valuechange/event-valuechange.js
Release 6.5.0
[Github/sugarcrm.git] / jssource / src_files / include / javascript / yui3 / build / event-valuechange / event-valuechange.js
1 /*
2 Copyright (c) 2010, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.com/yui/license.html
5 version: 3.3.0
6 build: 3167
7 */
8 YUI.add('event-valuechange', function(Y) {
9
10 /**
11  * Adds a synthetic <code>valueChange</code> event that fires when the
12  * <code>value</code> property of an input field or textarea changes as a result
13  * of a keystroke, mouse operation, or input method editor (IME) input event.
14  *
15  * @module event-valuechange
16  */
17
18 /**
19  * Provides the implementation for the synthetic <code>valueChange</code> event.
20  *
21  * @class ValueChange
22  * @static
23  */
24
25 var YArray = Y.Array,
26
27     VALUE = 'value',
28
29 // Just a simple namespace to make methods overridable.
30 VC = {
31     // -- Static Constants -----------------------------------------------------
32
33     /**
34      * Interval (in milliseconds) at which to poll for changes to the value of
35      * an element with one or more <code>valueChange</code> subscribers when the
36      * user is likely to be interacting with it.
37      *
38      * @property POLL_INTERVAL
39      * @type Number
40      * @default 50
41      * @static
42      */
43     POLL_INTERVAL: 50,
44
45     /**
46      * Timeout (in milliseconds) after which to stop polling when there hasn't
47      * been any new activity (keypresses, mouse clicks, etc.) on an element.
48      *
49      * @property TIMEOUT
50      * @type Number
51      * @default 10000
52      * @static
53      */
54     TIMEOUT: 10000,
55
56     // -- Protected Static Properties ------------------------------------------
57     _history  : {},
58     _intervals: {},
59     _notifiers: {},
60     _timeouts : {},
61
62     // -- Protected Static Methods ---------------------------------------------
63
64     /**
65      * Called at an interval to poll for changes to the value of the specified
66      * node.
67      *
68      * @method _poll
69      * @param {Node} node
70      * @param {String} stamp
71      * @param {EventFacade} e
72      * @protected
73      * @static
74      */
75     _poll: function (node, stamp, e) {
76         var domNode = node._node, // performance cheat; getValue() is a big hit when polling
77             newVal  = domNode && domNode.value,
78             prevVal = VC._history[stamp],
79             facade;
80
81         if (!domNode) {
82             VC._stopPolling(node, stamp);
83             return;
84         }
85
86         if (newVal !== prevVal) {
87             VC._history[stamp] = newVal;
88
89             facade = {
90                 _event : e,
91                 newVal : newVal,
92                 prevVal: prevVal
93             };
94
95             YArray.each(VC._notifiers[stamp], function (notifier) {
96                 notifier.fire(facade);
97             });
98
99             VC._refreshTimeout(node, stamp);
100         }
101     },
102
103     /**
104      * Restarts the inactivity timeout for the specified node.
105      *
106      * @method _refreshTimeout
107      * @param {Node} node
108      * @param {String} stamp
109      * @protected
110      * @static
111      */
112     _refreshTimeout: function (node, stamp) {
113         VC._stopTimeout(node, stamp); // avoid dupes
114
115         // If we don't see any changes within the timeout period (10 seconds by
116         // default), stop polling.
117         VC._timeouts[stamp] = setTimeout(function () {
118             VC._stopPolling(node, stamp);
119         }, VC.TIMEOUT);
120
121     },
122
123     /**
124      * Begins polling for changes to the <code>value</code> property of the
125      * specified node. If polling is already underway for the specified node,
126      * it will not be restarted unless the <i>force</i> parameter is
127      * <code>true</code>
128      *
129      * @method _startPolling
130      * @param {Node} node Node to watch.
131      * @param {String} stamp (optional) Object stamp for the node. Will be
132      *   generated if not provided (provide it to improve performance).
133      * @param {EventFacade} e (optional) Event facade of the event that
134      *   initiated the polling (if any).
135      * @param {Boolean} force (optional) If <code>true</code>, polling will be
136      *   restarted even if we're already polling this node.
137      * @protected
138      * @static
139      */
140     _startPolling: function (node, stamp, e, force) {
141         if (!stamp) {
142             stamp = Y.stamp(node);
143         }
144
145         // Don't bother continuing if we're already polling.
146         if (!force && VC._intervals[stamp]) {
147             return;
148         }
149
150         VC._stopPolling(node, stamp); // avoid dupes
151
152         // Poll for changes to the node's value. We can't rely on keyboard
153         // events for this, since the value may change due to a mouse-initiated
154         // paste event, an IME input event, or for some other reason that
155         // doesn't trigger a key event.
156         VC._intervals[stamp] = setInterval(function () {
157             VC._poll(node, stamp, e);
158         }, VC.POLL_INTERVAL);
159
160         VC._refreshTimeout(node, stamp, e);
161
162     },
163
164     /**
165      * Stops polling for changes to the specified node's <code>value</code>
166      * attribute.
167      *
168      * @method _stopPolling
169      * @param {Node} node
170      * @param {String} stamp (optional)
171      * @protected
172      * @static
173      */
174     _stopPolling: function (node, stamp) {
175         if (!stamp) {
176             stamp = Y.stamp(node);
177         }
178
179         VC._intervals[stamp] = clearInterval(VC._intervals[stamp]);
180         VC._stopTimeout(node, stamp);
181
182     },
183
184     /**
185      * Clears the inactivity timeout for the specified node, if any.
186      *
187      * @method _stopTimeout
188      * @param {Node} node
189      * @param {String} stamp (optional)
190      * @protected
191      * @static
192      */
193     _stopTimeout: function (node, stamp) {
194         if (!stamp) {
195             stamp = Y.stamp(node);
196         }
197
198         VC._timeouts[stamp] = clearTimeout(VC._timeouts[stamp]);
199     },
200
201     // -- Protected Static Event Handlers --------------------------------------
202
203     /**
204      * Stops polling when a node's blur event fires.
205      *
206      * @method _onBlur
207      * @param {EventFacade} e
208      * @protected
209      * @static
210      */
211     _onBlur: function (e) {
212         VC._stopPolling(e.currentTarget);
213     },
214
215     /**
216      * Resets a node's history and starts polling when a focus event occurs.
217      *
218      * @method _onFocus
219      * @param {EventFacade} e
220      * @protected
221      * @static
222      */
223     _onFocus: function (e) {
224         var node = e.currentTarget;
225
226         VC._history[Y.stamp(node)] = node.get(VALUE);
227         VC._startPolling(node, null, e);
228     },
229
230     /**
231      * Starts polling when a node receives a keyDown event.
232      *
233      * @method _onKeyDown
234      * @param {EventFacade} e
235      * @protected
236      * @static
237      */
238     _onKeyDown: function (e) {
239         VC._startPolling(e.currentTarget, null, e);
240     },
241
242     /**
243      * Starts polling when an IME-related keyUp event occurs on a node.
244      *
245      * @method _onKeyUp
246      * @param {EventFacade} e
247      * @protected
248      * @static
249      */
250     _onKeyUp: function (e) {
251         // These charCodes indicate that an IME has started. We'll restart
252         // polling and give the IME up to 10 seconds (by default) to finish.
253         if (e.charCode === 229 || e.charCode === 197) {
254             VC._startPolling(e.currentTarget, null, e, true);
255         }
256     },
257
258     /**
259      * Starts polling when a node receives a mouseDown event.
260      *
261      * @method _onMouseDown
262      * @param {EventFacade} e
263      * @protected
264      * @static
265      */
266     _onMouseDown: function (e) {
267         VC._startPolling(e.currentTarget, null, e);
268     },
269
270     /**
271      * Called when event-valuechange receives a new subscriber.
272      *
273      * @method _onSubscribe
274      * @param {Node} node
275      * @param {Subscription} subscription
276      * @param {SyntheticEvent.Notifier} notifier
277      * @protected
278      * @static
279      */
280     _onSubscribe: function (node, subscription, notifier) {
281         var stamp     = Y.stamp(node),
282             notifiers = VC._notifiers[stamp];
283
284         VC._history[stamp] = node.get(VALUE);
285
286         notifier._handles = node.on({
287             blur     : VC._onBlur,
288             focus    : VC._onFocus,
289             keydown  : VC._onKeyDown,
290             keyup    : VC._onKeyUp,
291             mousedown: VC._onMouseDown
292         });
293
294         if (!notifiers) {
295             notifiers = VC._notifiers[stamp] = [];
296         }
297
298         notifiers.push(notifier);
299     },
300
301     /**
302      * Called when event-valuechange loses a subscriber.
303      *
304      * @method _onUnsubscribe
305      * @param {Node} node
306      * @param {Subscription} subscription
307      * @param {SyntheticEvent.Notifier} notifier
308      * @protected
309      * @static
310      */
311     _onUnsubscribe: function (node, subscription, notifier) {
312         var stamp     = Y.stamp(node),
313             notifiers = VC._notifiers[stamp],
314             index     = YArray.indexOf(notifiers, notifier);
315
316         notifier._handles.detach();
317
318         if (index !== -1) {
319             notifiers.splice(index, 1);
320
321             if (!notifiers.length) {
322                 VC._stopPolling(node, stamp);
323
324                 delete VC._notifiers[stamp];
325                 delete VC._history[stamp];
326             }
327         }
328     }
329 };
330
331 /**
332  * <p>
333  * Synthetic event that fires when the <code>value</code> property of an input
334  * field or textarea changes as a result of a keystroke, mouse operation, or
335  * input method editor (IME) input event.
336  * </p>
337  *
338  * <p>
339  * Unlike the <code>onchange</code> event, this event fires when the value
340  * actually changes and not when the element loses focus. This event also
341  * reports IME and multi-stroke input more reliably than <code>oninput</code> or
342  * the various key events across browsers.
343  * </p>
344  *
345  * <p>
346  * This event is provided by the <code>event-valuechange</code> module.
347  * </p>
348  *
349  * <p>
350  * <strong>Usage example:</strong>
351  * </p>
352  *
353  * <code><pre>
354  * YUI().use('event-valuechange', function (Y) {
355  * &nbsp;&nbsp;Y.one('input').on('valueChange', function (e) {
356  * &nbsp;&nbsp;&nbsp;&nbsp;// Handle valueChange events on the first input element on the page.
357  * &nbsp;&nbsp;});
358  * });
359  * </pre></code>
360  *
361  * @event valueChange
362  * @param {EventFacade} e Event facade with the following additional
363  *   properties:
364  *
365  * <dl>
366  *   <dt>prevVal (String)</dt>
367  *   <dd>
368  *     Previous value before the latest change.
369  *   </dd>
370  *
371  *   <dt>newVal (String)</dt>
372  *   <dd>
373  *     New value after the latest change.
374  *   </dd>
375  * </dl>
376  *
377  * @for YUI
378  */
379
380 Y.Event.define('valueChange', {
381     detach: VC._onUnsubscribe,
382     on    : VC._onSubscribe,
383
384     publishConfig: {
385         emitFacade: true
386     }
387 });
388
389 Y.ValueChange = VC;
390
391
392 }, '3.3.0' ,{requires:['event-focus', 'event-synthetic']});