src/cm/media/js/lib/yui/yui3-3.15.0/build/event-valuechange/event-valuechange-debug.js
changeset 602 e16a97fb364a
equal deleted inserted replaced
601:d334a616c023 602:e16a97fb364a
       
     1 YUI.add('event-valuechange', function (Y, NAME) {
       
     2 
       
     3 /**
       
     4 Adds a synthetic `valuechange` event that fires when the `value` property of an
       
     5 `<input>`, `<textarea>`, `<select>`, or `[contenteditable="true"]` node changes
       
     6 as a result of a keystroke, mouse operation, or input method editor (IME)
       
     7 input event.
       
     8 
       
     9 Usage:
       
    10 
       
    11     YUI().use('event-valuechange', function (Y) {
       
    12         Y.one('#my-input').on('valuechange', function (e) {
       
    13             Y.log('previous value: ' + e.prevVal);
       
    14             Y.log('new value: ' + e.newVal);
       
    15         });
       
    16     });
       
    17 
       
    18 @module event-valuechange
       
    19 **/
       
    20 
       
    21 /**
       
    22 Provides the implementation for the synthetic `valuechange` event. This class
       
    23 isn't meant to be used directly, but is public to make monkeypatching possible.
       
    24 
       
    25 Usage:
       
    26 
       
    27     YUI().use('event-valuechange', function (Y) {
       
    28         Y.one('#my-input').on('valuechange', function (e) {
       
    29             Y.log('previous value: ' + e.prevVal);
       
    30             Y.log('new value: ' + e.newVal);
       
    31         });
       
    32     });
       
    33 
       
    34 @class ValueChange
       
    35 @static
       
    36 */
       
    37 
       
    38 var DATA_KEY = '_valuechange',
       
    39     VALUE    = 'value',
       
    40     NODE_NAME = 'nodeName',
       
    41 
       
    42     config, // defined at the end of this file
       
    43 
       
    44 // Just a simple namespace to make methods overridable.
       
    45 VC = {
       
    46     // -- Static Constants -----------------------------------------------------
       
    47 
       
    48     /**
       
    49     Interval (in milliseconds) at which to poll for changes to the value of an
       
    50     element with one or more `valuechange` subscribers when the user is likely
       
    51     to be interacting with it.
       
    52 
       
    53     @property POLL_INTERVAL
       
    54     @type Number
       
    55     @default 50
       
    56     @static
       
    57     **/
       
    58     POLL_INTERVAL: 50,
       
    59 
       
    60     /**
       
    61     Timeout (in milliseconds) after which to stop polling when there hasn't been
       
    62     any new activity (keypresses, mouse clicks, etc.) on an element.
       
    63 
       
    64     @property TIMEOUT
       
    65     @type Number
       
    66     @default 10000
       
    67     @static
       
    68     **/
       
    69     TIMEOUT: 10000,
       
    70 
       
    71     // -- Protected Static Methods ---------------------------------------------
       
    72 
       
    73     /**
       
    74     Called at an interval to poll for changes to the value of the specified
       
    75     node.
       
    76 
       
    77     @method _poll
       
    78     @param {Node} node Node to poll.
       
    79 
       
    80     @param {Object} options Options object.
       
    81         @param {EventFacade} [options.e] Event facade of the event that
       
    82             initiated the polling.
       
    83 
       
    84     @protected
       
    85     @static
       
    86     **/
       
    87     _poll: function (node, options) {
       
    88         var domNode  = node._node, // performance cheat; getValue() is a big hit when polling
       
    89             event    = options.e,
       
    90             vcData   = node._data && node._data[DATA_KEY], // another perf cheat
       
    91             stopped  = 0,
       
    92             facade, prevVal, newVal, nodeName, selectedOption, stopElement;
       
    93 
       
    94         if (!(domNode && vcData)) {
       
    95             Y.log('_poll: node #' + node.get('id') + ' disappeared; stopping polling and removing all notifiers.', 'warn', 'event-valuechange');
       
    96             VC._stopPolling(node);
       
    97             return;
       
    98         }
       
    99 
       
   100         prevVal = vcData.prevVal;
       
   101         nodeName  = vcData.nodeName;
       
   102 
       
   103         if (vcData.isEditable) {
       
   104             // Use innerHTML for performance
       
   105             newVal = domNode.innerHTML;
       
   106         } else if (nodeName === 'input' || nodeName === 'textarea') {
       
   107             // Use value property for performance
       
   108             newVal = domNode.value;
       
   109         } else if (nodeName === 'select') {
       
   110             // Back-compatibility with IE6 <select> element values.
       
   111             // Huge performance cheat to get past node.get('value').
       
   112             selectedOption = domNode.options[domNode.selectedIndex];
       
   113             newVal = selectedOption.value || selectedOption.text;
       
   114         }
       
   115 
       
   116         if (newVal !== prevVal) {
       
   117             vcData.prevVal = newVal;
       
   118 
       
   119             facade = {
       
   120                 _event       : event,
       
   121                 currentTarget: (event && event.currentTarget) || node,
       
   122                 newVal       : newVal,
       
   123                 prevVal      : prevVal,
       
   124                 target       : (event && event.target) || node
       
   125             };
       
   126 
       
   127             Y.Object.some(vcData.notifiers, function (notifier) {
       
   128                 var evt = notifier.handle.evt,
       
   129                     newStopped;
       
   130 
       
   131                 // support e.stopPropagation()
       
   132                 if (stopped !== 1) {
       
   133                     notifier.fire(facade);
       
   134                 } else if (evt.el === stopElement) {
       
   135                     notifier.fire(facade);
       
   136                 }
       
   137 
       
   138                 newStopped = evt && evt._facade ? evt._facade.stopped : 0;
       
   139 
       
   140                 // need to consider the condition in which there are two
       
   141                 // listeners on the same element:
       
   142                 // listener 1 calls e.stopPropagation()
       
   143                 // listener 2 calls e.stopImmediatePropagation()
       
   144                 if (newStopped > stopped) {
       
   145                     stopped = newStopped;
       
   146 
       
   147                     if (stopped === 1) {
       
   148                         stopElement = evt.el;
       
   149                     }
       
   150                 }
       
   151 
       
   152                 // support e.stopImmediatePropagation()
       
   153                 if (stopped === 2) {
       
   154                     return true;
       
   155                 }
       
   156             });
       
   157 
       
   158             VC._refreshTimeout(node);
       
   159         }
       
   160     },
       
   161 
       
   162     /**
       
   163     Restarts the inactivity timeout for the specified node.
       
   164 
       
   165     @method _refreshTimeout
       
   166     @param {Node} node Node to refresh.
       
   167     @param {SyntheticEvent.Notifier} notifier
       
   168     @protected
       
   169     @static
       
   170     **/
       
   171     _refreshTimeout: function (node, notifier) {
       
   172         // The node may have been destroyed, so check that it still exists
       
   173         // before trying to get its data. Otherwise an error will occur.
       
   174         if (!node._node) {
       
   175             Y.log('_stopPolling: node disappeared', 'warn', 'event-valuechange');
       
   176             return;
       
   177         }
       
   178 
       
   179         var vcData = node.getData(DATA_KEY);
       
   180 
       
   181         VC._stopTimeout(node); // avoid dupes
       
   182 
       
   183         // If we don't see any changes within the timeout period (10 seconds by
       
   184         // default), stop polling.
       
   185         vcData.timeout = setTimeout(function () {
       
   186             Y.log('timeout: #' + node.get('id'), 'info', 'event-valuechange');
       
   187             VC._stopPolling(node, notifier);
       
   188         }, VC.TIMEOUT);
       
   189 
       
   190         Y.log('_refreshTimeout: #' + node.get('id'), 'info', 'event-valuechange');
       
   191     },
       
   192 
       
   193     /**
       
   194     Begins polling for changes to the `value` property of the specified node. If
       
   195     polling is already underway for the specified node, it will not be restarted
       
   196     unless the `force` option is `true`
       
   197 
       
   198     @method _startPolling
       
   199     @param {Node} node Node to watch.
       
   200     @param {SyntheticEvent.Notifier} notifier
       
   201 
       
   202     @param {Object} options Options object.
       
   203         @param {EventFacade} [options.e] Event facade of the event that
       
   204             initiated the polling.
       
   205         @param {Boolean} [options.force=false] If `true`, polling will be
       
   206             restarted even if we're already polling this node.
       
   207 
       
   208     @protected
       
   209     @static
       
   210     **/
       
   211     _startPolling: function (node, notifier, options) {
       
   212         var vcData, isEditable;
       
   213 
       
   214         if (!node.test('input,textarea,select') && !(isEditable = VC._isEditable(node))) {
       
   215             Y.log('_startPolling: aborting poll on #' + node.get('id') + ' -- not a detectable node', 'warn', 'event-valuechange');
       
   216             return;
       
   217         }
       
   218 
       
   219         vcData = node.getData(DATA_KEY);
       
   220 
       
   221         if (!vcData) {
       
   222             vcData = {
       
   223                 nodeName   : node.get(NODE_NAME).toLowerCase(),
       
   224                 isEditable : isEditable,
       
   225                 prevVal    : isEditable ? node.getDOMNode().innerHTML : node.get(VALUE)
       
   226             };
       
   227 
       
   228             node.setData(DATA_KEY, vcData);
       
   229         }
       
   230 
       
   231         vcData.notifiers || (vcData.notifiers = {});
       
   232 
       
   233         // Don't bother continuing if we're already polling this node, unless
       
   234         // `options.force` is true.
       
   235         if (vcData.interval) {
       
   236             if (options.force) {
       
   237                 VC._stopPolling(node, notifier); // restart polling, but avoid dupe polls
       
   238             } else {
       
   239                 vcData.notifiers[Y.stamp(notifier)] = notifier;
       
   240                 return;
       
   241             }
       
   242         }
       
   243 
       
   244         // Poll for changes to the node's value. We can't rely on keyboard
       
   245         // events for this, since the value may change due to a mouse-initiated
       
   246         // paste event, an IME input event, or for some other reason that
       
   247         // doesn't trigger a key event.
       
   248         vcData.notifiers[Y.stamp(notifier)] = notifier;
       
   249 
       
   250         vcData.interval = setInterval(function () {
       
   251             VC._poll(node, options);
       
   252         }, VC.POLL_INTERVAL);
       
   253 
       
   254         Y.log('_startPolling: #' + node.get('id'), 'info', 'event-valuechange');
       
   255 
       
   256         VC._refreshTimeout(node, notifier);
       
   257     },
       
   258 
       
   259     /**
       
   260     Stops polling for changes to the specified node's `value` attribute.
       
   261 
       
   262     @method _stopPolling
       
   263     @param {Node} node Node to stop polling on.
       
   264     @param {SyntheticEvent.Notifier} [notifier] Notifier to remove from the
       
   265         node. If not specified, all notifiers will be removed.
       
   266     @protected
       
   267     @static
       
   268     **/
       
   269     _stopPolling: function (node, notifier) {
       
   270         // The node may have been destroyed, so check that it still exists
       
   271         // before trying to get its data. Otherwise an error will occur.
       
   272         if (!node._node) {
       
   273             Y.log('_stopPolling: node disappeared', 'info', 'event-valuechange');
       
   274             return;
       
   275         }
       
   276 
       
   277         var vcData = node.getData(DATA_KEY) || {};
       
   278 
       
   279         clearInterval(vcData.interval);
       
   280         delete vcData.interval;
       
   281 
       
   282         VC._stopTimeout(node);
       
   283 
       
   284         if (notifier) {
       
   285             vcData.notifiers && delete vcData.notifiers[Y.stamp(notifier)];
       
   286         } else {
       
   287             vcData.notifiers = {};
       
   288         }
       
   289 
       
   290         Y.log('_stopPolling: #' + node.get('id'), 'info', 'event-valuechange');
       
   291     },
       
   292 
       
   293     /**
       
   294     Clears the inactivity timeout for the specified node, if any.
       
   295 
       
   296     @method _stopTimeout
       
   297     @param {Node} node
       
   298     @protected
       
   299     @static
       
   300     **/
       
   301     _stopTimeout: function (node) {
       
   302         var vcData = node.getData(DATA_KEY) || {};
       
   303 
       
   304         clearTimeout(vcData.timeout);
       
   305         delete vcData.timeout;
       
   306     },
       
   307 
       
   308     /**
       
   309     Check to see if a node has editable content or not.
       
   310 
       
   311     TODO: Add additional checks to get it to work for child nodes
       
   312     that inherit "contenteditable" from parent nodes. This may be
       
   313     too computationally intensive to be placed inside of the `_poll`
       
   314     loop, however.
       
   315 
       
   316     @method _isEditable
       
   317     @param {Node} node
       
   318     @protected
       
   319     @static
       
   320     **/
       
   321     _isEditable: function (node) {
       
   322         // Performance cheat because this is used inside `_poll`
       
   323         var domNode = node._node;
       
   324         return domNode.contentEditable === 'true' ||
       
   325                domNode.contentEditable === '';
       
   326     },
       
   327 
       
   328 
       
   329 
       
   330     // -- Protected Static Event Handlers --------------------------------------
       
   331 
       
   332     /**
       
   333     Stops polling when a node's blur event fires.
       
   334 
       
   335     @method _onBlur
       
   336     @param {EventFacade} e
       
   337     @param {SyntheticEvent.Notifier} notifier
       
   338     @protected
       
   339     @static
       
   340     **/
       
   341     _onBlur: function (e, notifier) {
       
   342         VC._stopPolling(e.currentTarget, notifier);
       
   343     },
       
   344 
       
   345     /**
       
   346     Resets a node's history and starts polling when a focus event occurs.
       
   347 
       
   348     @method _onFocus
       
   349     @param {EventFacade} e
       
   350     @param {SyntheticEvent.Notifier} notifier
       
   351     @protected
       
   352     @static
       
   353     **/
       
   354     _onFocus: function (e, notifier) {
       
   355         var node       = e.currentTarget,
       
   356             vcData     = node.getData(DATA_KEY);
       
   357 
       
   358         if (!vcData) {
       
   359             vcData = {
       
   360                 isEditable : VC._isEditable(node),
       
   361                 nodeName   : node.get(NODE_NAME).toLowerCase()
       
   362             };
       
   363             node.setData(DATA_KEY, vcData);
       
   364         }
       
   365 
       
   366         vcData.prevVal = vcData.isEditable ? node.getDOMNode().innerHTML : node.get(VALUE);
       
   367 
       
   368         VC._startPolling(node, notifier, {e: e});
       
   369     },
       
   370 
       
   371     /**
       
   372     Starts polling when a node receives a keyDown event.
       
   373 
       
   374     @method _onKeyDown
       
   375     @param {EventFacade} e
       
   376     @param {SyntheticEvent.Notifier} notifier
       
   377     @protected
       
   378     @static
       
   379     **/
       
   380     _onKeyDown: function (e, notifier) {
       
   381         VC._startPolling(e.currentTarget, notifier, {e: e});
       
   382     },
       
   383 
       
   384     /**
       
   385     Starts polling when an IME-related keyUp event occurs on a node.
       
   386 
       
   387     @method _onKeyUp
       
   388     @param {EventFacade} e
       
   389     @param {SyntheticEvent.Notifier} notifier
       
   390     @protected
       
   391     @static
       
   392     **/
       
   393     _onKeyUp: function (e, notifier) {
       
   394         // These charCodes indicate that an IME has started. We'll restart
       
   395         // polling and give the IME up to 10 seconds (by default) to finish.
       
   396         if (e.charCode === 229 || e.charCode === 197) {
       
   397             VC._startPolling(e.currentTarget, notifier, {
       
   398                 e    : e,
       
   399                 force: true
       
   400             });
       
   401         }
       
   402     },
       
   403 
       
   404     /**
       
   405     Starts polling when a node receives a mouseDown event.
       
   406 
       
   407     @method _onMouseDown
       
   408     @param {EventFacade} e
       
   409     @param {SyntheticEvent.Notifier} notifier
       
   410     @protected
       
   411     @static
       
   412     **/
       
   413     _onMouseDown: function (e, notifier) {
       
   414         VC._startPolling(e.currentTarget, notifier, {e: e});
       
   415     },
       
   416 
       
   417     /**
       
   418     Called when the `valuechange` event receives a new subscriber.
       
   419 
       
   420     Child nodes that aren't initially available when this subscription is
       
   421     called will still fire the `valuechange` event after their data is
       
   422     collected when the delegated `focus` event is captured. This includes
       
   423     elements that haven't been inserted into the DOM yet, as well as
       
   424     elements that aren't initially `contenteditable`.
       
   425 
       
   426     @method _onSubscribe
       
   427     @param {Node} node
       
   428     @param {Subscription} sub
       
   429     @param {SyntheticEvent.Notifier} notifier
       
   430     @param {Function|String} [filter] Filter function or selector string. Only
       
   431         provided for delegate subscriptions.
       
   432     @protected
       
   433     @static
       
   434     **/
       
   435     _onSubscribe: function (node, sub, notifier, filter) {
       
   436         var _valuechange, callbacks, isEditable, inputNodes, editableNodes;
       
   437 
       
   438         callbacks = {
       
   439             blur     : VC._onBlur,
       
   440             focus    : VC._onFocus,
       
   441             keydown  : VC._onKeyDown,
       
   442             keyup    : VC._onKeyUp,
       
   443             mousedown: VC._onMouseDown
       
   444         };
       
   445 
       
   446         // Store a utility object on the notifier to hold stuff that needs to be
       
   447         // passed around to trigger event handlers, polling handlers, etc.
       
   448         _valuechange = notifier._valuechange = {};
       
   449 
       
   450         if (filter) {
       
   451             // If a filter is provided, then this is a delegated subscription.
       
   452             _valuechange.delegated = true;
       
   453 
       
   454             // Add a function to the notifier that we can use to find all
       
   455             // nodes that pass the delegate filter.
       
   456             _valuechange.getNodes = function () {
       
   457                 inputNodes    = node.all('input,textarea,select').filter(filter);
       
   458                 editableNodes = node.all('[contenteditable="true"],[contenteditable=""]').filter(filter);
       
   459 
       
   460                 return inputNodes.concat(editableNodes);
       
   461             };
       
   462 
       
   463             // Store the initial values for each descendant of the container
       
   464             // node that passes the delegate filter.
       
   465             _valuechange.getNodes().each(function (child) {
       
   466                 if (!child.getData(DATA_KEY)) {
       
   467                     child.setData(DATA_KEY, {
       
   468                         nodeName   : child.get(NODE_NAME).toLowerCase(),
       
   469                         isEditable : VC._isEditable(child),
       
   470                         prevVal    : isEditable ? child.getDOMNode().innerHTML : child.get(VALUE)
       
   471                     });
       
   472                 }
       
   473             });
       
   474 
       
   475             notifier._handles = Y.delegate(callbacks, node, filter, null,
       
   476                 notifier);
       
   477         } else {
       
   478             isEditable = VC._isEditable(node);
       
   479             // This is a normal (non-delegated) event subscription.
       
   480             if (!node.test('input,textarea,select') && !isEditable) {
       
   481                 return;
       
   482             }
       
   483 
       
   484             if (!node.getData(DATA_KEY)) {
       
   485                 node.setData(DATA_KEY, {
       
   486                     nodeName   : node.get(NODE_NAME).toLowerCase(),
       
   487                     isEditable : isEditable,
       
   488                     prevVal    : isEditable ? node.getDOMNode().innerHTML : node.get(VALUE)
       
   489                 });
       
   490             }
       
   491 
       
   492             notifier._handles = node.on(callbacks, null, null, notifier);
       
   493         }
       
   494     },
       
   495 
       
   496     /**
       
   497     Called when the `valuechange` event loses a subscriber.
       
   498 
       
   499     @method _onUnsubscribe
       
   500     @param {Node} node
       
   501     @param {Subscription} subscription
       
   502     @param {SyntheticEvent.Notifier} notifier
       
   503     @protected
       
   504     @static
       
   505     **/
       
   506     _onUnsubscribe: function (node, subscription, notifier) {
       
   507         var _valuechange = notifier._valuechange;
       
   508 
       
   509         notifier._handles && notifier._handles.detach();
       
   510 
       
   511         if (_valuechange.delegated) {
       
   512             _valuechange.getNodes().each(function (child) {
       
   513                 VC._stopPolling(child, notifier);
       
   514             });
       
   515         } else {
       
   516             VC._stopPolling(node, notifier);
       
   517         }
       
   518     }
       
   519 };
       
   520 
       
   521 /**
       
   522 Synthetic event that fires when the `value` property of an `<input>`,
       
   523 `<textarea>`, `<select>`, or `[contenteditable="true"]` node changes as a
       
   524 result of a user-initiated keystroke, mouse operation, or input method
       
   525 editor (IME) input event.
       
   526 
       
   527 Unlike the `onchange` event, this event fires when the value actually changes
       
   528 and not when the element loses focus. This event also reports IME and
       
   529 multi-stroke input more reliably than `oninput` or the various key events across
       
   530 browsers.
       
   531 
       
   532 For performance reasons, only focused nodes are monitored for changes, so
       
   533 programmatic value changes on nodes that don't have focus won't be detected.
       
   534 
       
   535 @example
       
   536 
       
   537     YUI().use('event-valuechange', function (Y) {
       
   538         Y.one('#my-input').on('valuechange', function (e) {
       
   539             Y.log('previous value: ' + e.prevVal);
       
   540             Y.log('new value: ' + e.newVal);
       
   541         });
       
   542     });
       
   543 
       
   544 @event valuechange
       
   545 @param {String} prevVal Previous value prior to the latest change.
       
   546 @param {String} newVal New value after the latest change.
       
   547 @for YUI
       
   548 **/
       
   549 
       
   550 config = {
       
   551     detach: VC._onUnsubscribe,
       
   552     on    : VC._onSubscribe,
       
   553 
       
   554     delegate      : VC._onSubscribe,
       
   555     detachDelegate: VC._onUnsubscribe,
       
   556 
       
   557     publishConfig: {
       
   558         emitFacade: true
       
   559     }
       
   560 };
       
   561 
       
   562 Y.Event.define('valuechange', config);
       
   563 Y.Event.define('valueChange', config); // deprecated, but supported for backcompat
       
   564 
       
   565 Y.ValueChange = VC;
       
   566 
       
   567 
       
   568 }, '@VERSION@', {"requires": ["event-focus", "event-synthetic"]});