jquery.iframe-transport.js | |
|---|---|
| This jQuery plugin implements an | |
| The source for the plugin is available on Github and dual licensed under the MIT or GPL Version 2 licenses. | |
Usage | |
| To use this plugin, you simply add an | |
| |
| The plugin will construct a hidden | |
| If you want to include other form fields in the form submission, include
them in the | |
| |
Response Data Types | |
| 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), you will
need to employ a workaround on the server side: Send back an HTML document
containing just a | |
| |
| The iframe transport plugin will detect this and pass the value of the
| |
Handling Server Errors | |
| Another problem with using an | |
| |
Compatibility | |
| 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 | $.ajaxPrefilter(function(options, origOptions, jqXHR) {
if (options.iframe) {
return "iframe";
}
}); |
| 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,
name = "iframe-" + $.now(),
files = $(options.files).filter(":file:enabled"),
markers = null; |
| 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() {
markers.replaceWith(function(idx) {
return files.get(idx);
});
form.remove();
iframe.attr("src", "javascript:false;").remove();
} |
| 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) {
form = $("<form enctype='multipart/form-data' method='post'></form>").
hide().attr({action: options.url, target: name}); |
| If there is any additional data specified via the | if (typeof(options.data) === "string" && options.data.length > 0) {
$.error("data must not be serialized");
}
$.each(options.data || {}, function(name, value) {
if ($.isPlainObject(value)) {
name = value.name;
value = value.value;
}
$("<input type='hidden'>").attr({name: name, value: value}).
appendTo(form);
}); |
| Add a hidden | $("<input type='hidden' value='IFrame' name='X-Requested-With'>").
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(headers, completeCallback) {
iframe = $("<iframe src='javascript:false;' name='" + 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 | 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,
content = {
html: root.innerHTML,
text: type ?
textarea.value :
root ? (root.textContent || root.innerText) : null
};
cleanUp();
completeCallback(200, "OK", content, type ?
("Content-Type: " + type) :
null);
}); |
| Now that the load handler has been set up, submit the form. | form[0].submit();
}); |
| 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() {
if (iframe !== null) {
iframe.unbind("load").attr("src", "javascript:false;");
cleanUp();
}
}
};
}
});
})(jQuery);
|