Source: Context.js

/**
 * @file context
 * @author Leon(leon@outlook.com)
 */

var u = require('underscore');

var invariant = require('./util/invariant');

/**
 * `ei`视图上下文
 *
 * @constructor
 * @param {*} initialValue 初始化状态
 * @param {!Function} reducer 裁剪器
 */
function Context(initialValue, reducer) {

    invariant(u.isFunction(reducer), 'Context need a reducer');

    /**
     * 剪裁器
     *
     * @private
     * @member {Function}
     */
    this.reducer = reducer;

    /**
     * 数据仓库
     *
     * @private
     * @member {*}
     */
    this.store = initialValue;

    this.dispatch = u.bind(this.dispatch, this);

    this.getState = u.bind(this.getState, this);

    /**
     * 数据变化侦听函数
     *
     * @private
     * @member {Array.<Function>}
     */
    this.listeners = [];

}

/**
 * 对当前的数据进行剪裁
 *
 * @public
 * @param  {*}       state 数据状态
 * @param  {!Object} action 动作
 * @return {*} 新的数据状态
 */
Context.prototype.reduce = function (state, action) {
    return this.reducer(state, action);
};

/**
 * 获取当前上下文中的数据
 *
 * @public
 * @return {*}
 */
Context.prototype.getState = function () {
    return this.store;
};

/**
 * 设置当前的数据状态
 *
 * @param {*} store 数据状态
 * @return {module:Context}
 */
Context.prototype.setState = function (store) {
    this.store = store;
    return this;
};


/**
 * 派发动作,激活数据剪裁
 *
 * @public
 * @param {!Object} action 动作
 * @return {Object} 动作
 */
Context.prototype.dispatch = function (action) {

    if (u.isFunction(action)) {
        return action(this.dispatch, this.getState);
    }

    var nextState = this.reduce(this.store, action);

    this.setState(nextState);

    for (var listeners = this.listeners.slice(), i = 0, len = listeners.length; i < len; ++i) {
        listeners[i]();
    }

    return action;

};

/**
 * 添加数据变化侦听器
 *
 * @public
 * @param {!Function} handler 处理函数
 * @return {module:Context}
 */
Context.prototype.addChangeListener = function (handler) {
    this.listeners.push(handler);
    return this;
},

/**
 * 移除数据变化侦听器
 *
 * @param {!Function} handler 处理函数
 * @return {module:Context}
 */
Context.prototype.removeChangeListener = function (handler) {

    for (var listeners = this.listeners, i = listeners.length - 1; i >= 0; --i) {
        if (listeners[i] === handler) {
            listeners.splice(i, 1);
            return this;
        }
    }

    return this;

};

module.exports = Context;