docs: function debounce(func, wait, options) {
        if (!isFun(func)) throwErr('fun');
        var lastArgs, lastThis, maxWait, result, timerId, lastCallTime
          , lastInvokeTime = 0, leading = false, maxing = false, trailing = true;
        wait = +wait || 0;
        if (typeVal(options, 'bol')) {
          leading = true, trailing = false;
        } else if (isObj(options)) {
          leading = !!options.leading;
          maxing = 'maxWait' in options;
          if (maxing) maxWait = Math.max(+options.maxWait || 0, wait);
          if (has(options, 'trailing')) trailing= !!options.trailing;
        };
        function invokeFunc(time) {
          var args = lastArgs, thisArg = lastThis;
          lastArgs = lastThis = UDF;
          lastInvokeTime = time;
          result = func.apply(thisArg, args);
          return result;
        }
        function remainingWait(time) {
          var timeWaiting = wait - (time - lastCallTime);
          return maxing ? Math.min(timeWaiting, maxWait - (time - lastInvokeTime)) : timeWaiting;
        }
        function shouldInvoke(time) {
          var timeSinceLastCall = time - lastCallTime;
          return (isUdf(lastCallTime) || (timeSinceLastCall >= wait) ||
            (timeSinceLastCall < 0) || (maxing && (time - lastInvokeTime) >= maxWait));
        }
        function timerExpired() {
          var time = Date.now();
          if (shouldInvoke(time)) return trailingEdge(time);
          timerId = setTimeout(timerExpired, remainingWait(time));
        }
        function trailingEdge(time) {
          timerId = UDF;
          if (trailing && lastArgs) return invokeFunc(time);
          lastArgs = lastThis = UDF;
          return result;
        }
        function debounced() {
          var time = Date.now(), isInvoking = shouldInvoke(time);
          lastArgs = arguments, lastThis = this, lastCallTime = time;
          if (isInvoking) {
            if (isUdf(timerId)) {
              lastInvokeTime = lastCallTime;
              timerId = setTimeout(timerExpired, wait);
              return leading ? invokeFunc(lastCallTime) : result;
            }
            if (maxing) {
              timerId = setTimeout(timerExpired, wait);
              return invokeFunc(lastCallTime);
            }
          }
          if (isUdf(timerId)) timerId = setTimeout(timerExpired, wait);
          return result;
        }
        debounced.cancel = function () {
          if (!isUdf(timerId)) clearTimeout(timerId);
          lastInvokeTime = 0;
          lastArgs = lastCallTime = lastThis = timerId = UDF;
        };
        debounced.flush = function () {
          return isUdf(timerId) ? result : trailingEdge(Date.now());
        };
        return debounced;
      }