ajax-form.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. function replaceDocument(docString) {
  2. var doc = document.open("text/html");
  3. doc.write(docString);
  4. doc.close();
  5. if (window.djdt) {
  6. // If Django Debug Toolbar is available, reinitialize it so that
  7. // it can show updated panels from new `docString`.
  8. window.addEventListener("load", djdt.init);
  9. }
  10. }
  11. function doAjaxSubmit(e) {
  12. var form = $(this);
  13. var btn = $(this.clk);
  14. var method = (
  15. btn.data('method') ||
  16. form.data('method') ||
  17. form.attr('method') || 'GET'
  18. ).toUpperCase();
  19. if (method === 'GET') {
  20. // GET requests can always use standard form submits.
  21. return;
  22. }
  23. var contentType =
  24. form.find('input[data-override="content-type"]').val() ||
  25. form.find('select[data-override="content-type"] option:selected').text();
  26. if (method === 'POST' && !contentType) {
  27. // POST requests can use standard form submits, unless we have
  28. // overridden the content type.
  29. return;
  30. }
  31. // At this point we need to make an AJAX form submission.
  32. e.preventDefault();
  33. var url = form.attr('action');
  34. var data;
  35. if (contentType) {
  36. data = form.find('[data-override="content"]').val() || ''
  37. if (contentType === 'multipart/form-data') {
  38. // We need to add a boundary parameter to the header
  39. // We assume the first valid-looking boundary line in the body is correct
  40. // regex is from RFC 2046 appendix A
  41. var boundaryCharNoSpace = "0-9A-Z'()+_,-./:=?";
  42. var boundaryChar = boundaryCharNoSpace + ' ';
  43. var re = new RegExp('^--([' + boundaryChar + ']{0,69}[' + boundaryCharNoSpace + '])[\\s]*?$', 'im');
  44. var boundary = data.match(re);
  45. if (boundary !== null) {
  46. contentType += '; boundary="' + boundary[1] + '"';
  47. }
  48. // Fix textarea.value EOL normalisation (multipart/form-data should use CR+NL, not NL)
  49. data = data.replace(/\n/g, '\r\n');
  50. }
  51. } else {
  52. contentType = form.attr('enctype') || form.attr('encoding')
  53. if (contentType === 'multipart/form-data') {
  54. if (!window.FormData) {
  55. alert('Your browser does not support AJAX multipart form submissions');
  56. return;
  57. }
  58. // Use the FormData API and allow the content type to be set automatically,
  59. // so it includes the boundary string.
  60. // See https://developer.mozilla.org/en-US/docs/Web/API/FormData/Using_FormData_Objects
  61. contentType = false;
  62. data = new FormData(form[0]);
  63. } else {
  64. contentType = 'application/x-www-form-urlencoded; charset=UTF-8'
  65. data = form.serialize();
  66. }
  67. }
  68. var ret = $.ajax({
  69. url: url,
  70. method: method,
  71. data: data,
  72. contentType: contentType,
  73. processData: false,
  74. headers: {
  75. 'Accept': 'text/html; q=1.0, */*'
  76. },
  77. });
  78. ret.always(function(data, textStatus, jqXHR) {
  79. if (textStatus != 'success') {
  80. jqXHR = data;
  81. }
  82. var responseContentType = jqXHR.getResponseHeader("content-type") || "";
  83. if (responseContentType.toLowerCase().indexOf('text/html') === 0) {
  84. replaceDocument(jqXHR.responseText);
  85. try {
  86. // Modify the location and scroll to top, as if after page load.
  87. history.replaceState({}, '', url);
  88. scroll(0, 0);
  89. } catch (err) {
  90. // History API not supported, so redirect.
  91. window.location = url;
  92. }
  93. } else {
  94. // Not HTML content. We can't open this directly, so redirect.
  95. window.location = url;
  96. }
  97. });
  98. return ret;
  99. }
  100. function captureSubmittingElement(e) {
  101. var target = e.target;
  102. var form = this;
  103. form.clk = target;
  104. }
  105. $.fn.ajaxForm = function() {
  106. var options = {}
  107. return this
  108. .unbind('submit.form-plugin click.form-plugin')
  109. .bind('submit.form-plugin', options, doAjaxSubmit)
  110. .bind('click.form-plugin', options, captureSubmittingElement);
  111. };