MINOR Updated jquery.form.js plugin, and re-added it to standard includes (its handy for clearing out forms, and consistently getting form field values)

This commit is contained in:
Ingo Schommer 2012-03-02 13:42:15 +01:00
parent c7a301521d
commit 3659c63afe
2 changed files with 358 additions and 151 deletions

View File

@ -239,6 +239,7 @@ class LeftAndMain extends Controller {
THIRDPARTY_DIR . '/jquery-entwine/dist/jquery.entwine-dist.js',
THIRDPARTY_DIR . '/jquery-cookie/jquery.cookie.js',
THIRDPARTY_DIR . '/jquery-query/jquery.query.js',
THIRDPARTY_DIR . '/jquery-form/jquery.form.js',
SAPPHIRE_ADMIN_DIR . '/thirdparty/jquery-notice/jquery.notice.js',
SAPPHIRE_ADMIN_DIR . '/thirdparty/jsizes/lib/jquery.sizes.js',
SAPPHIRE_ADMIN_DIR . '/thirdparty/jlayout/lib/jlayout.border.js',

View File

@ -1,6 +1,6 @@
/*!
* jQuery Form Plugin
* version: 2.63 (29-JAN-2011)
* version: 2.96 (16-FEB-2012)
* @requires jQuery v1.3.2 or later
*
* Examples and documentation at: http://malsup.com/jquery/form/
@ -14,7 +14,7 @@
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() {
@ -35,6 +35,14 @@
});
});
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.
*/
@ -50,21 +58,25 @@ $.fn.ajaxSubmit = function(options) {
return this;
}
var method, action, url, $form = this;
if (typeof options == 'function') {
options = { success: options };
}
var action = this.attr('action');
var url = (typeof action === 'string') ? $.trim(action) : '';
method = this.attr('method');
action = this.attr('action');
url = (typeof action === 'string') ? $.trim(action) : '';
url = url || window.location.href || '';
if (url) {
// clean url (don't include hash vaue)
url = (url.match(/^([^#]+)/)||[])[1];
}
url = url || window.location.href || '';
options = $.extend(true, {
url: url,
type: this[0].getAttribute('method') || 'GET', // IE7 massage (see issue 57)
success: $.ajaxSettings.success,
type: method || 'GET',
iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank'
}, options);
@ -83,21 +95,15 @@ $.fn.ajaxSubmit = function(options) {
return this;
}
var n,v,a = this.formToArray(options.semantic);
var traditional = options.traditional;
if ( traditional === undefined ) {
traditional = $.ajaxSettings.traditional;
}
var qx,n,v,a = this.formToArray(options.semantic);
if (options.data) {
options.extraData = options.data;
for (n in options.data) {
if(options.data[n] instanceof Array) {
for (var k in options.data[n]) {
a.push( { name: n, value: options.data[n][k] } );
}
}
else {
v = options.data[n];
v = $.isFunction(v) ? v() : v; // if value is fn, invoke it
a.push( { name: n, value: v } );
}
}
qx = $.param(options.data, traditional);
}
// give pre-submit callback an opportunity to abort the submit
@ -113,8 +119,10 @@ $.fn.ajaxSubmit = function(options) {
return this;
}
var q = $.param(a);
var q = $.param(a, traditional);
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'
@ -123,12 +131,12 @@ $.fn.ajaxSubmit = function(options) {
options.data = q; // data is the query string for 'post'
}
var $form = this, callbacks = [];
var callbacks = [];
if (options.resetForm) {
callbacks.push(function() { $form.resetForm(); });
}
if (options.clearForm) {
callbacks.push(function() { $form.clearForm(); });
callbacks.push(function() { $form.clearForm(options.includeHidden); });
}
// perform a load on the target only if dataType is not provided
@ -151,22 +159,33 @@ $.fn.ajaxSubmit = function(options) {
};
// are there files to upload?
var fileInputs = $('input:file', this).length > 0;
var fileInputs = $('input:file:enabled[value]', this); // [value] (issue #113)
var hasFileInputs = fileInputs.length > 0;
var mp = 'multipart/form-data';
var multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp);
var fileAPI = !!(hasFileInputs && fileInputs.get(0).files && window.FormData);
log("fileAPI :" + fileAPI);
var shouldUseFrame = (hasFileInputs || multipart) && !fileAPI;
// 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, fileUpload);
$.get(options.closeKeepAlive, function() {
fileUploadIframe(a);
});
}
else {
fileUpload();
fileUploadIframe(a);
}
}
else if ((hasFileInputs || multipart) && fileAPI) {
options.progress = options.progress || $.noop;
fileUploadXhr(a);
}
else {
$.ajax(options);
}
@ -175,10 +194,74 @@ $.fn.ajaxSubmit = function(options) {
this.trigger('form-submit-notify', [this, options]);
return this;
// 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++) {
if (a[i].type == 'file')
continue;
formdata.append(a[i].name, a[i].value);
}
$form.find('input:file:enabled').each(function(){
var name = $(this).attr('name'), files = this.files;
if (name) {
for (var i=0; i < files.length; i++)
formdata.append(name, files[i]);
}
});
if (options.extraData) {
for (var k in options.extraData)
formdata.append(k, options.extraData[k])
}
options.data = null;
var s = $.extend(true, {}, $.ajaxSettings, options, {
contentType: false,
processData: false,
cache: false,
type: 'POST'
});
//s.context = s.context || s;
s.data = null;
var beforeSend = s.beforeSend;
s.beforeSend = function(xhr, o) {
o.data = formdata;
if(xhr.upload) { // unfortunately, jQuery doesn't expose this prop (http://bugs.jquery.com/ticket/10190)
xhr.upload.onprogress = function(event) {
o.progress(event.position, event.total);
};
}
if(beforeSend)
beforeSend.call(o, xhr, options);
};
$.ajax(s);
}
// private function for handling file uploads (hat tip to YAHOO!)
function fileUpload() {
var form = $form[0];
function fileUploadIframe(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);
}
} else {
for (i=0; i < a.length; i++) {
el = $(form[a[i].name]);
el.removeAttr('disabled');
}
};
}
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
@ -187,15 +270,25 @@ $.fn.ajaxSubmit = function(options) {
return;
}
var s = $.extend(true, {}, $.ajaxSettings, options);
s = $.extend(true, {}, $.ajaxSettings, options);
s.context = s.context || s;
var id = 'jqFormIO' + (new Date().getTime()), fn = '_'+id;
var $io = $('<iframe id="' + id + '" name="' + id + '" src="'+ s.iframeSrc +'" />');
var io = $io[0];
id = 'jqFormIO' + (new Date().getTime());
if (s.iframeTarget) {
$io = $(s.iframeTarget);
n = $io.attr('name');
if (n == null)
$io.attr('name', id);
else
id = n;
}
else {
$io = $('<iframe name="' + id + '" src="'+ s.iframeSrc +'" />');
$io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });
}
io = $io[0];
var xhr = { // mock object
xhr = { // mock object
aborted: 0,
responseText: null,
responseXML: null,
@ -204,13 +297,19 @@ $.fn.ajaxSubmit = function(options) {
getAllResponseHeaders: function() {},
getResponseHeader: function() {},
setRequestHeader: function() {},
abort: function() {
abort: function(status) {
var e = (status === 'timeout' ? 'timeout' : 'aborted');
log('aborting upload... ' + e);
this.aborted = 1;
$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);
}
};
var g = s.global;
g = s.global;
// trigger ajax global events so that activity/block indicators work like normal
if (g && ! $.active++) {
$.event.trigger("ajaxStart");
@ -229,12 +328,10 @@ $.fn.ajaxSubmit = function(options) {
return;
}
var timedOut = 0;
// add submitting element to data if we know it
var sub = form.clk;
sub = form.clk;
if (sub) {
var n = sub.name;
n = sub.name;
if (n && !sub.disabled) {
s.extraData = s.extraData || {};
s.extraData[n] = sub.value;
@ -245,6 +342,22 @@ $.fn.ajaxSubmit = function(options) {
}
}
var CLIENT_TIMEOUT_ABORT = 1;
var SERVER_ABORT = 2;
function getDoc(frame) {
var doc = frame.contentWindow ? frame.contentWindow.document : frame.contentDocument ? frame.contentDocument : frame.document;
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
@ -252,15 +365,15 @@ $.fn.ajaxSubmit = function(options) {
// update form attrs in IE friendly way
form.setAttribute('target',id);
if (form.getAttribute('method') != 'POST') {
if (!method) {
form.setAttribute('method', 'POST');
}
if (form.getAttribute('action') != s.url) {
if (a != s.url) {
form.setAttribute('action', s.url);
}
// ie borks in some cases when setting encoding
if (! s.skipEncodingOverride) {
if (! s.skipEncodingOverride && (!method || /post/i.test(method))) {
$form.attr({
encoding: 'multipart/form-data',
enctype: 'multipart/form-data'
@ -269,7 +382,23 @@ $.fn.ajaxSubmit = function(options) {
// support timout
if (s.timeout) {
setTimeout(function() { timedOut = true; cb(); }, s.timeout);
timeoutHandle = setTimeout(function() { timedOut = true; cb(CLIENT_TIMEOUT_ABORT); }, s.timeout);
}
// look for server aborts
function checkState() {
try {
var state = getDoc(io).readyState;
log('state = ' + state);
if (state.toLowerCase() == 'uninitialized')
setTimeout(checkState,50);
}
catch(e) {
log('Server abort: ' , e, ' (', e.name, ')');
cb(SERVER_ABORT);
timeoutHandle && clearTimeout(timeoutHandle);
timeoutHandle = undefined;
}
}
// add "extra" data to form if provided in options
@ -278,14 +407,17 @@ $.fn.ajaxSubmit = function(options) {
if (s.extraData) {
for (var n in s.extraData) {
extraInputs.push(
$('<input type="hidden" name="'+n+'" value="'+s.extraData[n]+'" />')
$('<input type="hidden" name="'+n+'">').attr('value',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);
}
setTimeout(checkState,15);
form.submit();
}
finally {
@ -307,17 +439,36 @@ $.fn.ajaxSubmit = function(options) {
setTimeout(doSubmit, 10); // this lets dom updates render
}
var data, doc, domCheckCount = 50;
var data, doc, domCheckCount = 50, callbackProcessed;
function cb(e) {
if (xhr.aborted || callbackProcessed) {
return;
}
try {
doc = getDoc(io);
}
catch(ex) {
log('cannot access response document: ', ex);
e = SERVER_ABORT;
}
if (e === CLIENT_TIMEOUT_ABORT && xhr) {
xhr.abort('timeout');
return;
}
else if (e == SERVER_ABORT && xhr) {
xhr.abort('server abort');
return;
}
function cb() {
doc = io.contentWindow ? io.contentWindow.document : io.contentDocument ? io.contentDocument : io.document;
if (!doc || doc.location.href == s.iframeSrc) {
// response not received yet
if (!timedOut)
return;
}
io.detachEvent ? io.detachEvent('onload', cb) : io.removeEventListener('load', cb, false);
var ok = true;
var status = 'success', errMsg;
try {
if (timedOut) {
throw 'timeout';
@ -339,56 +490,82 @@ $.fn.ajaxSubmit = function(options) {
}
//log('response detected');
xhr.responseText = doc.body ? doc.body.innerHTML : doc.documentElement ? doc.documentElement.innerHTML : null;
var docRoot = doc.body ? doc.body : doc.documentElement;
xhr.responseText = docRoot ? docRoot.innerHTML : null;
xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
if (isXml)
s.dataType = 'xml';
xhr.getResponseHeader = function(header){
var headers = {'content-type': s.dataType};
return headers[header];
};
// support for XHR 'status' & 'statusText' emulation :
if (docRoot) {
xhr.status = Number( docRoot.getAttribute('status') ) || xhr.status;
xhr.statusText = docRoot.getAttribute('statusText') || xhr.statusText;
}
var scr = /(json|script)/.test(s.dataType);
var dt = (s.dataType || '').toLowerCase();
var scr = /(json|script|text)/.test(dt);
if (scr || s.textarea) {
// see if user embedded response in textarea
var ta = doc.getElementsByTagName('textarea')[0];
if (ta) {
xhr.responseText = ta.value;
// support for XHR 'status' & 'statusText' emulation :
xhr.status = Number( ta.getAttribute('status') ) || xhr.status;
xhr.statusText = ta.getAttribute('statusText') || xhr.statusText;
}
else if (scr) {
// account for browsers injecting pre around json response
var pre = doc.getElementsByTagName('pre')[0];
var b = doc.getElementsByTagName('body')[0];
if (pre) {
xhr.responseText = pre.textContent;
xhr.responseText = pre.textContent ? pre.textContent : pre.innerText;
}
else if (b) {
xhr.responseText = b.innerHTML;
xhr.responseText = b.textContent ? b.textContent : b.innerText;
}
}
}
else if (s.dataType == 'xml' && !xhr.responseXML && xhr.responseText != null) {
else if (dt == 'xml' && !xhr.responseXML && xhr.responseText != null) {
xhr.responseXML = toXml(xhr.responseText);
}
data = httpData(xhr, s.dataType, s);
try {
data = httpData(xhr, dt, s);
}
catch (e) {
status = 'parsererror';
xhr.error = errMsg = (e || status);
}
}
catch (e) {
log('error caught: ',e);
ok = false;
xhr.error = e;
s.error.call(s.context, xhr, 'error', e);
g && $.event.trigger("ajaxError", [xhr, s, e]);
status = 'error';
xhr.error = errMsg = (e || status);
}
if (xhr.aborted) {
log('upload aborted');
ok = false;
status = null;
}
if (xhr.status) { // we've set xhr.status
status = (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) ? 'success' : 'error';
}
// ordering of these callbacks/triggers is odd, but that's how $.ajax does it
if (ok) {
s.success.call(s.context, data, 'success', xhr);
if (status === 'success') {
s.success && s.success.call(s.context, data, 'success', xhr);
g && $.event.trigger("ajaxSuccess", [xhr, s]);
}
else if (status) {
if (errMsg == undefined)
errMsg = xhr.statusText;
s.error && s.error.call(s.context, xhr, status, errMsg);
g && $.event.trigger("ajaxError", [xhr, s, errMsg]);
}
g && $.event.trigger("ajaxComplete", [xhr, s]);
@ -396,11 +573,15 @@ $.fn.ajaxSubmit = function(options) {
$.event.trigger("ajaxStop");
}
s.complete && s.complete.call(s.context, xhr, ok ? 'success' : 'error');
s.complete && s.complete.call(s.context, xhr, status);
callbackProcessed = true;
if (s.timeout)
clearTimeout(timeoutHandle);
// clean up
setTimeout(function() {
$io.removeData('form-plugin-onload');
if (!s.iframeTarget)
$io.remove();
xhr.responseXML = null;
}, 100);
@ -422,6 +603,7 @@ $.fn.ajaxSubmit = function(options) {
};
var httpData = function( xhr, type, s ) { // mostly lifted from jq1.4.4
var ct = xhr.getResponseHeader('content-type') || '',
xml = type === 'xml' || !type && ct.indexOf('xml') >= 0,
data = xml ? xhr.responseXML : xhr.responseText;
@ -460,8 +642,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');
@ -475,12 +660,30 @@ $.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) {
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) {
var target = e.target;
var $el = $(target);
if (!($el.is(":submit,input:image"))) {
@ -497,7 +700,7 @@ $.fn.ajaxForm = function(options) {
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;
@ -508,9 +711,9 @@ $.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() {
return this.unbind('submit.form-plugin click.form-plugin');
@ -550,7 +753,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;
@ -563,7 +766,7 @@ $.fn.formToArray = function(semantic) {
}
}
else if (v !== null && typeof v != 'undefined') {
a.push({name: n, value: v});
a.push({name: n, value: v, type: el.type});
}
}
@ -713,19 +916,20 @@ $.fieldValue = function(el, successful) {
* - inputs of type submit, button, reset, and hidden will *not* be effected
* - button elements will *not* be effected
*/
$.fn.clearForm = function() {
$.fn.clearForm = function(includeHidden) {
return this.each(function() {
$('input,select,textarea', this).clearFields();
$('input,select,textarea', this).clearFields(includeHidden);
});
};
/**
* Clears the selected form elements.
*/
$.fn.clearFields = $.fn.clearInputs = function() {
$.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 (t == 'text' || t == 'password' || tag == 'textarea') {
if (re.test(t) || tag == 'textarea' || (includeHidden && /hidden/.test(t)) ) {
this.value = '';
}
else if (t == 'checkbox' || t == 'radio') {
@ -786,10 +990,13 @@ $.fn.selected = function(select) {
});
};
// expose debug var
$.fn.ajaxSubmit.debug = false;
// helper fn for console logging
// set $.fn.ajaxSubmit.debug to true to enable debug logging
function log() {
if ($.fn.ajaxSubmit.debug) {
if (!$.fn.ajaxSubmit.debug)
return;
var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,'');
if (window.console && window.console.log) {
window.console.log(msg);
@ -797,7 +1004,6 @@ function log() {
else if (window.opera && window.opera.postError) {
window.opera.postError(msg);
}
}
};
})(jQuery);