/** * @ignore * Utils for kissy loader * @author yiminghe@gmail.com */ (function (S) { var Loader = S.Loader, Path = S.Path, logger = S.getLogger('s/loader'), host = S.Env.host, startsWith = S.startsWith, data = Loader.Status, ATTACHED = data.ATTACHED, LOADED = data.LOADED, ERROR = data.ERROR, /** * @class KISSY.Loader.Utils * Utils for KISSY Loader * @singleton * @private */ Utils = Loader.Utils = {}, doc = host.document; // http://wiki.commonjs.org/wiki/Packages/Mappings/A // 如果模块名以 / 结尾,自动加 index function indexMap(s) { if (typeof s == 'string') { return indexMapStr(s); } else { var ret = [], i = 0, l = s.length; for (; i < l; i++) { ret[i] = indexMapStr(s[i]); } return ret; } } function indexMapStr(s) { // 'x/' 'x/y/z/' if (s.charAt(s.length - 1) == '/') { s += 'index'; } return s; } function pluginAlias(runtime, name) { var index = name.indexOf('!'); if (index != -1) { var pluginName = name.substring(0, index); name = name.substring(index + 1); S.use(pluginName, { sync: true, success: function (S, Plugin) { if (Plugin.alias) { //noinspection JSReferencingMutableVariableFromClosure name = Plugin.alias(runtime, name, pluginName); } } }); } return name; } 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 {String} moduleName current module 's name * @param {String|String[]} depName dependency module 's name * @return {String|String[]} normalized dependency module 's name */ normalDepModuleName: function (moduleName, depName) { var i = 0, l; if (!depName) { return depName; } if (typeof depName == 'string') { if (startsWith(depName, '../') || startsWith(depName, './')) { // x/y/z -> x/y/ return Path.resolve(Path.dirname(moduleName), depName); } return Path.normalize(depName); } for (l = depName.length; i < l; i++) { depName[i] = Utils.normalDepModuleName(moduleName, depName[i]); } return depName; }, /** * create modules info * @param runtime Module container, such as KISSY * @param {String[]} modNames to be created module names */ createModulesInfo: function (runtime, modNames) { S.each(modNames, function (m) { Utils.createModuleInfo(runtime, m); }); }, /** * create single module info * @param runtime Module container, such as KISSY * @param {String} modName to be created module name * @param {Object} [cfg] module config * @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 Module container, such as KISSY * @param modNames * @return {Boolean} */ 'isAttached': function (runtime, modNames) { return isStatus(runtime, modNames, ATTACHED); }, /** * Get module values * @param runtime Module container, such as KISSY * @param {String[]} modNames module names * @return {Array} module values */ 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; }, /** * attach modules and their dependency modules recursively * @param {String[]} modNames module names * @param runtime Module container, such as KISSY * @param {String[]} [stack] stack for detecting circular dependency * @param {Array} [errorList] errors when attach mods * @param {Object} [cache] cached modules to avoid duplicate check * @returns whether success attach all modules */ attachModsRecursively: function (modNames, runtime, stack, errorList, cache) { // for debug. prevent circular dependency stack = stack || []; // for efficiency. avoid duplicate non-attach check cache = cache || {}; var i, s = 1, l = modNames.length, stackDepth = stack.length; for (i = 0; i < l; i++) { s = s && Utils.attachModRecursively(modNames[i], runtime, stack, errorList, cache); stack.length = stackDepth; } return s; }, /** * attach module and its dependency modules recursively * @param {String} modName module name * @param runtime Module container, such as KISSY * @param {String[]} [stack] stack for detecting circular dependency * @param {Array} [errorList] errors when attach mods * @param {Object} [cache] cached modules to avoid duplicate check * @returns whether success attach all modules */ attachModRecursively: function (modName, runtime, stack, errorList, cache) { var mods = runtime.Env.mods, status, m = mods[modName]; if (modName in cache) { return cache[modName]; } if (!m) { return cache[modName] = 0; } status = m.status; if (status == ATTACHED) { return cache[modName] = 1; } if (status == ERROR) { errorList.push(m); } if (status != LOADED) { return cache[modName] = 0; } if ('@DEBUG@') { if (S.inArray(modName, stack)) { stack.push(modName); S.error('find cyclic dependency between mods: ' + stack); return cache[modName] = 0; } stack.push(modName); } if (Utils.attachModsRecursively(m.getNormalizedRequires(), runtime, stack, errorList, cache)) { Utils.attachMod(runtime, m); return cache[modName] = 1; } return cache[modName] = 0; }, /** * Attach specified mod. * @param runtime Module container, such as KISSY * @param {KISSY.Loader.Module} mod module instance */ attachMod: function (runtime, mod) { if (mod.status != LOADED) { return; } var fn = mod.fn; if (typeof fn === 'function') { // 需要解开 index,相对路径 // 但是需要保留 alias,防止值不对应 mod.value = fn.apply(mod, Utils.getModules(runtime, mod.getRequiresWithAlias())); } else { mod.value = fn; } mod.status = ATTACHED; }, /** * Get mod names as array. * @param {String|String[]} modNames module names array or module names string separated by ',' * @return {String[]} */ getModNamesAsArray: function (modNames) { if (typeof modNames == 'string') { modNames = modNames.replace(/\s+/g, '').split(','); } return modNames; }, /** * normalize module names * 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[]} normalized module names */ normalizeModNames: function (runtime, modNames, refModName) { return Utils.unalias(runtime, Utils.normalizeModNamesWithAlias(runtime, modNames, refModName)); }, /** * unalias module name. * @param runtime Module container, such as KISSY * @param {String} names moduleNames * @return {String[]} unaliased module names */ 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 with alias * @param runtime Module container, such as KISSY * @param {String[]} modNames module names * @param [refModName] module to be referred if module name path is relative * @return {String[]} normalize module names with alias */ 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(pluginAlias(runtime, indexMap(modNames[i]))); } } } // 2. relative to absolute (optional) if (refModName) { ret = Utils.normalDepModuleName(refModName, ret); } return ret; }, /** * register module with factory * @param runtime Module container, such as KISSY * @param {String} name module name * @param {Function|*} fn module's factory or value * @param [config] module config, such as dependency */ registerModule: function (runtime, name, fn, config) { name = indexMapStr(name); var mods = runtime.Env.mods, mod = mods[name]; if (mod && mod.fn) { logger.error(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); }, /** * Get mapped path. * @param runtime Module container, such as KISSY * @param {String} path module path * @param [rules] map rules * @return {String} mapped path */ 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);