Source: svent/eventManager.js

/**
 * Created by shitake on 16-10-26.
 */
import Event from './event'
import EventCallbackFiber from './eventCallbackFiber'
import Fiber from 'fibers'

/**
 * Class EventManager.
 * @author shitake <z1522716486@hotmail.com>
 * @license MIT <https://mit-license.org/>
 */
class EventManager {
    /**
     * Create a eventManager.
     */
    constructor() {
        this.events = new Map();
        this.eventCallbackFibers = [];
        this.eventCallbackFibers.delete = function (obj) {
            this.splice(this.indexOf(obj), 1)
        };
        this.timers = new Map();
        this.counters = new Map();
        this.timerFilters = new Map();
        this.counterfilters = new Map();
    }

    /**
     * Update evnManager.
     */
    update() {
        if(this.eventCallbackFibers.size != 0) {
            this.eventCallbackFibers.forEach(obj => {
                this.self = obj;
                obj.next();
                this.self = null;
                if(!obj.alive) this.eventCallbackFibers.delete(obj)
            })
        }
    }

    /**
     * trigger a event.
     * @param {String} name - The event name.
     * @param {Object} info - The event info.
     */
    trigger(name, info = {}) {
        let event = this.events[name];
        if(event) {
            event.map(callback => {
                if(callback.async) {
                    this.eventCallbackFibers.push(new EventCallbackFiber(this, name, callback, info))
                } else {
                    if(callback.immediately){
                        callback(this, info)
                    } else {
                        let callbackFiber = {
                            name: name,
                            info: info,
                            alive: true,
                            callback: callback,
                            next: function () {
                                callback(this, this.info);
                                this.alive = false
                            }
                        };
                        this.eventCallbackFibers.push(callbackFiber)
                    }
                }
            })
        }
    }

  /**
   * trigger a event.
   * @param {RegExp} nameRegExp - The event name RegExp.
   * @param {Object} info - The event info.
   */
  triggers(nameRegExp, info = {}) {
    for(let key in this.events) {
      if(key.match(nameRegExp) == key) this.trigger(key, info)
    }
  }

    /**
     * change event info.
     * @param name - event name.
     * @param key - event info key.
     * @param value event info value.
     */
    eventInfo(name, key, value) {
        let event = this.events[name];
        if(event) {
            event.info = event.info || {};
            event.info[key] = value
        }
    }

    /**
     * Whether to include the specified event.
     * @param name
     * @returns {boolean}
     */
    have(name) {
        return this.events[name] != void 0
    }

    _on(name, conf, callback, immediately) {
        if(name == 'isEventManagerStop'){
            Error("error:The event(isEventManagerStop) can only have one callback.")
        }
        let event = this.events[name] = this.events[name] ? this.events[name] : new Event(name, conf.info);
        callback.immediately = immediately;
        conf.index == void 0 ? event.push(callback) : event[conf.index] = callback
    }

    /**
     * on a async event callback.
     * @param {String} name - The event name.
     * @param {Object} conf - The event callback conf.
     * @param {EventManager~eventCallback} callback - The event callback.
     * @param {Boolean} immediately
     */
    onAsync(name, conf, callback, immediately = false) {
        callback.async = true;
        this._on(name, conf, callback, immediately)
    }

    /**
     * on a event callback.
     * @param {String} name - The event name.
     * @param {Object} conf - The event callback conf.
     * @param {EventManager~eventCallback} callback - The event callback.
     * @param {Boolean} immediately
     */
    on(name, conf, callback, immediately = false) {
        callback.async = false;
        this._on(name, conf, callback, immediately)
    }
    /**
     * This eventCallback is added a event event.
     * @callback EventManager~eventCallback
     * @param {EventManager} eventManager
     * @param {Object} callback info.
     */

    /**
     * Stop EventManager.
     */
    stop() {
        this.trigger('eventManagerStop');
        this.trigger('isEventManagerStop')
    }

    /**
     * Determines whether the EventManager is stopped.
     */
    isStop() {
        return this.eventCallbackFibers.length == 1 && this.eventCallbackFibers[0].name == 'isEventManagerStop'
    }

    // helper function

    /**
     * kill the designated event callback.
     * @param {String|RegExp} name - The event name. It can be a regexp(exact match).
     */
    killEventCallback(name) {
        if(typeof name == "string") {
            this.eventCallbackFibers.forEach((obj)=>{
                if(obj.name == name) this.eventCallbackFibers.delete(obj)
            })
        } else {
            this.eventCallbackFibers.forEach((obj)=>{
                if(obj.name.match(name) == obj.name) this.eventCallbackFibers.delete(obj)
            })
        }
    }

    /**
     * if the event callback is running, it returns true.
     * @param {String|RegExp} name - The event name. It can be a regexp(exact match).
     * @returns {boolean}
     */
    isEventCallbackRun(name) {
        let back = false;
        if(typeof name == "string") {
            this.eventCallbackFibers.forEach((obj)=>{if(obj.name == name) return back = true})
        } else {
            this.eventCallbackFibers.forEach((obj)=>{if(obj.name.match(name) == obj.name) return back = true})
        }
        return back
    }

    /**
     * Helper methods.<br>
     * Delete this callback.
     */
    delete() {
        this.afterDelete();
        Fiber.yield(true)
    }

    /**
     * Helper methods.<br>
     * Delete this callback.
     */
    afterDelete() {
        let event = this.events[this.self.name];
        let index = event.indexOf(this.self.callback);
        event.splice(index, 1)
    }

    /**
     * Helper methods.<br>
     * isOk.
     * @param {EventManager~isOkCallback} callback
     */
    isOk(callback) {
        while(true){
            if(callback()) break;
            Fiber.yield()
        }
    }
    /**
     * This isOkCallback is added a event event.
     * @callback EventManager~isOkCallback
     * @return {Boolean} The callback function executes the result.
     */

    /**
     * Helper methods.<br>
     * @param {EventManager~filterCallback} callback
     */
    filter(callback) {
        if(!callback()) Fiber.yield(true)
    }
    /**
     * This filterCallback is added a event event.
     * @callback EventManager~filterCallback
     * @return {Boolean} The callback function executes the result.
     */


    /**
     * Helper methods.<br>
     * @param {Number} value - sec.
     */
    wait(value) {
        if(this.timers[this.self.objectId] == void 0) this.timers[this.self.objectId] = Date.now();
        while(true) {
            if(Date.now() - this.timers[this.self.objectId] > value * 1000) break;
            Fiber.yield()
        }
        this.timers.delete(this.self.objectId)
    }

    /**
     * Helper methods.<br>
     * @param {Number} value - times.
     */
    times(value) {
        if (this.counters[this.self.name]) {
            this.counters[this.self.name] += 1;
        } else {
            this.counters[this.self.name] = 1;
        }
        while(true) {
            if(this.counters[this.self.name] >= value) {
                this.counters[this.self.name] = 0;
                break
            }
            Fiber.yield()
        }
    }

    /**
     * Helper methods.<br>
     * @param {Number} value - sec.
     */
    waitFilter(value) {
        if(!this.timerFilters[this.self.name]) this.timerFilters[this.self.name] = Date.now();
        while(true){
            if(Date.now() - this.timerFilters[this.self.name] > value * 1000) {
                this.timerFilters[this.self.name] = Date.now();
                break
            }
            Fiber.yield(true)
        }
    }

    /**
     * Helper methods.<br>
     * @param {Number} value - times.
     */
    timesFilter(value) {
        if( this.counterfilters[this.self.name]) {
            this.counterfilters[this.self.name] += 1
        } else {
            return this.counterfilters[this.self.name] = 1
        }
        while(true) {
            if( this.counterfilters[this.self.name] > value) {
                this.counterfilters[this.self.name] = 0;
                break
            }
            Fiber.yield(true)
        }
    }
}


export default EventManager