1 /**
  2  * Jx loader module
  3  * */
  4 Jx().$package(function(J){
  5     var depends={}, //key:module name, value:array of module name
  6         sheet2Module={},
  7         module2Sheet={};
  8         moduleLoaded={};
  9     var included,
 10         processing;
 11     /**
 12      * @namespace
 13      * @name loader
 14      */
 15     var $L = J.loader = J.loader || {};
 16     /**
 17      * @param {string} url
 18      * @param {function} callback
 19      */
 20     var loadScript = function(url,callback){
 21         var node=document.createElement('script');
 22         node.onload=node.onreadystatechange=function(){
 23             var rs=node.readyState;
 24             if('undefined'===typeof rs || 'loaded'===rs || 'complete'===rs){
 25                 try{
 26                     callback && callback(J);
 27                 }finally{
 28                     node.onload=node.onreadystatechange=null;
 29                     node=null;
 30                 }
 31             }
 32         };
 33         node.async=true;
 34         node.charset='utf-8';
 35         node.src=url;
 36         (document.head || document.documentElement).appendChild(node);
 37     };
 38     /**
 39      * @param {string} key 模块名称
 40      * @return {array} 依赖模块名称
 41      */
 42     var getDepend = function(key){
 43         if(depends.hasOwnProperty(key)){
 44             return depends[key];
 45         }
 46         return [];
 47     };
 48     /**
 49      * @method addDepend
 50      * @memberOf loader
 51      * @param {string} key 模块名称
 52      * @param {array} list 依赖模块名称
 53      */
 54     var addDepend = function(key,list){
 55         if('string'===typeof list){
 56             list=Array.prototype.slice.call(arguments,1);
 57         }
 58         if(!depends.hasOwnProperty(key)){
 59             depends[key]=list.slice(0);
 60         }else{
 61             depends[key]=depends[key].concat(list);
 62         }
 63         depends[key].sort();
 64     };
 65     /**
 66      * @method addDepends
 67      * @memberOf loader
 68      * @param {object} depends {key:list,...}
 69      */
 70     var addDepends = function(depends){
 71         for(var key in depends){
 72             if(depends.hasOwnProperty(key)){
 73                 addDepend(key,depends[key]);
 74             }
 75         }
 76     };
 77     /**
 78      * @method addSheet
 79      * @memberOf loader
 80      * @param {string} sheet 样式表名称
 81      * @param {string} module 对应的模块,没有对应模块填null
 82      */
 83     var addSheet = function(sheet,module){
 84         sheet2Module[sheet]=module;
 85         if(module){
 86             module2Sheet[module]=sheet;
 87         }
 88     };
 89     /**
 90      * @method addSheets
 91      * @memberOf loader
 92      * @param {object} sheets {sheet:module,...}
 93      */
 94     var addSheets = function(sheets){
 95         for(var sheet in sheets){
 96             if(sheets.hasOwnProperty(sheet)){
 97                 addSheet(sheet,sheets[sheet]);
 98             }
 99         }
100     };
101     /**
102      * @param {string} name 模块名称
103      * @return {boolean} 是否已加载
104      */
105     var isModuleLoaded = function(name){
106         if(moduleLoaded[name.toLowerCase()]){
107             return true;
108         }else{
109             if('function'===typeof $L.featureDetect){
110                 return moduleLoaded[name.toLowerCase()]=$L.featureDetect(name);
111             }
112             return false;
113         }
114     };
115     /**
116      * @param {string} name 模块名称
117      */
118     var markModuleLoaded = function(name){
119         moduleLoaded[name.toLowerCase()]=true;
120     };
121     /**
122      * @memberOf loader
123      * @method require
124      * @param {array} modules 请求的模块
125      * @param {function} callback
126      * @example
127      * Jx().$require(['dom'],function(J){
128      *  var $D=J.dom,
129      *      node=$D.node('div');
130      * });
131      */
132     var require = function(modules,callback){
133         if('string'===typeof(modules)){
134             modules=Array.prototype.slice.call(arguments,0,-1);
135             callback=arguments[arguments.length-1];
136         }
137         var list=getRequiredList(modules),
138             cssList=[],
139             sheet;
140         for(var i=0,len=list.length;i<len;i++){
141             if(sheet=module2Sheet[list[i]]){
142                 cssList.push(sheet+'.css');
143             }
144         }
145         if(list.length){
146             var url=$L.joinUrlByList(cssList.concat(list));
147             loadScript(url,callback);
148         }else{
149             try{
150                 callback && callback(J);
151             }finally{}
152         }
153     };
154     /**
155      * @param {array} modules
156      * @return {array} require list
157      */
158     var getRequiredList = function(modules){
159         modules.sort();
160         processing={};
161         included={};
162         var result=deepSearch(modules);
163         processing=included=null;
164         return result;
165     };
166     /**
167      * @param {array} modules
168      * @return {array} require list
169      */
170     var deepSearch = function(modules){
171         var module,
172             dependList,
173             resList,
174             result=[];
175         for(var i=0,l=modules.length;i<l;i++){
176             module=modules[i];
177             if(processing[module]){
178                 throw new Error('Dependency error in ['+module+'] module!'); //出现环,不合理的模块依赖关系
179             }
180             if(isModuleLoaded(module) || included[module]){
181                 
182             }else{
183                 var dependList=getDepend(module);
184                 if(dependList.length){
185                     processing[module]=true;
186                     resList=deepSearch(dependList);
187                     result=result.concat(resList);
188                     delete processing[module];
189                 }
190                 result.push(module);
191                 included[module]=true;
192             }
193         }
194         return result;
195     };
196     /**
197      * @param {array} require list
198      * @return {string} script url
199      */
200     var joinUrlByList = function(list){
201         throw new Error('[loader] module needs your own joinUrlByList method!');
202     };
203     $L.addDepend = addDepend;
204     $L.addDepends = addDepends;
205     $L.addSheet = addSheet;
206     $L.addSheets = addSheets;
207     $L.joinUrlByList = joinUrlByList;
208     $L.featureDetect = null;
209     $L.markModuleLoaded = markModuleLoaded;
210     J.$require = $L.require = require;
211 });