all files / dui/src/core/ core.js

100% Statements 82/82
87.76% Branches 43/49
100% Functions 14/14
100% Lines 82/82
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269                                                                                34×                               34×         16×                                                       37×   34× 10×   34×     33× 33× 32× 32×                                                           40×   38× 38× 38×                 32×           26× 26×           38× 38×                             22×                       33× 33× 33× 33×           33×                   38× 38× 38× 38×             38×        
/**
 * 模板核心
 *
 * @file UI核心
 * @author Brian Li(lbxxlht@163.com)
 * @frozen 8/30/2015
 * 1.封装模板和模板数据结构
 * 2.负责负责模板编译,在tpl属性被设置时进行编译
 * 3.负责数据源处理,在datasource属性被设置时调用数据源预处理,得到预处理结果后,用该结果渲染出html字符串
 * 4.对外提供tpl接口、datasource接口、html接口,其中html接口只负责返回渲染好的html字符串,为只读
 * 5.负责UI对象的自定义事件处理,对外提供on接口用来绑定事件,对内提供fire接口用来触发事件。
 * 6.使用getter、setter,不兼容老浏览器
 */
var define = typeof define === 'function' && define.amd ? define : function (factory) {
    typeof module === 'object' ? (module.exports = factory(require)) : '';
};
 
define(function (require) {
 
    require('./extend');
    var doT = require('./doT');
    var tool = require('./tool');
    var core = {};
 
    // public protected/////////////////////////////////////////////////////////////////
 
    /**
     * 绑定事件
     *
     * @param {string} type 事件类型
     * @param {Function} callback 事件触发时的回调
     * @param {Object | null} me callback绑定的上下位
     */
    core.on = function (type, callback, me) {
        Eif (me === undefined) {
            me = this;
        }
        callback = tool.bind(callback, me);
        this._temp.handlers[type] = this._temp.handlers[type] || [];
        this._temp.handlers[type].push(callback);
    };
 
    /**
     * 触发事件
     *
     * @param {string} type 事件类型
     * @param {Object | null} param callback的回传
     */
    core.fire = function (type, param) {
        param = param || {};
        param.type = type;
        param.target = this;
        Eif (this._temp.handlers[type] instanceof Array) {
            for (var n = 0; n < this._temp.handlers[type].length; n++) {
                this._temp.handlers[type][n](param);
            }
        }
    };
 
    // 初始化数据结构
    core._format = function () {
        this._temp = {
            // 皮肤样式,存储向container注入的class,用于dispose是移除
            className: '',
            // 事件hash1:此hash中的事件已绑定到DOM上
            events: {},
            // 事件hash2:此hash中为自定义事件,通过on从外部注入,通过fire从内部触发
            handlers: {},
            // 模板数组,存储最原始的模板
            tpl: [],
            // 编译结果,用于渲染
            renderer: function () {},
            // 数据源
            datasource: {},
            // 渲染结果,外部只读,内部可写
            html: ''
        };
        Object.defineProperty(this, '_temp', {enumerable: false});
    };
 
    // 销毁数据结构
    core._dispose = function () {
        /* eslint-disable */
        for (var key in this._temp) {
            this._temp[key] = null;
        }
        this._temp = null;
        /* eslint-enable */
    };
 
    Object.defineProperty(core, '_format', {enumerable: false});
    Object.defineProperty(core, '_init', {enumerable: false});
    Object.defineProperty(core, '_dispose', {enumerable: false});
 
    // public //////////////////////////////////////////////////////////////////////////
 
    /**
     * tpl模板接口
     *
     * @attribute read and write
     */
    Object.defineProperty(core, 'tpl', {
 
        /**
         * getter
         *
         * @return {Array} tpl数组
         */
        get: function () {
            return this._temp != null ? this._temp.tpl : null;
        },
 
        /**
         * setter
         *
         * @param {Array|string} tpl 模板字符串或字符串数组
         */
        set: function (tpl) {
            if (tpl == null || this._temp == null) {
                return;
            }
            if (typeof tpl === 'string') {
                tpl = [tpl];
            }
            if (!(tpl instanceof Array)) {
                return;
            }
            // 编译一次,确保模板是正确的
            var func = compile(tpl);
            if (func != null) {
                this._temp.tpl = tpl;
                this._temp.renderer = func;
            }
        }
    });
 
    /**
     * datasource 数据源接口
     *
     * @attribute read and write
     */
    Object.defineProperty(core, 'datasource', {
 
        /**
         * getter
         *
         * @return {Object} 数据源对象
         */
        get: function () {
            var result = null;
            if (this._temp == null) {
                return result;
            }
            var datasource = this._temp.datasource;
            if (datasource.hasOwnProperty('__prepare__') && typeof datasource.__prepare__ === 'function') {
                result = datasource.__datasource__;
            }
            else {
                result = datasource;
            }
            return result;
        },
 
        /**
         * setter
         *
         * @param {Object} datasource 数据源对象,在模板中被翻译成it
         * 用编译结果将传入的数据源渲染,并保存渲染结果
         */
        set: function (datasource) {
            if (this._temp == null) {
                return;
            }
            var oldData = this._temp.datasource;
            var renderData = null;
            if (
                oldData != null
                && oldData.hasOwnProperty('__prepare__')
                && typeof oldData.__prepare__ === 'function'
            ) {
                // 走此分支场景:
                // 用户通过someUiObj.datasource = ... 再次操作数据源
                // 该UI原始数据源和渲染数据源不同,并且UI指定了原始数据源处理函数
                oldData.__datasource__ = datasource;
                renderData = oldData.__prepare__(datasource);
            }
            else if (
                datasource.hasOwnProperty('__prepare__')
                && typeof datasource.__prepare__ === 'function'
            ) {
                // 走此分支场景:
                // UI初始化时,将原始数据源和处理处理函数包装后,进行Caller
                renderData = datasource.__prepare__(datasource.__datasource__);
                this._temp.datasource = datasource;
            }
            else {
                // 走此分支场景:
                // UI原始数据源跟渲染数据源格式相同,不用处理
                renderData = datasource;
                this._temp.datasource = datasource;
            }
            this._temp.renderData = renderData;
            this._temp.html = render(this._temp.renderer, renderData);
        }
    });
 
    /**
     * html 渲染结果接口
     * @attribute readonly
     */
    Object.defineProperty(core, 'html', {
 
        /**
         * get方法
         *
         * @return {string} 渲染结果
         */
        get: function () {
            return this._temp != null ? this._temp.html : null;
        }
    });
 
    // private //////////////////////////////////////
 
    /**
     * 编译模板
     *
     * @param {Array<string>} tpl 模板数组
     * @return {Function|null} 编辑结果
     */
    function compile(tpl) {
        var func = null;
        var template = tpl.join('');
        try {
            func = doT.compile(template);
        }
        catch (e) {
            /* eslint-disable */
            console.error('Tpl can not be compiled, please check tpl\'s grammar.');
            /* eslint-enable */
        }
        return func;
    }
 
    /**
     * 渲染数据
     *
     * @param {Function} func 渲染函数
     * @param {Object} datasource 数据源
     * @return {string} 渲染结果
     */
    function render(func, datasource) {
        var str = '';
        Eif (typeof func === 'function' && datasource != null) {
            try {
                str = func(datasource);
            }
            catch (e) {
                /* eslint-disable */
                console.error('Datasource can not match Renderer.');
                /* eslint-enable */
            }
        }
        return str;
    }
 
    return core;
});