1 /** 
  2  * JET (Javascript Extension Tools) 
  3  * Copyright (c) 2009, KDV.cn, All rights reserved.
  4  * Code licensed under the BSD License:
  5  * http://developer.kdv.cn/jet/license.txt
  6  *
  7  * @fileOverview Jx!
  8  * @version 1.0
  9  * @author  Kinvix(<a href="mailto:Kinvix@gmail.com">Kinvix@gmail.com</a>)
 10  * @description 
 11  * 
 12  */
 13 
 14 /** 
 15  * @description
 16  * Package: jet.http
 17  *
 18  * Need package:
 19  * jet.core.js
 20  * 
 21  */
 22 
 23 /**
 24  * 1.[Browser part]: http 包,含有ajax,comet,loadScript,loadCss封装
 25  */
 26 Jx().$package(function(J){
 27     var $=J.dom.id,
 28         $D=J.dom,
 29         $E=J.event,
 30         ajax,
 31         comet,
 32         load,
 33         loadCss,
 34         loadScript,
 35         formSend;
 36     
 37     // 兼容不同浏览器的 Adapter 适配层
 38     if(typeof window.XMLHttpRequest === "undefined"){
 39         /**
 40          * @ignore
 41          */
 42         window.XMLHttpRequest = function(){
 43             return new window.ActiveXObject(navigator.userAgent.indexOf("MSIE 5") >=0 ? "Microsoft.XMLHTTP" : "Msxml2.XMLHTTP");
 44         };
 45     }
 46     
 47     /**
 48      * http 名字空间
 49      * 
 50      * @namespace
 51      * @name http
 52      */
 53     J.http = J.http || {};
 54 
 55     /**
 56      * 这是Ajax对象名字空间的一个方法
 57      * 
 58      * @memberOf http
 59      * @method  ajax
 60      * 
 61      * @param {String} uri 要加载的数据的uri
 62      * @param {Object} option 配置对象,如:isAsync,data,arguments,onSuccess,onError,onComplete,onTimeout,timeout,contentType,type
 63      * @return {Object} ajax 返回一个ajax对象,可以abort掉
 64      */
 65     ajax = function(uri, option){
 66         var httpRequest,
 67             httpSuccess,
 68             timeout,
 69             isTimeout = false,
 70             isComplete = false;
 71         
 72         option = {
 73             method: (option.method || "GET").toUpperCase(),
 74             data: option.data || null,
 75             arguments: option.arguments || null,
 76 
 77             onSuccess: option.onSuccess || function(){},
 78             onError: option.onError || function(){},
 79             onComplete: option.onComplete || function(){},
 80             //尚未测试
 81             onTimeout: option.onTimeout || function(){},
 82 
 83             isAsync: option.isAsync || true,
 84             timeout: option.timeout || 30000,
 85             contentType: option.contentType,
 86             type: option.type || "xml"
 87         };
 88         if(option.data && typeof option.data === "object"){
 89             option.data = J.string.toQueryString(option.data);
 90         }
 91 
 92         uri = uri || "";
 93         timeout = option.timeout;
 94         
 95         httpRequest = new window.XMLHttpRequest();
 96 
 97         /**
 98          * @ignore
 99          */
100         httpSuccess = function(r){
101             try{
102                 return (!r.status && location.protocol == "file:")
103                     || (r.status>=200 && r.status<300)
104                     || (r.status==304)
105                     || (navigator.userAgent.indexOf("Safari")>-1 && typeof r.status=="undefined");
106             }catch(e){
107                 //J.out("错误:[" + e.name + "] "+e.message+", " + e.fileName+", 行号:"+e.lineNumber+"; stack:"+typeof e.stack, 2);
108             }
109             return false;
110         }
111         
112         /**
113          * @ignore
114          */
115         httpRequest.onreadystatechange=function (){
116             if(httpRequest.readyState==4){
117                 if(!isTimeout){
118                     var o={};
119                         o.responseText = httpRequest.responseText;
120                         o.responseXML = httpRequest.responseXML;
121                         o.data= option.data;
122                         o.status= httpRequest.status;
123                         o.uri=uri;
124                         o.arguments=option.arguments;
125                         if(option.type === 'json'){
126                             try{
127                                 o.responseJSON = J.json.parse(httpRequest.responseText);
128                             }catch(e){
129                             }
130                         }
131                     if(httpSuccess(httpRequest)){
132                         option.onSuccess(o);
133                     }else{
134                         option.onError(o);
135                     }
136                     option.onComplete(o);
137                 }
138                 isComplete = true;
139                 //删除对象,防止内存溢出
140                 httpRequest=null;
141             }
142         };
143         
144         if(option.method === "GET"){
145             if(option.data){
146                 uri += (uri.indexOf("?")>-1?"&":"?") + option.data;
147                 option.data = null;
148             }
149             httpRequest.open("GET", uri, option.isAsync);
150             httpRequest.setRequestHeader("Content-Type",option.contentType || "text/plain;charset=UTF-8");
151             httpRequest.send();
152         }else if(option.method === "POST"){
153             httpRequest.open("POST", uri, option.isAsync);
154             httpRequest.setRequestHeader("Content-Type",option.contentType || "application/x-www-form-urlencoded;charset=UTF-8");
155             httpRequest.send(option.data);
156         }else{
157             httpRequest.open(option.method, uri, option.isAsync);
158             httpRequest.send();
159         }
160         
161         window.setTimeout(function(){
162             var o;
163             if(!isComplete){
164                 isTimeout = true;
165                 o={};
166                 o.uri=uri;
167                 o.arguments=option.arguments;
168                 option.onTimeout(o);
169                 option.onComplete(o);
170             }
171         }, timeout);    
172         
173         return httpRequest;
174     };
175 
176     
177     // /**
178     //  * comet方法
179     //  * 
180     //  * @memberOf http
181     //  * @method  comet
182     //  * @param {String} uri uri地址
183     //  * @param {Object} option 配置对象
184     //  * @return {Object} 返回一个comet dom对象
185     //  */
186     // comet = function(uri, option){
187 
188     //     uri = uri || "";
189     //     option = {
190     //         method : option.method || "GET",
191     //         data : option.data || null,
192     //         arguments : option.arguments || null,
193     //         callback : option.callback || function(){},
194     //         onLoad : option.onLoad || function(){},
195 
196     //         contentType: option.contentType ? option.contentType : "utf-8"
197     //     };
198 
199     //     var connection;
200     //     if(J.browser.ie){
201     //         var htmlfile = new ActiveXObject("htmlfile");
202     //         htmlfile.open();
203     //         htmlfile.close();
204     //         var iframediv = htmlfile.createElement("div");
205     //         htmlfile.appendChild(iframediv);
206     //         htmlfile.parentWindow._parent = self;
207     //         iframediv.innerHTML = '<iframe id="_cometIframe" src="'+uri+'?callback=window.parent._parent.'+option.callback+'"></iframe>';
208             
209     //         connection = htmlfile.getElementById("_cometIframe");
210         
211     //     }
212     //     else{
213     //         connection = $D.node("iframe");
214     //         connection.setAttribute("id", "_cometIframe");
215     //         connection.setAttribute("src", uri+'?callback=window.parent._parent.'+option.callback);
216     //         connection.style.position = "absolute";
217     //         connection.style.visibility = "hidden";
218     //         connection.style.left = connection.style.top = "-999px";
219     //         connection.style.width = connection.style.height = "1px";
220     //         document.body.appendChild(connection);
221     //         self._parent = self;
222     //     };
223 
224     //     $E.on(connection,"load", option.onLoad);
225 
226     //     return connection;
227         
228     // };
229     
230 
231     
232     
233     
234     
235     /**
236      * 这是load方法
237      * 
238      * @memberOf http
239      * @method load
240      * 
241      * @param {String} type 一个配置对象
242      * @param {Object} option 一个配置对象
243      * @return {Object} ajax 返回一个ajax对象
244      */
245     load = function(type, uri, option){
246         var node,
247             linkNode,
248             scriptNode,
249             id,
250             head = document.getElementsByTagName("head") ? document.getElementsByTagName("head")[0] : document.documentElement,
251             timer,
252             isTimeout = false,
253             isComplete = false,
254             option = option || {},
255             isDefer = option.isDefer || false,
256             query = option.query || null,
257             arguments = option.arguments || null,
258             
259             onSuccess = option.onSuccess || function(){},
260             onError = option.onError || function(){},
261             onComplete = option.onComplete || function(){},
262             purge,
263             //尚未测试
264             onTimeout = option.onTimeout || function(){},
265 
266 //          timeout = option.timeout ? option.timeout : 10000,
267             timeout = option.timeout, //az 2011-2-21 修改为默认不需要超时
268             charset = option.charset ? option.charset : "utf-8",
269             win = option.win || window,
270             o,
271             
272             getId;
273 
274         uri = uri || "";
275         if(query !== null){
276             uri = uri + "?" + query;
277         }
278         /**
279          * @ignore
280          */
281         getId = function(){
282             return load.Id++;
283         };
284         id = getId();
285         
286         /**
287          * @ignore
288          */
289         purge = function(id){
290             var el=$("jet_load_" + id)
291             if(el){
292                 head.removeChild(el);
293             }
294         };
295 
296         /**
297          * Generates a link node
298          * @method _linkNode
299          * @param uri {string} the uri for the css file
300          * @param win {Window} optional window to create the node in
301          * @return {HTMLElement} the generated node
302          * @private
303          */
304         linkNode = function(uri, win, charset) {
305             var c = charset || "utf-8";
306             return $D.node("link", {
307                         "id":       "jet_load_" + id,
308                         "type":     "text/css",
309                         "charset":  c,
310                         "rel":      "stylesheet",
311                         "href":     uri
312                     }, win);
313         };
314         
315         /**
316          * Generates a script node
317          * @method _scriptNode
318          * @param uri {string} the uri for the script file
319          * @param win {Window} optional window to create the node in
320          * @return {HTMLElement} the generated node
321          * @private
322          */
323         scriptNode = function(uri, win, charset, isDefer) {
324             var c = charset || "utf-8";
325             var node = $D.node("script", {
326                         "id":       "jet_load_" + id,
327                         "type":     "text/javascript",
328                         "charset":  c,
329                         "src":      uri
330                     }, win);
331             if(isDefer){
332                 node.setAttribute("defer", "defer");
333             }
334             
335             return node;
336         };
337         
338         
339         
340         if(type === "script"){
341             node = option.node || scriptNode(uri, win, charset, isDefer);
342         }else if(type === "css"){
343             node = option.node || linkNode(uri, win, charset);
344         }
345         
346 
347         if(J.browser.engine.trident && parseInt(J.browser.ie)<9){
348             /**
349              * @ignore
350              */
351             node.onreadystatechange = function() {
352                 var rs = this.readyState;
353                 if (rs === "loaded" || rs === "complete") {
354                     /**
355                      * @ignore
356                      */
357                     node.onreadystatechange = null;
358 
359                     if(!isTimeout){
360                         isComplete = true;
361                         window.clearTimeout(timer);
362                         timer = null;
363                         o={};
364                         o.id = id;
365                         o.uri = uri;
366                         o.arguments = arguments;
367                         onSuccess(o);
368                         onComplete(o);
369                         //if(type === "script"){
370                             //purge(id);
371                         //}
372                     }
373                 }
374             };
375 
376         // webkit prior to 3.x is no longer supported
377         }else if(J.browser.engine.webkit){
378             // Safari 3.x supports the load event for script nodes (DOM2)
379             $E.on(node, "load", function(){
380                 var o;
381                 if(!isTimeout){
382                     isComplete = true;
383                     window.clearTimeout(timer);
384                     timer = null;
385                     o={};
386                     o.id = id;
387                     o.uri = uri;
388                     o.arguments = arguments;
389                     onSuccess(o);
390                     onComplete(o);
391                     if(type === "script"){
392                         purge(id);
393                     }
394                 }
395             });
396 
397 
398         // FireFox and Opera support onload (but not DOM2 in FF) handlers for
399         // script nodes.  Opera, but not FF, supports the onload event for link
400         // nodes.
401         }else{ 
402             /**
403              * @ignore
404              */
405             node.onload = function(){
406                 var o;
407                 //J.out("else:"+J.browser.engine.name);
408                 if(!isTimeout){
409                     isComplete = true;
410                     window.clearTimeout(timer);
411                     timer = null;
412                     o={};
413                     o.id = id;
414                     o.uri = uri;
415                     o.arguments = option.arguments;
416                     onSuccess(o);
417                     onComplete(o);
418                     
419                     if(type === "script"){
420                         purge(id);
421                     }
422                 }
423             };
424             /**
425              * @ignore
426              */
427             node.onerror = function(e){
428                 var o;
429                 //J.out("else:"+J.browser.engine.name);
430                 if(!isTimeout){
431                     isComplete = true;
432                     window.clearTimeout(timer);
433                     timer = null;
434                     o={};
435                     o.id = id;
436                     o.uri = uri;
437                     o.arguments = arguments;
438                     o.error = e;
439                     onError(o);
440                     onComplete(o);
441                     //if(type === "script"){
442                         purge(id);
443                     //}
444                 }
445             };
446         }
447         
448         
449         if(option.node){
450             if(type === "script"){
451                 node.src = uri;
452             }else if(type === "css"){
453                 node.href = uri;
454             }
455         }else{
456             head.appendChild(node);
457         }
458        
459         
460         if(type === "script"){
461             if(timeout){
462                 timer = window.setTimeout(function(){
463                     var o;
464                     if(!isComplete){
465                         isTimeout = true;
466                         o = {};
467                         o.uri = uri;
468                         o.arguments = arguments;
469                         onTimeout(o);
470                         onComplete(o);
471                         purge(id);
472                     }
473                 }, timeout);
474             }
475         }
476         /**
477          * @ignore
478          */
479         var func = function(node){
480             this._node = node;
481             this._head = head;
482         };
483         func.prototype={
484             /**
485              * @ignore
486              */
487             abort:function(){
488                 this._node.src="";
489                 this._head.removeChild(this._node);
490                 delete this._node;
491             }
492             
493         };
494         return new func(node);
495     };
496     load.Id=0;
497     
498     /**
499      * 加载CSS
500      * 
501      * @memberOf http
502      * @method loadCss
503      * 
504      * @param {String} uri 要加载的css的uri
505      * @param {Object} option 配置对象,如:isDefer,query,arguments,onSuccess,onError,onComplete,onTimeout,timeout,charset
506      * @return {Object} ajax 返回一个ajax对象
507      */
508     loadCss = function(uri, option){
509         return load("css", uri, option);
510     };
511     
512     /**
513      * 加载Javascript
514      * 
515      * @memberOf http
516      * @method loadScript
517      * 
518      * @param {String} uri 要加载的js脚本的uri
519      * @param {Object} option 配置对象,如:isDefer,query,arguments,onSuccess,onError,onComplete,onTimeout,timeout,charset
520      * @return {Element} 返回控制对象,可以abort掉
521      */
522     loadScript = function(uri, option){
523         return load("script", uri, option);
524     };
525     
526     
527     
528     /**
529      * TODO 这里的form send需要改造,建立一个iframe池,来处理并发问题
530      */
531     /**
532      * @description formSend的iframe池,目前定为长度为2
533      * @type {Object}
534      */
535     var divEl;
536     var formSendIframePool = {
537         /**
538          * @example
539          * [[divElement,formElement,iframeElement],[divElement,formElement,iframeElement],,,]
540          */
541         _iframes: [],
542         _tick: 0,
543         /**
544          * 顺序返回一个iframe
545          */
546         _select: function() {
547             this._tick++;
548             return  this._iframes[(this._tick-1)%this._len];
549         },
550         /**
551          * @description 初始化
552          * @argument {Number} len the number of iframes in poll
553          */
554         init: function(len) {
555             if(this._isInit==true) {
556                 return;
557             }
558             this._len= len;
559             var bodyEl=document.body;
560             for(var i=0;i<len;i++) {
561                 divEl = $D.node("div",{
562                     "class": "RPCService_hDiv"
563                 });
564                 $D.hide(divEl);
565                 divEl.innerHTML = '<iframe id="RPCService_hIframe_'+i+'" name="RPCService_hIframe_'+i+'" src="'+alloy.CONST.MAIN_URL+'domain.html"></iframe>';  
566                 bodyEl.appendChild(divEl);
567                 //not have a iframe yet
568                 this._iframes[i]=[divEl,null,"RPCService_hIframe_"+i];
569             }
570             this._isInit= true;
571         },
572         /**
573          * @description 使用的入口
574          * @argument {Element} iframeEL
575          */
576         take: function(formEl) {
577             var doms= this._select();
578             //if iframe exist
579             if(doms[1]) {
580                 doms[0].removeChild(doms[1]);
581             }
582             formEl.setAttribute("target",doms[2]);
583             doms[1]= formEl;
584             doms[0].appendChild(formEl);
585         }
586     };
587     /**
588      * 使用form请求数据
589      * @memberOf http
590      * @param {String} url 
591      * @param {Object} option 请求参数
592      */
593     formSend = function(url, option){
594         formSendIframePool.init(2);
595         var opt = {
596             method: option.method || "GET",
597             enctype: option.enctype || "",  //"multipart/form-data",
598             data: option.data || {},  //表单项 
599             //尚未测试
600             onSuccess: option.onSuccess || function(){},   //iframe 载入成功回调,区别与获取数据成功
601             onError: option.onError || function(){},      //iframe 载入失败回调
602             onComplete: option.onComplete || function(){},
603             
604             onTimeout: option.onTimeout || function(){},
605             timeout: option.timeout ? option.timeout : 10000
606         };
607         var formEl = $D.node("form",{
608                     "class": "RPCService_form",
609                     method: opt.method,
610                     action: url+"?t=" + (new Date().getTime()),
611                     enctype: opt.enctype
612                 })
613          
614         if(Object.prototype.toString.call(opt.data).indexOf("String")>-1) {
615             var inputEl=$D.node("input");
616             inputEl.type="text";
617             inputEl.name=opt.data;
618             formEl.appendChild(inputEl);            
619         }else {
620             for(var p in opt.data){
621                 var inputEl=$D.node("input");
622                 inputEl.type="text";
623                 inputEl.name=p;
624                 inputEl.setAttribute("value",opt.data[p]);
625                 formEl.appendChild(inputEl);
626             }
627         }
628         formSendIframePool.take(formEl);            
629         formEl.submit();
630         
631         /* 
632           iframe事件未处理,当载入成功会自动调用回调函数     
633           var iframeEl = $D.id("RPCService_hIframe");
634           $E.on(iframeEl, "load", opt.onSuccess); 
635           oFrm.onload = oFrm.onreadystatechange = function() {   
636              if (this.readyState && this.readyState != 'complete') return;   
637              else {   
638                  onComplete();   
639              }  
640         */
641     };
642     
643     
644     
645     J.http.ajax = ajax;
646     J.http.comet = comet;
647     J.http.load = load;
648     J.http.loadCss = loadCss;
649     J.http.loadScript = loadScript;
650     J.http.formSend = formSend;
651 });
652 
653 
654 
655