core/engine.js

define('engine', ['zepto', 'underscore'], function ($, _) {
    /**
     * 框架的方法集合,是全局公用对象
     * @namespace xjs
     */
    window.xjs = xjs = {};

    var containerNode = document.getElementById('appview');
    var _class = {};
    var _instances = xjs._instances = {};

    /**
     * 销毁一个Page类,并触发onExit事件
     * @method destroyView
     * @memberOf xjs
     * @param {string} id 实例化后的Page类的`id`
     * @see widget
     */
    xjs.destroyView = function (id) {
        if (!Object.getOwnPropertyNames(_instances).length) return;

        var obj = {};
        if (id) {
            obj[id] = _instances[id];
        } else {
            obj = _instances;
        }

        $.each(obj, function (name, widget) {
            if (widget.keepInside)
                return;

            widget.onExit();
            delete _instances[name];
        });
    };

    /**
     * 实例化一个Page类,请确已申明这个类。[xjs.declare]{@link xjs.declare}
     * @method createView
     * @memberOf xjs
     * @param {String} name 已申明的Page类名
     * @param {Object}[param] 可选参数,可选此对象将会和Page对象合并
     * @param {Object}[wrapper] 可选参数,传入Dom节点则会以这个节点为父节点,否则就会在`#appview`下创建一个新的dom节点
     * @param {Boolean}[defaultNode] 可选参数,选择以Wrapper或Wrapper的子节点作为主节点,将会插入模板到此结点
     * @see xjs.declare
     */
    xjs.createView = function (name, param, wrapper, defaultNode) {
        if (!wrapper) {
            wrapper = document.createElement('div');
            $(containerNode).append(wrapper);
        } else {
            if (!defaultNode) {
                wrapper = $('<div></div>').appendTo(wrapper).get(0);
            } else {
                wrapper = $(wrapper).get(0);
            }
        }
        return xjs.getDeclare(name, param || {}, wrapper);
    };

    xjs.getDeclare = function (name, param, node) {
        return arguments.length > 1 ? (function () {
            var dtd = $.Deferred();

            node.id = (function () {
                var o = 0, taskName = name.replace(/\./, '_') + '_';
                while (_instances[taskName + o]) {
                    o += 1;
                }
                return taskName + o;
            })();

            require([name], function (page) {
                _instances[node.id] = mixinProp(page, param);
                _instances[node.id].init(node);
                dtd.resolve(_instances[node.id]);
            });

            return dtd.promise();
        })() : _class[name];
    };

    /**
     * 申明一个Page类,所有Page类都需要先申明后才可以作为参数被被creatView使用
     * @method declare
     * @memberOf xjs
     * @param {String} classname Page类的名字
     * @param {Object} parents 此Page类的父类继承对象,通常使用[widget]{@link widget}作为父类
     * @param {Object} prop Page类的方法
     * @see widget
     */
    xjs.declare = function (classname, parents, prop) {
        return _class[classname] = mixinProp(parents, prop);
    };

    /**
     * 获取一个已实例化的Page类,传入Page类的id后获取到对象
     * @method byId
     * @memberOf xjs
     * @param {String} id 传入实例化的Page类的`id`
     * @return {Object} 当前id所对应的Page类的this对象
     */
    xjs.byId = function (id) {
        var id = id.indexOf('#') >= 0 ? id.substr(1) : id;
        return _instances[id];
    };

    /**
     * 注册一个全局事件,可以通过xjs.triggerAnnounceEvent触发
     * @method addAnnounceEvent
     * @memberOf xjs
     * @param {String} name 事件名
     * @param {Function} fn 回调函数
     */
    xjs.addAnnounceEvent = function (name, fn) {
        $(document).on(name, fn);
    };

    /**
     * 触发一个全局事件
     * @method triggerAnounceEvent
     * @memberOf xjs
     * @param {String} name 事件名
     */
    xjs.triggerAnnounceEvent = function (name) {
        var parameter = [].slice.apply(arguments, [1, arguments.length]);
        $(document).trigger(name, parameter);
    };

    /**
     * 注销一个全局事件
     * @method removeAnnounceEvent
     * @memberOf xjs
     * @param {String} name 事件名
     */
    xjs.removeAnnounceEvent = function (name) {
        $(document).off(name);
    };

    function mixinProp(parentClass, prop) {
        if (!prop) {
            prop = parentClass;
            parentClass = {};
        }
        var fnTest = /xyz/.test(function () {
            xyz;
        }) ? /\b_super\b/ : /.*/;
        var _super = parentClass;
        var prototype = _.create(parentClass);

        for (var name in prop) {
            prototype[name] = typeof prop[name] == "function" &&
            typeof _super[name] == "function" && fnTest.test(prop[name]) ?
                (function (name, fn) {
                    return function () {
                        var tmp = this._super;
                        this._super = _super[name];
                        var ret = fn.apply(this, arguments);
                        this._super = tmp;
                        return ret;
                    };
                })(name, prop[name]) :
                prop[name];
        }
        return prototype;
    }

    return xjs;
});