jquery.iframe-transport.js 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. /*
  2. * jQuery Iframe Transport Plugin 1.7
  3. * https://github.com/blueimp/jQuery-File-Upload
  4. *
  5. * Copyright 2011, Sebastian Tschan
  6. * https://blueimp.net
  7. *
  8. * Licensed under the MIT license:
  9. * http://www.opensource.org/licenses/MIT
  10. */
  11. /*jslint unparam: true, nomen: true */
  12. /*global define, window, document */
  13. (function (factory) {
  14. 'use strict';
  15. if (typeof define === 'function' && define.amd) {
  16. // Register as an anonymous AMD module:
  17. define(['jquery'], factory);
  18. } else {
  19. // Browser globals:
  20. factory(window.jQuery);
  21. }
  22. }(function ($) {
  23. 'use strict';
  24. // Helper variable to create unique names for the transport iframes:
  25. var counter = 0;
  26. // The iframe transport accepts three additional options:
  27. // options.fileInput: a jQuery collection of file input fields
  28. // options.paramName: the parameter name for the file form data,
  29. // overrides the name property of the file input field(s),
  30. // can be a string or an array of strings.
  31. // options.formData: an array of objects with name and value properties,
  32. // equivalent to the return data of .serializeArray(), e.g.:
  33. // [{name: 'a', value: 1}, {name: 'b', value: 2}]
  34. $.ajaxTransport('iframe', function (options) {
  35. if (options.async) {
  36. var form,
  37. iframe,
  38. addParamChar;
  39. return {
  40. send: function (_, completeCallback) {
  41. form = $('<form style="display:none;"></form>');
  42. form.attr('accept-charset', options.formAcceptCharset);
  43. addParamChar = /\?/.test(options.url) ? '&' : '?';
  44. // XDomainRequest only supports GET and POST:
  45. if (options.type === 'DELETE') {
  46. options.url = options.url + addParamChar + '_method=DELETE';
  47. options.type = 'POST';
  48. } else if (options.type === 'PUT') {
  49. options.url = options.url + addParamChar + '_method=PUT';
  50. options.type = 'POST';
  51. } else if (options.type === 'PATCH') {
  52. options.url = options.url + addParamChar + '_method=PATCH';
  53. options.type = 'POST';
  54. }
  55. // javascript:false as initial iframe src
  56. // prevents warning popups on HTTPS in IE6.
  57. // IE versions below IE8 cannot set the name property of
  58. // elements that have already been added to the DOM,
  59. // so we set the name along with the iframe HTML markup:
  60. counter += 1;
  61. iframe = $(
  62. '<iframe src="javascript:false;" name="iframe-transport-' +
  63. counter + '"></iframe>'
  64. ).bind('load', function () {
  65. var fileInputClones,
  66. paramNames = $.isArray(options.paramName) ?
  67. options.paramName : [options.paramName];
  68. iframe
  69. .unbind('load')
  70. .bind('load', function () {
  71. var response;
  72. // Wrap in a try/catch block to catch exceptions thrown
  73. // when trying to access cross-domain iframe contents:
  74. try {
  75. response = iframe.contents();
  76. // Google Chrome and Firefox do not throw an
  77. // exception when calling iframe.contents() on
  78. // cross-domain requests, so we unify the response:
  79. if (!response.length || !response[0].firstChild) {
  80. throw new Error();
  81. }
  82. } catch (e) {
  83. response = undefined;
  84. }
  85. // The complete callback returns the
  86. // iframe content document as response object:
  87. completeCallback(
  88. 200,
  89. 'success',
  90. {'iframe': response}
  91. );
  92. // Fix for IE endless progress bar activity bug
  93. // (happens on form submits to iframe targets):
  94. $('<iframe src="javascript:false;"></iframe>')
  95. .appendTo(form);
  96. window.setTimeout(function () {
  97. // Removing the form in a setTimeout call
  98. // allows Chrome's developer tools to display
  99. // the response result
  100. form.remove();
  101. }, 0);
  102. });
  103. form
  104. .prop('target', iframe.prop('name'))
  105. .prop('action', options.url)
  106. .prop('method', options.type);
  107. if (options.formData) {
  108. $.each(options.formData, function (index, field) {
  109. $('<input type="hidden"/>')
  110. .prop('name', field.name)
  111. .val(field.value)
  112. .appendTo(form);
  113. });
  114. }
  115. if (options.fileInput && options.fileInput.length &&
  116. options.type === 'POST') {
  117. fileInputClones = options.fileInput.clone();
  118. // Insert a clone for each file input field:
  119. options.fileInput.after(function (index) {
  120. return fileInputClones[index];
  121. });
  122. if (options.paramName) {
  123. options.fileInput.each(function (index) {
  124. $(this).prop(
  125. 'name',
  126. paramNames[index] || options.paramName
  127. );
  128. });
  129. }
  130. // Appending the file input fields to the hidden form
  131. // removes them from their original location:
  132. form
  133. .append(options.fileInput)
  134. .prop('enctype', 'multipart/form-data')
  135. // enctype must be set as encoding for IE:
  136. .prop('encoding', 'multipart/form-data');
  137. }
  138. form.submit();
  139. // Insert the file input fields at their original location
  140. // by replacing the clones with the originals:
  141. if (fileInputClones && fileInputClones.length) {
  142. options.fileInput.each(function (index, input) {
  143. var clone = $(fileInputClones[index]);
  144. $(input).prop('name', clone.prop('name'));
  145. clone.replaceWith(input);
  146. });
  147. }
  148. });
  149. form.append(iframe).appendTo(document.body);
  150. },
  151. abort: function () {
  152. if (iframe) {
  153. // javascript:false as iframe src aborts the request
  154. // and prevents warning popups on HTTPS in IE6.
  155. // concat is used to avoid the "Script URL" JSLint error:
  156. iframe
  157. .unbind('load')
  158. .prop('src', 'javascript'.concat(':false;'));
  159. }
  160. if (form) {
  161. form.remove();
  162. }
  163. }
  164. };
  165. }
  166. });
  167. // The iframe transport returns the iframe content document as response.
  168. // The following adds converters from iframe to text, json, html, xml
  169. // and script.
  170. // Please note that the Content-Type for JSON responses has to be text/plain
  171. // or text/html, if the browser doesn't include application/json in the
  172. // Accept header, else IE will show a download dialog.
  173. // The Content-Type for XML responses on the other hand has to be always
  174. // application/xml or text/xml, so IE properly parses the XML response.
  175. // See also
  176. // https://github.com/blueimp/jQuery-File-Upload/wiki/Setup#content-type-negotiation
  177. $.ajaxSetup({
  178. converters: {
  179. 'iframe text': function (iframe) {
  180. return iframe && $(iframe[0].body).text();
  181. },
  182. 'iframe json': function (iframe) {
  183. return iframe && $.parseJSON($(iframe[0].body).text());
  184. },
  185. 'iframe html': function (iframe) {
  186. return iframe && $(iframe[0].body).html();
  187. },
  188. 'iframe xml': function (iframe) {
  189. var xmlDoc = iframe && iframe[0];
  190. return xmlDoc && $.isXMLDoc(xmlDoc) ? xmlDoc :
  191. $.parseXML((xmlDoc.XMLDocument && xmlDoc.XMLDocument.xml) ||
  192. $(xmlDoc.body).html());
  193. },
  194. 'iframe script': function (iframe) {
  195. return iframe && $.globalEval($(iframe[0].body).text());
  196. }
  197. }
  198. });
  199. }));