function debounced(func, tmin, tmax) {
  let id = 0;
  let t = tmin;
  function wrappedFunc() {
    let start = Date.now();
    id = 0;
    func();
    t = tmin + Date.now() - start; // tmin + [func's execution time]
    t = t < tmax ? t : tmax;
  }
  function debouncedFunc() {
    clearTimeout(id);
    id = setTimeout(wrappedFunc, t);
  }
  // id is nonzero only when a debounced function is pending.
  debouncedFunc.delay = () => { id && debouncedFunc(); }
  return debouncedFunc;
}
debounce time = min(tmin + [func's execution time], tmax).

@param {Function} func
@param {number} tmin Minimum debounce time
@param {number} tmax Maximum debounce time
@return {Function} A debounced version of func with an attached "delay"
    function. "delay" will delay any pending debounced function by the
    current debounce time. If there are none pending, it is a no-op.