1 /**
  2  * @fileOverview inspired by yui3
  3  * Synthetic event that fires when the <code>value</code> property of an input
  4  * field or textarea changes as a result of a keystroke, mouse operation, or
  5  * input method editor (IME) input event.
  6  *
  7  * Unlike the <code>onchange</code> event, this event fires when the value
  8  * actually changes and not when the element loses focus. This event also
  9  * reports IME and multi-stroke input more reliably than <code>oninput</code> or
 10  * the various key events across browsers.
 11  *
 12  * @author yiminghe@gmail.com
 13  */
 14 KISSY.add('event/valuechange', function (S, Event, DOM, special) {
 15     var VALUE_CHANGE = "valuechange",
 16         getNodeName = DOM.nodeName,
 17         KEY = "event/valuechange",
 18         HISTORY_KEY = KEY + "/history",
 19         POLL_KEY = KEY + "/poll",
 20         interval = 50;
 21 
 22     function clearPollTimer(target) {
 23         if (DOM.hasData(target, POLL_KEY)) {
 24             var poll = DOM.data(target, POLL_KEY);
 25             clearTimeout(poll);
 26             DOM.removeData(target, POLL_KEY);
 27         }
 28     }
 29 
 30     function stopPoll(target) {
 31         DOM.removeData(target, HISTORY_KEY);
 32         clearPollTimer(target);
 33     }
 34 
 35     function stopPollHandler(ev) {
 36         clearPollTimer(ev.target);
 37     }
 38 
 39     function checkChange(target) {
 40         var v = target.value,
 41             h = DOM.data(target, HISTORY_KEY);
 42         if (v !== h) {
 43             // 只触发自己绑定的 handler
 44             Event.fire(target, VALUE_CHANGE, {
 45                 prevVal:h,
 46                 newVal:v
 47             }, true);
 48             DOM.data(target, HISTORY_KEY, v);
 49         }
 50     }
 51 
 52     function startPoll(target) {
 53         if (DOM.hasData(target, POLL_KEY)) {
 54             return;
 55         }
 56         DOM.data(target, POLL_KEY, setTimeout(function () {
 57             checkChange(target);
 58             DOM.data(target, POLL_KEY, setTimeout(arguments.callee, interval));
 59         }, interval));
 60     }
 61 
 62     function startPollHandler(ev) {
 63         var target = ev.target;
 64         // when focus ,record its current value immediately
 65         if (ev.type == "focus") {
 66             DOM.data(target, HISTORY_KEY, target.value);
 67         }
 68         startPoll(target);
 69     }
 70 
 71     function webkitSpeechChangeHandler(e) {
 72         checkChange(e.target);
 73     }
 74 
 75     function monitor(target) {
 76         unmonitored(target);
 77         Event.on(target, "blur", stopPollHandler);
 78         // fix #94
 79         // see note 2012-02-08
 80         Event.on(target, "webkitspeechchange", webkitSpeechChangeHandler);
 81         Event.on(target, "mousedown keyup keydown focus", startPollHandler);
 82     }
 83 
 84     function unmonitored(target) {
 85         stopPoll(target);
 86         Event.remove(target, "blur", stopPollHandler);
 87         Event.remove(target, "webkitspeechchange", webkitSpeechChangeHandler);
 88         Event.remove(target, "mousedown keyup keydown focus", startPollHandler);
 89     }
 90 
 91     special[VALUE_CHANGE] = {
 92         setup:function () {
 93             var target = this, nodeName = getNodeName(target);
 94             if (nodeName == "input" || nodeName == "textarea") {
 95                 monitor(target);
 96             }
 97         },
 98         tearDown:function () {
 99             var target = this;
100             unmonitored(target);
101         }
102     };
103     return Event;
104 }, {
105     requires:["./base", "dom", "./special"]
106 });
107 
108 /**
109  * 2012-02-08 yiminghe@gmail.com note about webkitspeechchange :
110  *  当 input 没焦点立即点击语音
111  *   -> mousedown -> blur -> focus -> blur -> webkitspeechchange -> focus
112  *  第二次:
113  *   -> mousedown -> blur -> webkitspeechchange -> focus
114  **/