vendor/assets/javascripts/jquery.iframe-transport.js in remotipart-1.0.5 vs vendor/assets/javascripts/jquery.iframe-transport.js in remotipart-1.1.0
- old
+ new
@@ -1,18 +1,18 @@
// This [jQuery](http://jquery.com/) plugin implements an `<iframe>`
// [transport](http://api.jquery.com/extending-ajax/#Transports) so that
// `$.ajax()` calls support the uploading of files using standard HTML file
-// input fields. This is done by switching the exchange from `XMLHttpRequest` to
-// a hidden `iframe` element containing a form that is submitted.
+// input fields. This is done by switching the exchange from `XMLHttpRequest`
+// to a hidden `iframe` element containing a form that is submitted.
// The [source for the plugin](http://github.com/cmlenz/jquery-iframe-transport)
// is available on [Github](http://github.com/) and dual licensed under the MIT
// or GPL Version 2 licenses.
// ## Usage
-// To use this plugin, you simply add a `iframe` option with the value `true`
+// To use this plugin, you simply add an `iframe` option with the value `true`
// to the Ajax settings an `$.ajax()` call, and specify the file fields to
// include in the submssion using the `files` option, which can be a selector,
// jQuery object, or a list of DOM elements containing one or more
// `<input type="file">` elements:
@@ -23,16 +23,15 @@
// }).complete(function(data) {
// console.log(data);
// });
// });
-// The plugin will construct a hidden `<iframe>` element containing a copy of
-// the form the file field belongs to, will disable any form fields not
-// explicitly included, submit that form, and process the response.
+// The plugin will construct hidden `<iframe>` and `<form>` elements, add the
+// file field(s) to that form, submit the form, and process the response.
-// If you want to include other form fields in the form submission, include them
-// in the `data` option, and set the `processData` option to `false`:
+// If you want to include other form fields in the form submission, include
+// them in the `data` option, and set the `processData` option to `false`:
// $("#myform").submit(function() {
// $.ajax(this.action, {
// data: $(":text", this).serializeArray(),
// files: $(":file", this),
@@ -41,181 +40,181 @@
// }).complete(function(data) {
// console.log(data);
// });
// });
-// ### The Server Side
+// ### Response Data Types
-// If the response is not HTML or XML, you (unfortunately) need to apply some
-// trickery on the server side. To send back a JSON payload, send back an HTML
-// `<textarea>` element with a `data-type` attribute that contains the MIME
+// As the transport does not have access to the HTTP headers of the server
+// response, it is not as simple to make use of the automatic content type
+// detection provided by jQuery as with regular XHR. If you can't set the
+// expected response data type (for example because it may vary depending on
+// the outcome of processing by the server), you will need to employ a
+// workaround on the server side: Send back an HTML document containing just a
+// `<textarea>` element with a `data-type` attribute that specifies the MIME
// type, and put the actual payload in the textarea:
// <textarea data-type="application/json">
// {"ok": true, "message": "Thanks so much"}
// </textarea>
-// The iframe transport plugin will detect this and attempt to apply the same
-// conversions that jQuery applies to regular responses. That means for the
-// example above you should get a Javascript object as the `data` parameter of
-// the `complete` callback, with the properties `ok: true` and
-// `message: "Thanks so much"`.
+// The iframe transport plugin will detect this and pass the value of the
+// `data-type` attribute on to jQuery as if it was the "Content-Type" response
+// header, thereby enabling the same kind of conversions that jQuery applies
+// to regular responses. For the example above you should get a Javascript
+// object as the `data` parameter of the `complete` callback, with the
+// properties `ok: true` and `message: "Thanks so much"`.
+// ### Handling Server Errors
+
+// Another problem with using an `iframe` for file uploads is that it is
+// impossible for the javascript code to determine the HTTP status code of the
+// servers response. Effectively, all of the calls you make will look like they
+// are getting successful responses, and thus invoke the `done()` or
+// `complete()` callbacks. You can only determine communicate problems using
+// the content of the response payload. For example, consider using a JSON
+// response such as the following to indicate a problem with an uploaded file:
+
+// <textarea data-type="application/json">
+// {"ok": false, "message": "Please only upload reasonably sized files."}
+// </textarea>
+
// ### Compatibility
-// This plugin has primarily been tested on Safari 5, Firefox 4, and Internet
-// Explorer all the way back to version 6. While I haven't found any issues with
-// it so far, I'm fairly sure it still doesn't work around all the quirks in all
-// different browsers. But the code is still pretty simple overall, so you
-// should be able to fix it and contribute a patch :)
+// This plugin has primarily been tested on Safari 5 (or later), Firefox 4 (or
+// later), and Internet Explorer (all the way back to version 6). While I
+// haven't found any issues with it so far, I'm fairly sure it still doesn't
+// work around all the quirks in all different browsers. But the code is still
+// pretty simple overall, so you should be able to fix it and contribute a
+// patch :)
// ## Annotated Source
(function($, undefined) {
+ "use strict";
// Register a prefilter that checks whether the `iframe` option is set, and
- // switches to the iframe transport if it is `true`.
+ // switches to the "iframe" data type if it is `true`.
$.ajaxPrefilter(function(options, origOptions, jqXHR) {
if (options.iframe) {
return "iframe";
}
});
- // Register an iframe transport, independent of requested data type. It will
- // only activate when the "files" option has been set to a non-empty list of
- // enabled file inputs.
+ // Register a transport for the "iframe" data type. It will only activate
+ // when the "files" option has been set to a non-empty list of enabled file
+ // inputs.
$.ajaxTransport("iframe", function(options, origOptions, jqXHR) {
var form = null,
iframe = null,
- origAction = null,
- origTarget = null,
- origEnctype = null,
- addedFields = [],
- disabledFields = [],
- files = $(options.files).filter(":file:enabled");
+ name = "iframe-" + $.now(),
+ files = $(options.files).filter(":file:enabled"),
+ markers = null,
+ accepts;
// This function gets called after a successful submission or an abortion
// and should revert all changes made to the page to enable the
// submission via this transport.
function cleanUp() {
- $(addedFields).each(function() {
- this.remove();
- });
- $(disabledFields).each(function() {
- this.disabled = false;
- });
- form.attr("action", origAction || "")
- .attr("target", origTarget || "")
- .attr("enctype", origEnctype || "");
- iframe.attr("src", "javascript:false;").remove();
+ markers.prop('disabled', false);
+ form.remove();
+ iframe.bind("load", function() { iframe.remove(); });
+ iframe.attr("src", "javascript:false;");
}
// Remove "iframe" from the data types list so that further processing is
// based on the content type returned by the server, without attempting an
// (unsupported) conversion from "iframe" to the actual type.
options.dataTypes.shift();
if (files.length) {
- // Determine the form the file fields belong to, and make sure they all
- // actually belong to the same form.
- files.each(function() {
- if (form !== null && this.form !== form) {
- jQuery.error("All file fields must belong to the same form");
- }
- form = this.form;
- });
- form = $(form);
+ form = $("<form enctype='multipart/form-data' method='post'></form>").
+ hide().attr({action: options.url, target: name});
- // Store the original form attributes that we'll be replacing temporarily.
- origAction = form.attr("action");
- origTarget = form.attr("target");
- origEnctype = form.attr("enctype");
-
- // We need to disable all other inputs in the form so that they don't get
- // included in the submitted data unexpectedly.
- form.find(":input:not(:submit)").each(function() {
- if (!this.disabled && (this.type != "file" || files.index(this) < 0)) {
- this.disabled = true;
- disabledFields.push(this);
- }
- });
-
// If there is any additional data specified via the `data` option,
// we add it as hidden fields to the form. This (currently) requires
// the `processData` option to be set to false so that the data doesn't
// get serialized to a string.
if (typeof(options.data) === "string" && options.data.length > 0) {
- jQuery.error("data must not be serialized");
+ $.error("data must not be serialized");
}
$.each(options.data || {}, function(name, value) {
if ($.isPlainObject(value)) {
name = value.name;
value = value.value;
}
- addedFields.push($("<input type='hidden'>").attr("name", name)
- .attr("value", value).appendTo(form));
+ $("<input type='hidden' />").attr({name: name, value: value}).
+ appendTo(form);
});
// Add a hidden `X-Requested-With` field with the value `IFrame` to the
// field, to help server-side code to determine that the upload happened
// through this transport.
- addedFields.push($("<input type='hidden' name='X-Requested-With'>")
- .attr("value", "IFrame").appendTo(form));
+ $("<input type='hidden' value='IFrame' name='X-Requested-With' />").
+ appendTo(form);
// Borrowed straight from the JQuery source
// Provides a way of specifying the accepted data type similar to HTTP_ACCEPTS
accepts = options.dataTypes[ 0 ] && options.accepts[ options.dataTypes[0] ] ?
options.accepts[ options.dataTypes[0] ] + ( options.dataTypes[ 0 ] !== "*" ? ", */*; q=0.01" : "" ) :
options.accepts[ "*" ]
- addedFields.push($("<input type='hidden' name='X-Http-Accept'>")
- .attr("value", accepts).appendTo(form));
+ $("<input type='hidden' name='X-Http-Accept'>")
+ .attr("value", accepts).appendTo(form);
+ // Move the file fields into the hidden form, but first remember their
+ // original locations in the document by replacing them with disabled
+ // clones. This should also avoid introducing unwanted changes to the
+ // page layout during submission.
+ markers = files.after(function(idx) {
+ return $(this).clone().prop("disabled", true);
+ }).next();
+ files.appendTo(form);
+
return {
// The `send` function is called by jQuery when the request should be
// sent.
send: function(headers, completeCallback) {
- iframe = $("<iframe src='javascript:false;' name='iframe-" + $.now()
- + "' style='display:none'></iframe>");
+ iframe = $("<iframe src='javascript:false;' name='" + name +
+ "' id='" + name + "' style='display:none'></iframe>");
// The first load event gets fired after the iframe has been injected
// into the DOM, and is used to prepare the actual submission.
iframe.bind("load", function() {
// The second load event gets fired when the response to the form
// submission is received. The implementation detects whether the
// actual payload is embedded in a `<textarea>` element, and
// prepares the required conversions to be made in that case.
iframe.unbind("load").bind("load", function() {
-
var doc = this.contentWindow ? this.contentWindow.document :
(this.contentDocument ? this.contentDocument : this.document),
root = doc.documentElement ? doc.documentElement : doc.body,
textarea = root.getElementsByTagName("textarea")[0],
- type = textarea ? textarea.getAttribute("data-type") : null;
-
- var status = textarea ? parseInt(textarea.getAttribute("response-code")) : 200,
- statusText = "OK",
- responses = { text: type ? textarea.value : root ? root.innerHTML : null },
- headers = "Content-Type: " + (type || "text/html")
-
- completeCallback(status, statusText, responses, headers);
-
- setTimeout(cleanUp, 50);
+ type = textarea && textarea.getAttribute("data-type") || null,
+ status = textarea && textarea.getAttribute("data-status") || 200,
+ statusText = textarea && textarea.getAttribute("data-statusText") || "OK",
+ content = {
+ html: root.innerHTML,
+ text: type ?
+ textarea.value :
+ root ? (root.textContent || root.innerText) : null
+ };
+ cleanUp();
+ completeCallback(status, statusText, content, type ?
+ ("Content-Type: " + type) :
+ null);
});
- // Now that the load handler has been set up, reconfigure and
- // submit the form.
- form.attr("action", options.url)
- .attr("target", iframe.attr("name"))
- .attr("enctype", "multipart/form-data")
- .get(0).submit();
+ // Now that the load handler has been set up, submit the form.
+ form[0].submit();
});
- // After everything has been set up correctly, the iframe gets
- // injected into the DOM so that the submission can be initiated.
- iframe.insertAfter(form);
+ // After everything has been set up correctly, the form and iframe
+ // get injected into the DOM so that the submission can be
+ // initiated.
+ $("body").append(form, iframe);
},
// The `abort` function is called by jQuery when the request should be
// aborted.
abort: function() {