/** * ESL (Enterprise Standard Loader) * Copyright 2013 Baidu Inc. All rights reserved. * * @file Browser端标准加载器,符合AMD规范 * @author errorrik(errorrik@gmail.com) * Firede(firede@firede.us) */ /* jshint ignore:start */ var define; var require; var esl; /* jshint ignore:end */ (function (global) { // "mod"开头的变量或函数为内部模块管理函数 // 为提高压缩率,不使用function或object包装 /** * 模块容器 * * @inner * @type {Object} */ var modModules = {}; /** * 自动定义的模块表 * * 模块define factory是用到时才执行,但是以下几种情况需要自动马上执行: * 1. require( [moduleId], callback ) * 2. plugin module: require( 'plugin!resource' ) * * @inner * @type {Object} */ var autoDefineModules = {}; // 模块状态枚举量 var MODULE_PRE_DEFINED = 1; var MODULE_ANALYZED = 2; var MODULE_PREPARED = 3; var MODULE_DEFINED = 4; /** * 内建module名称集合 * * @inner * @type {Object} */ var BUILDIN_MODULE = { require: globalRequire, exports: 1, module: 1 }; /** * 全局require函数 * * @inner * @type {Function} */ var actualGlobalRequire = createLocalRequire(); // #begin-ignore /** * 超时提醒定时器 * * @inner * @type {number} */ var waitTimeout; // #end-ignore /* eslint-disable fecs-key-spacing */ /* eslint-disable key-spacing */ /** * require配置 * * @inner * @type {Object} */ var requireConf = { baseUrl : './', paths : {}, config : {}, map : {}, packages : [], // #begin-ignore waitSeconds: 0, // #end-ignore noRequests : {}, urlArgs : {} }; /* eslint-enable key-spacing */ /** * 加载模块 * * @param {string|Array} requireId 模块id或模块id数组, * @param {Function=} callback 加载完成的回调函数 * @return {*} requireId为string时返回模块暴露对象 */ function globalRequire(requireId, callback) { // #begin-ignore // #begin assertNotContainRelativeId // 确定require的模块id不包含相对id。用于global require,提前预防难以跟踪的错误出现 var invalidIds = []; /** * 监测模块id是否relative id * * @inner * @param {string} id 模块id */ function monitor(id) { if (id.indexOf('.') === 0) { invalidIds.push(id); } } if (typeof requireId === 'string') { monitor(requireId); } else { each( requireId, function (id) { monitor(id); } ); } // 包含相对id时,直接抛出错误 if (invalidIds.length > 0) { throw new Error( '[REQUIRE_FATAL]Relative ID is not allowed in global require: ' + invalidIds.join(', ') ); } // #end assertNotContainRelativeId // 超时提醒 var timeout = requireConf.waitSeconds; if (timeout && (requireId instanceof Array)) { if (waitTimeout) { clearTimeout(waitTimeout); } waitTimeout = setTimeout(waitTimeoutNotice, timeout * 1000); } // #end-ignore return actualGlobalRequire(requireId, callback); } /** * 版本号 * * @type {string} */ globalRequire.version = '1.8.8'; /** * loader名称 * * @type {string} */ globalRequire.loader = 'esl'; /** * 将模块标识转换成相对的url * * @param {string} id 模块标识 * @return {string} */ globalRequire.toUrl = actualGlobalRequire.toUrl; // #begin-ignore /** * 超时提醒函数 * * @inner */ function waitTimeoutNotice() { var hangModules = []; var missModules = []; var hangModulesMap = {}; var missModulesMap = {}; var visited = {}; /** * 检查模块的加载错误 * * @inner * @param {string} id 模块id * @param {boolean} hard 是否装载时依赖 */ function checkError(id, hard) { if (visited[id] || modIs(id, MODULE_DEFINED)) { return; } visited[id] = 1; if (!modIs(id, MODULE_PREPARED)) { // HACK: 为gzip后体积优化,不做抽取 if (!hangModulesMap[id]) { hangModulesMap[id] = 1; hangModules.push(id); } } var mod = modModules[id]; if (!mod) { if (!missModulesMap[id]) { missModulesMap[id] = 1; missModules.push(id); } } else if (hard) { if (!hangModulesMap[id]) { hangModulesMap[id] = 1; hangModules.push(id); } each( mod.depMs, function (dep) { checkError(dep.absId, dep.hard); } ); } } /* eslint-disable guard-for-in */ for (var id in autoDefineModules) { checkError(id, 1); } /* eslint-enable guard-for-in */ if (hangModules.length || missModules.length) { throw new Error( '[MODULE_TIMEOUT]Hang( ' + (hangModules.join(', ') || 'none') + ' ) Miss( ' + (missModules.join(', ') || 'none') + ' )' ); } } // #end-ignore /** * 未预定义的模块集合 * 主要存储匿名方式define的模块 * * @inner * @type {Array} */ var wait4PreDefine = []; /** * 完成模块预定义,此时处理的模块是匿名define的模块 * * @inner * @param {string} currentId 匿名define的模块的id */ function completePreDefine(currentId) { // HACK: 这里在IE下有个性能陷阱,不能使用任何变量。 // 否则貌似会形成变量引用和修改的读写锁,导致wait4PreDefine释放困难 each(wait4PreDefine, function (mod) { modPreDefine( currentId, mod.deps, mod.factory ); }); wait4PreDefine.length = 0; modAnalyse(currentId); } /** * 定义模块 * * @param {string=} id 模块标识 * @param {Array=} dependencies 依赖模块列表 * @param {Function=} factory 创建模块的工厂方法 */ function globalDefine(id, dependencies, factory) { // define(factory) // define(dependencies, factory) // define(id, factory) // define(id, dependencies, factory) if (factory == null) { if (dependencies == null) { factory = id; id = null; } else { factory = dependencies; dependencies = null; if (id instanceof Array) { dependencies = id; id = null; } } } if (factory == null) { return; } var opera = window.opera; // IE下通过current script的data-require-id获取当前id if ( !id && document.attachEvent && (!(opera && opera.toString() === '[object Opera]')) ) { var currentScript = getCurrentScript(); id = currentScript && currentScript.getAttribute('data-require-id'); } if (id) { modPreDefine(id, dependencies, factory); } else { // 纪录到共享变量中,在load或readystatechange中处理 // 标准浏览器下,使用匿名define时,将进入这个分支 wait4PreDefine[0] = { deps: dependencies, factory: factory }; } } globalDefine.amd = {}; /** * 模块配置获取函数 * * @inner * @return {Object} 模块配置对象 */ function moduleConfigGetter() { var conf = requireConf.config[this.id]; if (conf && typeof conf === 'object') { return conf; } return {}; } /** * 预定义模块 * * @inner * @param {string} id 模块标识 * @param {Array.} dependencies 显式声明的依赖模块列表 * @param {*} factory 模块定义函数或模块对象 */ function modPreDefine(id, dependencies, factory) { // 将模块存入容器 // // 模块内部信息包括 // ----------------------------------- // id: module id // depsDec: 模块定义时声明的依赖 // deps: 模块依赖,默认为['require', 'exports', 'module'] // factory: 初始化函数或对象 // factoryDeps: 初始化函数的参数依赖 // exports: 模块的实际暴露对象(AMD定义) // config: 用于获取模块配置信息的函数(AMD定义) // state: 模块当前状态 // require: local require函数 // depMs: 实际依赖的模块集合,数组形式 // depMkv: 实际依赖的模块集合,表形式,便于查找 // depRs: 实际依赖的资源集合 // depPMs: 用于加载资源的模块集合,key是模块名,value是1,仅用于快捷查找 // ------------------------------------ if (!modModules[id]) { /* eslint-disable key-spacing */ modModules[id] = { id : id, depsDec : dependencies, deps : dependencies || ['require', 'exports', 'module'], factoryDeps : [], factory : factory, exports : {}, config : moduleConfigGetter, state : MODULE_PRE_DEFINED, require : createLocalRequire(id), depMs : [], depMkv : {}, depRs : [], depPMs : [] }; /* eslint-enable key-spacing */ } } /** * 预分析模块 * * 首先,完成对factory中声明依赖的分析提取 * 然后,尝试加载"资源加载所需模块" * * 需要先加载模块的原因是:如果模块不存在,无法进行resourceId normalize化 * modAnalyse完成后续的依赖分析处理,并进行依赖模块的加载 * * @inner * @param {string} id 模块id */ function modAnalyse(id) { var mod = modModules[id]; if (!mod || modIs(id, MODULE_ANALYZED)) { return; } var deps = mod.deps; var factory = mod.factory; var hardDependsCount = 0; // 分析function body中的require // 如果包含显式依赖声明,根据AMD规定和性能考虑,可以不分析factoryBody if (typeof factory === 'function') { hardDependsCount = Math.min(factory.length, deps.length); // If the dependencies argument is present, the module loader // SHOULD NOT scan for dependencies within the factory function. !mod.depsDec && factory.toString() .replace(/(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg, '') .replace(/require\(\s*(['"'])([^'"]+)\1\s*\)/g, function ($0, $1, depId) { deps.push(depId); } ); } var requireModules = []; each(deps, function (depId, index) { var idInfo = parseId(depId); var absId = normalize(idInfo.mod, id); var moduleInfo; var resInfo; if (absId && !BUILDIN_MODULE[absId]) { // 如果依赖是一个资源,将其信息添加到module.depRs // // module.depRs中的项有可能是重复的。 // 在这个阶段,加载resource的module可能还未defined, // 导致此时resource id无法被normalize。 // // 比如对a/b/c而言,下面几个resource可能指的是同一个资源: // - js!../name.js // - js!a/name.js // - ../../js!../name.js // // 所以加载资源的module ready时,需要遍历module.depRs进行处理 if (idInfo.res) { resInfo = { id: depId, mod: absId, res: idInfo.res }; autoDefineModules[absId] = 1; mod.depPMs.push(absId); mod.depRs.push(resInfo); } // 对依赖模块的id normalize能保证正确性,在此处进行去重 moduleInfo = mod.depMkv[absId]; if (!moduleInfo) { moduleInfo = { id: idInfo.mod, absId: absId, hard: index < hardDependsCount }; mod.depMs.push(moduleInfo); mod.depMkv[absId] = moduleInfo; requireModules.push(absId); } } else { moduleInfo = {absId: absId}; } // 如果当前正在分析的依赖项是define中声明的, // 则记录到module.factoryDeps中 // 在factory invoke前将用于生成invoke arguments if (index < hardDependsCount) { mod.factoryDeps.push(resInfo || moduleInfo); } }); mod.state = MODULE_ANALYZED; modInitFactoryInvoker(id); nativeRequire(requireModules); } /** * 对一些需要自动定义的模块进行自动定义 * * @inner */ function modAutoInvoke() { /* eslint-disable guard-for-in */ for (var id in autoDefineModules) { modUpdatePreparedState(id); modTryInvokeFactory(id); } /* eslint-enable guard-for-in */ } /** * 更新模块的准备状态 * * @inner * @param {string} id 模块id */ function modUpdatePreparedState(id) { var visited = {}; update(id); function update(id) { if (!modIs(id, MODULE_ANALYZED)) { return false; } if (modIs(id, MODULE_PREPARED) || visited[id]) { return true; } visited[id] = 1; var mod = modModules[id]; var prepared = true; each( mod.depMs, function (dep) { return (prepared = update(dep.absId)); } ); // 判断resource是否加载完成。如果resource未加载完成,则认为未准备好 /* jshint ignore:start */ prepared && each( mod.depRs, function (dep) { prepared = !!(dep.absId && modIs(dep.absId, MODULE_DEFINED)); return prepared; } ); /* jshint ignore:end */ if (prepared) { mod.state = MODULE_PREPARED; } return prepared; } } /** * 初始化模块定义时所需的factory执行器 * * @inner * @param {string} id 模块id */ function modInitFactoryInvoker(id) { var mod = modModules[id]; var invoking; mod.invokeFactory = invokeFactory; /* eslint-disable max-nested-callbacks */ each( mod.depPMs, function (pluginModuleId) { modAddDefinedListener( pluginModuleId, function () { each(mod.depRs, function (res) { if (!res.absId && res.mod === pluginModuleId) { res.absId = normalize(res.id, id); nativeRequire([res.absId], modAutoInvoke); } }); } ); } ); /* eslint-enable max-nested-callbacks */ /** * 初始化模块 * * @inner */ function invokeFactory() { if (invoking || mod.state !== MODULE_PREPARED) { return; } invoking = 1; // 拼接factory invoke所需的arguments var factoryReady = 1; var factoryDeps = []; each( mod.factoryDeps, function (dep) { var depId = dep.absId; if (!BUILDIN_MODULE[depId]) { modTryInvokeFactory(depId); if (!modIs(depId, MODULE_DEFINED)) { factoryReady = 0; return false; } } factoryDeps.push(depId); } ); if (factoryReady) { try { var args = modGetModulesExports( factoryDeps, { require: mod.require, exports: mod.exports, module: mod } ); // 调用factory函数初始化module var factory = mod.factory; var exports = typeof factory === 'function' ? factory.apply(global, args) : factory; if (exports != null) { mod.exports = exports; } mod.invokeFactory = null; delete autoDefineModules[id]; } catch (ex) { invoking = 0; if (/^\[MODULE_MISS\]"([^"]+)/.test(ex.message)) { // 出错,则说明在factory的运行中,该require的模块是需要的 // 所以把它加入强依赖中 var hardCirclurDep = mod.depMkv[RegExp.$1]; hardCirclurDep && (hardCirclurDep.hard = 1); return; } throw ex; } // 完成define // 不放在try里,避免后续的运行错误被这里吞掉 modDefined(id); } } } /** * 判断模块是否完成相应的状态 * * @inner * @param {string} id 模块标识 * @param {number} state 状态码,使用时传入相应的枚举变量,比如`MODULE_DEFINED` * @return {boolean} 是否完成相应的状态 */ function modIs(id, state) { return modModules[id] && modModules[id].state >= state; } /** * 尝试执行模块factory函数,进行模块初始化 * * @inner * @param {string} id 模块id */ function modTryInvokeFactory(id) { var mod = modModules[id]; if (mod && mod.invokeFactory) { mod.invokeFactory(); } } /** * 根据模块id数组,获取其的exports数组 * 用于模块初始化的factory参数或require的callback参数生成 * * @inner * @param {Array} modules 模块id数组 * @param {Object} buildinModules 内建模块对象 * @return {Array} 模块exports数组 */ function modGetModulesExports(modules, buildinModules) { var args = []; each( modules, function (id, index) { args[index] = buildinModules[id] || modGetModuleExports(id); } ); return args; } /** * 模块定义完成事件监听器容器 * * @inner * @type {Object} */ var modDefinedListeners = {}; /** * 添加模块定义完成时间的监听器 * * @inner * @param {string} id 模块标识 * @param {Function} listener 监听函数 */ function modAddDefinedListener(id, listener) { if (modIs(id, MODULE_DEFINED)) { listener(); return; } var listeners = modDefinedListeners[id]; if (!listeners) { listeners = modDefinedListeners[id] = []; } listeners.push(listener); } /** * 模块状态切换为定义完成 * 因为需要触发事件,MODULE_DEFINED状态切换通过该函数 * * @inner * @param {string} id 模块标识 */ function modDefined(id) { var listeners = modDefinedListeners[id] || []; var mod = modModules[id]; mod.state = MODULE_DEFINED; var len = listeners.length; while (len--) { // 这里不做function类型的检测 // 因为listener都是通过modOn传入的,modOn为内部调用 listeners[len](); } // 清理listeners listeners.length = 0; delete modDefinedListeners[id]; } /** * 获取模块的exports * * @inner * @param {string} id 模块标识 * @return {*} 模块的exports */ function modGetModuleExports(id) { if (modIs(id, MODULE_DEFINED)) { return modModules[id].exports; } return null; } /** * 获取模块 * * @param {string|Array} ids 模块名称或模块名称列表 * @param {Function=} callback 获取模块完成时的回调函数 * @param {string} baseId 基础id,用于当ids是relative id时的normalize * @param {Object} noRequests 无需发起请求的模块集合 * @return {Object} 模块对象 */ function nativeRequire(ids, callback, baseId, noRequests) { // 根据 https://github.com/amdjs/amdjs-api/wiki/require // It MUST throw an error if the module has not // already been loaded and evaluated. if (typeof ids === 'string') { modTryInvokeFactory(ids); if (!modIs(ids, MODULE_DEFINED)) { throw new Error('[MODULE_MISS]"' + ids + '" is not exists!'); } return modGetModuleExports(ids); } noRequests = noRequests || {}; var isCallbackCalled = 0; if (ids instanceof Array) { tryFinishRequire(); if (!isCallbackCalled) { each(ids, function (id) { if (!(BUILDIN_MODULE[id] || modIs(id, MODULE_DEFINED))) { modAddDefinedListener(id, tryFinishRequire); if (!noRequests[id]) { (id.indexOf('!') > 0 ? loadResource : loadModule )(id, baseId); } modAnalyse(id); } }); modAutoInvoke(); } } /** * 尝试完成require,调用callback * 在模块与其依赖模块都加载完时调用 * * @inner */ function tryFinishRequire() { if (!isCallbackCalled) { var isAllCompleted = 1; each(ids, function (id) { if (!BUILDIN_MODULE[id]) { return (isAllCompleted = !!modIs(id, MODULE_DEFINED)); } }); // 检测并调用callback if (isAllCompleted) { isCallbackCalled = 1; (typeof callback === 'function') && callback.apply( global, modGetModulesExports(ids, BUILDIN_MODULE) ); } } } } /** * 正在加载的模块列表 * * @inner * @type {Object} */ var loadingModules = {}; /** * 加载模块 * * @inner * @param {string} moduleId 模块标识 */ function loadModule(moduleId) { if (loadingModules[moduleId] || modModules[moduleId]) { return; } loadingModules[moduleId] = 1; // 创建script标签 // // 这里不挂接onerror的错误处理 // 因为高级浏览器在devtool的console面板会报错 // 再throw一个Error多此一举了 var script = document.createElement('script'); script.setAttribute('data-require-id', moduleId); script.src = toUrl(moduleId + '.js'); script.async = true; if (script.readyState) { script.onreadystatechange = loadedListener; } else { script.onload = loadedListener; } appendScript(script); /** * script标签加载完成的事件处理函数 * * @inner */ function loadedListener() { var readyState = script.readyState; if ( typeof readyState === 'undefined' || /^(loaded|complete)$/.test(readyState) ) { script.onload = script.onreadystatechange = null; script = null; completePreDefine(moduleId); /* eslint-disable guard-for-in */ for (var key in autoDefineModules) { modAnalyse(key); } /* eslint-enable guard-for-in */ modAutoInvoke(); } } } /** * 加载资源 * * @inner * @param {string} pluginAndResource 插件与资源标识 * @param {string} baseId 当前环境的模块标识 */ function loadResource(pluginAndResource, baseId) { if (modModules[pluginAndResource]) { return; } var idInfo = parseId(pluginAndResource); var resource = { id: pluginAndResource, state: MODULE_ANALYZED }; modModules[pluginAndResource] = resource; /** * plugin加载完成的回调函数 * * @inner * @param {*} value resource的值 */ function pluginOnload(value) { resource.exports = value || true; modDefined(pluginAndResource); } /* jshint ignore:start */ /** * 该方法允许plugin使用加载的资源声明模块 * * @param {string} id 模块id * @param {string} text 模块声明字符串 */ pluginOnload.fromText = function (id, text) { autoDefineModules[id] = 1; new Function(text)(); completePreDefine(id); }; /* jshint ignore:end */ /** * 加载资源 * * @inner * @param {Object} plugin 用于加载资源的插件模块 */ function load(plugin) { var pluginRequire = baseId ? modModules[baseId].require : actualGlobalRequire; plugin.load( idInfo.res, pluginRequire, pluginOnload, moduleConfigGetter.call({id: pluginAndResource}) ); } load(modGetModuleExports(idInfo.mod)); } /** * 配置require * * @param {Object} conf 配置对象 */ globalRequire.config = function (conf) { if (conf) { /* eslint-disable guard-for-in */ for (var key in requireConf) { var newValue = conf[key]; var oldValue = requireConf[key]; if (!newValue) { continue; } if (key === 'urlArgs' && typeof newValue === 'string') { requireConf.urlArgs['*'] = newValue; } else { // 简单的多处配置还是需要支持,所以配置实现为支持二级mix if (oldValue instanceof Array) { oldValue.push.apply(oldValue, newValue); } else if (typeof oldValue === 'object') { for (var k in newValue) { oldValue[k] = newValue[k]; } } else { requireConf[key] = newValue; } } } /* eslint-enable guard-for-in */ createConfIndex(); } // 配置信息对象clone返回,避免返回结果对象被用户程序修改可能导致的问题 // return clone(requireConf); }; /** * 对象克隆,支持raw type, Array, raw Object * * @inner * @param {*} source 要克隆的对象 * @return {*} */ // function clone(source) { // var result = source; // if (source instanceof Array) { // result = []; // each(source, function (item, i) { // result[i] = clone(item); // }); // } // else if (typeof source === 'object') { // result = {}; // for (var key in source) { // if (source.hasOwnProperty(key)) { // result[key] = clone(source[key]); // } // } // } // return result; // } // 初始化时需要创建配置索引 createConfIndex(); /** * paths内部索引 * * @inner * @type {Array} */ var pathsIndex; /** * packages内部索引 * * @inner * @type {Array} */ var packagesIndex; /** * mapping内部索引 * * @inner * @type {Array} */ var mappingIdIndex; /** * urlArgs内部索引 * * @inner * @type {Array} */ var urlArgsIndex; /** * noRequests内部索引 * * @inner * @type {Array} */ var noRequestsIndex; /** * 将key为module id prefix的Object,生成数组形式的索引,并按照长度和字面排序 * * @inner * @param {Object} value 源值 * @param {boolean} allowAsterisk 是否允许*号表示匹配所有 * @return {Array} 索引对象 */ function createKVSortedIndex(value, allowAsterisk) { var index = kv2List(value, 1, allowAsterisk); index.sort(descSorterByKOrName); return index; } /** * 创建配置信息内部索引 * * @inner */ function createConfIndex() { requireConf.baseUrl = requireConf.baseUrl.replace(/\/$/, '') + '/'; // create paths index pathsIndex = createKVSortedIndex(requireConf.paths); // create mappingId index mappingIdIndex = createKVSortedIndex(requireConf.map, 1); each( mappingIdIndex, function (item) { item.v = createKVSortedIndex(item.v); } ); // create packages index packagesIndex = []; each( requireConf.packages, function (packageConf) { var pkg = packageConf; if (typeof packageConf === 'string') { pkg = { name: packageConf.split('/')[0], location: packageConf, main: 'main' }; } pkg.location = pkg.location || pkg.name; pkg.main = (pkg.main || 'main').replace(/\.js$/i, ''); pkg.reg = createPrefixRegexp(pkg.name); packagesIndex.push(pkg); } ); packagesIndex.sort(descSorterByKOrName); // create urlArgs index urlArgsIndex = createKVSortedIndex(requireConf.urlArgs, 1); // create noRequests index noRequestsIndex = createKVSortedIndex(requireConf.noRequests); each(noRequestsIndex, function (item) { var value = item.v; var mapIndex = {}; item.v = mapIndex; if (!(value instanceof Array)) { value = [value]; } each(value, function (meetId) { mapIndex[meetId] = 1; }); }); } /** * 对配置信息的索引进行检索 * * @inner * @param {string} value 要检索的值 * @param {Array} index 索引对象 * @param {Function} hitBehavior 索引命中的行为函数 */ function indexRetrieve(value, index, hitBehavior) { each(index, function (item) { if (item.reg.test(value)) { hitBehavior(item.v, item.k, item); return false; } }); } /** * 将`模块标识+'.extension'`形式的字符串转换成相对的url * * @inner * @param {string} source 源字符串 * @return {string} url */ function toUrl(source) { // 分离 模块标识 和 .extension var extReg = /(\.[a-z0-9]+)$/i; var queryReg = /(\?[^#]*)$/; var extname = ''; var id = source; var query = ''; if (queryReg.test(source)) { query = RegExp.$1; source = source.replace(queryReg, ''); } if (extReg.test(source)) { extname = RegExp.$1; id = source.replace(extReg, ''); } var url = id; // paths处理和匹配 var isPathMap; indexRetrieve(id, pathsIndex, function (value, key) { url = url.replace(key, value); isPathMap = 1; }); // packages处理和匹配 if (!isPathMap) { indexRetrieve(id, packagesIndex, function (value, key, item) { url = url.replace(item.name, item.location); }); } // 相对路径时,附加baseUrl if (!/^([a-z]{2,10}:\/)?\//i.test(url)) { url = requireConf.baseUrl + url; } // 附加 .extension 和 query url += extname + query; // urlArgs处理和匹配 indexRetrieve(id, urlArgsIndex, function (value) { url += (url.indexOf('?') > 0 ? '&' : '?') + value; }); return url; } /** * 创建local require函数 * * @inner * @param {number} baseId 当前module id * @return {Function} local require函数 */ function createLocalRequire(baseId) { var requiredCache = {}; function req(requireId, callback) { if (typeof requireId === 'string') { if (!requiredCache[requireId]) { requiredCache[requireId] = nativeRequire(normalize(requireId, baseId)); } return requiredCache[requireId]; } else if (requireId instanceof Array) { // 分析是否有resource,取出pluginModule先 var pluginModules = []; var pureModules = []; var normalizedIds = []; each( requireId, function (id, i) { var idInfo = parseId(id); var absId = normalize(idInfo.mod, baseId); pureModules.push(absId); autoDefineModules[absId] = 1; if (idInfo.res) { pluginModules.push(absId); normalizedIds[i] = null; } else { normalizedIds[i] = absId; } } ); var noRequestModules = {}; each( pureModules, function (id) { var meet; indexRetrieve( id, noRequestsIndex, function (value) { meet = value; } ); if (meet) { if (meet['*']) { noRequestModules[id] = 1; } else { each(pureModules, function (meetId) { if (meet[meetId]) { noRequestModules[id] = 1; return false; } }); } } } ); // 加载模块 nativeRequire( pureModules, function () { /* jshint ignore:start */ each(normalizedIds, function (id, i) { if (id == null) { normalizedIds[i] = normalize(requireId[i], baseId); } }); /* jshint ignore:end */ nativeRequire(normalizedIds, callback, baseId); }, baseId, noRequestModules ); } } /** * 将[module ID] + '.extension'格式的字符串转换成url * * @inner * @param {string} id 符合描述格式的源字符串 * @return {string} url */ req.toUrl = function (id) { return toUrl(normalize(id, baseId)); }; return req; } /** * id normalize化 * * @inner * @param {string} id 需要normalize的模块标识 * @param {string} baseId 当前环境的模块标识 * @return {string} normalize结果 */ function normalize(id, baseId) { if (!id) { return ''; } baseId = baseId || ''; var idInfo = parseId(id); if (!idInfo) { return id; } var resourceId = idInfo.res; var moduleId = relative2absolute(idInfo.mod, baseId); each( packagesIndex, function (packageConf) { var name = packageConf.name; if (name === moduleId) { moduleId = name + '/' + packageConf.main; return false; } } ); // 根据config中的map配置进行module id mapping indexRetrieve( baseId, mappingIdIndex, function (value) { indexRetrieve( moduleId, value, function (mdValue, mdKey) { moduleId = moduleId.replace(mdKey, mdValue); } ); } ); if (resourceId) { var mod = modGetModuleExports(moduleId); resourceId = mod.normalize ? mod.normalize( resourceId, function (resId) { return normalize(resId, baseId); } ) : normalize(resourceId, baseId); moduleId += '!' + resourceId; } return moduleId; } /** * 相对id转换成绝对id * * @inner * @param {string} id 要转换的相对id * @param {string} baseId 当前所在环境id * @return {string} 绝对id */ function relative2absolute(id, baseId) { if (id.indexOf('.') === 0) { var basePath = baseId.split('/'); var namePath = id.split('/'); var baseLen = basePath.length - 1; var nameLen = namePath.length; var cutBaseTerms = 0; var cutNameTerms = 0; /* eslint-disable block-scoped-var */ pathLoop: for (var i = 0; i < nameLen; i++) { switch (namePath[i]) { case '..': if (cutBaseTerms < baseLen) { cutBaseTerms++; cutNameTerms++; } else { break pathLoop; } break; case '.': cutNameTerms++; break; default: break pathLoop; } } /* eslint-enable block-scoped-var */ basePath.length = baseLen - cutBaseTerms; namePath = namePath.slice(cutNameTerms); return basePath.concat(namePath).join('/'); } return id; } /** * 解析id,返回带有module和resource属性的Object * * @inner * @param {string} id 标识 * @return {Object} id解析结果对象 */ function parseId(id) { var segs = id.split('!'); if (segs[0]) { return { mod: segs[0], res: segs[1] }; } return null; } /** * 将对象数据转换成数组,数组每项是带有k和v的Object * * @inner * @param {Object} source 对象数据 * @param {boolean} keyMatchable key是否允许被前缀匹配 * @param {boolean} allowAsterisk 是否支持*匹配所有 * @return {Array.} 对象转换数组 */ function kv2List(source, keyMatchable, allowAsterisk) { var list = []; for (var key in source) { if (source.hasOwnProperty(key)) { var item = { k: key, v: source[key] }; list.push(item); if (keyMatchable) { item.reg = key === '*' && allowAsterisk ? /^/ : createPrefixRegexp(key); } } } return list; } // 感谢requirejs,通过currentlyAddingScript兼容老旧ie // // For some cache cases in IE 6-8, the script executes before the end // of the appendChild execution, so to tie an anonymous define // call to the module name (which is stored on the node), hold on // to a reference to this node, but clear after the DOM insertion. var currentlyAddingScript; var interactiveScript; /** * 获取当前script标签 * 用于ie下define未指定module id时获取id * * @inner * @return {HTMLScriptElement} 当前script标签 */ function getCurrentScript() { if (currentlyAddingScript) { return currentlyAddingScript; } else if ( interactiveScript && interactiveScript.readyState === 'interactive' ) { return interactiveScript; } var scripts = document.getElementsByTagName('script'); var scriptLen = scripts.length; while (scriptLen--) { var script = scripts[scriptLen]; if (script.readyState === 'interactive') { interactiveScript = script; return script; } } } var headElement = document.getElementsByTagName('head')[0]; var baseElement = document.getElementsByTagName('base')[0]; if (baseElement) { headElement = baseElement.parentNode; } /** * 向页面中插入script标签 * * @inner * @param {HTMLScriptElement} script script标签 */ function appendScript(script) { currentlyAddingScript = script; // If BASE tag is in play, using appendChild is a problem for IE6. // See: http://dev.jquery.com/ticket/2709 baseElement ? headElement.insertBefore(script, baseElement) : headElement.appendChild(script); currentlyAddingScript = null; } /** * 创建id前缀匹配的正则对象 * * @inner * @param {string} prefix id前缀 * @return {RegExp} 前缀匹配的正则对象 */ function createPrefixRegexp(prefix) { return new RegExp('^' + prefix + '(/|$)'); } /** * 循环遍历数组集合 * * @inner * @param {Array} source 数组源 * @param {function(Array,Number):boolean} iterator 遍历函数 */ function each(source, iterator) { if (source instanceof Array) { for (var i = 0, len = source.length; i < len; i++) { if (iterator(source[i], i) === false) { break; } } } } /** * 根据元素的k或name项进行数组字符数逆序的排序函数 * * @inner * @param {Object} a 要比较的对象a * @param {Object} b 要比较的对象b * @return {number} 比较结果 */ function descSorterByKOrName(a, b) { var aValue = a.k || a.name; var bValue = b.k || b.name; if (bValue === '*') { return -1; } if (aValue === '*') { return 1; } return bValue.length - aValue.length; } // 暴露全局对象 if (!define) { define = globalDefine; // 可能碰到其他形式的loader,所以,不要覆盖人家 if (!require) { require = globalRequire; } // 如果存在其他版本的esl,在define那里就判断过了,不会进入这个分支 // 所以这里就不判断了,直接写 esl = globalRequire; } })(this);