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