Issue #1928202 by dawehner: Update jQuery Form to the current version (3.27).

8.0.x
webchick 2013-02-27 17:33:28 -05:00
parent e898c4ddcb
commit 894e16417f
2 changed files with 1049 additions and 841 deletions

View File

@ -1,24 +1,27 @@
/*!
* jQuery Form Plugin
* version: 2.87 (20-OCT-2011)
* @requires jQuery v1.3.2 or later
* version: 3.27.0-2013.02.06
* @requires jQuery v1.5 or later
*
* Examples and documentation at: http://malsup.com/jquery/form/
* Project repository: https://github.com/malsup/form
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
* http://malsup.github.com/mit-license.txt
* http://malsup.github.com/gpl-license-v2.txt
*/
/*global ActiveXObject alert */
;(function($) {
"use strict";
/*
Usage Note:
-----------
Do not use both ajaxSubmit and ajaxForm on the same form. These
functions are intended to be exclusive. Use ajaxSubmit if you want
functions are mutually exclusive. Use ajaxSubmit if you want
to bind your own submit handler to the form. For example,
$(document).ready(function() {
$('#myForm').bind('submit', function(e) {
$('#myForm').on('submit', function(e) {
e.preventDefault(); // <-- important
$(this).ajaxSubmit({
target: '#output'
@ -35,15 +38,32 @@
});
});
You can also use ajaxForm with delegation (requires jQuery v1.7+), so the
form does not have to exist when you invoke ajaxForm:
$('#myForm').ajaxForm({
delegation: true,
target: '#output'
});
When using ajaxForm, the ajaxSubmit function will be invoked for you
at the appropriate time.
*/
/**
* Feature detection
*/
var feature = {};
feature.fileapi = $("<input type='file'/>").get(0).files !== undefined;
feature.formdata = window.FormData !== undefined;
/**
* ajaxSubmit() provides a mechanism for immediately submitting
* an HTML form using AJAX.
*/
$.fn.ajaxSubmit = function(options) {
/*jshint scripturl:true */
// fast fail if nothing selected (http://dev.jquery.com/ticket/2752)
if (!this.length) {
log('ajaxSubmit: skipping submit process - no element selected');
@ -92,7 +112,8 @@ $.fn.ajaxSubmit = function(options) {
traditional = $.ajaxSettings.traditional;
}
var qx,n,v,a = this.formToArray(options.semantic);
var elements = [];
var qx, a = this.formToArray(options.semantic, elements);
if (options.data) {
options.extraData = options.data;
qx = $.param(options.data, traditional);
@ -112,9 +133,9 @@ $.fn.ajaxSubmit = function(options) {
}
var q = $.param(a, traditional);
if (qx)
if (qx) {
q = ( q ? (q + '&' + qx) : qx );
}
if (options.type.toUpperCase() == 'GET') {
options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
options.data = null; // data is null for 'get'
@ -144,69 +165,143 @@ $.fn.ajaxSubmit = function(options) {
}
options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg
var context = options.context || options; // jQuery 1.4+ supports scope context
var context = options.context || this ; // jQuery 1.4+ supports scope context
for (var i=0, max=callbacks.length; i < max; i++) {
callbacks[i].apply(context, [data, status, xhr || $form, $form]);
}
};
// are there files to upload?
var fileInputs = $('input:file', this).length > 0;
// [value] (issue #113), also see comment:
// https://github.com/malsup/form/commit/588306aedba1de01388032d5f42a60159eea9228#commitcomment-2180219
var fileInputs = $('input[type=file]:enabled[value!=""]', this);
var hasFileInputs = fileInputs.length > 0;
var mp = 'multipart/form-data';
var multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp);
var fileAPI = feature.fileapi && feature.formdata;
log("fileAPI :" + fileAPI);
var shouldUseFrame = (hasFileInputs || multipart) && !fileAPI;
var jqxhr;
// options.iframe allows user to force iframe mode
// 06-NOV-09: now defaulting to iframe mode if file input is detected
if (options.iframe !== false && (fileInputs || options.iframe || multipart)) {
if (options.iframe !== false && (options.iframe || shouldUseFrame)) {
// hack to fix Safari hang (thanks to Tim Molendijk for this)
// see: http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
if (options.closeKeepAlive) {
$.get(options.closeKeepAlive, function() { fileUpload(a); });
$.get(options.closeKeepAlive, function() {
jqxhr = fileUploadIframe(a);
});
}
else {
fileUpload(a);
jqxhr = fileUploadIframe(a);
}
}
else if ((hasFileInputs || multipart) && fileAPI) {
jqxhr = fileUploadXhr(a);
}
else {
// IE7 massage (see issue 57)
if ($.browser.msie && method == 'get' && typeof options.type === "undefined") {
var ieMeth = $form[0].getAttribute('method');
if (typeof ieMeth === 'string')
options.type = ieMeth;
}
$.ajax(options);
jqxhr = $.ajax(options);
}
$form.removeData('jqxhr').data('jqxhr', jqxhr);
// clear element array
for (var k=0; k < elements.length; k++)
elements[k] = null;
// fire 'notify' event
this.trigger('form-submit-notify', [this, options]);
return this;
// private function for handling file uploads (hat tip to YAHOO!)
function fileUpload(a) {
var form = $form[0], el, i, s, g, id, $io, io, xhr, sub, n, timedOut, timeoutHandle;
var useProp = !!$.fn.prop;
if (a) {
if ( useProp ) {
// ensure that every serialized input is still enabled
for (i=0; i < a.length; i++) {
el = $(form[a[i].name]);
el.prop('disabled', false);
// utility fn for deep serialization
function deepSerialize(extraData){
var serialized = $.param(extraData).split('&');
var len = serialized.length;
var result = [];
var i, part;
for (i=0; i < len; i++) {
// #252; undo param space replacement
serialized[i] = serialized[i].replace(/\+/g,' ');
part = serialized[i].split('=');
// #278; use array instead of object storage, favoring array serializations
result.push([decodeURIComponent(part[0]), decodeURIComponent(part[1])]);
}
} else {
for (i=0; i < a.length; i++) {
el = $(form[a[i].name]);
el.removeAttr('disabled');
return result;
}
// XMLHttpRequest Level 2 file uploads (big hat tip to francois2metz)
function fileUploadXhr(a) {
var formdata = new FormData();
for (var i=0; i < a.length; i++) {
formdata.append(a[i].name, a[i].value);
}
if (options.extraData) {
var serializedData = deepSerialize(options.extraData);
for (i=0; i < serializedData.length; i++)
if (serializedData[i])
formdata.append(serializedData[i][0], serializedData[i][1]);
}
options.data = null;
var s = $.extend(true, {}, $.ajaxSettings, options, {
contentType: false,
processData: false,
cache: false,
type: method || 'POST'
});
if (options.uploadProgress) {
// workaround because jqXHR does not expose upload property
s.xhr = function() {
var xhr = jQuery.ajaxSettings.xhr();
if (xhr.upload) {
xhr.upload.addEventListener('progress', function(event) {
var percent = 0;
var position = event.loaded || event.position; /*event.position is deprecated*/
var total = event.total;
if (event.lengthComputable) {
percent = Math.ceil(position / total * 100);
}
options.uploadProgress(event, position, total, percent);
}, false);
}
return xhr;
};
}
if ($(':input[name=submit],:input[id=submit]', form).length) {
// if there is an input with a name or id of 'submit' then we won't be
// able to invoke the submit fn on the form (at least not x-browser)
alert('Error: Form elements must not have name or id of "submit".');
return;
s.data = null;
var beforeSend = s.beforeSend;
s.beforeSend = function(xhr, o) {
o.data = formdata;
if(beforeSend)
beforeSend.call(this, xhr, o);
};
return $.ajax(s);
}
// private function for handling file uploads (hat tip to YAHOO!)
function fileUploadIframe(a) {
var form = $form[0], el, i, s, g, id, $io, io, xhr, sub, n, timedOut, timeoutHandle;
var useProp = !!$.fn.prop;
var deferred = $.Deferred();
if (a) {
// ensure that every serialized input is still enabled
for (i=0; i < elements.length; i++) {
el = $(elements[i]);
if ( useProp )
el.prop('disabled', false);
else
el.removeAttr('disabled');
}
}
s = $.extend(true, {}, $.ajaxSettings, options);
@ -215,7 +310,7 @@ $.fn.ajaxSubmit = function(options) {
if (s.iframeTarget) {
$io = $(s.iframeTarget);
n = $io.attr('name');
if (n == null)
if (!n)
$io.attr('name', id);
else
id = n;
@ -240,17 +335,28 @@ $.fn.ajaxSubmit = function(options) {
var e = (status === 'timeout' ? 'timeout' : 'aborted');
log('aborting upload... ' + e);
this.aborted = 1;
try { // #214, #257
if (io.contentWindow.document.execCommand) {
io.contentWindow.document.execCommand('Stop');
}
}
catch(ignore) {}
$io.attr('src', s.iframeSrc); // abort op in progress
xhr.error = e;
s.error && s.error.call(s.context, xhr, e, status);
g && $.event.trigger("ajaxError", [xhr, s, e]);
s.complete && s.complete.call(s.context, xhr, e);
if (s.error)
s.error.call(s.context, xhr, e, status);
if (g)
$.event.trigger("ajaxError", [xhr, s, e]);
if (s.complete)
s.complete.call(s.context, xhr, e);
}
};
g = s.global;
// trigger ajax global events so that activity/block indicators work like normal
if (g && ! $.active++) {
if (g && 0 === $.active++) {
$.event.trigger("ajaxStart");
}
if (g) {
@ -261,10 +367,12 @@ $.fn.ajaxSubmit = function(options) {
if (s.global) {
$.active--;
}
return;
deferred.reject();
return deferred;
}
if (xhr.aborted) {
return;
deferred.reject();
return deferred;
}
// add submitting element to data if we know it
@ -289,6 +397,14 @@ $.fn.ajaxSubmit = function(options) {
return doc;
}
// Rails CSRF hack (thanks to Yvan Barthelemy)
var csrf_token = $('meta[name=csrf-token]').attr('content');
var csrf_param = $('meta[name=csrf-param]').attr('content');
if (csrf_param && csrf_token) {
s.extraData = s.extraData || {};
s.extraData[csrf_param] = csrf_token;
}
// take a breath so that pending repaints get some cpu time before the upload starts
function doSubmit() {
// make sure form attrs are set
@ -321,13 +437,14 @@ $.fn.ajaxSubmit = function(options) {
try {
var state = getDoc(io).readyState;
log('state = ' + state);
if (state.toLowerCase() == 'uninitialized')
if (state && state.toLowerCase() == 'uninitialized')
setTimeout(checkState,50);
}
catch(e) {
log('Server abort: ' , e, ' (', e.name, ')');
cb(SERVER_ABORT);
timeoutHandle && clearTimeout(timeoutHandle);
if (timeoutHandle)
clearTimeout(timeoutHandle);
timeoutHandle = undefined;
}
}
@ -337,19 +454,33 @@ $.fn.ajaxSubmit = function(options) {
try {
if (s.extraData) {
for (var n in s.extraData) {
if (s.extraData.hasOwnProperty(n)) {
// if using the $.param format that allows for multiple values with the same name
if($.isPlainObject(s.extraData[n]) && s.extraData[n].hasOwnProperty('name') && s.extraData[n].hasOwnProperty('value')) {
extraInputs.push(
$('<input type="hidden" name="'+n+'" />').attr('value',s.extraData[n])
$('<input type="hidden" name="'+s.extraData[n].name+'">').val(s.extraData[n].value)
.appendTo(form)[0]);
} else {
extraInputs.push(
$('<input type="hidden" name="'+n+'">').val(s.extraData[n])
.appendTo(form)[0]);
}
}
}
}
if (!s.iframeTarget) {
// add iframe to doc and submit the form
$io.appendTo('body');
io.attachEvent ? io.attachEvent('onload', cb) : io.addEventListener('load', cb, false);
if (io.attachEvent)
io.attachEvent('onload', cb);
else
io.addEventListener('load', cb, false);
}
setTimeout(checkState,15);
form.submit();
// just in case form has element with name/id of 'submit'
var submitFn = document.createElement('form').submit;
submitFn.apply(form);
}
finally {
// reset attrs and remove "extra" input elements
@ -385,10 +516,12 @@ $.fn.ajaxSubmit = function(options) {
}
if (e === CLIENT_TIMEOUT_ABORT && xhr) {
xhr.abort('timeout');
deferred.reject(xhr, 'timeout');
return;
}
else if (e == SERVER_ABORT && xhr) {
xhr.abort('server abort');
deferred.reject(xhr, 'error', 'server abort');
return;
}
@ -397,7 +530,10 @@ $.fn.ajaxSubmit = function(options) {
if (!timedOut)
return;
}
io.detachEvent ? io.detachEvent('onload', cb) : io.removeEventListener('load', cb, false);
if (io.detachEvent)
io.detachEvent('onload', cb);
else
io.removeEventListener('load', cb, false);
var status = 'success', errMsg;
try {
@ -407,7 +543,7 @@ $.fn.ajaxSubmit = function(options) {
var isXml = s.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc);
log('isXml='+isXml);
if (!isXml && window.opera && (doc.body == null || doc.body.innerHTML == '')) {
if (!isXml && window.opera && (doc.body === null || !doc.body.innerHTML)) {
if (--domCheckCount) {
// in some browsers (Opera) the iframe DOM is not always traversable when
// the onload callback fires, so we loop a bit to accommodate
@ -459,7 +595,7 @@ $.fn.ajaxSubmit = function(options) {
}
}
}
else if (dt == 'xml' && !xhr.responseXML && xhr.responseText != null) {
else if (dt == 'xml' && !xhr.responseXML && xhr.responseText) {
xhr.responseXML = toXml(xhr.responseText);
}
@ -488,23 +624,31 @@ $.fn.ajaxSubmit = function(options) {
// ordering of these callbacks/triggers is odd, but that's how $.ajax does it
if (status === 'success') {
s.success && s.success.call(s.context, data, 'success', xhr);
g && $.event.trigger("ajaxSuccess", [xhr, s]);
if (s.success)
s.success.call(s.context, data, 'success', xhr);
deferred.resolve(xhr.responseText, 'success', xhr);
if (g)
$.event.trigger("ajaxSuccess", [xhr, s]);
}
else if (status) {
if (errMsg == undefined)
if (errMsg === undefined)
errMsg = xhr.statusText;
s.error && s.error.call(s.context, xhr, status, errMsg);
g && $.event.trigger("ajaxError", [xhr, s, errMsg]);
if (s.error)
s.error.call(s.context, xhr, status, errMsg);
deferred.reject(xhr, 'error', errMsg);
if (g)
$.event.trigger("ajaxError", [xhr, s, errMsg]);
}
g && $.event.trigger("ajaxComplete", [xhr, s]);
if (g)
$.event.trigger("ajaxComplete", [xhr, s]);
if (g && ! --$.active) {
$.event.trigger("ajaxStop");
}
s.complete && s.complete.call(s.context, xhr, status);
if (s.complete)
s.complete.call(s.context, xhr, status);
callbackProcessed = true;
if (s.timeout)
@ -530,6 +674,7 @@ $.fn.ajaxSubmit = function(options) {
return (doc && doc.documentElement && doc.documentElement.nodeName != 'parsererror') ? doc : null;
};
var parseJSON = $.parseJSON || function(s) {
/*jslint evil:true */
return window['eval']('(' + s + ')');
};
@ -540,7 +685,8 @@ $.fn.ajaxSubmit = function(options) {
data = xml ? xhr.responseXML : xhr.responseText;
if (xml && data.documentElement.nodeName === 'parsererror') {
$.error && $.error('parsererror');
if ($.error)
$.error('parsererror');
}
if (s && s.dataFilter) {
data = s.dataFilter(data, type);
@ -554,6 +700,8 @@ $.fn.ajaxSubmit = function(options) {
}
return data;
};
return deferred;
}
};
@ -573,8 +721,11 @@ $.fn.ajaxSubmit = function(options) {
* the form itself.
*/
$.fn.ajaxForm = function(options) {
options = options || {};
options.delegation = options.delegation && $.isFunction($.fn.on);
// in jQuery 1.3+ we can fix mistakes with the ready state
if (this.length === 0) {
if (!options.delegation && this.length === 0) {
var o = { s: this.selector, c: this.context };
if (!$.isReady && o.s) {
log('DOM not ready, queuing ajaxForm');
@ -588,18 +739,38 @@ $.fn.ajaxForm = function(options) {
return this;
}
return this.ajaxFormUnbind().bind('submit.form-plugin', function(e) {
if ( options.delegation ) {
$(document)
.off('submit.form-plugin', this.selector, doAjaxSubmit)
.off('click.form-plugin', this.selector, captureSubmittingElement)
.on('submit.form-plugin', this.selector, options, doAjaxSubmit)
.on('click.form-plugin', this.selector, options, captureSubmittingElement);
return this;
}
return this.ajaxFormUnbind()
.bind('submit.form-plugin', options, doAjaxSubmit)
.bind('click.form-plugin', options, captureSubmittingElement);
};
// private event handlers
function doAjaxSubmit(e) {
/*jshint validthis:true */
var options = e.data;
if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed
e.preventDefault();
$(this).ajaxSubmit(options);
}
}).bind('click.form-plugin', function(e) {
}
function captureSubmittingElement(e) {
/*jshint validthis:true */
var target = e.target;
var $el = $(target);
if (!($el.is(":submit,input:image"))) {
if (!($el.is("[type=submit],[type=image]"))) {
// is this a child element of the submit el? (ex: a span within a button)
var t = $el.closest(':submit');
if (t.length == 0) {
var t = $el.closest('[type=submit]');
if (t.length === 0) {
return;
}
target = t[0];
@ -607,10 +778,10 @@ $.fn.ajaxForm = function(options) {
var form = this;
form.clk = target;
if (target.type == 'image') {
if (e.offsetX != undefined) {
if (e.offsetX !== undefined) {
form.clk_x = e.offsetX;
form.clk_y = e.offsetY;
} else if (typeof $.fn.offset == 'function') { // try to use dimensions plugin
} else if (typeof $.fn.offset == 'function') {
var offset = $el.offset();
form.clk_x = e.pageX - offset.left;
form.clk_y = e.pageY - offset.top;
@ -621,8 +792,8 @@ $.fn.ajaxForm = function(options) {
}
// clear form vars
setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 100);
});
};
}
// ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
$.fn.ajaxFormUnbind = function() {
@ -640,7 +811,7 @@ $.fn.ajaxFormUnbind = function() {
* It is this array that is passed to pre-submit callback functions provided to the
* ajaxSubmit() and ajaxForm() methods.
*/
$.fn.formToArray = function(semantic) {
$.fn.formToArray = function(semantic, elements) {
var a = [];
if (this.length === 0) {
return a;
@ -663,7 +834,7 @@ $.fn.formToArray = function(semantic) {
if (semantic && form.clk && el.type == "image") {
// handle image inputs on the fly when semantic == true
if(!el.disabled && form.clk == el) {
a.push({name: n, value: $(el).val()});
a.push({name: n, value: $(el).val(), type: el.type });
a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
}
continue;
@ -671,12 +842,30 @@ $.fn.formToArray = function(semantic) {
v = $.fieldValue(el, true);
if (v && v.constructor == Array) {
if (elements)
elements.push(el);
for(j=0, jmax=v.length; j < jmax; j++) {
a.push({name: n, value: v[j]});
}
}
else if (feature.fileapi && el.type == 'file' && !el.disabled) {
if (elements)
elements.push(el);
var files = el.files;
if (files.length) {
for (j=0; j < files.length; j++) {
a.push({name: n, value: files[j], type: el.type});
}
}
else {
// #180
a.push({ name: n, value: '', type: el.type });
}
}
else if (v !== null && typeof v != 'undefined') {
a.push({name: n, value: v});
if (elements)
elements.push(el);
a.push({name: n, value: v, type: el.type, required: el.required});
}
}
@ -738,19 +927,19 @@ $.fn.fieldSerialize = function(successful) {
* <input name="C" type="radio" value="C2" />
* </fieldset></form>
*
* var v = $(':text').fieldValue();
* var v = $('input[type=text]').fieldValue();
* // if no values are entered into the text inputs
* v == ['','']
* // if values entered into the text inputs are 'foo' and 'bar'
* v == ['foo','bar']
*
* var v = $(':checkbox').fieldValue();
* var v = $('input[type=checkbox]').fieldValue();
* // if neither checkbox is checked
* v === undefined
* // if both checkboxes are checked
* v == ['B1', 'B2']
*
* var v = $(':radio').fieldValue();
* var v = $('input[type=radio]').fieldValue();
* // if neither radio is checked
* v === undefined
* // if first radio is checked
@ -771,7 +960,10 @@ $.fn.fieldValue = function(successful) {
if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) {
continue;
}
v.constructor == Array ? $.merge(val, v) : val.push(v);
if (v.constructor == Array)
$.merge(val, v);
else
val.push(v);
}
return val;
};
@ -839,7 +1031,7 @@ $.fn.clearFields = $.fn.clearInputs = function(includeHidden) {
var re = /^(?:color|date|datetime|email|month|number|password|range|search|tel|text|time|url|week)$/i; // 'hidden' is not in this list
return this.each(function() {
var t = this.type, tag = this.tagName.toLowerCase();
if (re.test(t) || tag == 'textarea' || (includeHidden && /hidden/.test(t)) ) {
if (re.test(t) || tag == 'textarea') {
this.value = '';
}
else if (t == 'checkbox' || t == 'radio') {
@ -848,6 +1040,22 @@ $.fn.clearFields = $.fn.clearInputs = function(includeHidden) {
else if (tag == 'select') {
this.selectedIndex = -1;
}
else if (t == "file") {
if (/MSIE/.test(navigator.userAgent)) {
$(this).replaceWith($(this).clone());
} else {
$(this).val('');
}
}
else if (includeHidden) {
// includeHidden can be the value true, or it can be a selector string
// indicating a special test; for example:
// $('#myForm').clearForm('.special:hidden')
// the above would clean hidden inputs that have the class of 'special'
if ( (includeHidden === true && /hidden/.test(t)) ||
(typeof includeHidden == 'string' && $(this).is(includeHidden)) )
this.value = '';
}
});
};
@ -914,6 +1122,6 @@ function log() {
else if (window.opera && window.opera.postError) {
window.opera.postError(msg);
}
};
}
})(jQuery);

View File

@ -1420,7 +1420,7 @@ function system_library_info() {
$libraries['jquery.form'] = array(
'title' => 'jQuery Form Plugin',
'website' => 'http://malsup.com/jquery/form/',
'version' => '2.87',
'version' => '3.27',
'js' => array(
'core/misc/jquery.form.js' => array(),
),