loader.js 11.7 KB
(function (g) {
    var font = {
        DEFAULT: 'default',
        BIG: 'big',
        LARGE: 'large'
    };
    
    var _createScript = function (src, onload) {
        var script = document.createElement("script");

        script.src = src;
        script.type = "text/javascript";
        script.onload = onload;
        document.head.appendChild(script);
    };

    var isEnabledCache = "localStorage" in window, // 不支持PWA 则使用localStorage缓存方案
        MMSRC = "mobile_static_resource_cache";

    var mmsrc = {
        get: function () {
            return JSON.parse(localStorage.getItem(MMSRC) || "{}");
        },
        set: function (o) {
            try {
                localStorage.setItem(MMSRC, JSON.stringify(o));
            } catch (e) {
                console.log("超出localStorage上限");
            }
        }
    };

    g.Loader = function (opts) {
        opts.require.version = opts.require.path.split("v=")[1];
        opts.module.version = opts.module.path.split("v=")[1];

        var JavascriptLoader = function (type) {
            var r = opts.require;
            var mod = opts.module;
            var _loader = {
                init: function (modName, cb) {
                    cb = cb || function () { };

                    return this[modName](cb);
                },
                require: function (cb) {
                    _createScript(r.path, cb);
                },
                module: function (cb) {
                    require([mod.path], function () {
                        require([mod.name]);
                        cb && cb();
                    });
                }
            };

            return _loader.init.apply(_loader, [].slice.call(arguments));
        };

        var HtmlLoader = function () {
            var meta = window.__meta__;
            var appid = meta.appid;
            var version = meta.resourceVersion || new Date().getTime();
            var path = "./js/frame/index.js?v=" + version;
            var u = HtmlLoader.utils;
            var _setGlobal = function (htm) {
                window[opts.module.name + "_mmsrcHtml"] = htm;
            }, _onHtmlLoaded = function(html) {
                g.Loader.htmlLoaded = true;

                if (g.Loader.onHtmlLoaded) {
                    g.Loader.onHtmlLoaded(html);
                }
            }, _getAppHomepageId = function (htmJson) {
                var id = "";

                htmJson.pages.every(function (page) {
                    if (!page.h) return true;

                    id = page.i;
                });

                return id;
            }, _setCurrentPageId = function (pageid) {
                pageid = String(pageid);

                meta.appHomepageId = ~pageid.indexOf("page_") ? pageid : "page_"+pageid;
            };

            if(!isEnabledCache) {
                return require([path], function (htmJson) {
                    _setCurrentPageId(meta.appHomepageId || _getAppHomepageId(htmJson));
                    html = u.toHtml(htmJson);

                    u.initRuntimeConfig(htmJson.runtime);
                    _setGlobal(html);
                    _onHtmlLoaded(html);
                });
            }

            var mmsrcHtmlObj = mmsrc.get().htm || { history: [] }, // history 用于存放应用模板的存放顺序 当localstorage容量不够时 取最早应用删除
                currHtmlObj = mmsrcHtmlObj[appid] || {},
                mmsrcHtml = currHtmlObj[version];

            if (mmsrcHtml && version in currHtmlObj) {
                _setCurrentPageId(meta.appHomepageId || currHtmlObj.appHomepageId);
                u.initRuntimeConfig(JSON.parse(currHtmlObj.runtime));
                
                return _setGlobal(mmsrcHtml);
            }

            currHtmlObj = {};

            require([path], function (htmJson) {
                var issuccess = false;
                var isdelete = false;
                var html = JSON.stringify(htmJson);
                var appHomepageId = _getAppHomepageId(htmJson);
                var _cache = function () {
                    var o = mmsrc.get();
                    try {
                        var index = mmsrcHtmlObj.history.indexOf(appid);

                        currHtmlObj[version] = html;
                        currHtmlObj.appHomepageId = appHomepageId;
                        currHtmlObj.runtime = JSON.stringify(htmJson.runtime);
                        _setGlobal(html);
                        mmsrcHtmlObj[appid] = currHtmlObj;
                        // 删除并更新histroy中的应用顺序
                        ~index && mmsrcHtmlObj.history.splice(index, 1);
                        mmsrcHtmlObj.history.push(appid);
                        o.htm = mmsrcHtmlObj;
                        mmsrc.set(o);
                        issuccess = true;
                    } catch (e) {
                        if (isdelete) return (issuccess = true);

                        var originalId = mmsrcHtmlObj.history.shift();

                        delete mmsrcHtmlObj[originalId];
                        originalId === appid && !mmsrcHtmlObj.history.length && (isdelete = true);
                        o.htm = mmsrcHtmlObj;
                        mmsrc.set(o);
                        issuccess = true;
                    }
                };

                html = u.toHtml(htmJson);

                u.initRuntimeConfig(htmJson.runtime);
                _setCurrentPageId(meta.appHomepageId || appHomepageId);

                while (!issuccess) {
                    try {
                        _cache();
                    } catch (e) {
                        _cache();
                    }
                }
                _onHtmlLoaded(html);
            });
        };

        HtmlLoader.utils = {
            map: function(pages) {
                var u = this;

                return pages.map(function(page) {
                    return {
                        id: "page_" + page.i,
                        name: page.n,
                        attrs: "class='page out'" + (page.h ? " data-homepage" : ""),
                        compHtml: page.c.reduce(function (prev, comp) { // comps
                            return prev + u.toCompTmpl(comp);
                        }, ""),
                        tabBarPageHtml: page.p && u.map(page.p).reduce(function (prev, p) { // innerPages 只有tabbar有
                            p.attrs = "class='tabpanel show_hide out' data-form='show_hide'";
                            return prev + u.toPageTmpl(p);
                        }, "") || "",
                    };
                });
            },
            toCompTmpl: function(comp) {
                if (comp.TabBar) return "";

                var attrs = "";

                if(comp.c) { // category
                    attrs += "data-category='" + comp.c + "' ";
                }

                if (comp.l) { // lazyload
                    attrs += "data-lazyload='true' ";
                }

                if (!isNaN(comp.p)) { // proirity
                    attrs += "data-priority='" + comp.p + "'";
                }

                // i => id; t => type
                return "<abbr id='" + comp.i + "' data-type='" + comp.t + "' " + attrs + " ></abbr>";
            },
            toPageTmpl: function(page) {
                return "\
                    <div id='" + page.id + "' data-title='" + page.name + "' " + page.attrs + " >\
                        <div class='page-content' id='" + page.id + "_content'>\
                            <div class='page-scroller' id='" + page.id + "_scroller'>" + page.compHtml + "</div>\
                            " + page.tabBarPageHtml + "\
                        </div>\
                    </div>\
                ";
            },
            toPagesTmpl: function(pages) {
                var u = this;

                return pages.reduce(function(prev, page) {
                    if(page.innerPages) {
                        prev += u.toPagesTmpl(page.innerPages);
                    }

                    return prev + u.toPageTmpl(page);
                }, "");
            },
            toHtml: function(html) {
                var htm = "";
                var skin = html.skin;

                if (skin) {
                    htm += "<link rel='stylesheet' href='./css/skin/" + skin.id + ".css?v=" + skin.version + "' />";
                }

                if (html.custom_css) {
                    htm += "<link rel='stylesheet' href='./css/custom/index.css?v=" + g.MPageLastModified + "' />";
                }

                htm += this.toPagesTmpl( this.map(html.pages) );

                return htm;
            },
            initRuntimeConfig: function (runtime) {
                if(!runtime) return;

                var config = runtime.config;
                var modules = runtime.modules;
                var meta = window.__meta__;
                var compos = window.requiredComponents.concat(modules.compos || []);
                var fontsExp = new RegExp('-(' + Object.keys(font).join('|') + ')', 'gi');
                var addFontExt = function (name) {
                    // 重置localStorage缓存中config已处理的字体
                    var matchs = name.match(fontsExp);

                    if(matchs) {
                        name = name.replace(matchs[0], '');
                    }

                    return name + '-' + meta.clientFont;
                };

                // 系统插件
                compos.forEach(function (name) {
                    config.paths[name] = "js/component/" + name + "_wev8";
                    config.paths[name + "_css"] = addFontExt("css/component/" + name);
                });
                // 自定义插件
                (modules.custom_compos || []).forEach(function (name) {
                    config.paths[name] = "js/component/" + name + "_wev8";
                    config.paths[name + "_css"] = "css/component/" + name + '-' + font.DEFAULT;
                });

                (modules.servs || []).forEach(function (name) {
                    var mname = "mService/" + name;
                    var fname = 'service.' + name.replace('/', '.');

                    config.paths[mname] = "js/service/" + fname + "_wev8";
                    config.paths[mname + "_html"] = "template/service/" + fname + ".html";

                    if(~name.indexOf('/')) return;

                    config.paths[mname + "_css"] = addFontExt("css/service/" + fname);
                });

                // format common中的css路径
                Object.keys(config.paths).forEach(function (modName) {
                    var relPath = config.paths[modName];

                    if(~modName.indexOf('_css') && ~relPath.indexOf('css/common')) {
                        relPath = relPath.replace(/_wev8$/, '');

                        config.paths[modName] = addFontExt(relPath);
                    }
                });

                require(config);
                window.requiredComponents = null;
            }
        };
        var initRequireConfig = function () {
            var config = window.amdConfig;

            // 处理service页面css配置
            Object.keys(config.paths).filter(function (key) {
                var path = config.paths[key];

                return ~key.indexOf('_css') && ~path.indexOf("css/service/");
            }).forEach(function (key) {
                var serviceId = key.replace(/(mService\/|_css)/g, '');

                config.paths[key] = "css/service/service." + serviceId + '-default';
            });

            require(config);
            window.amdConfig = null;
        };

        JavascriptLoader("require", function () {
            initRequireConfig();
            HtmlLoader();
            JavascriptLoader("module");
        });
    };
}(window));