blob: 206ea64b706fc36cd47c8f27b294cd6eacd2d5f2 [file] [log] [blame]
/**
* 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 */
/* eslint-disable guard-for-in */
/* eslint-env amd:false */
(function (global) {
// "mod"开头的变量或函数为内部模块管理函数
// 为提高压缩率,不使用function或object包装
/**
* 模块容器
*
* @inner
* @type {Object}
*/
var modModules = {};
// 模块状态枚举量
var MODULE_PRE_DEFINED = 1;
var MODULE_ANALYZED = 2;
var MODULE_PREPARED = 3;
var MODULE_DEFINED = 4;
/**
* 自动定义的模块表
*
* 模块define factory是用到时才执行,但是以下几种情况需要自动马上执行:
* 1. require([moduleId], callback)
* 2. plugin module and plugin resource: require('plugin!resource')
* 3. shim module
*
* @inner
* @type {Object}
*/
var modAutoDefineModules = {};
/**
* 标记模块自动进行定义
*
* @inner
* @param {string} id 模块id
*/
function modFlagAutoDefine(id) {
if (!modIs(id, MODULE_DEFINED)) {
modAutoDefineModules[id] = 1;
}
}
/**
* 内建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 : [],
shim : {},
// #begin-ignore
waitSeconds: 0,
// #end-ignore
bundles : {},
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 = '2.0.4';
/**
* 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);
}
);
}
}
for (var id in modAutoDefineModules) {
checkError(id, 1);
}
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 modCompletePreDefine(currentId) {
// HACK: 这里在IE下有个性能陷阱,不能使用任何变量。
// 否则貌似会形成变量引用和修改的读写锁,导致wait4PreDefine释放困难
each(wait4PreDefine, function (mod) {
modPreDefine(
currentId,
mod.deps,
mod.factory
);
});
wait4PreDefine.length = 0;
}
/**
* 定义模块
*
* @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.<string>} 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: 实际依赖的资源集合
// ------------------------------------
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 : []
};
/* eslint-enable key-spacing */
}
}
/**
* 开始执行模块定义前的准备工作
*
* 首先,完成对factory中声明依赖的分析提取
* 然后,尝试加载"资源加载所需模块"
*
* 需要先加载模块的原因是:如果模块不存在,无法进行resourceId normalize化
*
* @inner
* @param {string} id 模块id
*/
function modPrepare(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 = [];
var depResources = [];
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
};
depResources.push(depId);
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);
nativeAsyncRequire(requireModules);
depResources.length && mod.require(
depResources,
function () {
each(mod.depRs, function (res) {
if (!res.absId) {
res.absId = normalize(res.id, id);
}
});
modAutoDefine();
}
);
}
/**
* 对一些需要自动定义的模块进行自动定义
*
* @inner
*/
function modAutoDefine() {
for (var id in modAutoDefineModules) {
modPrepare(id);
modUpdatePreparedState(id);
modTryInvokeFactory(id);
}
}
/**
* 更新模块的准备状态
*
* @inner
* @param {string} id 模块id
*/
function modUpdatePreparedState(id) {
var visited = {};
update(id);
function update(id) {
modPrepare(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;
return prepared;
}
);
/* jshint ignore:end */
if (prepared && !modIs(id, MODULE_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;
/**
* 初始化模块
*
* @inner
*/
function invokeFactory() {
if (invoking || mod.state !== MODULE_PREPARED) {
return;
}
invoking = 1;
// 拼接factory invoke所需的arguments
var factoryReady = 1;
each(
mod.factoryDeps,
function (dep) {
var depId = dep.absId;
if (!BUILDIN_MODULE[depId]) {
modTryInvokeFactory(depId);
return (factoryReady = modIs(depId, MODULE_DEFINED));
}
}
);
if (factoryReady) {
try {
// 调用factory函数初始化module
var factory = mod.factory;
var exports = typeof factory === 'function'
? factory.apply(global, modGetModulesExports(
mod.factoryDeps,
{
require: mod.require,
exports: mod.exports,
module: mod
}
))
: factory;
if (exports != null) {
mod.exports = exports;
}
mod.invokeFactory = null;
}
catch (ex) {
if (/^\[MODULE_MISS\]"([^"]+)/.test(ex.message)) {
// 出错,则说明在factory的运行中,该require的模块是需要的
// 所以把它加入强依赖中
var hardCirclurDep = mod.depMkv[RegExp.$1];
hardCirclurDep && (hardCirclurDep.hard = 1);
// 如果是模块本身有问题导致的运行错误
// 就不要把invoking置回去了,避免影响autoInvoke其他模块的初始化
invoking = 0;
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) {
if (typeof id === 'object') {
id = id.absId;
}
args[index] = buildinModules[id] || modModules[id].exports;
}
);
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 mod = modModules[id];
mod.state = MODULE_DEFINED;
delete modAutoDefineModules[id];
var listeners = modDefinedListeners[id] || [];
var len = listeners.length;
while (len--) {
// 这里不做function类型的检测
// 因为listener都是通过modOn传入的,modOn为内部调用
listeners[len]();
}
// 清理listeners
listeners.length = 0;
modDefinedListeners[id] = null;
}
/**
* 异步加载模块
* 内部使用,模块ID必须是经过normalize的Top-Level ID
*
* @inner
* @param {Array} ids 模块名称或模块名称列表
* @param {Function=} callback 获取模块完成时的回调函数
* @param {string} baseId 基础id,用于当ids是relative id时的normalize
*/
function nativeAsyncRequire(ids, callback, baseId) {
var isCallbackCalled = 0;
each(ids, function (id) {
if (!(BUILDIN_MODULE[id] || modIs(id, MODULE_DEFINED))) {
modAddDefinedListener(id, tryFinishRequire);
(id.indexOf('!') > 0
? loadResource
: loadModule
)(id, baseId);
}
});
tryFinishRequire();
/**
* 尝试完成require,调用callback
* 在模块与其依赖模块都加载完时调用
*
* @inner
*/
function tryFinishRequire() {
if (typeof callback === 'function' && !isCallbackCalled) {
var isAllCompleted = 1;
each(ids, function (id) {
if (!BUILDIN_MODULE[id]) {
return (isAllCompleted = !!modIs(id, MODULE_DEFINED));
}
});
// 检测并调用callback
if (isAllCompleted) {
isCallbackCalled = 1;
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;
// 初始化相关 shim 的配置
var shimConf = requireConf.shim[moduleId];
if (shimConf instanceof Array) {
requireConf.shim[moduleId] = shimConf = {
deps: shimConf
};
}
// shim依赖的模块需要自动标识为shim
// 无论是纯正的shim模块还是hybird模块
var shimDeps = shimConf && (shimConf.deps || []);
if (shimDeps) {
each(shimDeps, function (dep) {
if (!requireConf.shim[dep]) {
requireConf.shim[dep] = {};
}
});
actualGlobalRequire(shimDeps, load);
}
else {
load();
}
/**
* 发送请求去加载模块
*
* @inner
*/
function load() {
/* eslint-disable no-use-before-define */
var bundleModuleId = bundlesIndex[moduleId];
createScript(bundleModuleId || moduleId, loaded);
/* eslint-enable no-use-before-define */
}
/**
* script标签加载完成的事件处理函数
*
* @inner
*/
function loaded() {
if (shimConf) {
var exports;
if (typeof shimConf.init === 'function') {
exports = shimConf.init.apply(
global,
modGetModulesExports(shimDeps, BUILDIN_MODULE)
);
}
if (exports == null && shimConf.exports) {
exports = global;
each(
shimConf.exports.split('.'),
function (prop) {
exports = exports[prop];
return !!exports;
}
);
}
globalDefine(moduleId, shimDeps, exports || {});
}
else {
modCompletePreDefine(moduleId);
}
modAutoDefine();
}
}
/**
* 加载资源
*
* @inner
* @param {string} pluginAndResource 插件与资源标识
* @param {string} baseId 当前环境的模块标识
*/
function loadResource(pluginAndResource, baseId) {
if (modModules[pluginAndResource]) {
return;
}
/* eslint-disable no-use-before-define */
var bundleModuleId = bundlesIndex[pluginAndResource];
if (bundleModuleId) {
loadModule(bundleModuleId);
return;
}
/* eslint-enable no-use-before-define */
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) {
new Function(text)();
modCompletePreDefine(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(actualGlobalRequire(idInfo.mod));
}
/**
* 配置require
*
* @param {Object} conf 配置对象
*/
globalRequire.config = function (conf) {
if (conf) {
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;
}
}
}
createConfIndex();
}
};
// 初始化时需要创建配置索引
createConfIndex();
/**
* paths内部索引
*
* @inner
* @type {Array}
*/
var pathsIndex;
/**
* packages内部索引
*
* @inner
* @type {Array}
*/
var packagesIndex;
/**
* mapping内部索引
*
* @inner
* @type {Array}
*/
var mappingIdIndex;
/**
* bundles内部索引
*
* @inner
* @type {Object}
*/
var bundlesIndex;
/**
* urlArgs内部索引
*
* @inner
* @type {Array}
*/
var urlArgsIndex;
/**
* 将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 bundles index
bundlesIndex = {};
/* eslint-disable no-use-before-define */
function bundlesIterator(id) {
bundlesIndex[resolvePackageId(id)] = key;
}
/* eslint-enable no-use-before-define */
for (var key in requireConf.bundles) {
each(requireConf.bundles[key], bundlesIterator);
}
}
/**
* 对配置信息的索引进行检索
*
* @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]) {
var topLevelId = normalize(requireId, baseId);
// 根据 https://github.com/amdjs/amdjs-api/wiki/require
// It MUST throw an error if the module has not
// already been loaded and evaluated.
modTryInvokeFactory(topLevelId);
if (!modIs(topLevelId, MODULE_DEFINED)) {
throw new Error('[MODULE_MISS]"' + topLevelId + '" is not exists!');
}
requiredCache[requireId] = modModules[topLevelId].exports;
}
return requiredCache[requireId];
}
else if (requireId instanceof Array) {
// 分析是否有resource,取出pluginModule先
var pureModules = [];
var normalizedIds = [];
each(
requireId,
function (id, i) {
var idInfo = parseId(id);
var absId = normalize(idInfo.mod, baseId);
var resId = idInfo.res;
var normalizedId = absId;
if (resId) {
var trueResId = absId + '!' + resId;
if (resId.indexOf('.') !== 0 && bundlesIndex[trueResId]) {
absId = normalizedId = trueResId;
}
else {
normalizedId = null;
}
}
normalizedIds[i] = normalizedId;
modFlagAutoDefine(absId);
pureModules.push(absId);
}
);
// 加载模块
nativeAsyncRequire(
pureModules,
function () {
/* jshint ignore:start */
each(normalizedIds, function (id, i) {
if (id == null) {
id = normalizedIds[i] = normalize(requireId[i], baseId);
modFlagAutoDefine(id);
}
});
/* jshint ignore:end */
// modAutoDefine中,factory invoke可能发生错误
// 从而导致nativeAsyncRequire没有被调用,callback没挂上
// 所以nativeAsyncRequire要先运行
nativeAsyncRequire(normalizedIds, callback, baseId);
modAutoDefine();
},
baseId
);
modAutoDefine();
}
}
/**
* 将[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 = resolvePackageId(
relative2absolute(idInfo.mod, baseId)
);
// 根据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 = modIs(moduleId, MODULE_DEFINED) && actualGlobalRequire(moduleId);
resourceId = mod && mod.normalize
? mod.normalize(
resourceId,
function (resId) {
return normalize(resId, baseId);
}
)
: normalize(resourceId, baseId);
moduleId += '!' + resourceId;
}
return moduleId;
}
/**
* 对id进行package解析
* 如果是package,则附加主模块id
*
* @inner
* @param {string} id 模块id
* @return {string} 解析后的id
*/
function resolvePackageId(id) {
each(
packagesIndex,
function (packageConf) {
var name = packageConf.name;
if (name === id) {
id = name + '/' + packageConf.main;
return false;
}
}
);
return id;
}
/**
* 相对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]
};
}
}
/**
* 将对象数据转换成数组,数组每项是带有k和v的Object
*
* @inner
* @param {Object} source 对象数据
* @param {boolean} keyMatchable key是否允许被前缀匹配
* @param {boolean} allowAsterisk 是否支持*匹配所有
* @return {Array.<Object>} 对象转换数组
*/
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;
}
function createScript(moduleId, onload) {
// 创建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 = innerOnload;
}
else {
script.onload = innerOnload;
}
function innerOnload() {
var readyState = script.readyState;
if (
typeof readyState === 'undefined'
|| /^(loaded|complete)$/.test(readyState)
) {
script.onload = script.onreadystatechange = null;
script = null;
// Script onload hooker
// ECHARTS-BUILDER-WEB
esl.onModuleLoad && esl.onModuleLoad(moduleId);
onload();
}
}
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;
// 为 echarts-builder-web 暴露模块
// ECHARTS-BUILDER-WEB
esl.modules = modModules;
}
})(this);