easyloader源码

  1 /**
  2  * easyloader - jQuery EasyUI
  3  * 
  4  * Licensed under the GPL:
  5  *   http://www.gnu.org/licenses/gpl.txt
  6  *
  7  * Copyright 2010 stworthy [ stworthy@gmail.com ] 
  8  * 
  9  */
 10 (function(){
 11     //将所有的插件,和插件资源和依赖文件放进modules对象中。
 12     var modules = {
 13         
 14         draggable:{
 15             js:'jquery.draggable.js'
 16         },
 17         droppable:{
 18             js:'jquery.droppable.js'
 19         },
 20         resizable:{
 21             js:'jquery.resizable.js'
 22         },
 23         linkbutton:{
 24             js:'jquery.linkbutton.js',
 25             css:'linkbutton.css'
 26         },
 27         pagination:{
 28             js:'jquery.pagination.js',
 29             css:'pagination.css',
 30             dependencies:['linkbutton']
 31         },
 32         datagrid:{
 33             js:'jquery.datagrid.js',
 34             css:'datagrid.css',
 35             dependencies:['panel','resizable','linkbutton','pagination']
 36         },
 37         treegrid:{
 38             js:'jquery.treegrid.js',
 39             css:'tree.css',
 40             dependencies:['datagrid']
 41         },
 42         panel: {
 43             js:'jquery.panel.js',
 44             css:'panel.css'
 45         },
 46         window:{
 47             js:'jquery.window.js',
 48             css:'window.css',
 49             dependencies:['resizable','draggable','panel']
 50         },
 51         dialog:{
 52             js:'jquery.dialog.js',
 53             css:'dialog.css',
 54             dependencies:['linkbutton','window']
 55         },
 56         messager:{
 57             js:'jquery.messager.js',
 58             css:'messager.css',
 59             dependencies:['linkbutton','window']
 60         },
 61         layout:{
 62             js:'jquery.layout.js',
 63             css:'layout.css',
 64             dependencies:['resizable','panel']
 65         },
 66         form:{
 67             js:'jquery.form.js'
 68         },
 69         menu:{
 70             js:'jquery.menu.js',
 71             css:'menu.css'
 72         },
 73         tabs:{
 74             js:'jquery.tabs.js',
 75             css:'tabs.css',
 76             dependencies:['panel','linkbutton']
 77         },
 78         splitbutton:{
 79             js:'jquery.splitbutton.js',
 80             css:'splitbutton.css',
 81             dependencies:['linkbutton','menu']
 82         },
 83         menubutton:{
 84             js:'jquery.menubutton.js',
 85             css:'menubutton.css',
 86             dependencies:['linkbutton','menu']
 87         },
 88         accordion:{
 89             js:'jquery.accordion.js',
 90             css:'accordion.css',
 91             dependencies:['panel']
 92         },
 93         calendar:{
 94             js:'jquery.calendar.js',
 95             css:'calendar.css'
 96         },
 97         combo:{
 98             js:'jquery.combo.js',
 99             css:'combo.css',
100             dependencies:['panel','validatebox']
101         },
102         combobox:{
103             js:'jquery.combobox.js',
104             css:'combobox.css',
105             dependencies:['combo']
106         },
107         combotree:{
108             js:'jquery.combotree.js',
109             dependencies:['combo','tree']
110         },
111         combogrid:{
112             js:'jquery.combogrid.js',
113             dependencies:['combo','datagrid']
114         },
115         validatebox:{
116             js:'jquery.validatebox.js',
117             css:'validatebox.css'
118         },
119         numberbox:{
120             js:'jquery.numberbox.js',
121             dependencies:['validatebox']
122         },
123         spinner:{
124             js:'jquery.spinner.js',
125             css:'spinner.css',
126             dependencies:['validatebox']
127         },
128         numberspinner:{
129             js:'jquery.numberspinner.js',
130             dependencies:['spinner','numberbox']
131         },
132         timespinner:{
133             js:'jquery.timespinner.js',
134             dependencies:['spinner']
135         },
136         tree:{
137             js:'jquery.tree.js',
138             css:'tree.css',
139             dependencies:['draggable','droppable']
140         },
141         datebox:{
142             js:'jquery.datebox.js',
143             css:'datebox.css',
144             dependencies:['calendar','validatebox']
145         },
146         parser:{
147             js:'jquery.parser.js'
148         }
149     };
150     //将国际化文件放入一个locales对象中
151     var locales = {
152         'af':'easyui-lang-af.js',
153         'bg':'easyui-lang-bg.js',
154         'ca':'easyui-lang-ca.js',
155         'cs':'easyui-lang-cs.js',
156         'da':'easyui-lang-da.js',
157         'de':'easyui-lang-de.js',
158         'en':'easyui-lang-en.js',
159         'fr':'easyui-lang-fr.js',
160         'nl':'easyui-lang-nl.js',
161         'zh_CN':'easyui-lang-zh_CN.js',
162         'zh_TW':'easyui-lang-zh_TW.js'
163     };
164     
165     //定义一个局部变量,做循环遍历时候,存放状态
166     var queues = {};
167     
168     //加载js方法
169     function loadJs(url, callback){
170         //标志变量,js是否加载并执行
171         var done = false;
172         var script = document.createElement('script');//创建script dom
173         script.type = 'text/javascript';
174         script.language = 'javascript';
175         script.src = url;
176         script.onload = script.onreadystatechange = function(){ //onload是firefox 浏览器事件,onreadystatechange,是ie的,为了兼容,两个都写上,这样写会导致内存泄露
177             //script.readyState只是ie下有这个属性,如果这个值为undefined,说明是在firefox,就直接可以执行下面的代码了。反之为ie,需要对script.readyState
178             //状态具体值进行判别,loaded和complete状态表示,脚本加载了并执行了。
179             if (!done && (!script.readyState || script.readyState == 'loaded' || script.readyState == 'complete')){
180                 done = true;
181                 
182                 script.onload = script.onreadystatechange = null;//释放内存,还会泄露。
183                 if (callback){//加载后执行回调
184                     callback.call(script);
185                 }
186             }
187         }
188         //具体加载动作,上面的onload是注册事件,
189         document.getElementsByTagName("head")[0].appendChild(script);
190     }
191     //运行js ,看代码逻辑可知,运行js,只是在js执行后,将这个script删除而已,主要用来加载国际化文件
192     function runJs(url, callback){
193         loadJs(url, function(){
194             document.getElementsByTagName("head")[0].removeChild(this);
195             if (callback){
196                 callback();
197             }
198         });
199     }
200     
201     //加载css没什么好说的
202     function loadCss(url, callback){
203         var link = document.createElement('link');
204         link.rel = 'stylesheet';
205         link.type = 'text/css';
206         link.media = 'screen';
207         link.href = url;
208         document.getElementsByTagName('head')[0].appendChild(link);
209         if (callback){
210             callback.call(link);
211         }
212     }
213     //加载单一一个plugin,仔细研究module ,可以发现,pingin之间通过dependence,构造成了一颗依赖树,
214     //这个方法,就是加载具体树中的一个节点
215     function loadSingle(name, callback){
216         //把整个plugin的状态设置为loading
217         queues[name] = 'loading';
218         
219         var module = modules[name];
220         //把js状态设置为loading
221         var jsStatus = 'loading';
222         //如果允许css,并且plugin有css,则加载css,否则设置加载过了,其实是不加载
223         var cssStatus = (easyloader.css && module['css']) ? 'loading' : 'loaded';
224         //加载css,plugin 的css,如果是全称,就用全称,否则把简写换成全称,所以简写的css文件要放入到themes/type./文件下
225         if (easyloader.css && module['css']){
226             if (/^http/i.test(module['css'])){
227                 var url = module['css'];
228             } else {
229                 var url = easyloader.base + 'themes/' + easyloader.theme + '/' + module['css'];
230             }
231             loadCss(url, function(){
232                 cssStatus = 'loaded';
233                 //js, css加载完,才调用回调
234                 if (jsStatus == 'loaded' && cssStatus == 'loaded'){
235                     finish();
236                 }
237             });
238         }
239         //加载js,全称用全称,简写补全。
240         if (/^http/i.test(module['js'])){
241             var url = module['js'];
242         } else {
243             var url = easyloader.base + 'plugins/' + module['js'];
244         }
245         loadJs(url, function(){
246             jsStatus = 'loaded';
247             if (jsStatus == 'loaded' && cssStatus == 'loaded'){
248                 finish();
249             }
250         });
251         //加载完调用的方法,改plugin状态
252         function finish(){
253             queues[name] = 'loaded';
254             //调用正在加载的方法,其实已经加载完了,
255             easyloader.onProgress(name);
256             if (callback){
257                 callback();
258             }
259         }
260     }
261     //加载主模块入口,
262     function loadModule(name, callback){
263         //定义数组,最后是形成的是依赖插件列表,最独立的插件放在首位,name是末尾
264         var mm = [];
265         var doLoad = false;
266         //name有两种,一种是string ,一种是string array,这样一次可以加载多个plugin,都是调用add方法进行添加
267         if (typeof name == 'string'){
268             add(name);
269         } else {
270             for(var i=0; i<name.length; i++){
271                 add(name[i]);
272             }
273         }
274         
275         function add(name){
276             //如果modules中没有这个plugin那退出
277             if (!modules[name]) return;
278             //如果有,查看它是否依赖其他plugin
279             var d = modules[name]['dependencies'];
280             //如果依赖,就加载依赖的plugin.同时在加载依赖的plugin的依赖。注意循环中调用了add,是递归
281             if (d){
282                 for(var i=0; i<d.length; i++){
283                     add(d[i]);
284                 }
285             }
286             mm.push(name);
287         }
288         
289         function finish(){
290             if (callback){
291                 callback();
292             }
293             //调用onLoad,传递name 为参数
294             easyloader.onLoad(name);
295         }
296         //形成依赖树,不行还没有做实质性工作呢,那就是加载。打起精神来,最核心的代码就是以下的了
297         //超时用
298         var time = 0;
299         //定义一个加载方法,定义后直接调用
300         function loadMm(){
301             //如果mm有长度,长度!=0,加载plugin,为0,即加载完毕,开始加载国际化文件。
302             if (mm.length){
303                 var m = mm[0];    // the first module
304                 if (!queues[m]){//状态序列中没有这个plugin的信息,说明没有加载这个plug,调用laodSingle进行加载
305                     doLoad = true; 
306                     loadSingle(m, function(){
307                         mm.shift();//加载完成后,将这个元素从数组去除,在继续加载,直到数组
308                         loadMm();
309                     });
310                 } else if (queues[m] == 'loaded'){//如果这个plugin已经加载,就不用加载,以为mm中可能有重复项
311                     mm.shift();
312                     loadMm();
313                 } else {
314                     if (time < easyloader.timeout){//超时时候,10秒钟调用一次loadMn().注意arguments.callee代表函数本身
315                         time += 10;
316                         setTimeout(arguments.callee, 10); 
317                     }
318                 }
319             } else {
320                 if (easyloader.locale && doLoad == true && locales[easyloader.locale]){
321                     var url = easyloader.base + 'locale/' + locales[easyloader.locale];
322                     runJs(url, function(){
323                         finish();
324                     });
325                 } else {
326                     finish();
327                 }
328             }
329         }
330         
331         loadMm();
332     }
333 //    定义一个加载器,注意,是全局变量,没有var,
334     easyloader = {
335         modules:modules,
336         locales:locales,
337         
338         base:'.',//该属性是为了加载js,记录文件夹路径的
339         theme:'default', //默认主题
340         css:true,  
341         locale:null,
342         timeout:2000,//加载超时事件
343     //easyloader.load(),该模块加载的调用方法,先加载css,然后加载js
344         load: function(name, callback){
345             //如果加载是*.css文件,判断是不是以http开头,如果是,直接调用
346             if (/.css$/i.test(name)){
347                 if (/^http/i.test(name)){
348                     loadCss(name, callback);
349                 } else {
350                     //不是http的,加上base.文件夹路径
351                     loadCss(easyloader.base + name, callback);
352                 }
353             } 
354             //加载js文件
355             else if (/.js$/i.test(name)){
356                 if (/^http/i.test(name)){
357                     loadJs(name, callback);
358                 } else {
359                     loadJs(easyloader.base + name, callback);
360                 }
361             } else {
362                 //如果直接传递一个插件名,就去modole数组中加载。改方法是重点,也是easyui自带的plugin加载方式
363                 loadModule(name, callback);
364             }
365         },
366         
367         onProgress: function(name){},
368         onLoad: function(name){}
369     };
370 //以上一直在定义函数,和变量,此处为真正执行处
371 //获取页面的所有的script,主要是为了获取我们现在解释的easyloader.js文件路径,来设置base属性
372     var scripts = document.getElementsByTagName('script');
373     for(var i=0; i<scripts.length; i++){
374         var src = scripts[i].src;
375         if (!src) continue;
376         var m = src.match(/easyloader.js(W|$)/i);//判断文件是否含有easyloadr.js
377         if (m){
378             //如果有,base为easyloadr.js 的相同前缀
379             easyloader.base = src.substring(0, m.index);
380         }
381     }
382 //定义一个简化调用接口
383     window.using = easyloader.load;
384     
385     if (window.jQuery){
386         jQuery(function(){
387             //系统数据加载完后,加载parser.js插件,该插件是渲染界面的
388             easyloader.load('parser', function(){
389                 jQuery.parser.parse();//渲染方法
390             });
391         });
392     }
393     
394 })();
原文地址:https://www.cnblogs.com/dhl-2013/p/4562990.html