K_Reverter的网页开发记录

要么不做,要么就当作艺术品来做!

导航

google.loader代码分析

    好久没有发布关于技术的文章了,我最近可能会陆续发布一些,我现在先发布一个简单的,供大家参考一下,该内容是Google Ajax之中的loader,用来动态加载Google Maps API,Google Search API等API文件,不过说实在话,在我对代码进行捷思完成之后,我觉得相当的失望,因为这段代码虽然在Google API之中承担着比较重要的内容,可是实际上我认为设计的不太合理,起码我觉得这个文件从代码上来看好像不支持在onload之后执行,这就使loader这个类的作用大打折扣,用户不直接在页面上调用maps api可能最大的原因是希望能够根据用户的需求选择性的调用,也就是说在需要的时候再调用,也就是类似51ditu maps api的这个功能:(异步加载地图文件),不过我看loader之中并没有实现该内容。
    以上的想法都是个人意见,未必正确,希望大家指正,下面我贴出对该JavaScript文件的解释内容,供大家参考:

  1    //定义window.google命名空间
  2    if (!window['google'])
  3    {
  4        window['google'] = {};
  5    }

  6    //定义window.google.loader命名空间
  7    if (!window['google']['loader'])
  8    {
  9        window['google']['loader'] = {};
 10        google.loader.ServiceBase = 'http://www.google.com/uds';//默认文件加载路径
 11        google.loader.ApiKey = 'internal';//用户的key值,该值对所有的API产品都应该是一致的
 12        google.loader.KeyVerified = true;//该值在其他的API产品之中通过这个值来判断是否需要验证key的有效性
 13        google.loader.LoadFailure = false;//该值没有被调用,应该是提供给用户来调用的接口,判断loader是否失败
 14        google.loader.AdditionalParams = '';//请求文件的附加参数
 15        google.loader.OriginalAppPath = 'http://code.google.com/apis/ajax/documentation/example.html';//原始HTML文件地址
 16        (
 17            function()
 18            
 19                //判断用户使用的浏览器是否是指定的浏览器
 20                //参数a是浏览器名称,例如"msie"
 21                function isBroswer(a)
 22                {
 23                    if(a in isBroswerArray)
 24                    {
 25                        return isBroswerArray[a]
 26                    }

 27                    return isBroswerArray[a]=navigator.userAgent.toLowerCase().indexOf(a)!=-1
 28                }

 29                //isBroswerArray:本对象记录浏览器判断信息,可以认为是一个缓存
 30                var isBroswerArray={};
 31                function isIE()
 32                {
 33                    return isBroswer("msie")
 34                }

 35                function isSafari()
 36                {
 37                    return isBroswer("safari")||isBroswer("konqueror")
 38                }
;
 39                //继承,让b作为a的基类
 40                function inherit(a,b)
 41                {
 42                    var c=function(){};
 43                    c.prototype=b.prototype;
 44                    a.o=b.prototype;
 45                    a.prototype=new c
 46                }

 47                //预先定义好的API文件,如Maps API等
 48                var preDefinedApis={};
 49                //类文件加载完成后执行的指定函数句柄
 50                var callbacks={};
 51                //用户的key值,用来发送给服务器验证
 52                var userKey=null;
 53                //用来记录是否已经向服务器发送了key值如果发送过了,则不再发送
 54                var userKeySent=false;
 55                //使用该类来定义一个API文件,例如maps api的类文件
 56                function ApiFile(fileName)
 57                {
 58                    this.fileName=fileName
 59                }

 60                //获得API文件的路径URL,参数为版本号和版本选项
 61                ApiFile.prototype.getLoadUrl=function(version,config)
 62                {
 63                    var c="";
 64                    if(config!=undefined)
 65                    {
 66                        if(config["locale"]!=undefined)
 67                        {
 68                            c+="&hl="+encodeURIComponent(config["locale"])
 69                        }

 70                        if(config["nocss"]!=undefined)
 71                        {
 72                            c+="&output="+encodeURIComponent("nocss="+config["nocss"])
 73                        }

 74                        if(config["callback"]!=undefined)
 75                        {//注册在API文件加载完成之后执行的函数,只是我不太明白为什么这个字段要发送给服务端,莫非服务器返回的内容会自动添加相应的语句?
 76                            var d=setApiCallback(config["callback"],this.fileName);
 77                            c+="&callback="+encodeURIComponent(d)
 78                        }

 79                        if(config["other_params"]!=undefined)
 80                        {
 81                            c+="&"+config["other_params"]
 82                        }

 83                    }

 84                    if(userKey!=null&&!userKeySent)
 85                    {
 86                        c+="&key="+encodeURIComponent(userKey);
 87                        userKeySent=true
 88                    }

 89                    return google.loader.ServiceBase+"/?file="+this.fileName+"&v="+version+google.loader.AdditionalParams+c
 90                }
;
 91                ApiFile.prototype.canCallback=function(){return true};
 92                function CustomApiFile(a,b,c,d,e,f,config)
 93                {
 94                    this.fileName=a;
 95                    this.j=b;
 96                    this.i=c;
 97                    this.keyName=d;
 98                    this.versionName=e;
 99                    this._canCallback=f;
100                    this.fileConfig=config||{}
101                }

102                inherit(CustomApiFile,ApiFile);
103                //获得API文件的路径URL,参数为版本号和版本选项
104                CustomApiFile.prototype.getLoadUrl=function(version,config)
105                {
106                    var c="";
107                    if(this.keyName!=undefined)//如果提供了发送用户key的参数名称,则使用该参数名称发送key值
108                    {
109                        c+="&"+this.keyName+"="+encodeURIComponent(userKey?userKey:google.loader.ApiKey)
110                    }

111                    if(this.versionName!=undefined)//如果提供了发送版本号的参数名称,则使用该参数名称发送版本号
112                    {
113                        c+="&"+this.versionName+"="+encodeURIComponent(version)
114                    }

115                    if(config!=undefined&&this.fileConfig!=undefined)
116                    {
117                        for(var d in config)
118                        {
119                            if(this.fileConfig[":"+d]!=undefined)
120                            {
121                                var e;
122                                if(d=="callback")
123                                {
124                                    e=setApiCallback(config[d],this.fileName)
125                                }

126                                else
127                                {
128                                    e=config[d]
129                                }

130                                var f=this.fileConfig[":"+d];
131                                if(typeof f=="string")
132                                {
133                                    c+="&"+f+"="+encodeURIComponent(e)
134                                }

135                                else
136                                {
137                                    c+="&"+f(e)
138                                }

139                            }

140                            else if(d=="other_params")
141                            {
142                                c+="&"+config[d]
143                            }

144                        }

145                    }

146                    google[this.fileName]={};//设置命名空间
147                    if(!this.i&&c!="")
148                    {
149                        c="?"+c.substring(1)
150                    }

151                    recordStat("el",this.fileName);
152                    return this.j+c
153                }
;
154                CustomApiFile.prototype.canCallback=function()
155                {
156                    return this._canCallback
157                }
;
158                //提供的loader方法
159                function load(apiName,version,config)
160                {
161                    var api=preDefinedApis[":"+apiName];
162                    if(!api)
163                    {
164                        throw getError("Module: '"+apiName+"' not found!");
165                    }

166                    else
167                    {
168                        //判断是否需要在加载完成之后调用回调函数,如果需要,则只能使用DOM方式加载,而不能使用document.write方式
169                        //google目前不支持在document.write的形式上使用回调函数
170                        var needCallback=config&&config["callback"]!=null;
171                        if(needCallback&&!api.canCallback())
172                        {
173                            throw getError("Module: '"+apiName+"' must be loaded before DOM onLoad!");
174                        }

175                        else if(needCallback)
176                        {
177                            if(callbacks[apiName])//如果已经定义了该API的回调函数数组,则添加到该数组,这种情况下,该API已经在加载过程之中,不需要重新加载
178                            {
179                                callbacks[apiName].actualCallbacks.push(config["callback"])
180                            }

181                            else if(google[apiName])//如果该API已经存在(下载完成),则直接调用回调函数
182                            {
183                                setTimeout(config["callback"],0)
184                            }

185                            else
186                            {
187                                writeLoadTag("script",api.getLoadUrl(version,config),needCallback)//调用加载进程
188                            }

189                        }

190                        else
191                        {
192                            writeLoadTag("script",api.getLoadUrl(version,config),needCallback)//调用加载进程
193                        }

194                    }

195                }

196                //返回一个Error对象
197                function getError(str)
198                {
199                    var b=new Error(str);
200                    b.toString=function()
201                    {
202                        return this.message
203                    }
;
204                    return b
205                }

206                //设置页面加载完成之后执行的函数,b参数我没有见到调用
207                function setOnLoadCallback(a,b)
208                {
209                    if(b)
210                    {
211                        addOnloadListener(a)
212                    }

213                    else
214                    {
215                        addDomListener(window,"load",a)
216                    }

217                }

218                //添加一个DOM事件监视器
219                function addDomListener(node,eventName,handle)
220                {
221                    if(node.addEventListener)
222                    {
223                        node.addEventListener(eventName,handle,false)
224                    }

225                    else if(node.attachEvent)
226                    {
227                        node.attachEvent("on"+eventName,handle)
228                    }

229                    else
230                    {
231                        var d=node["on"+eventName];
232                        if(d!=null)
233                        {
234                            node["on"+eventName]=getRunAllListener([handle,d])
235                        }

236                        node["on"+eventName]=handle
237                    }

238                }

239                //返回运行数组之中每一个函数的句柄
240                function getRunAllListener(a)
241                {
242                    return function()
243                    {
244                        for(var b=0;b<a.length;b++)
245                        {
246                            a[b]()
247                        }

248                    }

249                }

250                //保存在页面加载完成时执行函数的的数组
251                var onloadListeners=[];
252                function addOnloadListener(a)
253                {
254                    if(onloadListeners.length==0)//如果没有设置全局的onload事件监视器,则添加
255                    {
256                        addDomListener(window,"load",runOnloadListeners);
257                        if(!isIE()&&!isSafari()&&isBroswer("mozilla")||window.opera)//如果是opera浏览器
258                        {
259                            window.addEventListener("DOMContentLoaded",runOnloadListeners,false)
260                        }

261                        else if(isIE())
262                        {
263                            setTimeout(checkIELoaded,10)//每隔0.01秒检查页面是否加载完成
264                        }

265                        else if(isSafari())
266                        {
267                            setTimeout(checkSafariLoaded,10)//每隔0.01秒检查页面是否加载完成
268                        }

269                    }

270                    onloadListeners.push(a)
271                }

272                //在IE之中检查页面是否加载完成,如果加载完成,则执行所有的onload句柄
273                function checkIELoaded()
274                {
275                    try
276                    {
277                        if(onloadListeners.length>0)
278                        {
279                            document.firstChild.doScroll("left");
280                            runOnloadListeners()
281                        }

282                    }

283                    catch(a)
284                    {
285                        setTimeout(checkIELoaded,10)
286                    }

287                }

288                //在Safari之中检查页面是否加载完成,如果加载完成,则执行所有的onload句柄
289                var safariLoadedState={loaded:true,complete:true};//指定加载完成后的readyState状态
290                function checkSafariLoaded()
291                {
292                    if(safariLoadedState[document.readyState])
293                    {
294                        runOnloadListeners()
295                    }

296                    else if(onloadListeners>0)
297                    {
298                        setTimeout(checkSafariLoaded,10)
299                    }

300                }

301                //运行所有的onload事件句柄并清空onloadListeners函数
302                function runOnloadListeners()
303                {
304                    for(var a=0;a<onloadListeners.length;a++)
305                    {
306                        onloadListeners[a]()
307                    }

308                    onloadListeners.length=0
309                }

310                //该函数没有在本API之中调用,大体意思应该是在URI之中解析出用户所使用的key
311                function setApiKeyLookupMap(a)
312                {
313                    var b=window.location.href;
314                    var c;
315                    var d=b.length;
316                    for(var e in a)
317                    {
318                        var f=b.indexOf(e);
319                        if(f!=-1&&f<d)
320                        {
321                            c=e;
322                            d=f
323                        }

324                    }

325                    userKey=c?a[c]:null
326                }

327                //通过DOM方式加载内容
328                function writeLoadTag(type,url,needCallback)
329                {
330                    if(needCallback)//通过DOM方式调用内容
331                    {
332                        var d;
333                        if(type=="script")
334                        {
335                            d=document.createElement("script");
336                            d.type="text/javascript";
337                            d.src=url
338                        }

339                        else if(type=="css")
340                        {
341                            d=document.createElement("link");
342                            d.type="text/css";
343                            d.href=url;
344                            d.rel="stylesheet"
345                        }

346                        document.getElementsByTagName("head")[0].appendChild(d)
347                    }

348                    else//通过document.write方式调用内容
349                    {
350                        if(type=="script")
351                        {
352                            document.write('<script src="'+url+'" type="text/javascript"><\/script>')
353                        }

354                        else if(type=="css")
355                        {
356                            document.write('<link href="'+url+'" type="text/css" rel="stylesheet"></link>')
357                        }

358                    }

359                }

360                //设置API加载完成后的回调函数
361                function setApiCallback(handle,apiName)
362                {
363                    callbacks[apiName]=function()
364                    {
365                        for(var c=0;c<callbacks[apiName].actualCallbacks.length;c++)
366                        {
367                            setTimeout(callbacks[apiName].actualCallbacks[c],0)//运行每一个回调函数
368                        }

369                        delete callbacks[apiName]
370                    }
;
371                    callbacks[b].actualCallbacks=[handle];
372                    return "google.loader.callbacks."+apiName
373                }

374                //设置类到命名空间
375                //a是该类的命名空间路径
376                //b是该类的function
377                function exportSymbol(path,cla)
378                {
379                    var c=path.split(/\./);
380                    var d=window;
381                    for(var e=0;e<c.length-1;e++)
382                    {
383                        if(!d[c[e]])
384                        {
385                            d[c[e]]={}
386                        }

387                        d=d[c[e]]
388                    }

389                    d[c[c.length-1]]=cla
390                }

391                exportSymbol("google.load",load);
392                exportSymbol("google.setOnLoadCallback",setOnLoadCallback);
393                exportSymbol("google.loader.writeLoadTag",writeLoadTag);
394                exportSymbol("google.loader.setApiKeyLookupMap",setApiKeyLookupMap);
395                exportSymbol("google.loader.callbacks",callbacks);
396                exportSymbol("google_exportSymbol",exportSymbol);
397                //log对象用来向服务器发送一些记录信息,将每次需要发送的消息保存起来,在达到5条或者页面卸载的时候通过访问服务器端img的形式发送
398                function log(){}
399                var unloadListenerAdded=log.unloadListenerAdded=false;//是否已经添加unload事件监视器
400                var maxRecordNumber=log.maxRecordNumber=5;
401                var records=log.records=[];
402                //添加unload事件监视器
403                var addUnloadListener=log.addUnloadListener=function()
404                {
405                    if(!unloadListenerAdded)//只有在没有添加的情况下才添加
406                    {
407                        addDomListener(window,"unload",sendRecords);
408                        unloadListenerAdded=(log.unloadListenerAdded=true)
409                    }

410                }
;
411                //将r2=name,或r2=name=value格式的记录保存到数组
412                var recordStat=log.record=function(name,value)
413                {
414                    addUnloadListener();
415                    var c=name+(value?"="+value:"");
416                    records.push("r"+records.length+"="+encodeURIComponent(c));
417                    if(records.length>maxRecordNumber)
418                    {
419                        setTimeout(sendRecords,0)
420                    }

421                }
;
422                //在页面被卸载的时候执行的操作
423                var sendRecords=log.sendRecords=function()
424                {
425                    if(records.length)//如果存在记录
426                    {
427                        //通过创建Image对象来向服务器发送记录信息
428                        var a=new Image;
429                        a.src=google.loader.ServiceBase+"/stats?"+records.join("&")+"&nocache="+Number(new Date);
430                        records.length=0//清空对象
431                    }

432                }
;
433                exportSymbol("google.loader.recordStat",recordStat);
434                preDefinedApis[":search"]=new ApiFile("search");
435                preDefinedApis[":feeds"]=new ApiFile("feeds");
436                preDefinedApis[":language"]=new ApiFile("language");
437                preDefinedApis[":maps"]=new CustomApiFile("maps","http://maps.google.com/maps?file=googleapi",true,"key","v",true,{":locale":"hl",":callback":function(a){return"callback="+encodeURIComponent(a)+"&async=2"}});
438                preDefinedApis[":gdata"]=new CustomApiFile("gdata","http://gd.google.com/gd/api?file=gdata.js",true,"key","v",false);
439                preDefinedApis[":sharing"]=new CustomApiFile("sharing","http://www.google.com/s2/sharing/js",false,"key","v",false,{":locale":"hl"});
440            }

441        )()
442    }

443

    该文件的原文件内容可以在这个地址查看到。

posted on 2007-11-27 16:28  K_Reverter  阅读(4144)  评论(5编辑  收藏  举报