/** * @ignore * Utils for kissy loader * @author yiminghe@gmail.com */ (function (S) { var Loader = S.Loader, Path = S.Path, host = S.Env.host, startsWith = S.startsWith, data = Loader.Status, ATTACHED = data.ATTACHED, LOADED = data.LOADED, /** * @class KISSY.Loader.Utils * Utils for KISSY Loader * @singleton * @private */ Utils = S.Loader.Utils = {}, doc = host.document; // http://wiki.commonjs.org/wiki/Packages/Mappings/A // 如果模块名以 / 结尾,自动加 index function indexMap(s) { if (S.isArray(s)) { var ret = [], i = 0; for (; i < s.length; i++) { ret[i] = indexMapStr(s[i]); } return ret; } return indexMapStr(s); } function indexMapStr(s) { // 'x/' 'x/y/z/' if (S.endsWith(Path.basename(s), '/')) { s += 'index'; } return s; } S.mix(Utils, { /** * get document head * @return {HTMLElement} */ docHead: function () { return doc.getElementsByTagName('head')[0] || doc.documentElement; }, /** * Get absolute path of dep module.similar to {@link KISSY.Path#resolve} * @param moduleName current module 's name * @param depName dep module 's name * @return {string|Array} */ normalDepModuleName: function (moduleName, depName) { var i = 0; if (!depName) { return depName; } if (S.isArray(depName)) { for (; i < depName.length; i++) { depName[i] = Utils.normalDepModuleName(moduleName, depName[i]); } return depName; } if (startsWith(depName, '../') || startsWith(depName, './')) { // x/y/z -> x/y/ return Path.resolve(Path.dirname(moduleName), depName); } return Path.normalize(depName); }, /** * create modules info * @param runtime * @param modNames */ createModulesInfo: function (runtime, modNames) { S.each(modNames, function (m) { Utils.createModuleInfo(runtime, m); }); }, /** * create single module info * @param runtime * @param modName * @param [cfg] * @return {KISSY.Loader.Module} */ createModuleInfo: function (runtime, modName, cfg) { modName = indexMapStr(modName); var mods = runtime.Env.mods, mod = mods[modName]; if (mod) { return mod; } // 防止 cfg 里有 tag,构建 fullpath 需要 mods[modName] = mod = new Loader.Module(S.mix({ name: modName, runtime: runtime }, cfg)); return mod; }, /** * Whether modNames is attached. * @param runtime * @param modNames * @return {Boolean} */ isAttached: function (runtime, modNames) { return isStatus(runtime, modNames, ATTACHED); }, /** * Whether modNames is loaded. * @param runtime * @param modNames * @return {Boolean} */ isLoaded: function (runtime, modNames) { return isStatus(runtime, modNames, LOADED); }, /** * Get module values * @param runtime * @param modNames * @return {Array} */ getModules: function (runtime, modNames) { var mods = [runtime], mod, unalias, allOk, m, runtimeMods = runtime.Env.mods; S.each(modNames, function (modName) { mod = runtimeMods[modName]; if (!mod || mod.getType() != 'css') { unalias = Utils.unalias(runtime, modName); allOk = S.reduce(unalias, function (a, n) { m = runtimeMods[n]; return a && m && m.status == ATTACHED; }, true); if (allOk) { mods.push(runtimeMods[unalias[0]].value); } else { mods.push(null); } } }); return mods; }, attachModsRecursively: function (modNames, runtime, stack) { stack = stack || []; var i, s = 1, l = modNames.length, stackDepth = stack.length; for (i = 0; i < l; i++) { s = Utils.attachModRecursively(modNames[i], runtime, stack) && s; stack.length = stackDepth; } return s; }, attachModRecursively: function (modName, runtime, stack) { var mods = runtime.Env.mods, status, m = mods[modName]; if (!m) { return 0; } status = m.status; if (status == ATTACHED) { return 1; } if (status != LOADED) { return 0; } if (S.inArray(modName, stack)) { stack.push(modName); S.error('find cyclic dependency between mods: ' + stack); return 0; } stack.push(modName); if (Utils.attachModsRecursively(m.getNormalizedRequires(), runtime, stack)) { Utils.attachMod(runtime, m); return 1; } return 0; }, /** * Attach specified mod. * @param runtime * @param mod */ attachMod: function (runtime, mod) { if (mod.status != LOADED) { return; } var fn = mod.fn; if (fn) { // 需要解开 index,相对路径 // 但是需要保留 alias,防止值不对应 mod.value = fn.apply(mod, Utils.getModules(runtime, mod.getRequiresWithAlias())); } mod.status = ATTACHED; runtime.getLoader().fire('afterModAttached', { mod: mod }); }, /** * Get mod names as array. * @param modNames * @return {String[]} */ getModNamesAsArray: function (modNames) { if (typeof modNames == 'string') { modNames = modNames.replace(/\s+/g, '').split(','); } return modNames; }, /** * Three effects: * 1. add index : / => /index * 2. unalias : core => dom,event,ua * 3. relative to absolute : ./x => y/x * @param {KISSY} runtime Global KISSY instance * @param {String|String[]} modNames Array of module names * or module names string separated by comma * @param {String} [refModName] * @return {String[]} */ normalizeModNames: function (runtime, modNames, refModName) { return Utils.unalias(runtime, Utils.normalizeModNamesWithAlias(runtime, modNames, refModName)); }, /** * unalias module name. * @param runtime * @param names * @return {Array} */ unalias: function (runtime, names) { var ret = [].concat(names), i, m, alias, ok = 0, j, mods = runtime['Env'].mods; while (!ok) { ok = 1; for (i = ret.length - 1; i >= 0; i--) { if ((m = mods[ret[i]]) && (alias = m.alias)) { ok = 0; for (j = alias.length - 1; j >= 0; j--) { if (!alias[j]) { alias.splice(j, 1); } } ret.splice.apply(ret, [i, 1].concat(indexMap(alias))); } } } return ret; }, /** * normalize module names * @param runtime * @param modNames * @param [refModName] * @return {Array} */ normalizeModNamesWithAlias: function (runtime, modNames, refModName) { var ret = [], i, l; if (modNames) { // 1. index map for (i = 0, l = modNames.length; i < l; i++) { // conditional loader // requires:[window.localStorage?"local-storage":""] if (modNames[i]) { ret.push(indexMap(modNames[i])); } } } // 2. relative to absolute (optional) if (refModName) { ret = Utils.normalDepModuleName(refModName, ret); } return ret; }, /** * register module with factory * @param runtime * @param name * @param fn * @param [config] */ registerModule: function (runtime, name, fn, config) { var mods = runtime.Env.mods, mod = mods[name]; if (mod && mod.fn) { S.log(name + ' is defined more than once'); return; } // 没有 use,静态载入的 add 可能执行 Utils.createModuleInfo(runtime, name); mod = mods[name]; // 注意:通过 S.add(name[, fn[, config]]) 注册的代码,无论是页面中的代码, // 还是 js 文件里的代码,add 执行时,都意味着该模块已经 LOADED S.mix(mod, { name: name, status: LOADED, fn: fn }); S.mix(mod, config); // S.log(name + ' is loaded', 'info'); }, /** * Get mapped path. * @param runtime * @param path * @param [rules] * @return {String} */ getMappedPath: function (runtime, path, rules) { var mappedRules = rules || runtime.Config.mappedRules || [], i, m, rule; for (i = 0; i < mappedRules.length; i++) { rule = mappedRules[i]; if (m = path.match(rule[0])) { return path.replace(rule[0], rule[1]); } } return path; } }); function isStatus(runtime, modNames, status) { var mods = runtime.Env.mods, mod, i; modNames = S.makeArray(modNames); for (i = 0; i < modNames.length; i++) { mod = mods[modNames[i]]; if (!mod || mod.status !== status) { return 0; } } return 1; } })(KISSY);