00001 var isObject = require('./isObject'),
00002 now = require('./now'),
00003 toNumber = require('./toNumber');
00004
00006 var FUNC_ERROR_TEXT = 'Expected a function';
00007
00008
00009 var nativeMax = Math.max,
00010 nativeMin = Math.min;
00011
00066 function debounce(func, wait, options) {
00067 var lastArgs,
00068 lastThis,
00069 maxWait,
00070 result,
00071 timerId,
00072 lastCallTime,
00073 lastInvokeTime = 0,
00074 leading = false,
00075 maxing = false,
00076 trailing = true;
00077
00078 if (typeof func != 'function') {
00079 throw new TypeError(FUNC_ERROR_TEXT);
00080 }
00081 wait = toNumber(wait) || 0;
00082 if (isObject(options)) {
00083 leading = !!options.leading;
00084 maxing = 'maxWait' in options;
00085 maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;
00086 trailing = 'trailing' in options ? !!options.trailing : trailing;
00087 }
00088
00089 function invokeFunc(time) {
00090 var args = lastArgs,
00091 thisArg = lastThis;
00092
00093 lastArgs = lastThis = undefined;
00094 lastInvokeTime = time;
00095 result = func.apply(thisArg, args);
00096 return result;
00097 }
00098
00099 function leadingEdge(time) {
00100
00101 lastInvokeTime = time;
00102
00103 timerId = setTimeout(timerExpired, wait);
00104
00105 return leading ? invokeFunc(time) : result;
00106 }
00107
00108 function remainingWait(time) {
00109 var timeSinceLastCall = time - lastCallTime,
00110 timeSinceLastInvoke = time - lastInvokeTime,
00111 result = wait - timeSinceLastCall;
00112
00113 return maxing ? nativeMin(result, maxWait - timeSinceLastInvoke) : result;
00114 }
00115
00116 function shouldInvoke(time) {
00117 var timeSinceLastCall = time - lastCallTime,
00118 timeSinceLastInvoke = time - lastInvokeTime;
00119
00120
00121
00122
00123 return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||
00124 (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));
00125 }
00126
00127 function timerExpired() {
00128 var time = now();
00129 if (shouldInvoke(time)) {
00130 return trailingEdge(time);
00131 }
00132
00133 timerId = setTimeout(timerExpired, remainingWait(time));
00134 }
00135
00136 function trailingEdge(time) {
00137 timerId = undefined;
00138
00139
00140
00141 if (trailing && lastArgs) {
00142 return invokeFunc(time);
00143 }
00144 lastArgs = lastThis = undefined;
00145 return result;
00146 }
00147
00148 function cancel() {
00149 if (timerId !== undefined) {
00150 clearTimeout(timerId);
00151 }
00152 lastInvokeTime = 0;
00153 lastArgs = lastCallTime = lastThis = timerId = undefined;
00154 }
00155
00156 function flush() {
00157 return timerId === undefined ? result : trailingEdge(now());
00158 }
00159
00160 function debounced() {
00161 var time = now(),
00162 isInvoking = shouldInvoke(time);
00163
00164 lastArgs = arguments;
00165 lastThis = this;
00166 lastCallTime = time;
00167
00168 if (isInvoking) {
00169 if (timerId === undefined) {
00170 return leadingEdge(lastCallTime);
00171 }
00172 if (maxing) {
00173
00174 timerId = setTimeout(timerExpired, wait);
00175 return invokeFunc(lastCallTime);
00176 }
00177 }
00178 if (timerId === undefined) {
00179 timerId = setTimeout(timerExpired, wait);
00180 }
00181 return result;
00182 }
00183 debounced.cancel = cancel;
00184 debounced.flush = flush;
00185 return debounced;
00186 }
00187
00188 module.exports = debounce;