Initial commit
[yaffs-website] / node_modules / grunt-contrib-watch / node_modules / lodash / function / debounce.js
1 var isObject = require('../lang/isObject'),
2     now = require('../date/now');
3
4 /** Used as the `TypeError` message for "Functions" methods. */
5 var FUNC_ERROR_TEXT = 'Expected a function';
6
7 /* Native method references for those with the same name as other `lodash` methods. */
8 var nativeMax = Math.max;
9
10 /**
11  * Creates a debounced function that delays invoking `func` until after `wait`
12  * milliseconds have elapsed since the last time the debounced function was
13  * invoked. The debounced function comes with a `cancel` method to cancel
14  * delayed invocations. Provide an options object to indicate that `func`
15  * should be invoked on the leading and/or trailing edge of the `wait` timeout.
16  * Subsequent calls to the debounced function return the result of the last
17  * `func` invocation.
18  *
19  * **Note:** If `leading` and `trailing` options are `true`, `func` is invoked
20  * on the trailing edge of the timeout only if the the debounced function is
21  * invoked more than once during the `wait` timeout.
22  *
23  * See [David Corbacho's article](http://drupalmotion.com/article/debounce-and-throttle-visual-explanation)
24  * for details over the differences between `_.debounce` and `_.throttle`.
25  *
26  * @static
27  * @memberOf _
28  * @category Function
29  * @param {Function} func The function to debounce.
30  * @param {number} [wait=0] The number of milliseconds to delay.
31  * @param {Object} [options] The options object.
32  * @param {boolean} [options.leading=false] Specify invoking on the leading
33  *  edge of the timeout.
34  * @param {number} [options.maxWait] The maximum time `func` is allowed to be
35  *  delayed before it's invoked.
36  * @param {boolean} [options.trailing=true] Specify invoking on the trailing
37  *  edge of the timeout.
38  * @returns {Function} Returns the new debounced function.
39  * @example
40  *
41  * // avoid costly calculations while the window size is in flux
42  * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
43  *
44  * // invoke `sendMail` when the click event is fired, debouncing subsequent calls
45  * jQuery('#postbox').on('click', _.debounce(sendMail, 300, {
46  *   'leading': true,
47  *   'trailing': false
48  * }));
49  *
50  * // ensure `batchLog` is invoked once after 1 second of debounced calls
51  * var source = new EventSource('/stream');
52  * jQuery(source).on('message', _.debounce(batchLog, 250, {
53  *   'maxWait': 1000
54  * }));
55  *
56  * // cancel a debounced call
57  * var todoChanges = _.debounce(batchLog, 1000);
58  * Object.observe(models.todo, todoChanges);
59  *
60  * Object.observe(models, function(changes) {
61  *   if (_.find(changes, { 'user': 'todo', 'type': 'delete'})) {
62  *     todoChanges.cancel();
63  *   }
64  * }, ['delete']);
65  *
66  * // ...at some point `models.todo` is changed
67  * models.todo.completed = true;
68  *
69  * // ...before 1 second has passed `models.todo` is deleted
70  * // which cancels the debounced `todoChanges` call
71  * delete models.todo;
72  */
73 function debounce(func, wait, options) {
74   var args,
75       maxTimeoutId,
76       result,
77       stamp,
78       thisArg,
79       timeoutId,
80       trailingCall,
81       lastCalled = 0,
82       maxWait = false,
83       trailing = true;
84
85   if (typeof func != 'function') {
86     throw new TypeError(FUNC_ERROR_TEXT);
87   }
88   wait = wait < 0 ? 0 : (+wait || 0);
89   if (options === true) {
90     var leading = true;
91     trailing = false;
92   } else if (isObject(options)) {
93     leading = !!options.leading;
94     maxWait = 'maxWait' in options && nativeMax(+options.maxWait || 0, wait);
95     trailing = 'trailing' in options ? !!options.trailing : trailing;
96   }
97
98   function cancel() {
99     if (timeoutId) {
100       clearTimeout(timeoutId);
101     }
102     if (maxTimeoutId) {
103       clearTimeout(maxTimeoutId);
104     }
105     lastCalled = 0;
106     maxTimeoutId = timeoutId = trailingCall = undefined;
107   }
108
109   function complete(isCalled, id) {
110     if (id) {
111       clearTimeout(id);
112     }
113     maxTimeoutId = timeoutId = trailingCall = undefined;
114     if (isCalled) {
115       lastCalled = now();
116       result = func.apply(thisArg, args);
117       if (!timeoutId && !maxTimeoutId) {
118         args = thisArg = undefined;
119       }
120     }
121   }
122
123   function delayed() {
124     var remaining = wait - (now() - stamp);
125     if (remaining <= 0 || remaining > wait) {
126       complete(trailingCall, maxTimeoutId);
127     } else {
128       timeoutId = setTimeout(delayed, remaining);
129     }
130   }
131
132   function maxDelayed() {
133     complete(trailing, timeoutId);
134   }
135
136   function debounced() {
137     args = arguments;
138     stamp = now();
139     thisArg = this;
140     trailingCall = trailing && (timeoutId || !leading);
141
142     if (maxWait === false) {
143       var leadingCall = leading && !timeoutId;
144     } else {
145       if (!maxTimeoutId && !leading) {
146         lastCalled = stamp;
147       }
148       var remaining = maxWait - (stamp - lastCalled),
149           isCalled = remaining <= 0 || remaining > maxWait;
150
151       if (isCalled) {
152         if (maxTimeoutId) {
153           maxTimeoutId = clearTimeout(maxTimeoutId);
154         }
155         lastCalled = stamp;
156         result = func.apply(thisArg, args);
157       }
158       else if (!maxTimeoutId) {
159         maxTimeoutId = setTimeout(maxDelayed, remaining);
160       }
161     }
162     if (isCalled && timeoutId) {
163       timeoutId = clearTimeout(timeoutId);
164     }
165     else if (!timeoutId && wait !== maxWait) {
166       timeoutId = setTimeout(delayed, wait);
167     }
168     if (leadingCall) {
169       isCalled = true;
170       result = func.apply(thisArg, args);
171     }
172     if (isCalled && !timeoutId && !maxTimeoutId) {
173       args = thisArg = undefined;
174     }
175     return result;
176   }
177   debounced.cancel = cancel;
178   return debounced;
179 }
180
181 module.exports = debounce;