define(function () {
    var lib = {};
    var ORIGIN_METHOD = '\0__throttleOriginMethod';
    var RATE = '\0__throttleRate';
    /**
     * ???? ??????????fn ????????????????
     * ???????
     * notifyWhenChangesStop
     *      ???????????????
     *      ???trailing?true?debounce?true ??
     * notifyAtFixRate
     *      ?????????????
     *      ???trailing?true?debounce?false ??
     * ???
     *     ??model??view????????throttle?
     *     ????view??model??????????????????
     *     ???????model?server???????
     *
     * @public
     * @param {(Function|Array.<Function>)} fn ???????
     *                                         ??fn?array?????????????throttle?
     *                                         ???????timer?
     * @param {number} delay ?????????
     * @param {bool} trailing ?????????????
     *                        true?????????????????
     *                        ????????????????delay?
     *                        false??????????????????
     *                        ???????delay??????????
     * @param {bool} debounce ??
     *                        true?????????????delay????????
     *                        false?????????????delay??????????
     * @return {(Function|Array.<Function>)} ???????
     *                                       ????fn?array???????array?
     *                                       ???Function?
     */
    lib.throttle = function (fn, delay, trailing, debounce) {
        var currCall = (new Date()).getTime();
        var lastCall = 0;
        var lastExec = 0;
        var timer = null;
        var diff;
        var scope;
        var args;
        var isSingle = typeof fn === 'function';
        delay = delay || 0;
        if (isSingle) {
            return createCallback();
        }
        else {
            var ret = [];
            for (var i = 0; i < fn.length; i++) {
                ret[i] = createCallback(i);
            }
            return ret;
        }
        function createCallback(index) {
            function exec() {
                lastExec = (new Date()).getTime();
                timer = null;
                (isSingle ? fn : fn[index]).apply(scope, args || []);
            }
            var cb = function () {
                currCall = (new Date()).getTime();
                scope = this;
                args = arguments;
                diff = currCall - (debounce ? lastCall : lastExec) - delay;
                clearTimeout(timer);
                if (debounce) {
                    if (trailing) {
                        timer = setTimeout(exec, delay);
                    }
                    else if (diff >= 0) {
                        exec();
                    }
                }
                else {
                    if (diff >= 0) {
                        exec();
                    }
                    else if (trailing) {
                        timer = setTimeout(exec, -diff);
                    }
                }
                lastCall = currCall;
            };
            /**
             * Clear throttle.
             * @public
             */
            cb.clear = function () {
                if (timer) {
                    clearTimeout(timer);
                    timer = null;
                }
            };
            return cb;
        }
    };
    /**
     * ???????????????????
     *
     * @public
     */
    lib.fixRate = function (fn, delay) {
        return delay != null
            ? lib.throttle(fn, delay, true, false)
            : fn;
    };
    /**
     * ????????????????????????
     *
     * @public
     */
    lib.debounce = function (fn, delay) {
        return delay != null
             ? lib.throttle(fn, delay, true, true)
             : fn;
    };
    /**
     * Create throttle method or update throttle rate.
     *
     * @example
     * ComponentView.prototype.render = function () {
     *     ...
     *     throttle.createOrUpdate(
     *         this,
     *         '_dispatchAction',
     *         this.model.get('throttle'),
     *         'fixRate'
     *     );
     * };
     * ComponentView.prototype.remove = function () {
     *     throttle.clear(this, '_dispatchAction');
     * };
     * ComponentView.prototype.dispose = function () {
     *     throttle.clear(this, '_dispatchAction');
     * };
     *
     * @public
     * @param {Object} obj
     * @param {string} fnAttr
     * @param {number} rate
     * @param {string} throttleType 'fixRate' or 'debounce'
     */
    lib.createOrUpdate = function (obj, fnAttr, rate, throttleType) {
        var fn = obj[fnAttr];
        if (!fn || rate == null || !throttleType) {
            return;
        }
        var originFn = fn[ORIGIN_METHOD] || fn;
        var lastRate = fn[RATE];
        if (lastRate !== rate) {
            fn = obj[fnAttr] = lib[throttleType](originFn, rate);
            fn[ORIGIN_METHOD] = originFn;
            fn[RATE] = rate;
        }
    };
    /**
     * Clear throttle. Example see throttle.createOrUpdate.
     *
     * @public
     * @param {Object} obj
     * @param {string} fnAttr
     */
    lib.clear = function (obj, fnAttr) {
        var fn = obj[fnAttr];
        if (fn && fn[ORIGIN_METHOD]) {
            obj[fnAttr] = fn[ORIGIN_METHOD];
        }
    };
    return lib;
});
 
  |