blob: aca4bf02de6622834ac977938aa377c9eb46d25a [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)
*/
var define;
var require;
(function ( global ) {
// "mod"开头的变量或函数为内部模块管理函数
// 为提高压缩率,不使用function或object包装
/**
* 模块容器
*
* @inner
* @type {Object}
*/
var modModules = {};
var MODULE_STATE_PRE_DEFINED = 1;
var MODULE_STATE_PRE_ANALYZED = 2;
var MODULE_STATE_ANALYZED = 3;
var MODULE_STATE_READY = 4;
var MODULE_STATE_DEFINED = 5;
/**
* 全局require函数
*
* @inner
* @type {Function}
*/
var actualGlobalRequire = createLocalRequire( '' );
/**
* 超时提醒定时器
*
* @inner
* @type {number}
*/
var waitTimeout;
/**
* 加载模块
*
* @param {string|Array} requireId 模块id或模块id数组,
* @param {Function=} callback 加载完成的回调函数
* @return {*}
*/
function require( requireId, callback ) {
assertNotContainRelativeId( requireId );
// 超时提醒
var timeout = requireConf.waitSeconds;
if ( isArray( requireId ) && timeout ) {
if ( waitTimeout ) {
clearTimeout( waitTimeout );
}
waitTimeout = setTimeout( waitTimeoutNotice, timeout * 1000 );
}
return actualGlobalRequire( requireId, callback );
}
/**
* 将模块标识转换成相对的url
*
* @param {string} id 模块标识
* @return {string}
*/
require.toUrl = toUrl;
/**
* 超时提醒函数
*
* @inner
*/
function waitTimeoutNotice() {
var hangModules = [];
var missModules = [];
var missModulesMap = {};
var hasError;
for ( var id in modModules ) {
if ( !modIsDefined( id ) ) {
hangModules.push( id );
hasError = 1;
}
each(
modModules[ id ].realDeps || [],
function ( depId ) {
if ( !modModules[ depId ] && !missModulesMap[ depId ] ) {
hasError = 1;
missModules.push( depId );
missModulesMap[ depId ] = 1;
}
}
);
}
if ( hasError ) {
throw new Error( '[MODULE_TIMEOUT]Hang( '
+ ( hangModules.join( ', ' ) || 'none' )
+ ' ) Miss( '
+ ( missModules.join( ', ' ) || 'none' )
+ ' )'
);
}
}
/**
* 尝试完成模块定义的定时器
*
* @inner
* @type {number}
*/
var tryDefineTimeout;
/**
* 定义模块
*
* @param {string=} id 模块标识
* @param {Array=} dependencies 依赖模块列表
* @param {Function=} factory 创建模块的工厂方法
*/
function define() {
var argsLen = arguments.length;
if ( !argsLen ) {
return;
}
var id;
var dependencies;
var factory = arguments[ --argsLen ];
while ( argsLen-- ) {
var arg = arguments[ argsLen ];
if ( isString( arg ) ) {
id = arg;
}
else if ( isArray( arg ) ) {
dependencies = arg;
}
}
// 出现window不是疏忽
// esl设计是做为browser端的loader
// 闭包的global更多意义在于:
// define和require方法可以被挂到用户自定义对象中
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');
}
// 处理依赖声明
// 默认为['require', 'exports', 'module']
dependencies = dependencies || ['require', 'exports', 'module'];
if ( id ) {
modPreDefine( id, dependencies, factory );
// 在不远的未来尝试完成define
// define可能是在页面中某个地方调用,不一定是在独立的文件被require装载
if ( tryDefineTimeout ) {
clearTimeout( tryDefineTimeout );
}
tryDefineTimeout = setTimeout( modPreAnalyse, 10 );
}
else {
// 纪录到共享变量中,在load或readystatechange中处理
wait4PreDefines.push( {
deps : dependencies,
factory : factory
} );
}
}
define.amd = {};
/**
* 获取相应状态的模块列表
*
* @inner
* @param {number} state 状态码
* @return {Array}
*/
function modGetByState( state ) {
var modules = [];
for ( var key in modModules ) {
var module = modModules[ key ];
if ( module.state == state ) {
modules.push( module );
}
}
return modules;
}
/**
* 模块配置获取函数
*
* @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 ) {
if ( modExists( id ) ) {
return;
}
var module = {
id : id,
deps : dependencies,
factory : factory,
exports : {},
config : moduleConfigGetter,
state : MODULE_STATE_PRE_DEFINED,
hardDeps : {}
};
// 将模块预存入defining集合中
modModules[ id ] = module;
}
/**
* 预分析模块
*
* 首先,完成对factory中声明依赖的分析提取
* 然后,尝试加载"资源加载所需模块"
*
* 需要先加载模块的原因是:如果模块不存在,无法进行resourceId normalize化
* modAnalyse完成后续的依赖分析处理,并进行依赖模块的加载
*
* @inner
* @param {Object} modules 模块对象
*/
function modPreAnalyse() {
var pluginModuleIds = [];
var pluginModuleIdsMap = {};
var modules = modGetByState( MODULE_STATE_PRE_DEFINED );
each(
modules,
function ( module ) {
// 处理实际需要加载的依赖
var realDepends = module.deps.slice( 0 );
module.realDeps = realDepends;
// 分析function body中的require
// 如果包含显式依赖声明,为性能考虑,可以不分析factoryBody
// AMD规范的说明是`SHOULD NOT`,所以这里还是分析了
var factory = module.factory;
var requireRule = /require\(\s*(['"'])([^'"]+)\1\s*\)/g;
var commentRule = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg;
if ( isFunction( factory ) ) {
factory.toString()
.replace( commentRule, '' )
.replace( requireRule, function ( $0, $1, $2 ) {
realDepends.push( $2 );
});
}
// 分析resource加载的plugin module id
each(
realDepends,
function ( dependId ) {
var idInfo = parseId( dependId );
if ( idInfo.resource ) {
var plugId = normalize( idInfo.module, module.id );
if ( !pluginModuleIdsMap[ plugId ] ) {
pluginModuleIds.push( plugId );
pluginModuleIdsMap[ plugId ] = 1;
}
}
}
);
module.state = MODULE_STATE_PRE_ANALYZED;
}
);
nativeRequire( pluginModuleIds, function () {
modAnalyse( modules );
} );
}
/**
* 分析模块
* 对所有依赖id进行normalize化,完成分析,并尝试加载其依赖的模块
*
* @inner
* @param {Array} modules 模块对象列表
*/
function modAnalyse( modules ) {
var requireModules = [];
each(
modules,
function ( module ) {
if ( module.state !== MODULE_STATE_PRE_ANALYZED ) {
return;
}
var id = module.id;
// 对参数中声明的依赖进行normalize
var depends = module.deps;
var hardDepends = module.hardDeps;
var hardDependsCount = isFunction( module.factory )
? module.factory.length
: 0;
each(
depends,
function ( dependId, index ) {
dependId = normalize( dependId, id );
depends[ index ] = dependId;
if ( index < hardDependsCount ) {
hardDepends[ dependId ] = 1;
}
}
);
// 依赖模块id normalize化,并去除必要的依赖。去除的依赖模块有:
// 1. 内部模块:require/exports/module
// 2. 重复模块:dependencies参数和内部require可能重复
// 3. 空模块:dependencies中使用者可能写空
var realDepends = module.realDeps;
var len = realDepends.length;
var existsDepend = {};
while ( len-- ) {
// 此处和上部分循环存在重复normalize,因为deps和realDeps是重复的
// 为保持逻辑分界清晰,就不做优化了先
var dependId = normalize( realDepends[ len ], id );
if ( !dependId
|| dependId in existsDepend
|| dependId in BUILDIN_MODULE
) {
realDepends.splice( len, 1 );
}
else {
existsDepend[ dependId ] = 1;
realDepends[ len ] = dependId;
// 将实际依赖压入加载序列中,后续统一进行require
requireModules.push( dependId );
}
}
module.realDepsIndex = existsDepend;
module.state = MODULE_STATE_ANALYZED;
modWaitDependenciesLoaded( module );
modInvokeFactoryDependOn( id );
}
);
nativeRequire( requireModules );
}
/**
* 等待模块依赖加载完成
* 加载完成后尝试调用factory完成模块定义
*
* @inner
* @param {Object} module 模块对象
*/
function modWaitDependenciesLoaded( module ) {
var id = module.id;
module.invokeFactory = invokeFactory;
invokeFactory();
// 用于避免死依赖链的死循环尝试
var checkingLevel = 0;
/**
* 判断依赖加载完成
*
* @inner
* @return {boolean}
*/
function checkInvokeReadyState() {
checkingLevel++;
var isReady = 1;
var tryDeps = [];
each(
module.realDeps,
function ( depId ) {
if ( !modIsAnalyzed( depId ) ) {
isReady = 0;
}
else if ( !modIsDefined( depId ) ) {
switch ( modHasCircularDependency( id, depId ) ) {
case CIRCULAR_DEP_UNREADY:
case CIRCULAR_DEP_NO:
isReady = 0;
break;
case CIRCULAR_DEP_YES:
if ( module.hardDeps[ depId ] ) {
tryDeps.push( depId );
}
break;
}
}
return !!isReady;
}
);
// 只有当其他非循环依赖都装载了,才去尝试触发硬依赖模块的初始化
isReady && each(
tryDeps,
function ( depId ) {
modTryInvokeFactory( depId );
}
);
isReady = isReady && tryDeps.length === 0;
isReady && (module.state = MODULE_STATE_READY);
checkingLevel--;
return isReady;
}
/**
* 初始化模块
*
* @inner
*/
function invokeFactory() {
if ( module.state == MODULE_STATE_DEFINED
|| checkingLevel > 1
|| !checkInvokeReadyState()
) {
return;
}
// 调用factory函数初始化module
try {
var factory = module.factory;
var exports = isFunction( factory )
? factory.apply(
global,
modGetModulesExports(
module.deps,
{
require : createLocalRequire( id ),
exports : module.exports,
module : module
}
)
)
: factory;
if ( typeof exports != 'undefined' ) {
module.exports = exports;
}
module.state = MODULE_STATE_DEFINED;
module.invokeFactory = null;
}
catch ( ex ) {
if ( /^\[MODULE_MISS\]"([^"]+)/.test( ex.message ) ) {
// 出错说明在factory的运行中,该require的模块是需要的
// 所以把它加入硬依赖中
module.hardDeps[ RegExp.$1 ] = 1;
return;
}
throw ex;
}
modInvokeFactoryDependOn( id );
modFireDefined( id );
}
}
/**
* 根据模块id数组,获取其的exports数组
* 用于模块初始化的factory参数或require的callback参数生成
*
* @inner
* @param {Array} modules 模块id数组
* @param {Object} buildinModules 内建模块对象
* @return {Array}
*/
function modGetModulesExports( modules, buildinModules ) {
var args = [];
each(
modules,
function ( moduleId, index ) {
args[ index ] =
buildinModules[ moduleId ]
|| modGetModuleExports( moduleId );
}
);
return args;
}
var CIRCULAR_DEP_UNREADY = 0;
var CIRCULAR_DEP_NO = 1;
var CIRCULAR_DEP_YES = 2;
/**
* 判断source是否处于target的依赖链中
*
* @inner
* @return {number}
*/
function modHasCircularDependency( source, target, meet ) {
if ( !modIsAnalyzed( target ) ) {
return CIRCULAR_DEP_UNREADY;
}
meet = meet || {};
meet[ target ] = 1;
if ( target == source ) {
return CIRCULAR_DEP_YES;
}
var module = modGetModule( target );
var depends = module && module.realDeps;
if ( depends ) {
var len = depends.length;
while ( len-- ) {
var dependId = depends[ len ];
if ( meet[ dependId ] ) {
continue;
}
var state = modHasCircularDependency( source, dependId, meet );
switch ( state ) {
case CIRCULAR_DEP_UNREADY:
case CIRCULAR_DEP_YES:
return state;
}
}
}
return CIRCULAR_DEP_NO;
}
/**
* 让依赖自己的模块尝试初始化
*
* @inner
* @param {string} id 模块id
*/
function modInvokeFactoryDependOn( id ) {
for ( var key in modModules ) {
var realDeps = modModules[ key ].realDepsIndex || {};
realDeps[ id ] && modTryInvokeFactory( key );
}
}
/**
* 尝试执行模块factory函数,进行模块初始化
*
* @inner
* @param {string} id 模块id
*/
function modTryInvokeFactory( id ) {
var module = modModules[ id ];
if ( module && module.invokeFactory ) {
module.invokeFactory();
}
}
/**
* 模块定义完成的事件监听器
*
* @inner
* @type {Array}
*/
var modDefinedListener = [];
/**
* 模块定义完成事件监听器的移除索引
*
* @inner
* @type {Array}
*/
var modRemoveListenerIndex = [];
/**
* 模块定义完成事件fire层级
*
* @inner
* @type {number}
*/
var modFireLevel = 0;
/**
* 派发模块定义完成事件
*
* @inner
* @param {string} id 模块标识
*/
function modFireDefined( id ) {
modFireLevel++;
each(
modDefinedListener,
function ( listener ) {
listener && listener( id );
}
);
modFireLevel--;
modSweepDefinedListener();
}
/**
* 清理模块定义完成事件监听器
* modRemoveDefinedListener时只做标记
* 在modFireDefined执行清除动作
*
* @inner
* @param {Function} listener 模块定义监听器
*/
function modSweepDefinedListener() {
if ( modFireLevel < 1 ) {
modRemoveListenerIndex.sort(
function ( a, b ) { return b - a; }
);
each(
modRemoveListenerIndex,
function ( index ) {
modDefinedListener.splice( index, 1 );
}
);
modRemoveListenerIndex = [];
}
}
/**
* 移除模块定义监听器
*
* @inner
* @param {Function} listener 模块定义监听器
*/
function modRemoveDefinedListener( listener ) {
each(
modDefinedListener,
function ( item, index ) {
if ( listener == item ) {
modRemoveListenerIndex.push( index );
}
}
);
}
/**
* 添加模块定义监听器
*
* @inner
* @param {Function} listener 模块定义监听器
*/
function modAddDefinedListener( listener ) {
modDefinedListener.push( listener );
}
/**
* 判断模块是否存在
*
* @inner
* @param {string} id 模块标识
* @return {boolean}
*/
function modExists( id ) {
return id in modModules;
}
/**
* 判断模块是否已定义完成
*
* @inner
* @param {string} id 模块标识
* @return {boolean}
*/
function modIsDefined( id ) {
return modExists( id )
&& modModules[ id ].state == MODULE_STATE_DEFINED;
}
/**
* 判断模块是否已分析完成
*
* @inner
* @param {string} id 模块标识
* @return {boolean}
*/
function modIsAnalyzed( id ) {
return modExists( id )
&& modModules[ id ].state >= MODULE_STATE_ANALYZED;
}
/**
* 获取模块的exports
*
* @inner
* @param {string} id 模块标识
* @return {*}
*/
function modGetModuleExports( id ) {
if ( modIsDefined( id ) ) {
return modModules[ id ].exports;
}
return null;
}
/**
* 获取模块
*
* @inner
* @param {string} id 模块标识
* @return {Object}
*/
function modGetModule( id ) {
return modModules[ id ];
}
/**
* 添加资源
*
* @inner
* @param {string} resourceId 资源标识
* @param {*} value 资源对象
*/
function modAddResource( resourceId, value ) {
modModules[ resourceId ] = {
exports: value || true,
state: MODULE_STATE_DEFINED
};
modInvokeFactoryDependOn( resourceId );
modFireDefined( resourceId );
}
/**
* 内建module名称集合
*
* @inner
* @type {Object}
*/
var BUILDIN_MODULE = {
require : require,
exports : 1,
module : 1
};
/**
* 未预定义的模块集合
* 主要存储匿名方式define的模块
*
* @inner
* @type {Array}
*/
var wait4PreDefines = [];
/**
* 完成模块预定义
*
* @inner
*/
function completePreDefine( currentId ) {
var preDefines = wait4PreDefines.slice( 0 );
wait4PreDefines.length = 0;
wait4PreDefines = [];
// 预定义模块:
// 此时处理的模块都是匿名define的模块
each(
preDefines,
function ( module ) {
var id = module.id || currentId;
modPreDefine( id, module.deps, module.factory );
}
);
modPreAnalyse();
}
/**
* 获取模块
*
* @param {string|Array} ids 模块名称或模块名称列表
* @param {Function=} callback 获取模块完成时的回调函数
* @return {Object}
*/
function nativeRequire( ids, callback, baseId ) {
callback = callback || new Function();
baseId = baseId || '';
// 根据 https://github.com/amdjs/amdjs-api/wiki/require
// It MUST throw an error if the module has not
// already been loaded and evaluated.
if ( isString( ids ) ) {
if ( !modIsDefined( ids ) ) {
throw new Error( '[MODULE_MISS]"' + ids + '" is not exists!' );
}
return modGetModuleExports( ids );
}
if ( !isArray( ids ) ) {
return;
}
if ( ids.length === 0 ) {
callback();
return;
}
var isCallbackCalled = 0;
modAddDefinedListener( tryFinishRequire );
each(
ids,
function ( id ) {
if ( id in BUILDIN_MODULE ) {
return;
}
( id.indexOf( '!' ) > 0
? loadResource
: loadModule
)( id, baseId );
}
);
tryFinishRequire();
/**
* 尝试完成require,调用callback
* 在模块与其依赖模块都加载完时调用
*
* @inner
*/
function tryFinishRequire() {
if ( isCallbackCalled ) {
return;
}
var visitedModule = {};
/**
* 判断是否所有模块都已经加载完成,包括其依赖的模块
*
* @inner
* @param {Array} modules 直接模块标识列表
* @return {boolean}
*/
function isAllInited( modules ) {
var allInited = 1;
each(
modules,
function ( id ) {
if ( visitedModule[ id ] ) {
return;
}
visitedModule[ id ] = 1;
if ( BUILDIN_MODULE[ id ] ) {
return;
}
if (
!modIsDefined( id )
|| !isAllInited( modGetModule( id ).realDeps )
) {
allInited = 0;
return false;
}
}
);
return allInited;
}
// 检测并调用callback
if ( isAllInited( ids ) ) {
isCallbackCalled = 1;
modRemoveDefinedListener( tryFinishRequire );
callback.apply(
global,
modGetModulesExports( ids, BUILDIN_MODULE )
);
}
}
}
/**
* 正在加载的模块列表
*
* @inner
* @type {Object}
*/
var loadingModules = {};
/**
* 加载模块
*
* @inner
* @param {string} moduleId 模块标识
*/
function loadModule( moduleId ) {
if ( loadingModules[ moduleId ] ) {
return;
}
if ( modExists( moduleId ) ) {
modAnalyse( [ modGetModule( 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 ) ;
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 );
delete loadingModules[ moduleId ];
}
}
}
/**
* 加载资源
*
* @inner
* @param {string} pluginAndResource 插件与资源标识
* @param {string} baseId 当前环境的模块标识
*/
function loadResource( pluginAndResource, baseId ) {
var idInfo = parseId( pluginAndResource );
var pluginId = idInfo.module;
var resourceId = idInfo.resource;
/**
* plugin加载完成的回调函数
*
* @inner
* @param {*} value resource的值
*/
function pluginOnload( value ) {
modAddResource( pluginAndResource, value );
}
/**
* 该方法允许plugin使用加载的资源声明模块
*
* @param {string} name 模块id
* @param {string} body 模块声明字符串
*/
pluginOnload.fromText = function ( id, text ) {
new Function( text )();
completePreDefine( id );
};
/**
* 加载资源
*
* @inner
* @param {Object} plugin 用于加载资源的插件模块
*/
function load( plugin ) {
if ( !modIsDefined( pluginAndResource ) ) {
plugin.load(
resourceId,
createLocalRequire( baseId ),
pluginOnload,
moduleConfigGetter.call( { id: pluginAndResource } )
);
}
}
if ( !modIsDefined( pluginId ) ) {
nativeRequire( [ pluginId ], load );
}
else {
load( modGetModuleExports( pluginId ) );
}
}
/**
* require配置
*
* @inner
* @type {Object}
*/
var requireConf = {
baseUrl : './',
paths : {},
config : {},
map : {},
packages : [],
waitSeconds : 0,
urlArgs : {}
};
/**
* 混合当前配置项与用户传入的配置项
*
* @inner
* @param {string} name 配置项名称
* @param {Any} value 用户传入配置项的值
*/
function mixConfig( name, value ) {
var originValue = requireConf[ name ];
var type = typeof originValue;
if ( type == 'string' || type == 'number' ) {
requireConf[ name ] = value;
}
else if ( isArray( originValue ) ) {
each( value, function ( item ) {
originValue.push( item );
} );
}
else {
for ( var key in value ) {
originValue[ key ] = value[ key ];
}
}
}
/**
* 配置require
*
* @param {Object} conf 配置对象
*/
require.config = function ( conf ) {
// 简单的多处配置还是需要支持
// 所以实现更改为二级mix
for ( var key in requireConf ) {
if ( conf.hasOwnProperty( key ) ) {
var confItem = conf[ key ];
if ( key == 'urlArgs' && isString( confItem ) ) {
defaultUrlArgs = confItem;
}
else {
mixConfig( key, confItem );
}
}
}
createConfIndex();
};
// 初始化时需要创建配置索引
createConfIndex();
/**
* 创建配置信息内部索引
*
* @inner
*/
function createConfIndex() {
requireConf.baseUrl = requireConf.baseUrl.replace( /\/$/, '' ) + '/';
createPathsIndex();
createMappingIdIndex();
createPackagesIndex();
createUrlArgsIndex();
}
/**
* packages内部索引
*
* @inner
* @type {Array}
*/
var packagesIndex;
/**
* 创建packages内部索引
*
* @inner
*/
function createPackagesIndex() {
packagesIndex = [];
each(
requireConf.packages,
function ( packageConf ) {
var pkg = packageConf;
if ( isString( packageConf ) ) {
pkg = {
name: packageConf.split('/')[ 0 ],
location: packageConf,
main: 'main'
};
}
pkg.location = pkg.location || pkg.name;
pkg.main = (pkg.main || 'main').replace(/\.js$/i, '');
packagesIndex.push( pkg );
}
);
packagesIndex.sort( createDescSorter( 'name' ) );
}
/**
* paths内部索引
*
* @inner
* @type {Array}
*/
var pathsIndex;
/**
* 创建paths内部索引
*
* @inner
*/
function createPathsIndex() {
pathsIndex = kv2List( requireConf.paths );
pathsIndex.sort( createDescSorter() );
}
/**
* 默认的urlArgs
*
* @inner
* @type {string}
*/
var defaultUrlArgs;
/**
* urlArgs内部索引
*
* @inner
* @type {Array}
*/
var urlArgsIndex;
/**
* 创建urlArgs内部索引
*
* @inner
*/
function createUrlArgsIndex() {
urlArgsIndex = kv2List( requireConf.urlArgs );
urlArgsIndex.sort( createDescSorter() );
}
/**
* mapping内部索引
*
* @inner
* @type {Array}
*/
var mappingIdIndex;
/**
* 创建mapping内部索引
*
* @inner
*/
function createMappingIdIndex() {
mappingIdIndex = [];
mappingIdIndex = kv2List( requireConf.map );
mappingIdIndex.sort( createDescSorter() );
each(
mappingIdIndex,
function ( item ) {
var key = item.k;
item.v = kv2List( item.v );
item.v.sort( createDescSorter() );
item.reg = key == '*'
? /^/
: createPrefixRegexp( key );
}
);
}
/**
* 将`模块标识+'.extension'`形式的字符串转换成相对的url
*
* @inner
* @param {string} source 源字符串
* @return {string}
*/
function toUrl( source ) {
// 分离 模块标识 和 .extension
var extReg = /(\.[a-z0-9]+)$/i;
var queryReg = /(\?[^#]*)$/i;
var extname = '.js';
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, '' );
}
// 模块标识合法性检测
if ( !MODULE_ID_REG.test( id ) ) {
return source;
}
var url = id;
// paths处理和匹配
var isPathMap;
each( pathsIndex, function ( item ) {
var key = item.k;
if ( createPrefixRegexp( key ).test( id ) ) {
url = url.replace( key, item.v );
isPathMap = 1;
return false;
}
} );
// packages处理和匹配
if ( !isPathMap ) {
each(
packagesIndex,
function ( packageConf ) {
var name = packageConf.name;
if ( createPrefixRegexp( name ).test( id ) ) {
url = url.replace( name, packageConf.location );
return false;
}
}
);
}
// 相对路径时,附加baseUrl
if ( !/^([a-z]{2,10}:\/)?\//i.test( url ) ) {
url = requireConf.baseUrl + url;
}
// 附加 .extension 和 query
url += extname + query;
var isUrlArgsAppended;
/**
* 为url附加urlArgs
*
* @inner
* @param {string} args urlArgs串
*/
function appendUrlArgs( args ) {
if ( !isUrlArgsAppended ) {
url += ( url.indexOf( '?' ) > 0 ? '&' : '?' ) + args;
isUrlArgsAppended = 1;
}
}
// urlArgs处理和匹配
each( urlArgsIndex, function ( item ) {
if ( createPrefixRegexp( item.k ).test( id ) ) {
appendUrlArgs( item.v );
return false;
}
} );
defaultUrlArgs && appendUrlArgs( defaultUrlArgs );
return url;
}
/**
* 创建local require函数
*
* @inner
* @param {number} baseId 当前module id
* @return {Function}
*/
function createLocalRequire( baseId ) {
var requiredCache = {};
function req( requireId, callback ) {
if ( isString( requireId ) ) {
var requiredModule;
if ( !( requiredModule = requiredCache[ requireId ] ) ) {
requiredModule = nativeRequire(
normalize( requireId, baseId ),
callback,
baseId
);
requiredCache[ requireId ] = requiredModule;
}
return requiredModule;
}
else if ( isArray( requireId ) ) {
// 分析是否有resource使用的plugin没加载
var unloadedPluginModules = [];
each(
requireId,
function ( id ) {
var idInfo = parseId( id );
var pluginId = normalize( idInfo.module, baseId );
if ( idInfo.resource && !modIsDefined( pluginId ) ) {
unloadedPluginModules.push( pluginId );
}
}
);
// 加载模块
nativeRequire(
unloadedPluginModules,
function () {
var ids = [];
each(
requireId,
function ( id ) {
ids.push( normalize( id, baseId ) );
}
);
nativeRequire( ids, callback, baseId );
},
baseId
);
}
}
/**
* 将[module ID] + '.extension'格式的字符串转换成url
*
* @inner
* @param {string} source 符合描述格式的源字符串
* @return {string}
*/
req.toUrl = function ( id ) {
return toUrl( normalize( id, baseId ) );
};
return req;
}
/**
* id normalize化
*
* @inner
* @param {string} id 需要normalize的模块标识
* @param {string} baseId 当前环境的模块标识
* @return {string}
*/
function normalize( id, baseId ) {
if ( !id ) {
return '';
}
var idInfo = parseId( id );
if ( !idInfo ) {
return id;
}
var resourceId = idInfo.resource;
var moduleId = relative2absolute( idInfo.module, baseId );
each(
packagesIndex,
function ( packageConf ) {
var name = packageConf.name;
var main = name + '/' + packageConf.main;
if ( name == moduleId
) {
moduleId = moduleId.replace( name, main );
return false;
}
}
);
moduleId = mappingId( moduleId, baseId );
if ( resourceId ) {
var module = modGetModuleExports( moduleId );
resourceId = module && module.normalize
? module.normalize(
resourceId,
function ( resId ) {
return normalize( resId, baseId );
}
)
: normalize( resourceId, baseId );
return moduleId + '!' + resourceId;
}
return moduleId;
}
/**
* 相对id转换成绝对id
*
* @inner
* @param {string} id 要转换的id
* @param {string} baseId 当前所在环境id
* @return {string}
*/
function relative2absolute( id, baseId ) {
if ( /^\.{1,2}/.test( id ) ) {
var basePath = baseId.split( '/' );
var namePath = id.split( '/' );
var baseLen = basePath.length - 1;
var nameLen = namePath.length;
var cutBaseTerms = 0;
var cutNameTerms = 0;
pathLoop: for ( var i = 0; i < nameLen; i++ ) {
var term = namePath[ i ];
switch ( term ) {
case '..':
if ( cutBaseTerms < baseLen ) {
cutBaseTerms++;
cutNameTerms++;
}
else {
break pathLoop;
}
break;
case '.':
cutNameTerms++;
break;
default:
break pathLoop;
}
}
basePath.length = baseLen - cutBaseTerms;
namePath = namePath.slice( cutNameTerms );
basePath.push.apply( basePath, namePath );
return basePath.join( '/' );
}
return id;
}
/**
* 确定require的模块id不包含相对id。用于global require,提前预防难以跟踪的错误出现
*
* @inner
* @param {string|Array} requireId require的模块id
*/
function assertNotContainRelativeId( requireId ) {
var invalidIds = [];
/**
* 监测模块id是否relative id
*
* @inner
* @param {string} id 模块id
*/
function monitor( id ) {
if ( /^\.{1,2}/.test( id ) ) {
invalidIds.push( id );
}
}
if ( isString( requireId ) ) {
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( ', ' )
);
}
}
/**
* 模块id正则
*
* @const
* @inner
* @type {RegExp}
*/
var MODULE_ID_REG = /^[-_a-z0-9\.]+(\/[-_a-z0-9\.]+)*$/i;
/**
* 解析id,返回带有module和resource属性的Object
*
* @inner
* @param {string} id 标识
* @return {Object}
*/
function parseId( id ) {
var segs = id.split( '!' );
if ( MODULE_ID_REG.test( segs[ 0 ] ) ) {
return {
module : segs[ 0 ],
resource : segs[ 1 ] || ''
};
}
return null;
}
/**
* 基于map配置项的id映射
*
* @inner
* @param {string} id 模块id
* @param {string} baseId 当前环境的模块id
* @return {string}
*/
function mappingId( id, baseId ) {
each(
mappingIdIndex,
function ( item ) {
if ( item.reg.test( baseId ) ) {
each( item.v, function ( mapData ) {
var key = mapData.k;
var rule = createPrefixRegexp( key );
if ( rule.test( id ) ) {
id = id.replace( key, mapData.v );
return false;
}
} );
return false;
}
}
);
return id;
}
/**
* 将对象数据转换成数组,数组每项是带有k和v的Object
*
* @inner
* @param {Object} source 对象数据
* @return {Array.<Object>}
*/
function kv2List( source ) {
var list = [];
for ( var key in source ) {
if ( source.hasOwnProperty( key ) ) {
list.push( {
k: key,
v: source[ 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 {HTMLDocument}
*/
function getCurrentScript() {
if ( currentlyAddingScript ) {
return currentlyAddingScript;
}
else if (
interactiveScript
&& interactiveScript.readyState == 'interactive'
) {
return interactiveScript;
}
else {
var scripts = document.getElementsByTagName( 'script' );
var scriptLen = scripts.length;
while ( scriptLen-- ) {
var script = scripts[ scriptLen ];
if ( script.readyState == 'interactive' ) {
interactiveScript = script;
return script;
}
}
}
}
/**
* 向页面中插入script标签
*
* @inner
* @param {HTMLScriptElement} script script标签
*/
function appendScript( script ) {
currentlyAddingScript = script;
var doc = document;
(doc.getElementsByTagName('head')[0] || doc.body).appendChild( script );
currentlyAddingScript = null;
}
/**
* 创建id前缀匹配的正则对象
*
* @inner
* @param {string} prefix id前缀
* @return {RegExp}
*/
function createPrefixRegexp( prefix ) {
return new RegExp( '^' + prefix + '(/|$)' );
}
/**
* 判断对象是否数组类型
*
* @inner
* @param {*} obj 要判断的对象
* @return {boolean}
*/
function isArray( obj ) {
return obj instanceof Array;
}
/**
* 判断对象是否函数类型
*
* @inner
* @param {*} obj 要判断的对象
* @return {boolean}
*/
function isFunction( obj ) {
return typeof obj == 'function';
}
/**
* 判断是否字符串
*
* @inner
* @param {*} obj 要判断的对象
* @return {boolean}
*/
function isString( obj ) {
return typeof obj == 'string';
}
/**
* 循环遍历数组集合
*
* @inner
* @param {Array} source 数组源
* @param {function(Array,Number):boolean} iterator 遍历函数
*/
function each( source, iterator ) {
if ( isArray( source ) ) {
for ( var i = 0, len = source.length; i < len; i++ ) {
if ( iterator( source[ i ], i ) === false ) {
break;
}
}
}
}
/**
* 创建数组字符数逆序排序函数
*
* @inner
* @param {string} property 数组项对象名
* @return {Function}
*/
function createDescSorter( property ) {
property = property || 'k';
return function ( a, b ) {
var aValue = a[ property ];
var bValue = b[ property ];
if ( bValue == '*' ) {
return -1;
}
if ( aValue == '*' ) {
return 1;
}
return bValue.length - aValue.length;
};
}
// 暴露全局对象
global.define = define;
global.require = require;
})( this );