jquery.iframe-transport.js 10 KB

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