Dojo 加載器源文件 dojo.js


(function(
userConfig,
defaultConfig
){
    /*
summary: 概述
This is the "source loader" and is the entry point for Dojo during development. You may also load Dojo with
any AMD-compliant loader via the package main module dojo/main.
            這個是一個源文件加載器,在dojo的開發過程中,它是整個應用程序的入口。 你也可以通過其它遵循AMD規範的加載器來加載Dojo, 如requirejs. 如果你採用第三方的加截器,可以不用使用本文件,而是直接加載 dojo/main
            模塊
description: 描述
This is the "source loader" for Dojo. It provides an AMD-compliant loader that can be configured
to operate in either synchronous or asynchronous modes. After the loader is defined, dojo is loaded
IAW the package main module dojo/main. In the event you wish to use a foreign loader, you may load dojo as a package
via the package main module dojo/main and this loader is not required; see dojo/package.json for details.
            這是Dojo的一個源文件加載器,它遵循 AMD規範,可以配置成同步或異步模式。當加載器被定義好後(即用於瀏覽器,nodejs環境), dojo的主體會被加載進來,這主要是依據 dojo/main模塊。如果想使用一個外部的
            加載器(如requirejs), 你可以直接將 dojo/main進行加載,而這個loader不必加載;需要了解更多,請查看 dojo/package.json;


In order to keep compatibility with the v1.x line, this loader includes additional machinery that enables
the dojo.provide, dojo.require et al API. This machinery is loaded by default, but may be dynamically removed
via the has.js API and statically removed via the build system.
            考慮到向前兼容dojo的版本, 這個加載器包含一些額外機制,如使 dojo.provide, dojo.requre; 這些機制默認情況下是包含的,但可以通過 has.js 的API動態刪除或通過 dojo的build system 手動刪除。


This loader includes sniffing machinery to determine the environment; the following environments are supported:
            這個加載器也包含嗅探機制去發現當前使用的環境, 以下三種會被支持。


- browser
- node.js
- rhino  Java 編寫的一個解釋器,由 Mozilla 開發


This is the so-called "source loader". As such, it includes many optional features that may be discarded by
building a customized version with the build system.
            這就是所謂的源文件加載器,如上陳述, 它可能通過 build system可以去除掉一些功能,而形成你自己想要的版本。.
Design and Implementation Notes 設計和實現日記


This function defines an AMD-compliant (http://wiki.commonjs.org/wiki/Modules/AsynchronousDefinition)
loader that can be configured to operate in either synchronous or asynchronous modes.
     這個功能定議一個遵循AMD規範(http://wiki.commonjs.org/wiki/Modules/AsynchronousDefinition)的加載器。 可以配置成同步或異步加載模式。


     ! 有空了解下AMD規範


     here is a road map of the contents:
     以下是這個內容的路線圖。
1. Small library for use implementing the loader.  加載器會使用到的一個很小的庫,即工具函數。
2. Define the has.js API; this is used throughout the loader to bracket features.定義一個has.js API; 整個加載器都會使用到它來特徵檢測
3. Define the node.js and rhino sniffs and sniff. 定義偵測node.js 及rhino 的嗅探器
    4. Define the loader's data. 定義一個loader的數據模型
5. Define the configuration machinery. 實現配置機制
6. Define the script element sniffing machinery and sniff for configuration data. 定義一個角本元素嗅探機制,並且將嗅探的結果作爲配置數據
7. Configure the loader IAW the provided user, default, and sniffing data. 依據提供的用戶,默認參數及嗅探到的信息,配置一個加載器
8. Define the global require function. 定義一個全局加載函數
9. Define the module resolution machinery 定義一個模塊解析機制.
10. Define the module and plugin module definition machinery 定義一個模塊及插件開發規範
11. Define the script injection machinery. 定義一個角本注入機制
12. Define the window load detection. 定義一個window 加載偵測
13. Define the logging API. 定義一個日誌API
14. Define the tracing API. 定義一個跟蹤API, 即調試用的API
15. Define the AMD define function. 實現AMD規範中需要定義的函數
16. Define the dojo v1.x provide/require machinery--so called "legacy" modes. 爲了兼容之前老的dojo, 定義provide/ require。
17. Publish global variables. 發佈一些全局變量


Language and Acronyms and Idioms
     束語
moduleId: a CJS(CommonJS) module identifier, (used for public APIs   對外的CommonJS 模塊標識符
mid: moduleId (used internally)   對內的模塊ID(標識符)
packageId: a package identifier (used for public APIs) 對外的包標識符
pid: packageId (used internally); the implied system or default package has pid==="" 對內的包id, 如是是默認的包,則pid爲空字符
pack: package is used internally to reference a package object (since javascript has reserved words including "package")  包對象
prid: plugin resource identifier 插件標識符
referenceModule: 參照模塊,用代碼來表示更好理解, require['dojo/dom'], 而 dom裏面依賴 define(["./sniff", "./_base/window"], sniff, window. 在加載sniff時, referenceModule代表的就是dom 模塊.


The integer constant 1 is used in place of true and 0 in place of false   常量1及0 ,分別用作ture及false的佔位符


*/




var noop = function(){
},


isEmpty = function(it){
for(var p in it){
return 0;
}
return 1;
},


toString = {}.toString,


isFunction = function(it){
return toString.call(it) == "[object Function]";
},


isString = function(it){
return toString.call(it) == "[object String]";
},


isArray = function(it){
return toString.call(it) == "[object Array]";
},


forEach = function(vector, callback){
if(vector){
for(var i = 0; i < vector.length;){
callback(vector[i++]);
}
}
},


mix = function(dest, src){
for(var p in src){
dest[p] = src[p];
}
return dest;
},


makeError = function(error, info){
return mix(new Error(error), {src:"dojoLoader", info:info});
},


uidSeed = 1,


uid = function(){
// Returns a unique identifier (within the lifetime of the document) of the form /_d+/.  返回一個唯一標識符(文檔的生命週期內),格式爲 “_數字” 形式


return "_" + uidSeed++;
},


// FIXME: how to doc window.require() api  FIXME 標識此註釋的內容代碼需要修正 TODO 標識此處功能需要實現, 這裏說的是需要修正 require的api 文檔


// this will be the global require function;
        // 全局的 require 函數
req = function(
config,  //(object, optional) hash of configuration properties  傳入的第一個值爲配置屬性的散列對象(可選)
dependencies, //(array of commonjs.moduleId, optional) list of modules to be loaded before applying callback 傳入的第二個參數爲,在調用回調函數之前,需要加載的模塊id, 該模塊id遵循commonjs模範(可選)
callback  //(function, optional) lambda expression to apply to module values implied by dependencies,傳入一個匿名函數,當依賴的模塊加載完後,會調用這個函數
){
return contextRequire(config, dependencies, callback, 0, req);
},


    // the loader uses the has.js API to control feature inclusion/exclusion; define then use throughout  loader 通過has.js API來包含或者排除某些特徵
global = this,


doc = global.document,


element = doc && doc.createElement("DiV"),


has = req.has = function(name){
return isFunction(hasCache[name]) ? (hasCache[name] = hasCache[name](global, doc, element)) : hasCache[name];
},


hasCache = has.cache = defaultConfig.hasCache;


has.add = function(name, test, now, force){ // 添加某個測試。
(hasCache[name]===undefined || force) && (hasCache[name] = test); // 測試緩存中還沒有測試結果,或者要強制覆蓋原有的測試, hasCache[name]=test;
return now && has(name);  //指定第三個參數爲true時,立即返回檢測的結果。
};


    /*
        以下測試是否爲 nodejs環境,如果是,加載./_base/configNode.js的文件。
        如果用戶的配置中有指定has對象,並且存在"host-node", 那麼直接返回用戶指定的值。 惹沒有指定,則測試是否有全局對象process, 並測試它的版本是否爲node.
        例:
        nodejs環境
         hasCache['host-node'] = 1
        在這裏,沒有設定has.add的第三個參數。所以只是設置了hasCache['host-node']的值,而不返回,在if中,會通過has("host-node")來再獲得相應的值。
    */
has.add("host-node", userConfig.has && "host-node" in userConfig.has ?
userConfig.has["host-node"] :
(typeof process == "object" && process.versions && process.versions.node && process.versions.v8));
if(has("host-node")){
// fixup the default config for node.js environment
require("./_base/configNode.js").config(defaultConfig);
// remember node's require (with respect to baseUrl==dojo's root)
defaultConfig.loaderPatch.nodeRequire = require;
}
    /*
     用於檢測rhino環境
     */
has.add("host-rhino", userConfig.has && "host-rhino" in userConfig.has ?
userConfig.has["host-rhino"] :
(typeof load == "function" && (typeof Packages == "function" || typeof Packages == "object")));
if(has("host-rhino")){
// owing to rhino's lame feature that hides the source of the script, give the user a way to specify the baseUrl...
for(var baseUrl = userConfig.baseUrl || ".", arg, rhinoArgs = this.arguments, i = 0; i < rhinoArgs.length;){
arg = (rhinoArgs[i++] + "").split("=");
if(arg[0] == "baseUrl"){
baseUrl = arg[1];
break;
}
}
load(baseUrl + "/_base/configRhino.js");
rhinoDojoConfig(defaultConfig, baseUrl, rhinoArgs);
}


// userConfig has tests override defaultConfig has tests; do this after the environment detection because
// the environment detection usually sets some has feature values in the hasCache.
    // 用戶配置中的has測試會覆蓋 defaultConfig 中的 has測試。 但這會發現在環境偵測之後, 因爲環境偵沒通常會在hasCache裏設置特徵檢測的值。
    // 總體的意思就是在確定loader使用環境後,用 userConfig中的has 替換defaultConfig指定的has測試
for(var p in userConfig.has){
has.add(p, userConfig.has[p], 0, 1);
}


//
// define the loader data  定義加載器的數據
//


// the loader will use these like symbols if the loader has the traceApi; otherwise
// define magic numbers so that modules can be provided as part of defaultConfig
    /*
    如果有指定traceApi(跟蹤功能), 定義的變量相當於標識的符號,表示模塊是在請求,到達,正在執行,執行完成等各種狀態。否則,定義的數字,模塊可以作爲defaultConfig的一部分。
    以下變量指定模塊當前的狀態
     */
var requested = 1,
arrived = 2,
nonmodule = 3,
executing = 4,
executed = 5;


if(has("dojo-trace-api")){


// these make debugging nice;  don't do it for production code 使調試變得容易,不要將他用於產品代碼。
requested = "requested";
arrived = "arrived";
nonmodule = "not-a-module";
executing = "executing";
executed = "executed";
}


var legacyMode = 0,
sync = "sync",
xd = "xd",
syncExecStack = [],
dojoRequirePlugin = 0,
checkDojoRequirePlugin = noop,
transformToAmd = noop,
getXhr;
    // 是否可以使用1.7之前的加載器
if(has("dojo-sync-loader")){
req.isXdUrl = noop;


req.initSyncLoader = function(dojoRequirePlugin_, checkDojoRequirePlugin_, transformToAmd_){
// the first dojo/_base/loader loaded gets to define these variables; they are designed to work
// in the presence of zero to many mapped dojo/_base/loaders
if(!dojoRequirePlugin){
dojoRequirePlugin = dojoRequirePlugin_;
checkDojoRequirePlugin = checkDojoRequirePlugin_;
transformToAmd = transformToAmd_;
}


return {
sync:sync,
requested:requested,
arrived:arrived,
nonmodule:nonmodule,
executing:executing,
executed:executed,
syncExecStack:syncExecStack,
modules:modules,
execQ:execQ,
getModule:getModule,
injectModule:injectModule,
setArrived:setArrived,
signal:signal,
finishExec:finishExec,
execModule:execModule,
dojoRequirePlugin:dojoRequirePlugin,
getLegacyMode:function(){return legacyMode;},
guardCheckComplete:guardCheckComplete
};
};


if(has("dom")){
// in legacy sync mode, the loader needs a minimal XHR library


var locationProtocol = location.protocol,
locationHost = location.host;
req.isXdUrl = function(url){
if(/^\./.test(url)){
// begins with a dot is always relative to page URL; therefore not xdomain
return false;
}
if(/^\/\//.test(url)){
// for v1.6- backcompat, url starting with // indicates xdomain
return true;
}
// get protocol and host
// \/+ takes care of the typical file protocol that looks like file:///drive/path/to/file
// locationHost is falsy if file protocol => if locationProtocol matches and is "file:", || will return false
var match = url.match(/^([^\/\:]+\:)\/+([^\/]+)/);
return match && (match[1] != locationProtocol || (locationHost && match[2] != locationHost));
};




// note: to get the file:// protocol to work in FF, you must set security.fileuri.strict_origin_policy to false in about:config
has.add("dojo-xhr-factory", 1);
has.add("dojo-force-activex-xhr", has("host-browser") && !doc.addEventListener && window.location.protocol == "file:");
has.add("native-xhr", typeof XMLHttpRequest != "undefined");
if(has("native-xhr") && !has("dojo-force-activex-xhr")){
getXhr = function(){
return new XMLHttpRequest();
};
}else{
// if in the browser an old IE; find an xhr
for(var XMLHTTP_PROGIDS = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'], progid, i = 0; i < 3;){
try{
progid = XMLHTTP_PROGIDS[i++];
if(new ActiveXObject(progid)){
// this progid works; therefore, use it from now on
break;
}
}catch(e){
// squelch; we're just trying to find a good ActiveX progid
// if they all fail, then progid ends up as the last attempt and that will signal the error
// the first time the client actually tries to exec an xhr
}
}
getXhr = function(){
return new ActiveXObject(progid);
};
}
req.getXhr = getXhr;


has.add("dojo-gettext-api", 1);
req.getText = function(url, async, onLoad){
var xhr = getXhr();
xhr.open('GET', fixupUrl(url), false);
xhr.send(null);
if(xhr.status == 200 || (!location.host && !xhr.status)){
if(onLoad){
onLoad(xhr.responseText, async);
}
}else{
throw makeError("xhrFailed", xhr.status);
}
return xhr.responseText;
};
}
}else{
req.async = 1;
}


//
// loader eval
//
var eval_ =
// use the function constructor so our eval is scoped close to (but not in) in the global space with minimal pollution 使用函數的構造函數,便我們定義的eval形成閉包,不會污染全局作域用。
new Function('return eval(arguments[0]);');


req.eval =
function(text, hint){


return eval_(text + "\r\n////@ sourceURL=" + hint);
};


//
// loader micro events API
// loader 內部事件系統






var listenerQueues = {},
error = "error",
signal = req.signal = function(type, args){
var queue = listenerQueues[type];
// notice we run a copy of the queue; this allows listeners to add/remove
// other listeners without affecting this particular signal
forEach(queue && queue.slice(0), function(listener){
listener.apply(null, isArray(args) ? args : [args]);
});
},
on = req.on = function(type, listener){


// notice a queue is not created until a client actually connects
var queue = listenerQueues[type] || (listenerQueues[type] = []);
queue.push(listener);
return {
remove:function(){
for(var i = 0; i<queue.length; i++){
if(queue[i]===listener){
queue.splice(i, 1);
return;
}
}
}
};
};




// configuration machinery; with an optimized/built defaultConfig, all configuration machinery can be discarded
// lexical variables hold key loader data structures to help with minification; these may be completely,
// one-time initialized by defaultConfig for optimized/built versions
    // 實現loader的配置機制;


var
aliases
// a vector of pairs of [regexs or string, replacement] => (alias, actual)
= [],


paths
// CommonJS paths
= {},


pathsMapProg
// list of (from-path, to-path, regex, length) derived from paths;
// a "program" to apply paths; see computeMapProg
= [],


packs
// a map from packageId to package configuration object; see fixupPackageInfo
= {},


map = req.map
// AMD map config variable; dojo/_base/kernel needs req.map to figure out the scope map
= {},


mapProgs
// vector of quads as described by computeMapProg; map-key is AMD map key, map-value is AMD map value
= [],


modules
// A hash:(mid) --> (module-object) the module namespace
//
// pid: the package identifier to which the module belongs (e.g., "dojo"); "" indicates the system or default package
// mid: the fully-resolved (i.e., mappings have been applied) module identifier without the package identifier (e.g., "dojo/io/script")
// url: the URL from which the module was retrieved
// pack: the package object of the package to which the module belongs
// executed: 0 => not executed; executing => in the process of traversing deps and running factory; executed => factory has been executed
// deps: the dependency vector for this module (vector of modules objects)
// def: the factory for this module
// result: the result of the running the factory for this module
// injected: (0 | requested | arrived) the status of the module; nonmodule means the resource did not call define
// load: plugin load function; applicable only for plugins
//
// Modules go through several phases in creation:
//
// 1. Requested: some other module's definition or a require application contained the requested module in
//  its dependency vector or executing code explicitly demands a module via req.require.
//
// 2. Injected: a script element has been appended to the insert-point element demanding the resource implied by the URL
//
// 3. Loaded: the resource injected in [2] has been evaluated.
//
// 4. Defined: the resource contained a define statement that advised the loader about the module. Notice that some
//  resources may just contain a bundle of code and never formally define a module via define
//
// 5. Evaluated: the module was defined via define and the loader has evaluated the factory and computed a result.
= {},


cacheBust
// query string to append to module URLs to bust browser cache
= "",


cache
// hash:(mid | url)-->(function | string)
//
// A cache of resources. The resources arrive via a config.cache object, which is a hash from either mid --> function or
// url --> string. The url key is distinguished from the mid key by always containing the prefix "url:". url keys as provided
// by config.cache always have a string value that represents the contents of the resource at the given url. mid keys as provided
// by config.cache always have a function value that causes the same code to execute as if the module was script injected.
//
// Both kinds of key-value pairs are entered into cache via the function consumePendingCache, which may relocate keys as given
// by any mappings *iff* the config.cache was received as part of a module resource request.
//
// Further, for mid keys, the implied url is computed and the value is entered into that key as well. This allows mapped modules
// to retrieve cached items that may have arrived consequent to another namespace.
//
= {},


urlKeyPrefix
// the prefix to prepend to a URL key in the cache.
= "url:",


pendingCacheInsert
// hash:(mid)-->(function)
//
// Gives a set of cache modules pending entry into cache. When cached modules are published to the loader, they are
// entered into pendingCacheInsert; modules are then pressed into cache upon (1) AMD define or (2) upon receiving another
// independent set of cached modules. (1) is the usual case, and this case allows normalizing mids given in the pending
// cache for the local configuration, possibly relocating modules.
= {},


dojoSniffConfig
// map of configuration variables
// give the data-dojo-config as sniffed from the document (if any)
= {},


insertPointSibling
// the nodes used to locate where scripts are injected into the document
= 0;
    // 確保在 dojo在 build時可配置。提供配置API,這樣可以將map或者paths中的值處理爲 mapProgs, pathsMapProgs, 主要是在編譯時,這些配置信息都是存放在profile 文件裏,可以查看profile文件的格式。 Ensures that the build is configurable
if(has("dojo-config-api")){
var consumePendingCacheInsert = function(referenceModule){


var p, item, match, now, m;


for(p in pendingCacheInsert){


item = pendingCacheInsert[p];
match = p.match(/^url\:(.+)/);
if(match){
cache[urlKeyPrefix + toUrl(match[1], referenceModule)] =  item;
}else if(p=="*now"){
now = item;
}else if(p!="*noref"){
m = getModuleInfo(p, referenceModule, true);
cache[m.mid] = cache[urlKeyPrefix + m.url] = item;
}
}
if(now){
now(createRequire(referenceModule));
}
pendingCacheInsert = {};
},


escapeString = function(s){
return s.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, function(c){ return "\\" + c; });
},


computeMapProg = function(map, dest){
// This routine takes a map as represented by a JavaScript object and initializes dest, a vector of
// quads of (map-key, map-value, refex-for-map-key, length-of-map-key), sorted decreasing by length-
// of-map-key. The regex looks for the map-key followed by either "/" or end-of-string at the beginning
// of a the search source. Notice the map-value is irrelevant to the algorithm
                /*
                    這段程序獲得一個由Javascript 對像表示的map配置數據, 然後會初始化dest(存放結果的數組),dest中存放的值都是四維向量(map-key, map-value, map-key的正則表達式, map-key的長度)
                    程序會對dest中的值,根據map-key的長度進行降序排序. 正則表達式用於搜索目標對像的開頭是否是may-key + "/" 或者may-key + "$" (完全匹配). 注意,map-value跟算法沒有什麼關係。
                 */
                /*
                 map: {
                     myOldApp: {
                     dojo: "dojo16",
                     dijit: "dijit16",
                     dojox: "dojox16"
                     }
                 }
                 [["myOldApp", Object { dojo="dojo16", dijit="dijit16", dojox="dojox16"}, RegExp /^myOldApp(\/|$)/, 8]]




                 paths:{
                 "a/b": "myApp/ core/widget"
                 }


                 [["a/b", "myApp/ core/widget", RegExp /^a\/b(\/|$)/, 3]]
                 */


dest.splice(0, dest.length);


for(var p in map){


dest.push([
p,
map[p],
new RegExp("^" + escapeString(p) + "(\/|$)"),
p.length]);


}
dest.sort(function(lhs, rhs){ return rhs[3] - lhs[3]; });
return dest;
},


computeAliases = function(config, dest){
                /*
                    將鍵值對,轉化爲正則表達式形式的鍵值對
                 aliases:[
                 ["text", "dojo/text"]
                 ]
                 [[RegExp /^text$/, "dojo/text"]]
                 */
forEach(config, function(pair){
// take a fixed-up copy...
                    //如果是字符串,則構造一個正則表達式,如果是函數或者正則表達式,直接使用鍵。
dest.push([isString(pair[0]) ? new RegExp("^" + escapeString(pair[0]) + "$") : pair[0], pair[1]]);
});
},




fixupPackageInfo = function(packageInfo){
// calculate the precise (name, location, main, mappings) for a package 計算每個包的準確信息,包的信息包括,名字,位置, 主文件名稱, 映射情況。
                /*
                 packages: [
                 {location:"lib/dojo"},  //  標註1
                 { name: "dojo16", location: "lib/dojo16" },
                 { name: "dijit16", location: "lib/dijit16" },
                 { name: "dojox16", location: "lib/dojox16" },
                 { name: "dojo", location: "lib/dojo" },
                 { name: "dijit", location: "lib/dijit" },
                 { name: "dojox", location: "lib/dojox" },
                 {name:"doh", location:"lib/util/doh"},
                 { name: "myOldApp", location: "myOldApp", packageMap:{dojo:"dojo16"} },
                 { name: "my", location: "my" }
                 ]


                 */
var name = packageInfo.name;
if(!name){
// packageInfo must be a string that gives the name
                    // 如果沒有在包中指定name, 則整個包對像作爲包的名稱, 如標註1, name= {location:"lib/dojo"}
name = packageInfo;
packageInfo = {name:name};
}
packageInfo = mix({main:"main"}, packageInfo);  //mix(dest,src)


packageInfo.location = packageInfo.location ? packageInfo.location : name;  //如果沒有指定包的位置,剛location爲包的名稱。


// packageMap is deprecated in favor of AMD map, packageMap已經被棄用,被map配置替代
if(packageInfo.packageMap){
map[name] = packageInfo.packageMap;  //將packageMap值作爲 map對像中的一個值。
}


if(!packageInfo.main.indexOf("./")){
packageInfo.main = packageInfo.main.substring(2);  //如果main以"./"開頭,則去掉"./"
}


// now that we've got a fully-resolved package object, push it into the configuration
                // 現在我們獲得一個完全解析後的一個包對像, 即原始的包對像可能只有名稱和location,處理後的包對像,都有 name, location, main屬性,有的還有packageMap,packageMap不是很重要,在這裏只是將他的值添加到map裏。
packs[name] = packageInfo;


},


delayedModuleConfig
// module config cannot be consumed until the loader is completely initialized; therefore, all
// module config detected during booting is memorized and applied at the end of loader initialization
// TODO: this is a bit of a kludge; all config should be moved to end of loader initialization, but
// we'll delay this chore and do it with a final loader 1.x cleanup after the 2.x loader prototyping is complete
            = [],




config = function(config, booting, referenceModule){
for(var p in config){
if(p=="waitSeconds"){
req.waitms = (config[p] || 0) * 1000;  //設置加載一個模塊的超時時間, 單位爲秒.
}
if(p=="cacheBust"){
cacheBust = config[p] ? (isString(config[p]) ? config[p] : (new Date()).getTime() + "") : ""; //添加到URL後綴的字符串。以打斷瀏覽器緩存。
}
if(p=="baseUrl" || p=="combo"){
req[p] = config[p];  //設置baseUrl和 combo的值( dojo-combo-api是啓動一些老版本的API, 可以不用設置)
}
if(has("dojo-sync-loader") && p=="async"){
// falsy or "sync" => legacy sync loader
// "xd" => sync but loading xdomain tree and therefore loading asynchronously (not configurable, set automatically by the loader)
// "legacyAsync" => permanently in "xd" by choice
// "debugAtAllCosts" => trying to load everything via script injection (not implemented)
// otherwise, must be truthy => AMD
// legacyMode: sync | legacyAsync | xd | false


                        //配置加載器的工作模式,AMD, 同步,或者跨域異步
var mode = config[p];
req.legacyMode = legacyMode = (isString(mode) && /sync|legacyAsync/.test(mode) ? mode : (!mode ? sync : false));
req.async = !legacyMode;
}


if(config[p]!==hasCache){
                        //將defautlConfig, dojoConfig, dojoSniffConfig中的配置數據存入rawConfig.
                        // 將原生配置存入hasCache
// accumulate raw config info for client apps which can use this to pass their own config
req.rawConfig[p] = config[p];
p!="has" && has.add("config-"+p, config[p], 0, booting);


}
}




// make sure baseUrl exists  確保存在 baseUrl


if(!req.baseUrl){
req.baseUrl = "./";
}
// make sure baseUrl ends with a slash  確保baseUrl是以"/"結尾,即表示一個文件夾路徑
if(!/\/$/.test(req.baseUrl)){
req.baseUrl += "/";
}


// now do the special work for has, packages, packagePaths, paths, aliases, and cache
                // 開始具體的配置工作,如has, packages, packagePaths, paths, aliases, mapProgs, pathsMapProg.
                // 如果在userConfig,defaultConfig中指定了has測試, 則把相應的has測試添加到hasCache中。
for(p in config.has){
                    /*
                    var dojoConfig={
                         has: {
                         "dojo-firebug": true
                         }
                     }
                     */
has.add(p, config.has[p], 0, booting);
}
                //如果指定了packages, 對每個包進行處理,使每個包有固定的格式{name:**, location:**, main:**}, 如果有packageMap屬性,則把值添加到map配置裏. 詳細請查看fixupPackageInfo.


forEach(config.packages, fixupPackageInfo);


// packagePath已經被棄用,會在2.0移除.
                /*
                var dojoConfig = {
                     packagePaths:{
                     "path/to/some/place":[
                     "myPackage",
                     {
                     name:"yourPackage",
                     main:"base"
                     }
                     ]
                     }
                 }
                 相當於
                     packages:[{
                     name:"myPackage",
                     location:"path/to/some/place/myPackage"
                     },{
                     name:"yourPackage",
                     location:"path/to/some/place/youPackage"
                     }]
                 */


for(baseUrl in config.packagePaths){
forEach(config.packagePaths[baseUrl], function(packageInfo){
var location = baseUrl + "/" + packageInfo;
                        //如果只指定了一個字符串,如說明中的"myPackage"
if(isString(packageInfo)){
packageInfo = {name:packageInfo};
}
packageInfo.location = location;
fixupPackageInfo(packageInfo);
});
}


// notice that computeMapProg treats the dest as a reference; therefore, if/when that variable
// is published (see dojo-publish-privates), the published variable will always hold a valid value.


// this must come after all package processing since package processing may mutate map


                /*
                    注意computeMapProg 會將結果直接保存到dest, 如果變量被髮布(可以通過require直接獲得), 那麼發佈的變量會直接獲得這個有效值
                    這個步驟必須在所有的package處理完成後才能進行,因爲在包的處理過程中,會改變map的值。可以看上面的 packageMap.


                 map: {
                 myOldApp: {
                 dojo: "dojo16",
                 dijit: "dijit16",
                 dojox: "dojox16"
                 }
                 }
                 [["myOldApp", Object { dojo="dojo16", dijit="dijit16", dojox="dojox16"}, RegExp /^myOldApp(\/|$)/, 8]]
                 */
computeMapProg(mix(map, config.map), mapProgs);




                /*
                  對上面的 may-key 進一步處理,即 Object { dojo="dojo16", dijit="dijit16", dojox="dojox16"} 也轉化爲一個四維向量數組


                    [["myOldApp", Object { dojo="dojo16", dijit="dijit16", dojox="dojox16"}, RegExp /^myOldApp(\/|$)/, 8]]


                 */
forEach(mapProgs, function(item){
item[1] = computeMapProg(item[1], []);
if(item[0]=="*"){
mapProgs.star = item;
}
});


                /*
                    計算路徑映射
                     paths:{
                     "a/b": "myApp/ core/widget"
                     }
                     [["a/b", "myApp/ core/widget", RegExp /^a\/b(\/|$)/, 3]]
                 */
computeMapProg(mix(paths, config.paths), pathsMapProg);


// aliases
                /*
                     aliases:[
                        ["text", "dojo/text"]
                     ]
                     [[RegExp /^text$/, "dojo/text"]]


                     1. 如果是字符串,別名需要跟目標字符串完全匹配
                     2. 別名可以採用正則表達式的方式.
                 */
computeAliases(config.aliases, aliases);


                //給某個模塊傳遞配置。如果指定了booting爲1,剛在loader啓動時,不進行模塊配置。
                // 在實際開發中,好像都不會對模塊進行配置。可以忽略delayedModuleConfig 和在 dojoConfig={ config:{模塊名稱: 配置}}
if(booting){


delayedModuleConfig.push({config:config.config});
}else{
for(p in config.config){


var module = getModule(p, referenceModule);
module.config = mix(module.config || {}, config.config[p]);
}
}


// push in any new cache values
if(config.cache){
                    /*
                       不能理解 cache, pedingCaccheInsert, consumePendingCacheInsert.
                       沒有找到相關的使用例子.


                     */
consumePendingCacheInsert();
pendingCacheInsert = config.cache;
if(config.cache["*noref"]){
consumePendingCacheInsert();
}
}


signal("config", [config, req.rawConfig]);
};


//
// execute the various sniffs; userConfig can override and value
//
        //  可以獲得瀏覽器 user agent的信息
if(has("dojo-cdn") || has("dojo-sniff")){
// the sniff regex looks for a src attribute ending in dojo.js, optionally preceded with a path.
// match[3] returns the path to dojo.js (if any) without the trailing slash. This is used for the
// dojo location on CDN deployments and baseUrl when either/both of these are not provided
// explicitly in the config data; this is the 1.6- behavior.


var scripts = doc.getElementsByTagName("script"),
i = 0,
script, dojoDir, src, match;
while(i < scripts.length){
script = scripts[i++];
if((src = script.getAttribute("src")) && (match = src.match(/(((.*)\/)|^)dojo\.js(\W|$)/i))){
// sniff dojoDir and baseUrl
dojoDir = match[3] || "";
defaultConfig.baseUrl = defaultConfig.baseUrl || dojoDir;


// remember an insertPointSibling
insertPointSibling = script;
}


// sniff configuration on attribute in script element
if((src = (script.getAttribute("data-dojo-config") || script.getAttribute("djConfig")))){


dojoSniffConfig = req.eval("({ " + src + " })", "data-dojo-config");


// remember an insertPointSibling
insertPointSibling = script;
}


// sniff requirejs attribute
if(has("dojo-requirejs-api")){
if((src = script.getAttribute("data-main"))){
dojoSniffConfig.deps = dojoSniffConfig.deps || [src];
}
}
}
}
        // 禁止將 doh(單元測試)裏的配置, 混合到dojoSniffConfig ( dojo.js 中 data-dojo-config掃描到的配置)
if(has("dojo-test-sniff")){
// pass down doh.testConfig from parent as if it were a data-dojo-config
try{
if(window.parent != window && window.parent.require){
var doh = window.parent.require("doh");


doh && mix(dojoSniffConfig, doh.testConfig);
}
}catch(e){}
}


// configure the loader; let the user override defaults
req.rawConfig = {};
config(defaultConfig, 1);


// do this before setting userConfig/sniffConfig to allow userConfig/sniff overrides
if(has("dojo-cdn")){
packs.dojo.location = dojoDir;
if(dojoDir){
dojoDir += "/";
}
packs.dijit.location = dojoDir + "../dijit/";
packs.dojox.location = dojoDir + "../dojox/";
}


config(userConfig, 1);




config(dojoSniffConfig, 1);


}else{
// no config API, assume defaultConfig has everything the loader needs...for the entire lifetime of the application
paths = defaultConfig.paths;


pathsMapProg = defaultConfig.pathsMapProg;
packs = defaultConfig.packs;
aliases = defaultConfig.aliases;
mapProgs = defaultConfig.mapProgs;
modules = defaultConfig.modules;
cache = defaultConfig.cache;
cacheBust = defaultConfig.cacheBust;


// remember the default config for other processes (e.g., dojo/config)
req.rawConfig = defaultConfig;
}




if(has("dojo-combo-api")){
        // 已被棄用
req.combo = req.combo || {add:noop};
var comboPending = 0,
combosPending = [],
comboPendingTimer = null;
}




// build the loader machinery iaw configuration, including has feature tests  依據配置數據, 和特徵檢測,建立加載器的核功能
var injectDependencies = function(module){
// checkComplete!=0 holds the idle signal; we're not idle if we're injecting dependencies
            /*
                方法簽名:需要注入依賴鏈接的模塊對像
             */


guardCheckComplete(function(){
forEach(module.deps, injectModule);
if(has("dojo-combo-api") && comboPending && !comboPendingTimer){
comboPendingTimer = setTimeout(function() {
comboPending = 0;
comboPendingTimer = null;
req.combo.done(function(mids, url) {
var onLoadCallback= function(){
// defQ is a vector of module definitions 1-to-1, onto mids
runDefQ(0, mids);
checkComplete();
};
combosPending.push(mids);
injectingModule = mids;
req.injectUrl(url, onLoadCallback, mids);
injectingModule = 0;
}, req);
}, 0);
}
});
},


contextRequire = function(a1, a2, a3, referenceModule, contextRequire){
            /*
                a1: 一般爲config object, 配置對像, 可以通過require對loader進行配置.
                a2: 函數的依賴數組。
                a3: 加載完依賴後,要執行的回調函數
                referenceModule: 引用模塊,舉例來說,如dom.js裏面, define(["./sniff","./_base/window"], 當要加載dom模塊時,需要先加載sniff及 _base/window, 那麼在這時相對於sniff時,引用模塊就是dojo/dom, 而dom的引用模塊是全局函數require,那麼引用模塊爲0
                contextRequire: 加載器的上下文,如果是在全局中使用require來加載模塊,那麼contextRequire爲require函數。如果在定義模塊時,如定義一個"my/app", define(['dom','require'],function(dom,require){require(require('dojo/on')}),即使用局部加載器,那麼contextRequire爲"my/app"模塊的require函數, 上下文是通過createContext方法創建。
             */
var module, syntheticMid;
            /*
               1. a1 如果是一個模塊mid字符串,則返回相對應的模塊
               2. a1 不是字符串,也不是數組(依賴或者要加載的模塊列表。則爲配置對像或者函數。require({ aliases:[["text", "dojo/text"]]})
               3. a1 第一個參數爲數組,代表直接要加載的模塊。
             */
if(isString(a1)){


// 方法簽名是一個模塊模塊標識符,用於檢測模塊是否在Modules
                /*
                    getModule獲得一個模塊信息,第一個參數是模塊 MID, 第二個爲引用模塊(在模塊標識符解析時,如果模塊標識符爲相對路徑,那麼先獲得引用模塊的路徑,在計算相對路徑)。 第三個參數是指, mid是否存在Modules, 如果沒有存在,則立即反回false,而不會把空上模塊信息添加到Modules.
                 */
module = getModule(a1, referenceModule, true);
if(module && module.executed){
return module.result;
}
throw makeError("undefinedModule", a1);
}
if(!isArray(a1)){


// a1 是一個配置對像, 調用上一節講過的配置機制中的config方法。
config(a1, 0, referenceModule);


// 處理參數, 如果a2爲請求加載的模塊列表,a3爲回調函數,而a1經過上一步驟已經完成了配置,將 a2,a3的位置提前, 方便下一步的處理; (a2, a3) may be (dependencies, callback)
a1 = a2;
a2 = a3;
}
if(isArray(a1)){
// 方法簽名爲 (requestList [,callback])
                // 如果第一個參數爲數組(1. require(['dojo/dom',function(){}], 2. require({}, 'dojo/dom',function(){}), 第二種情況會經過上一步處理參數。
if(!a1.length){
a2 && a2(); //如果數組爲空數組,直接調用回調函數。
}else{
syntheticMid = "require*" + uid();  //每一個require都會有一個唯一標識.


// 解析數組中的模塊請求
for(var mid, deps = [], i = 0; i < a1.length;){
mid = a1[i++];
deps.push(getModule(mid, referenceModule));  //根據mid獲取一個模塊, 並添加到依賴列表,如果是在define中定義的依賴,則referenceModule爲定義的模塊。referenceModule主要是提供相對路徑
}


// construct a synthetic module to control execution of the requestList, and, optionally, callback
                    /*
                        構造一個人造模塊(不是真實定義的模塊),用於控制執行請求的模塊鏈表(deps), 及回調函數。
                        可以這樣理解,Dojo loader中都是以模塊爲操作對象,指定模塊的依賴,工廠函數,狀態(注入,加載,執行)。 如果僅僅通過 require(['dojo/dom'],function(dom){}), 而不構造一個人造模塊。那麼需要判斷分別爲require, define定義不同的加載依賴鏈接的方法。
                        makeModuleInfo:
                            方法簽名(pid, mid, pack, url)
                            return: {pid:pid, mid:mid, pack:pack, url:url, executed:0, def:0};  //executed: 模塊未執行, def: 模塊的工廠函數
                     */
module = mix(makeModuleInfo("", syntheticMid, 0, ""), {
injected: arrived,  //在模塊中添加角本注入狀態
deps: deps,  //將 require中的請求鏈接,做爲人造模塊的依賴鏈表
def: a2 || noop,  //如果有指定回調函數,則工廠函數爲回調函數,否則爲空函數
require: referenceModule ? referenceModule.require : req,  // 指定加載器。如有指定了引用模塊,那麼調用局部加載器。如果沒有指定,調用全局加載器
gc: 1 //garbage collect  垃級回收。在加載完依賴和執行完回調函數後,銷燬這個構造的模塊。
});


                    //將構造的這個模塊添加到模塊數組中.(modules 變量在上一節的配置機制裏定義)
modules[module.mid] = module;


// checkComplete!=0 holds the idle signal; we're not idle if we're injecting dependencies
                    /*
                        注入依賴列表。
                     */


injectDependencies(module);


// try to immediately execute
// if already traversing a factory tree, then strict causes circular dependency to abort the execution; maybe
// it's possible to execute this require later after the current traversal completes and avoid the circular dependency.
// ...but *always* insist on immediate in synch mode
                    /*
                        嘗試直接運行模塊的工廠函數。
                         strict 主要是在遍歷工廠函數時(checkComplete), 避免循環依賴.可以通過下面的例子來理解.


                         require(['my/app'],function(app){


                         })
                         ** my/app.js
                         define(['./app1'],function(app1){
                         require(['dojo/dom'])
                         console.log('a')
                         })
                         ** my/app1.js
                         define(['require'],function(require){
                         require(["./app"])


                         })


                        第一個require因爲沒有遍歷到工廠函數組成的樹對像。即還沒有調用checkComplete(), 所以checkCompleteGuard爲0
                        第二個 require, 即app1.js裏面的require. 因爲正在處於app1 模塊的樹對像,所以checkCompleteGuard 爲 1. 可以查看 checkComplete().
                        checkComplete 會在guardCheckComplete中,先將checkCompleteGuard++, 然後遍歷所有的exeQ中的模塊,並執行模塊execModule(運行工廠方法)。當執行到app1模塊時,發現require(["./app"]).
                        那麼就會調用require方法,構造一個人造模塊require*_1 此時checkCompleteGuard爲1, leagacyMode!=sync.所以 strict 爲1.


                        在執行require*_1時,exeModule要先執行my/app模塊的工廠函數


                     */


var strict = checkCompleteGuard && legacyMode!=sync;
guardCheckComplete(function(){


execModule(module, strict);
});
if(!module.executed){
// some deps weren't on board or circular dependency detected and strict; therefore, push into the execQ
execQ.push(module);
}
checkComplete();
}
}
return contextRequire;
},


createRequire = function(module){


if(!module){
return req;
}


var result = module.require;
if(!result){
result = function(a1, a2, a3){
return contextRequire(a1, a2, a3, module, result);
};
module.require = mix(result, req);
result.module = module;
result.toUrl = function(name){
return toUrl(name, module);
};
result.toAbsMid = function(mid){
return toAbsMid(mid, module);
};
if(has("dojo-undef-api")){
result.undef = function(mid){
req.undef(mid, module);
};
}
if(has("dojo-sync-loader")){
result.syncLoadNls = function(mid){
var nlsModuleInfo = getModuleInfo(mid, module),
nlsModule = modules[nlsModuleInfo.mid];
if(!nlsModule || !nlsModule.executed){
cached = cache[nlsModuleInfo.mid] || cache[urlKeyPrefix + nlsModuleInfo.url];
if(cached){
evalModuleText(cached);
nlsModule = modules[nlsModuleInfo.mid];
}
}
return nlsModule && nlsModule.executed && nlsModule.result;
};
}


}


return result;
},


execQ =
// The list of modules that need to be evaluated.
[],


defQ =
// 每次調用了define函數後,都會把這個define函數的參數,組成一個數組,添加到defQ中,格式爲:[mid, dependencs, facotry], 然後在runDefQ中會彈出數組的元素。
[],


waiting =
// The set of modules upon which the loader is waiting for definition to arrive
{},


setRequested = function(module){
module.injected = requested;
waiting[module.mid] = 1;
if(module.url){
waiting[module.url] = module.pack || 1;
}
startTimer();
},


setArrived = function(module){
module.injected = arrived;
delete waiting[module.mid];
if(module.url){
delete waiting[module.url];
}
if(isEmpty(waiting)){
clearTimer();
has("dojo-sync-loader") && legacyMode==xd && (legacyMode = sync);
}
},


execComplete = req.idle =
// says the loader has completed (or not) its work
function(){
return !defQ.length && isEmpty(waiting) && !execQ.length && !checkCompleteGuard;
},


runMapProg = function(targetMid, map){


// search for targetMid in map; return the map item if found; falsy otherwise
if(map){
for(var i = 0; i < map.length; i++){
if(map[i][2].test(targetMid)){
return map[i];
}
}
}
return 0;
},


compactPath = function(path){


            /*
                路徑處理函數,返回一個簡潔的路徑
                傳入的path可能爲 MID, 或者referenceModule+".." +MID;


             */


var result = [],
segment, lastSegment;
path = path.replace(/\\/g, '/').split('/'); // 將path中的反斜線,夫換爲 linux下的路徑形式
while(path.length){
                /*
                    1. path 中包含 ".." 時去除掉  緊靠 ".." 的路徑 如 dojo/dom/../sniff 會變爲dojo/sniff;
                    2. path 中包含 "." 時,直接跳過
                 */
segment = path.shift();
if(segment==".." && result.length && lastSegment!=".."){
result.pop();
lastSegment = result[result.length - 1];
}else if(segment!="."){
result.push(lastSegment= segment);
} // else ignore "."
}
return result.join("/");
},


makeModuleInfo = function(pid, mid, pack, url){
            /*
                方法簽名:
                    pid: String, 包的標識符
                    mid: String,模塊標識符
                    pack: Object, 包的詳細信息 { main="main", name="dojo", location="lib/dojo"}
                    url: String, 模塊解析後的地址


                return: Object
                   {pid:pid, mid:mid, pack:pack, url:url, executed:0, def:0};  //executed: 模塊未執行, def: 模塊的工廠函數
             */


if(has("dojo-sync-loader")){
var xd= req.isXdUrl(url);


return {pid:pid, mid:mid, pack:pack, url:url, executed:0, def:0, isXd:xd, isAmd:!!(xd || (packs[pid] && packs[pid].isAmd))};
}else{
return {pid:pid, mid:mid, pack:pack, url:url, executed:0, def:0};  //executed: 模塊未執行, def: 模塊的工廠函數
}
},


getModuleInfo_ = function(mid, referenceModule, packs, modules, baseUrl, mapProgs, pathsMapProg, aliases, alwaysCreate){


            // 模塊標識符解析函數, 將一個字符解析爲實際的路徑。
            // 通過傳遞參數來代替使用詞法變量(查看詞法作用域,例如,不是modules, 不是直接訪問loader的本地變量), 使得 getModuleInfo_ 可以被loader單獨使用(例如, builder)
            // 在這種情況下 (builder) 會非常有用。 getModuleInfo 不會返回已以存在的模塊引用, 而總是創建一個新的。
            // arguments are passed instead of using lexical variables so that this function my be used independent of the loader (e.g., the builder)
            // alwaysCreate is useful in this case so that getModuleInfo never returns references to real modules owned by the loader


            /*
             mid: String 模塊Id標識符
             referenceModule: Object 參考模塊
             packs:  Dojo Config裏面的packages包對象。
             modules: 已經存在的模塊,
             baseUrl: 根路徑,
             mapProgs: 配置變量map或者packageMap,
             pathsMapProg: 配置變量paths,
             aliases 配置變量aliases的對象
             alwaysCreate: 如果指定爲true, 一直創建新的文檔,默認爲undefined.
             */




           /*




            當指定了map對象時,
            packages:[
            {name:"dojo16", location:"lib/dojo16"},
            {name:"dijit16", location:"lib/dijit16"},
            {name:"dojox16", location:"lib/dojox16"},
            {name:"dojo",location:"lib/dojo"},
            {name:"myOldApp",location:"myOldApp"},
            {name:"my", location:"my"}


            ],
            map:{
            myOldApp:{
            dojo:"dojo16",
            dijit:"dijit16",
            dojox:"dojox16"
            }
            }
            輸出如下, console.log(mapProgs)
            [["myOldApp", [["dijit", "dijit16", RegExp /^dijit(\/|$)/, 5], ["dojox", "dojox16", RegExp /^dojox(\/|$)/, 5], ["dojo", "dojo16", RegExp /^dojo(\/|$)/, 4]], RegExp /^myOldApp(\/|$)/, 8]]








             */


var pid, pack, midInPackage, mapItem, url, result, isRelative, requestedMid;
requestedMid = mid;


isRelative = /^\./.test(mid);  // 以  "." 開頭的mid爲相對路徑,主要用於package.
if(/(^\/)|(\:)|(\.js$)/.test(mid) || (isRelative && !referenceModule)){
// absolute path or protocol of .js filetype, or relative path but no reference module and therefore relative to page
// whatever it is, it's not a module but just a URL of some sort
// note: pid===0 indicates the routine is returning an unmodified mid
                /*
                    絕對路徑,以 "/"開頭,或者是一種協議如http:,或者是以 .js 結尾的文件類型, 或者是 相對路徑( 以 ".")開頭,而沒有referenceModule.
                    這裏請求的可能是任意數量的javascript代碼,而不是一AMD規範的模塊。
                    注意: 如果 pid===0 標誌着程序會返回一個未修改的mid.


                */


return makeModuleInfo(0, mid, 0, mid);
}else{
// relative module ids are relative to the referenceModule; get rid of any dots  以"."開頭的對頭模塊標識是相對於 referenceModule(解釋參考術語名詞). 而忽略其它的點。






mid = compactPath(isRelative ? (referenceModule.mid + "/../" + mid) : mid);


if(/^\./.test(mid)){
                    // require['dojo/../.d/dom'] 不知道這個函數是不是要處理這種極端的問題
throw makeError("irrationalPath", mid);
}
// at this point, mid is an absolute mid


// map the mid
                /*


                   !**  待解決
                */


if(referenceModule){
mapItem = runMapProg(referenceModule.mid, mapProgs);


}


mapItem = mapItem || mapProgs.star;
mapItem = mapItem && runMapProg(mid, mapItem[1]);


if(mapItem){
mid = mapItem[1] + mid.substring(mapItem[3]);
}


match = mid.match(/^([^\/]+)(\/(.+))?$/);


pid = match ? match[1] : "";
if((pack = packs[pid])){
mid = pid + "/" + (midInPackage = (match[3] || pack.main));
}else{
                    pid = "";
}


// search aliases
var candidateLength = 0,
candidate = 0;
forEach(aliases, function(pair){
var match = mid.match(pair[0]);
if(match && match.length>candidateLength){
candidate = isFunction(pair[1]) ? mid.replace(pair[0], pair[1]) : pair[1];
}
});
if(candidate){
return getModuleInfo_(candidate, 0, packs, modules, baseUrl, mapProgs, pathsMapProg, aliases, alwaysCreate);
}


result = modules[mid];
if(result){
return alwaysCreate ? makeModuleInfo(result.pid, result.mid, result.pack, result.url) : modules[mid];
}
}
// get here iff the sought-after module does not yet exist; therefore, we need to compute the URL given the
// fully resolved (i.e., all relative indicators and package mapping resolved) module id
            /*
                此時 尋找加載的模塊還沒有存在,因些我們需要對過完全解析的模塊id(如,完成所有的相對標識及包映射) 來計算它的URL
            */
// note: pid!==0 indicates the routine is returning a url that has .js appended unmodified mid 注意: 當 pid!==0時, 表明程序會返回未變的mid, 並在mid後添加.js的URL


mapItem = runMapProg(mid, pathsMapProg); // 配置變量中的paths.
if(mapItem){


url = mapItem[1] + mid.substring(mapItem[3]);
}else if(pid){
url = pack.location + "/" + midInPackage;
}else if(has("config-tlmSiblingOfDojo")){
url = "../" + mid;
}else{
url = mid;
}
// if result is not absolute, add baseUrl
if(!(/(^\/)|(\:)/.test(url))){
url = baseUrl + url;
}
url += ".js";
return makeModuleInfo(pid, mid, pack, compactPath(url));
},


getModuleInfo = function(mid, referenceModule, fromPendingCache){
return getModuleInfo_(mid, referenceModule, packs, modules, req.baseUrl, fromPendingCache ? [] : mapProgs, fromPendingCache ? [] : pathsMapProg, fromPendingCache ? [] : aliases);
},


resolvePluginResourceId = function(plugin, prid, referenceModule){
return plugin.normalize ? plugin.normalize(prid, function(mid){return toAbsMid(mid, referenceModule);}) : toAbsMid(prid, referenceModule);
},


dynamicPluginUidGenerator = 0,


getModule = function(mid, referenceModule, immediate){


// compute and optionally construct (if necessary) the module implied by the mid with respect to referenceModule
var match, plugin, prid, result;
match = mid.match(/^(.+?)\!(.*)$/);


if(match){
// name was <plugin-module>!<plugin-resource-id>
plugin = getModule(match[1], referenceModule, immediate);


if(has("dojo-sync-loader") && legacyMode == sync && !plugin.executed){
injectModule(plugin);
if(plugin.injected===arrived && !plugin.executed){
guardCheckComplete(function(){
execModule(plugin);
});
}
if(plugin.executed){
promoteModuleToPlugin(plugin);
}else{
// we are in xdomain mode for some reason
execQ.unshift(plugin);
}
}






if(plugin.executed === executed && !plugin.load){
// executed the module not knowing it was a plugin
promoteModuleToPlugin(plugin);
}


// if the plugin has not been loaded, then can't resolve the prid and  must assume this plugin is dynamic until we find out otherwise
if(plugin.load){
prid = resolvePluginResourceId(plugin, match[2], referenceModule);
mid = (plugin.mid + "!" + (plugin.dynamic ? ++dynamicPluginUidGenerator + "!" : "") + prid);
}else{
prid = match[2];
mid = plugin.mid + "!" + (++dynamicPluginUidGenerator) + "!waitingForPlugin";
}
result = {plugin:plugin, mid:mid, req:createRequire(referenceModule), prid:prid};
}else{


result = getModuleInfo(mid, referenceModule);


}




return modules[result.mid] || (!immediate && (modules[result.mid] = result));
},


toAbsMid = req.toAbsMid = function(mid, referenceModule){
            //將給定的mid, 通過模塊標識符解析後,獲得一個絕的模塊標識符。
return getModuleInfo(mid, referenceModule).mid;
},


toUrl = req.toUrl = function(name, referenceModule){
            /*
                 獲得一個資源的路徑
                 name: string, 由模塊標識符作爲前綴,如在dojo包內有一張圖片,地址爲js/lib/dojo/main.jpg, 那麼指定的名稱可以爲 dojo/main.jpg.
                 referenceModule: 哪個模塊調用了req.toUrl, 如 my/app.js 裏面調用require.toUrl("./main.jpg"), 那麼 referenceModule爲 my/app;
            */


var moduleInfo = getModuleInfo(name+"/x", referenceModule),
url= moduleInfo.url;


return fixupUrl(moduleInfo.pid===0 ?
// if pid===0, then name had a protocol or absolute path; either way, toUrl is the identify function in such cases
name :
// "/x.js" since getModuleInfo automatically appends ".js" and we appended "/x" to make name look like a module id
url.substring(0, url.length-5)
);
},


nonModuleProps = {
injected: arrived,
executed: executed,
def: nonmodule,
result: nonmodule
},


makeCjs = function(mid){
return modules[mid] = mix({mid:mid}, nonModuleProps);
},


cjsRequireModule = makeCjs("require"),
cjsExportsModule = makeCjs("exports"),
cjsModuleModule = makeCjs("module"),


runFactory = function(module, args){
req.trace("loader-run-factory", [module.mid]);
var factory = module.def,
result;


has("dojo-sync-loader") && syncExecStack.unshift(module);
if(has("config-dojo-loader-catches")){
try{
result= isFunction(factory) ? factory.apply(null, args) : factory;
}catch(e){
signal(error, module.result = makeError("factoryThrew", [module, e]));
}
}else{
result= isFunction(factory) ? factory.apply(null, args) : factory;
}




module.result = result===undefined && module.cjs ? module.cjs.exports : result;
has("dojo-sync-loader") && syncExecStack.shift(module);
},


abortExec = {},


defOrder = 0,


promoteModuleToPlugin = function(pluginModule){
var plugin = pluginModule.result;
pluginModule.dynamic = plugin.dynamic;
pluginModule.normalize = plugin.normalize;
pluginModule.load = plugin.load;
return pluginModule;
},


resolvePluginLoadQ = function(plugin){
// plugins is a newly executed module that has a loadQ waiting to run


// step 1: traverse the loadQ and fixup the mid and prid; remember the map from original mid to new mid
// recall the original mid was created before the plugin was on board and therefore it was impossible to
// compute the final mid; accordingly, prid may or may not change, but the mid will definitely change
var map = {};
forEach(plugin.loadQ, function(pseudoPluginResource){
// manufacture and insert the real module in modules
var prid = resolvePluginResourceId(plugin, pseudoPluginResource.prid, pseudoPluginResource.req.module),
mid = plugin.dynamic ? pseudoPluginResource.mid.replace(/waitingForPlugin$/, prid) : (plugin.mid + "!" + prid),
pluginResource = mix(mix({}, pseudoPluginResource), {mid:mid, prid:prid, injected:0});
if(!modules[mid]){
// create a new (the real) plugin resource and inject it normally now that the plugin is on board
injectPlugin(modules[mid] = pluginResource);
} // else this was a duplicate request for the same (plugin, rid) for a nondynamic plugin


// pluginResource is really just a placeholder with the wrong mid (because we couldn't calculate it until the plugin was on board)
// mark is as arrived and delete it from modules; the real module was requested above
map[pseudoPluginResource.mid] = modules[mid];
setArrived(pseudoPluginResource);
delete modules[pseudoPluginResource.mid];
});
plugin.loadQ = 0;


// step2: replace all references to any placeholder modules with real modules
var substituteModules = function(module){
for(var replacement, deps = module.deps || [], i = 0; i<deps.length; i++){
replacement = map[deps[i].mid];
if(replacement){
deps[i] = replacement;
}
}
};
for(var p in modules){
substituteModules(modules[p]);
}
forEach(execQ, substituteModules);
},


finishExec = function(module){
req.trace("loader-finish-exec", [module.mid]);
module.executed = executed;
module.defOrder = defOrder++;
has("dojo-sync-loader") && forEach(module.provides, function(cb){ cb(); });
if(module.loadQ){
// the module was a plugin
promoteModuleToPlugin(module);
resolvePluginLoadQ(module);
}
// remove all occurrences of this module from the execQ
for(i = 0; i < execQ.length;){
if(execQ[i] === module){
execQ.splice(i, 1);
}else{
i++;
}
}
// delete references to synthetic modules
if (/^require\*/.test(module.mid)) {
delete modules[module.mid];
}
},


circleTrace = [],


execModule = function(module, strict){
// run the dependency vector, then run the factory for module
            // 運行依賴向量, 然後運行模塊的工廠函數
if(module.executed === executing){
req.trace("loader-circular-dependency", [circleTrace.concat(module.mid).join("->")]);
return (!module.def || strict) ? abortExec :  (module.cjs && module.cjs.exports);
}
// at this point the module is either not executed or fully executed




if(!module.executed){
                /*
                    依賴的模塊剛剛請求完成,即完成角本注入,module.injected == "requested" 還不是received. 所以 def == 0
                 */
if(!module.def){


return abortExec;  //反回 {}
}
var mid = module.mid,
deps = module.deps || [],
arg, argResult,
args = [],
i = 0;


if(has("dojo-trace-api")){
circleTrace.push(mid);
req.trace("loader-exec-module", ["exec", circleTrace.length, mid]);
}


// for circular dependencies, assume the first module encountered was executed OK
// modules that circularly depend on a module that has not run its factory will get
// the pre-made cjs.exports===module.result. They can take a reference to this object and/or
// add properties to it. When the module finally runs its factory, the factory can
// read/write/replace this object. Notice that so long as the object isn't replaced, any
// reference taken earlier while walking the deps list is still valid.
module.executed = executing;
while((arg = deps[i++])){


argResult = ((arg === cjsRequireModule) ? createRequire(module) :
((arg === cjsExportsModule) ? module.cjs.exports :
((arg === cjsModuleModule) ? module.cjs :
execModule(arg, strict))));


if(argResult === abortExec){


module.executed = 0;
req.trace("loader-exec-module", ["abort", mid]);
has("dojo-trace-api") && circleTrace.pop();
return abortExec;
}
args.push(argResult);
}
runFactory(module, args);
finishExec(module);
has("dojo-trace-api") && circleTrace.pop();
}
// at this point the module is guaranteed fully executed


return module.result;
},




checkCompleteGuard = 0,


guardCheckComplete = function(proc){
try{
checkCompleteGuard++;
proc();
}finally{


checkCompleteGuard--;
}
if(execComplete()){


signal("idle", []);
}
},


checkComplete = function(){
// keep going through the execQ as long as at least one factory is executed
// plugins, recursion, cached modules all make for many execution path possibilities
if(checkCompleteGuard){
return;
}


guardCheckComplete(function(){
checkDojoRequirePlugin();
for(var currentDefOrder, module, i = 0; i < execQ.length;){
currentDefOrder = defOrder;


module = execQ[i];
execModule(module);
if(currentDefOrder!=defOrder){
// defOrder was bumped one or more times indicating something was executed (note, this indicates
// the execQ was modified, maybe a lot (for example a later module causes an earlier module to execute)
checkDojoRequirePlugin();
i = 0;
}else{
// nothing happened; check the next module in the exec queue
i++;
}
}
});
};




if(has("dojo-undef-api")){
req.undef = function(moduleId, referenceModule){
// In order to reload a module, it must be undefined (this routine) and then re-requested. 爲了重新加載一個模塊, 它必須是未定義的(當前程序), 然後在重新加載。
// This is useful for testing frameworks (at least). 至少在測試框架時很有用。
var module = getModule(moduleId, referenceModule);
setArrived(module);
mix(module, {def:0, executed:0, injected:0, node:0});
};
}
    // 以下是支持加載跨域的模塊(支持加載不同域名下的模塊)。
if(has("dojo-inject-api")){




if(has("dojo-loader-eval-hint-url")===undefined){
has.add("dojo-loader-eval-hint-url", 1);
}


var fixupUrl= function(url){
url += ""; // make sure url is a Javascript string (some paths may be a Java string)
return url + (cacheBust ? ((/\?/.test(url) ? "&" : "?") + cacheBust) : "");
},


injectPlugin = function(
module
){
// injects the plugin module given by module; may have to inject the plugin itself
var plugin = module.plugin;


if(plugin.executed === executed && !plugin.load){
// executed the module not knowing it was a plugin
promoteModuleToPlugin(plugin);
}


var onLoad = function(def){
module.result = def;
setArrived(module);
finishExec(module);
checkComplete();
};


if(plugin.load){
plugin.load(module.prid, module.req, onLoad);
}else if(plugin.loadQ){
plugin.loadQ.push(module);
}else{
// the unshift instead of push is important: we don't want plugins to execute as
// dependencies of some other module because this may cause circles when the plugin
// loadQ is run; also, generally, we want plugins to run early since they may load
// several other modules and therefore can potentially unblock many modules
plugin.loadQ = [module];
execQ.unshift(plugin);
injectModule(plugin);
}
},


// for IE, injecting a module may result in a recursive execution if the module is in the cache


cached = 0,


injectingModule = 0,//表示當前正在注入的模塊對像, 即正在創建一個script元素,並把它添到insertPointSibling之前。添加完後把injectingModule賦值回 0.


injectingCachedModule = 0,


evalModuleText = function(text, module){


// see def() for the injectingCachedModule bracket; it simply causes a short, safe circuit
if(has("config-stripStrict")){
text = text.replace(/"use strict"/g, '');


}


injectingCachedModule = 1;
if(has("config-dojo-loader-catches")){
try{
if(text===cached){
cached.call(null);
}else{
req.eval(text, has("dojo-loader-eval-hint-url") ? module.url : module.mid);
}
}catch(e){
signal(error, makeError("evalModuleThrew", module));
}
}else{
if(text===cached){
cached.call(null);
}else{
req.eval(text, has("dojo-loader-eval-hint-url") ? module.url : module.mid);
}
}
injectingCachedModule = 0;
},


injectModule = function(module){


// Inject the module. In the browser environment, this means appending a script element into
// the document; in other environments, it means loading a file.
//
// If in synchronous mode, then get the module synchronously if it's not xdomainLoading.
                /*
                    注入模塊。 在瀏覽器環境下, 它會在文檔中添加一個script 元素, 在其它環境下,它會直接加載一個文件。
                    如果是同步模式下, 而沒有指定跨域加載,則會以同步的方法獲得一個模塊。


                 */
var mid = module.mid,
url = module.url;
if(module.executed || module.injected || waiting[mid] || (module.url && ((module.pack && waiting[module.url]===module.pack) || waiting[module.url]==1))){
return;
}


setRequested(module);  //設置inject的狀態爲requested, 並將模塊添加到waiting對像中。表明這個模塊正在等到下載(從服務器請求這個模塊文件)
                // dojo-combo-api 已棄用
if(has("dojo-combo-api")){
var viaCombo = 0;
if(module.plugin && module.plugin.isCombo){
// a combo plugin; therefore, must be handled by combo service
// the prid should have already been converted to a URL (if required by the plugin) during
// the normalize process; in any event, there is no way for the loader to know how to
// to the conversion; therefore the third argument is zero
req.combo.add(module.plugin.mid, module.prid, 0, req);
viaCombo = 1;
}else if(!module.plugin){
viaCombo = req.combo.add(0, module.mid, module.url, req);
}
if(viaCombo){
comboPending= 1;
return;
}
}
                // 如果模塊爲插件,比如dojo/text!my/app.html, getModule方法會返回一個包含 plugin屬性的對像,瞭解更多,請查看getModule方法。
if(module.plugin){
injectPlugin(module);
return;
} // else a normal module (not a plugin)


                // 模塊在加載完成後的回調函數。
var onLoadCallback = function(){
runDefQ(module); //運行模塊的工廠函數


if(module.injected !== arrived){
// the script that contained the module arrived and has been executed yet
// nothing was added to the defQ (so it wasn't an AMD module) and the module
// wasn't marked as arrived by dojo.provide (so it wasn't a v1.6- module);
// therefore, it must not have been a module; adjust state accordingly
if(has("dojo-enforceDefine")){
signal(error, makeError("noDefine", module));
return;
}
setArrived(module);


mix(module, nonModuleProps);
req.trace("loader-define-nonmodule", [module.url]);
}


if(has("dojo-sync-loader") && legacyMode){
// must call checkComplete even in for sync loader because we may be in xdomainLoading mode;
// but, if xd loading, then don't call checkComplete until out of the current sync traversal
// in order to preserve order of execution of the dojo.required modules
!syncExecStack.length && checkComplete();
}else{
checkComplete();
}
};
                /*
                    cache 對像是的配置機制中定義的對像,用於緩存已加載的資源。可以查看cache變量聲明時的註釋。
                 */
cached = cache[mid] || cache[urlKeyPrefix + module.url];
if(cached){


req.trace("loader-inject", ["cache", module.mid, url]);
evalModuleText(cached, module);
onLoadCallback();
return;
}


if(has("dojo-sync-loader") && legacyMode){


if(module.isXd){
// switch to async mode temporarily; if current legacyMode!=sync, then is must be one of {legacyAsync, xd, false}
legacyMode==sync && (legacyMode = xd);
// fall through and load via script injection
}else if(module.isAmd && legacyMode!=sync){
// fall through and load via script injection
}else{
// mode may be sync, xd/legacyAsync, or async; module may be AMD or legacy; but module is always located on the same domain
var xhrCallback = function(text){
if(legacyMode==sync){
// the top of syncExecStack gives the current synchronously executing module; the loader needs
// to know this if it has to switch to async loading in the middle of evaluating a legacy module
// this happens when a modules dojo.require's a module that must be loaded async because it's xdomain
// (using unshift/shift because there is no back() methods for Javascript arrays)
syncExecStack.unshift(module);
evalModuleText(text, module);
syncExecStack.shift();


// maybe the module was an AMD module
runDefQ(module);


// legacy modules never get to defineModule() => cjs and injected never set; also evaluation implies executing
if(!module.cjs){
setArrived(module);
finishExec(module);
}


if(module.finish){
// while synchronously evaluating this module, dojo.require was applied referencing a module
// that had to be loaded async; therefore, the loader stopped answering all dojo.require
// requests so they could be answered completely in the correct sequence; module.finish gives
// the list of dojo.requires that must be re-applied once all target modules are available;
// make a synthetic module to execute the dojo.require's in the correct order


// compute a guaranteed-unique mid for the synthetic finish module; remember the finish vector; remove it from the reference module
// TODO: can we just leave the module.finish...what's it hurting?
var finishMid = mid + "*finish",
finish = module.finish;
delete module.finish;


def(finishMid, ["dojo", ("dojo/require!" + finish.join(",")).replace(/\./g, "/")], function(dojo){
forEach(finish, function(mid){ dojo.require(mid); });
});
// unshift, not push, which causes the current traversal to be reattempted from the top
execQ.unshift(getModule(finishMid));
}
onLoadCallback();
}else{


text = transformToAmd(module, text);
if(text){
evalModuleText(text, module);
onLoadCallback();
}else{
// if transformToAmd returned falsy, then the module was already AMD and it can be script-injected
// do so to improve debugability(even though it means another download...which probably won't happen with a good browser cache)
injectingModule = module;
req.injectUrl(fixupUrl(url), onLoadCallback, module);
injectingModule = 0;
}
}
};


req.trace("loader-inject", ["xhr", module.mid, url, legacyMode!=sync]);
if(has("config-dojo-loader-catches")){
try{
req.getText(url, legacyMode!=sync, xhrCallback);
}catch(e){
signal(error, makeError("xhrInjectFailed", [module, e]));
}
}else{
req.getText(url, legacyMode!=sync, xhrCallback);
}
return;
}
} // else async mode or fell through in xdomain loading mode; either way, load by script injection
req.trace("loader-inject", ["script", module.mid, url]);
injectingModule = module;
req.injectUrl(fixupUrl(url), onLoadCallback, module);
injectingModule = 0;
},


defineModule = function(module, deps, def){


                /*
                    定義一個模塊
                        1. 跟def函數的區別:def函數即全局define函數。只是構造一個數組[mid, dependences, factory], 並添加到defQ中。
                        2. 跟getModule的區別, getModule是獲得一個對像 {pid="dojo", mid="dojo/dom", pack={...}, more...} 並把這個對像存入到 modules對像中, 而沒有依賴deps,工廠函數def。
                 */
req.trace("loader-define-module", [module.mid, deps]);


if(has("dojo-combo-api") && module.plugin && module.plugin.isCombo){
// the module is a plugin resource loaded by the combo service
// note: check for module.plugin should be enough since normal plugin resources should
// not follow this path; module.plugin.isCombo is future-proofing belt and suspenders
module.result = isFunction(def) ? def() : def;
setArrived(module);
finishExec(module);
return module;
}


var mid = module.mid;
if(module.injected === arrived){
signal(error, makeError("multipleDefine", module));
return module;
}
mix(module, {
deps: deps,
def: def,
cjs: {  //CommonJS
id: module.mid,
uri: module.url,
exports: (module.result = {}),
setExports: function(exports){
module.cjs.exports = exports;
},
config:function(){
return module.config;
}
}
});


// resolve deps with respect to this module
for(var i = 0; deps[i]; i++){
deps[i] = getModule(deps[i], module);
}
                //  可以忽略
if(has("dojo-sync-loader") && legacyMode && !waiting[mid]){


// the module showed up without being asked for; it was probably in a <script> element
injectDependencies(module);
execQ.push(module);
checkComplete();
}
                //設置模塊爲到達狀態
setArrived(module);


if(!isFunction(def) && !deps.length){
module.result = def;
finishExec(module);
}


return module;
},


runDefQ = function(referenceModule, mids){
// defQ is an array of [id, dependencies, factory]
// mids (if any) is a vector of mids given by a combo service
var definedModules = [],
module, args;
                /*
                    defQ: 是每次調用了define函數後,都會把這個define函數的參數,組成一個數組,添加到defQ中,格式爲:[mid, dependenceies, factory]
                    defQ.length 爲 1, 因爲每次運行define之後,會把[mid, dependencies, factory]添加到defQ中,length爲 1。 之後調用injectModule的 onLoadCallback中調用 runDefQ(模塊對像). defQ.shift()之後,defQ變爲空數組。
                 */


while(defQ.length){
args = defQ.shift();
mids && (args[0]= mids.shift());
// explicit define indicates possible multiple modules in a single file; delay injecting dependencies until defQ fully
// processed since modules earlier in the queue depend on already-arrived modules that are later in the queue
// TODO: what if no args[0] and no referenceModule
                    /*
                        如果在define 的第一個參數沒有指定爲字符串(模塊名), 那麼就使用 injectModule中傳入的模塊對像。
                     */
module = (args[0] && getModule(args[0])) || referenceModule;
definedModules.push([module, args[1], args[2]]);
}
                // 可以忽略, 因爲只在配置中指定了cache纔有效。
consumePendingCacheInsert(referenceModule);
forEach(definedModules, function(args){
                    /*
                        先定義這個模塊。defindModule會給 referenceModule對像添加  deps, def兩個屬性。並返回模塊對像。之後通過injectDependencies加載依賴
                     */
injectDependencies(defineModule.apply(null, args));
});
};
}


var timerId = 0,
clearTimer = noop,
startTimer = noop;
if(has("dojo-timeout-api")){
// Timer machinery that monitors how long the loader is waiting and signals an error when the timer runs out.
        // 配合 waitSeconds 配置,提交一個模塊請求後,觸發一個setTimeout, 如果在這個時間內沒有加載完模塊,並調用clearTimer, 則觸發一個加載錯誤。
clearTimer = function(){
timerId && clearTimeout(timerId);
timerId = 0;
};


startTimer = function(){
clearTimer();
if(req.waitms){
timerId = window.setTimeout(function(){
clearTimer();
signal(error, makeError("timeout", waiting));
}, req.waitms);
}
};
}


if (has("dom")) {
// Test for IE's different way of signaling when scripts finish loading.  Note that according to
// http://bugs.dojotoolkit.org/ticket/15096#comment:14, IE9 also needs to follow the
// IE specific code path even though it has an addEventListener() method.
// Unknown if special path needed on IE10+, which also has a document.attachEvent() method.
// Should evaluate to false for Opera and Windows 8 apps, even though they document.attachEvent()
//  is defined in both those environments.
has.add("ie-event-behavior", doc.attachEvent && typeof Windows === "undefined" &&
(typeof opera === "undefined" || opera.toString() != "[object Opera]"));
}


if(has("dom") && (has("dojo-inject-api") || has("dojo-dom-ready-api"))){
var domOn = function(node, eventName, ieEventName, handler){
// Add an event listener to a DOM node using the API appropriate for the current browser;
// return a function that will disconnect the listener.
if(!has("ie-event-behavior")){
node.addEventListener(eventName, handler, false);
return function(){
node.removeEventListener(eventName, handler, false);
};
}else{
node.attachEvent(ieEventName, handler);
return function(){
node.detachEvent(ieEventName, handler);
};
}
},
windowOnLoadListener = domOn(window, "load", "onload", function(){
req.pageLoaded = 1;
doc.readyState!="complete" && (doc.readyState = "complete");
windowOnLoadListener();
});


if(has("dojo-inject-api")){
// if the loader is on the page, there must be at least one script element
// getting its parent and then doing insertBefore solves the "Operation Aborted"
// error in IE from appending to a node that isn't properly closed; see
// dojo/tests/_base/loader/requirejs/simple-badbase.html for an example
// don't use scripts with type dojo/... since these may be removed; see #15809
// prefer to use the insertPoint computed during the config sniff in case a script is removed; see #16958
var scripts = doc.getElementsByTagName("script"),
i = 0,
script;
while(!insertPointSibling){
if(!/^dojo/.test((script = scripts[i++]) && script.type)){
insertPointSibling= script;
}
}


req.injectUrl = function(url, callback, owner){
                /*
                    插入script元素到insert-point之前。 script的src的屬性值爲 url;
                    加載完成後調用callback.
                    ower的值爲模塊對像。
                 */


var node = owner.node = doc.createElement("script"),
onLoad = function(e){
e = e || window.event;
var node = e.target || e.srcElement;
if(e.type === "load" || /complete|loaded/.test(node.readyState)){
loadDisconnector();  //註銷load監聽器
errorDisconnector(); //註銷error監聽器
callback && callback();
}
},
loadDisconnector = domOn(node, "load", "onreadystatechange", onLoad),  // 添加一個監聽器,並返回一個註銷監聽器的方法
errorDisconnector = domOn(node, "error", "onerror", function(e){
loadDisconnector();
errorDisconnector();
signal(error, makeError("scriptError", [url, e]));
});


node.type = "text/javascript";
node.charset = "utf-8";
node.src = url;
insertPointSibling.parentNode.insertBefore(node, insertPointSibling);
return node;
};
}
}


if(has("dojo-log-api")){
req.log = function(){
            // 等於console.log, 每一個被傳遞的值會被單獨到輸出到一行裏。


try{
for(var i = 0; i < arguments.length; i++){
console.log(arguments[i]);
}
}catch(e){}
};
}else{
req.log = noop;
}


if(has("dojo-trace-api")){
var trace = req.trace = function(
group, // the trace group to which this application belongs
args // the contents of the trace
){
///
// Tracing interface by group.
//
// Sends the contents of args to the console iff (req.trace.on && req.trace[group])


if(trace.on && trace.group[group]){
signal("trace", [group, args]);
for(var arg, dump = [], text= "trace:" + group + (args.length ? (":" + args[0]) : ""), i= 1; i<args.length;){
arg = args[i++];
if(isString(arg)){
text += ", " + arg;
}else{
dump.push(arg);
}
}
req.log(text);
dump.length && dump.push(".");
req.log.apply(req, dump);
}
};
mix(trace, {
on:1,
group:{},
set:function(group, value){
if(isString(group)){
trace.group[group]= value;
}else{
mix(trace.group, group);
}
}
});
trace.set(mix(mix(mix({}, defaultConfig.trace), userConfig.trace), dojoSniffConfig.trace));
on("config", function(config){


config.trace && trace.set(config.trace);
});
}else{
req.trace = noop;
}


var def = function(
mid,  //(commonjs.moduleId, optional)
dependencies, //(array of commonjs.moduleId, optional) list of modules to be loaded before running factory
factory  //(any)
){
///
// Advises the loader of a module factory. //Implements http://wiki.commonjs.org/wiki/Modules/AsynchronousDefinition.
///
//note
// CommonJS factory scan courtesy of http://requirejs.org


var arity = arguments.length,
defaultDeps = ["require", "exports", "module"],
// the predominate signature...
args = [0, mid, dependencies];
if(arity==1){
args = [0, (isFunction(mid) ? defaultDeps : []), mid];
}else if(arity==2 && isString(mid)){
args = [mid, (isFunction(dependencies) ? defaultDeps : []), dependencies];
}else if(arity==3){
args = [mid, dependencies, factory];
}


if(has("dojo-amd-factory-scan") && args[1]===defaultDeps){
args[2].toString()
.replace(/(\/\*([\s\S]*?)\*\/|\/\/(.*)$)/mg, "")
.replace(/require\(["']([\w\!\-_\.\/]+)["']\)/g, function(match, dep){
args[1].push(dep);  // 如果是AMD 工廠函數,默認加入 require, exports, module.
});
}


req.trace("loader-define", args.slice(0, 2));


var targetModule = args[0] && getModule(args[0]),
module;


if(targetModule && !waiting[targetModule.mid]){
// given a mid that hasn't been requested; therefore, defined through means other than injecting
// consequent to a require() or define() application; examples include defining modules on-the-fly
// due to some code path or including a module in a script element. In any case,
// there is no callback waiting to finish processing and nothing to trigger the defQ and the
// dependencies are never requested; therefore, do it here.
injectDependencies(defineModule(targetModule, args[1], args[2]));
}else if(!has("ie-event-behavior") || !has("host-browser") || injectingCachedModule){
// not IE path: anonymous module and therefore must have been injected; therefore, onLoad will fire immediately
// after script finishes being evaluated and the defQ can be run from that callback to detect the module id


defQ.push(args);
}else{
// IE path: possibly anonymous module and therefore injected; therefore, cannot depend on 1-to-1,
// in-order exec of onLoad with script eval (since it's IE) and must manually detect here
targetModule = targetModule || injectingModule;
if(!targetModule){
for(mid in waiting){
module = modules[mid];
if(module && module.node && module.node.readyState === 'interactive'){
targetModule = module;
break;
}
}
if(has("dojo-combo-api") && !targetModule){
for(var i = 0; i<combosPending.length; i++){
targetModule = combosPending[i];
if(targetModule.node && targetModule.node.readyState === 'interactive'){
break;
}
targetModule= 0;
}
}
}
if(has("dojo-combo-api") && isArray(targetModule)){
injectDependencies(defineModule(getModule(targetModule.shift()), args[1], args[2]));
if(!targetModule.length){
combosPending.splice(i, 1);
}
}else if(targetModule){
consumePendingCacheInsert(targetModule);
injectDependencies(defineModule(targetModule, args[1], args[2]));
}else{
signal(error, makeError("ieDefineFailed", args[0]));
}
checkComplete();
}
};
def.amd = {
vendor:"dojotoolkit.org"
};


if(has("dojo-requirejs-api")){
req.def = def;
}


// allow config to override default implementation of named functions; this is useful for
// non-browser environments, e.g., overriding injectUrl, getText, log, etc. in node.js, Rhino, etc.
// also useful for testing and monkey patching loader
mix(mix(req, defaultConfig.loaderPatch), userConfig.loaderPatch);


// now that req is fully initialized and won't change, we can hook it up to the error signal
on(error, function(arg){
try{
console.error(arg);
if(arg instanceof Error){
for(var p in arg){
console.log(p + ":", arg[p]);
}
console.log(".");
}
}catch(e){}
});


// always publish these
mix(req, {
uid:uid,
cache:cache,
packs:packs
});


    // 禁止loader向外提供更多信息, build時會設置爲0
if(has("dojo-publish-privates")){


mix(req, {
// these may be interesting to look at when debugging
paths:paths,
aliases:aliases,
modules:modules,
legacyMode:legacyMode,
execQ:execQ,
defQ:defQ,
waiting:waiting,


// these are used for testing
// TODO: move testing infrastructure to a different has feature
packs:packs,
mapProgs:mapProgs,
pathsMapProg:pathsMapProg,
listenerQueues:listenerQueues,


// these are used by the builder (at least)
computeMapProg:computeMapProg,
computeAliases:computeAliases,
runMapProg:runMapProg,
compactPath:compactPath,
getModuleInfo:getModuleInfo_
});
}


// the loader can be defined exactly once; look for global define which is the symbol AMD loaders are
// *required* to define (as opposed to require, which is optional)
if(global.define){
if(has("dojo-log-api")){
signal(error, makeError("defineAlreadyDefined", 0));
}
return;
}else{
global.define = def;
global.require = req;
if(has("host-node")){
require = req;
}
}


if(has("dojo-combo-api") && req.combo && req.combo.plugins){
var plugins = req.combo.plugins,
pluginName;
for(pluginName in plugins){
mix(mix(getModule(pluginName), plugins[pluginName]), {isCombo:1, executed:"executed", load:1});
}
}


if(has("dojo-config-api")){
forEach(delayedModuleConfig, function(c){ config(c); });
var bootDeps = dojoSniffConfig.deps ||userConfig.deps || defaultConfig.deps,
bootCallback = dojoSniffConfig.callback || userConfig.callback || defaultConfig.callback;
req.boot = (bootDeps || bootCallback) ? [bootDeps || [], bootCallback] : 0;
}
if(!has("dojo-built")){
!req.async && req(["dojo"]);
req.boot && req.apply(null, req.boot);
}
})
//>>excludeStart("replaceLoaderConfig", kwArgs.replaceLoaderConfig);
(
// userConfig
(function(){
// make sure we're looking at global dojoConfig etc.
return this.dojoConfig || this.djConfig || this.require || {};
})(),


// defaultConfig
{
// the default configuration for a browser; this will be modified by other environments
hasCache:{
"host-browser":1,
"dom":1,
"dojo-amd-factory-scan":1,
"dojo-loader":1,
"dojo-has-api":1,
"dojo-inject-api":1,
"dojo-timeout-api":1,
"dojo-trace-api":1,
"dojo-log-api":1,
"dojo-dom-ready-api":1,
"dojo-publish-privates":1,
"dojo-config-api":1,
"dojo-sniff":1,
"dojo-sync-loader":1,
"dojo-test-sniff":1,
"config-deferredInstrumentation":1,
"config-useDeferredInstrumentation":"report-unhandled-rejections",
"config-tlmSiblingOfDojo":1
},
packages:[{
// note: like v1.6-, this bootstrap computes baseUrl to be the dojo directory
name:'dojo',
location:'.'
},{
name:'tests',
location:'./tests'
},{
name:'dijit',
location:'../dijit'
},{
name:'build',
location:'../util/build'
},{
name:'doh',
location:'../util/doh'
},{
name:'dojox',
location:'../dojox'
},{
name:'demos',
location:'../demos'
}],
trace:{
// 在加載一個模塊時,需要跟蹤哪些信息, 先設置以下的項,在調用require.trace.on
"loader-inject":0,  // 輸出模塊加入到應用程序時的信息
"loader-define":0,
"loader-exec-module":0,
"loader-run-factory":0,
"loader-finish-exec":0,
"loader-define-module":0,
"loader-circular-dependency":0,
"loader-define-nonmodule":0
},
async:0,
waitSeconds:15
}
);
//>>excludeEnd("replaceLoaderConfig")
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章