| /** |
| * Lo-Dash 2.4.1 (Custom Build) <http://lodash.com/> |
| * Build: `lodash modularize modern exports="node" -o ./modern/` |
| * Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/> |
| * Based on Underscore.js 1.5.2 <http://underscorejs.org/LICENSE> |
| * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors |
| * Available under MIT license <http://lodash.com/license> |
| */ |
| var isFunction = require('../objects/isFunction'), |
| isObject = require('../objects/isObject'), |
| now = require('../utilities/now'); |
| |
| /* Native method shortcuts for methods with the same name as other `lodash` methods */ |
| var nativeMax = Math.max; |
| |
| /** |
| * Creates a function that will delay the execution of `func` until after |
| * `wait` milliseconds have elapsed since the last time it was invoked. |
| * Provide an options object to indicate that `func` should be invoked on |
| * the leading and/or trailing edge of the `wait` timeout. Subsequent calls |
| * to the debounced function will return the result of the last `func` call. |
| * |
| * Note: If `leading` and `trailing` options are `true` `func` will be called |
| * on the trailing edge of the timeout only if the the debounced function is |
| * invoked more than once during the `wait` timeout. |
| * |
| * @static |
| * @memberOf _ |
| * @category Functions |
| * @param {Function} func The function to debounce. |
| * @param {number} wait The number of milliseconds to delay. |
| * @param {Object} [options] The options object. |
| * @param {boolean} [options.leading=false] Specify execution on the leading edge of the timeout. |
| * @param {number} [options.maxWait] The maximum time `func` is allowed to be delayed before it's called. |
| * @param {boolean} [options.trailing=true] Specify execution on the trailing edge of the timeout. |
| * @returns {Function} Returns the new debounced function. |
| * @example |
| * |
| * // avoid costly calculations while the window size is in flux |
| * var lazyLayout = _.debounce(calculateLayout, 150); |
| * jQuery(window).on('resize', lazyLayout); |
| * |
| * // execute `sendMail` when the click event is fired, debouncing subsequent calls |
| * jQuery('#postbox').on('click', _.debounce(sendMail, 300, { |
| * 'leading': true, |
| * 'trailing': false |
| * }); |
| * |
| * // ensure `batchLog` is executed once after 1 second of debounced calls |
| * var source = new EventSource('/stream'); |
| * source.addEventListener('message', _.debounce(batchLog, 250, { |
| * 'maxWait': 1000 |
| * }, false); |
| */ |
| function debounce(func, wait, options) { |
| var args, |
| maxTimeoutId, |
| result, |
| stamp, |
| thisArg, |
| timeoutId, |
| trailingCall, |
| lastCalled = 0, |
| maxWait = false, |
| trailing = true; |
| |
| if (!isFunction(func)) { |
| throw new TypeError; |
| } |
| wait = nativeMax(0, wait) || 0; |
| if (options === true) { |
| var leading = true; |
| trailing = false; |
| } else if (isObject(options)) { |
| leading = options.leading; |
| maxWait = 'maxWait' in options && (nativeMax(wait, options.maxWait) || 0); |
| trailing = 'trailing' in options ? options.trailing : trailing; |
| } |
| var delayed = function() { |
| var remaining = wait - (now() - stamp); |
| if (remaining <= 0) { |
| if (maxTimeoutId) { |
| clearTimeout(maxTimeoutId); |
| } |
| var isCalled = trailingCall; |
| maxTimeoutId = timeoutId = trailingCall = undefined; |
| if (isCalled) { |
| lastCalled = now(); |
| result = func.apply(thisArg, args); |
| if (!timeoutId && !maxTimeoutId) { |
| args = thisArg = null; |
| } |
| } |
| } else { |
| timeoutId = setTimeout(delayed, remaining); |
| } |
| }; |
| |
| var maxDelayed = function() { |
| if (timeoutId) { |
| clearTimeout(timeoutId); |
| } |
| maxTimeoutId = timeoutId = trailingCall = undefined; |
| if (trailing || (maxWait !== wait)) { |
| lastCalled = now(); |
| result = func.apply(thisArg, args); |
| if (!timeoutId && !maxTimeoutId) { |
| args = thisArg = null; |
| } |
| } |
| }; |
| |
| return function() { |
| args = arguments; |
| stamp = now(); |
| thisArg = this; |
| trailingCall = trailing && (timeoutId || !leading); |
| |
| if (maxWait === false) { |
| var leadingCall = leading && !timeoutId; |
| } else { |
| if (!maxTimeoutId && !leading) { |
| lastCalled = stamp; |
| } |
| var remaining = maxWait - (stamp - lastCalled), |
| isCalled = remaining <= 0; |
| |
| if (isCalled) { |
| if (maxTimeoutId) { |
| maxTimeoutId = clearTimeout(maxTimeoutId); |
| } |
| lastCalled = stamp; |
| result = func.apply(thisArg, args); |
| } |
| else if (!maxTimeoutId) { |
| maxTimeoutId = setTimeout(maxDelayed, remaining); |
| } |
| } |
| if (isCalled && timeoutId) { |
| timeoutId = clearTimeout(timeoutId); |
| } |
| else if (!timeoutId && wait !== maxWait) { |
| timeoutId = setTimeout(delayed, wait); |
| } |
| if (leadingCall) { |
| isCalled = true; |
| result = func.apply(thisArg, args); |
| } |
| if (isCalled && !timeoutId && !maxTimeoutId) { |
| args = thisArg = null; |
| } |
| return result; |
| }; |
| } |
| |
| module.exports = debounce; |