1 /**
  2  * @fileOverview non-refresh upload file with form by iframe
  3  * @author  yiminghe@gmail.com
  4  */
  5 KISSY.add("ajax/IframeTransport", function (S, DOM, Event, io) {
  6 
  7     var doc = S.Env.host.document,
  8         OK_CODE = 200,
  9         ERROR_CODE = 500,
 10         BREATH_INTERVAL = 30;
 11 
 12     // iframe 内的内容就是 body.innerText
 13     io.setupConfig({
 14         converters:{
 15             // iframe 到其他类型的转化和 text 一样
 16             iframe:io.getConfig().converters.text,
 17             text:{
 18                 // fake type, just mirror
 19                 iframe:function (text) {
 20                     return text;
 21                 }
 22             },
 23             xml:{
 24                 // fake type, just mirror
 25                 iframe:function (xml) {
 26                     return xml;
 27                 }
 28             }
 29         }
 30     });
 31 
 32     function createIframe(xhr) {
 33         var id = S.guid("ajax-iframe"),
 34             iframe,
 35             src = DOM.getEmptyIframeSrc();
 36 
 37         iframe = xhr.iframe = DOM.create("<iframe " +
 38             // ie6 need this when cross domain
 39             (src ? (" src=\"" + src + "\" ") : "") +
 40             " id='" + id + "'" +
 41             // need name for target of form
 42             " name='" + id + "'" +
 43             " style='position:absolute;left:-9999px;top:-9999px;'/>");
 44 
 45         DOM.prepend(iframe, doc.body || doc.documentElement);
 46         return iframe;
 47     }
 48 
 49     function addDataToForm(data, form, serializeArray) {
 50         data = S.unparam(data);
 51         var ret = [], d, isArray, vs, i, e;
 52         for (d in data) {
 53             isArray = S.isArray(data[d]);
 54             vs = S.makeArray(data[d]);
 55             // 数组和原生一样对待,创建多个同名输入域
 56             for (i = 0; i < vs.length; i++) {
 57                 e = doc.createElement("input");
 58                 e.type = 'hidden';
 59                 e.name = d + (isArray && serializeArray ? "[]" : "");
 60                 e.value = vs[i];
 61                 DOM.append(e, form);
 62                 ret.push(e);
 63             }
 64         }
 65         return ret;
 66     }
 67 
 68     function removeFieldsFromData(fields) {
 69         DOM.remove(fields);
 70     }
 71 
 72     function IframeTransport(xhrObject) {
 73         this.xhrObject = xhrObject;
 74     }
 75 
 76     S.augment(IframeTransport, {
 77         send:function () {
 78 
 79             var self = this,
 80                 xhrObject = self.xhrObject,
 81                 c = xhrObject.config,
 82                 fields,
 83                 iframe,
 84                 form = DOM.get(c.form);
 85 
 86             self.attrs = {
 87                 target:DOM.attr(form, "target") || "",
 88                 action:DOM.attr(form, "action") || "",
 89                 // enctype 区分 iframe 与 serialize
 90                 //encoding:DOM.attr(form, "encoding"),
 91                 //enctype:DOM.attr(form, "enctype"),
 92                 method:DOM.attr(form, "method")
 93             };
 94             self.form = form;
 95 
 96             iframe = createIframe(xhrObject);
 97 
 98             // set target to iframe to avoid main page refresh
 99             DOM.attr(form, {
100                 target:iframe.id,
101                 action:c.url,
102                 method:"post"
103                 //enctype:'multipart/form-data',
104                 //encoding:'multipart/form-data'
105             });
106 
107             if (c.data) {
108                 fields = addDataToForm(c.data, form, c.serializeArray);
109             }
110 
111             self.fields = fields;
112             // ie6 need a setTimeout to avoid handling load triggered if set iframe src
113             setTimeout(function () {
114                 Event.on(iframe, "load error", self._callback, self);
115                 form.submit();
116             }, 10);
117 
118         },
119 
120         _callback:function (event/*, abort*/) {
121             var self = this,
122                 form = self.form,
123                 xhrObject = self.xhrObject,
124                 eventType = event.type,
125                 iframeDoc,
126                 iframe = xhrObject.iframe;
127 
128             // 防止重复调用 , 成功后 abort
129             if (!iframe) {
130                 return;
131             }
132 
133             DOM.attr(form, self.attrs);
134 
135             removeFieldsFromData(this.fields);
136 
137             Event.detach(iframe);
138 
139             setTimeout(function () {
140                 // firefox will keep loading if not set timeout
141                 DOM.remove(iframe);
142             }, BREATH_INTERVAL);
143 
144             // nullify to prevent memory leak?
145             xhrObject.iframe = null;
146 
147             if (eventType == "load") {
148                 iframeDoc = iframe.contentWindow.document;
149                 // ie<9
150                 if (iframeDoc && iframeDoc.body) {
151                     xhrObject.responseText = S.trim(DOM.text(iframeDoc.body));
152                     // ie still can retrieve xml 's responseText
153                     if (S.startsWith(xhrObject.responseText, "<?xml")) {
154                         xhrObject.responseText = undefined;
155                     }
156                 }
157                 // ie<9
158                 // http://help.dottoro.com/ljbcjfot.php
159                 // http://msdn.microsoft.com/en-us/library/windows/desktop/ms766512(v=vs.85).aspx
160                 /*
161                  In Internet Explorer, XML documents can also be embedded into HTML documents with the xml HTML elements.
162                  To get an XMLDocument object that represents the embedded XML data island,
163                  use the XMLDocument property of the xml element.
164                  Note that the support for the XMLDocument property has been removed in Internet Explorer 9.
165                  */
166                 if (iframeDoc && iframeDoc['XMLDocument']) {
167                     xhrObject.responseXML = iframeDoc['XMLDocument'];
168                 }
169                 // ie9 firefox chrome
170                 else {
171                     xhrObject.responseXML = iframeDoc;
172                 }
173 
174                 xhrObject._xhrReady(OK_CODE, "success");
175             } else if (eventType == 'error') {
176                 xhrObject._xhrReady(ERROR_CODE, "error");
177             }
178         },
179 
180         abort:function () {
181             this._callback({});
182         }
183     });
184 
185     io.setupTransport("iframe", IframeTransport);
186 
187     return io;
188 
189 }, {
190     requires:["dom", "event", "./base"]
191 });