/** * 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.} 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.} */ 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 );