3 * version: 3.37.0-2013.07.11
4 * @requires jQuery v1.5 or later
5 * Copyright (c) 2013 M. Alsup
6 * Examples and documentation at: http://malsup.com/jquery/form/
7 * Project repository: https://github.com/malsup/form
8 * Dual licensed under the MIT and GPL licenses.
9 * https://github.com/malsup/form#copyright-and-license
11 /*global ActiveXObject */
18 Do not use both ajaxSubmit and ajaxForm on the same form. These
19 functions are mutually exclusive. Use ajaxSubmit if you want
20 to bind your own submit handler to the form. For example,
22 $(document).ready(function() {
23 $('#myForm').on('submit', function(e) {
24 e.preventDefault(); // <-- important
31 Use ajaxForm when you want the plugin to manage all the event binding
34 $(document).ready(function() {
35 $('#myForm').ajaxForm({
40 You can also use ajaxForm with delegation (requires jQuery v1.7+), so the
41 form does not have to exist when you invoke ajaxForm:
43 $('#myForm').ajaxForm({
48 When using ajaxForm, the ajaxSubmit function will be invoked for you
49 at the appropriate time.
56 feature.fileapi = $("<input type='file'/>").get(0).files !== undefined;
57 feature.formdata = window.FormData !== undefined;
59 var hasProp = !!$.fn.prop;
61 // attr2 uses prop when it can but checks the return type for
62 // an expected string. this accounts for the case where a form
63 // contains inputs with names like "action" or "method"; in those
64 // cases "prop" returns the element
65 $.fn.attr2 = function() {
67 return this.attr.apply(this, arguments);
68 var val = this.prop.apply(this, arguments);
69 if ( ( val && val.jquery ) || typeof val === 'string' )
71 return this.attr.apply(this, arguments);
75 * ajaxSubmit() provides a mechanism for immediately submitting
76 * an HTML form using AJAX.
78 $.fn.ajaxSubmit = function(options) {
79 /*jshint scripturl:true */
81 // fast fail if nothing selected (http://dev.jquery.com/ticket/2752)
83 log('ajaxSubmit: skipping submit process - no element selected');
87 var method, action, url, $form = this;
89 if (typeof options == 'function') {
90 options = { success: options };
92 else if ( options === undefined ) {
96 method = options.type || this.attr2('method');
97 action = options.url || this.attr2('action');
99 url = (typeof action === 'string') ? $.trim(action) : '';
100 url = url || window.location.href || '';
102 // clean url (don't include hash vaue)
103 url = (url.match(/^([^#]+)/)||[])[1];
106 options = $.extend(true, {
108 success: $.ajaxSettings.success,
109 type: method || 'GET',
110 iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank'
113 // hook for manipulating the form data before it is extracted;
114 // convenient for use with rich editors like tinyMCE or FCKEditor
116 this.trigger('form-pre-serialize', [this, options, veto]);
118 log('ajaxSubmit: submit vetoed via form-pre-serialize trigger');
122 // provide opportunity to alter form data before it is serialized
123 if (options.beforeSerialize && options.beforeSerialize(this, options) === false) {
124 log('ajaxSubmit: submit aborted via beforeSerialize callback');
128 var traditional = options.traditional;
129 if ( traditional === undefined ) {
130 traditional = $.ajaxSettings.traditional;
134 var qx, a = this.formToArray(options.semantic, elements);
136 options.extraData = options.data;
137 qx = $.param(options.data, traditional);
140 // give pre-submit callback an opportunity to abort the submit
141 if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) {
142 log('ajaxSubmit: submit aborted via beforeSubmit callback');
146 // fire vetoable 'validate' event
147 this.trigger('form-submit-validate', [a, this, options, veto]);
149 log('ajaxSubmit: submit vetoed via form-submit-validate trigger');
153 var q = $.param(a, traditional);
155 q = ( q ? (q + '&' + qx) : qx );
157 if (options.type.toUpperCase() == 'GET') {
158 options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
159 options.data = null; // data is null for 'get'
162 options.data = q; // data is the query string for 'post'
166 if (options.resetForm) {
167 callbacks.push(function() { $form.resetForm(); });
169 if (options.clearForm) {
170 callbacks.push(function() { $form.clearForm(options.includeHidden); });
173 // perform a load on the target only if dataType is not provided
174 if (!options.dataType && options.target) {
175 var oldSuccess = options.success || function(){};
176 callbacks.push(function(data) {
177 var fn = options.replaceTarget ? 'replaceWith' : 'html';
178 $(options.target)[fn](data).each(oldSuccess, arguments);
181 else if (options.success) {
182 callbacks.push(options.success);
185 options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg
186 var context = options.context || this ; // jQuery 1.4+ supports scope context
187 for (var i=0, max=callbacks.length; i < max; i++) {
188 callbacks[i].apply(context, [data, status, xhr || $form, $form]);
193 var oldError = options.error;
194 options.error = function(xhr, status, error) {
195 var context = options.context || this;
196 oldError.apply(context, [xhr, status, error, $form]);
200 if (options.complete) {
201 var oldComplete = options.complete;
202 options.complete = function(xhr, status) {
203 var context = options.context || this;
204 oldComplete.apply(context, [xhr, status, $form]);
208 // are there files to upload?
210 // [value] (issue #113), also see comment:
211 // https://github.com/malsup/form/commit/588306aedba1de01388032d5f42a60159eea9228#commitcomment-2180219
212 var fileInputs = $('input[type=file]:enabled[value!=""]', this);
214 var hasFileInputs = fileInputs.length > 0;
215 var mp = 'multipart/form-data';
216 var multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp);
218 var fileAPI = feature.fileapi && feature.formdata;
219 log("fileAPI :" + fileAPI);
220 var shouldUseFrame = (hasFileInputs || multipart) && !fileAPI;
224 // options.iframe allows user to force iframe mode
225 // 06-NOV-09: now defaulting to iframe mode if file input is detected
226 if (options.iframe !== false && (options.iframe || shouldUseFrame)) {
227 // hack to fix Safari hang (thanks to Tim Molendijk for this)
228 // see: http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
229 if (options.closeKeepAlive) {
230 $.get(options.closeKeepAlive, function() {
231 jqxhr = fileUploadIframe(a);
235 jqxhr = fileUploadIframe(a);
238 else if ((hasFileInputs || multipart) && fileAPI) {
239 jqxhr = fileUploadXhr(a);
242 jqxhr = $.ajax(options);
245 $form.removeData('jqxhr').data('jqxhr', jqxhr);
247 // clear element array
248 for (var k=0; k < elements.length; k++)
251 // fire 'notify' event
252 this.trigger('form-submit-notify', [this, options]);
255 // utility fn for deep serialization
256 function deepSerialize(extraData){
257 var serialized = $.param(extraData, options.traditional).split('&');
258 var len = serialized.length;
261 for (i=0; i < len; i++) {
262 // #252; undo param space replacement
263 serialized[i] = serialized[i].replace(/\+/g,' ');
264 part = serialized[i].split('=');
265 // #278; use array instead of object storage, favoring array serializations
266 result.push([decodeURIComponent(part[0]), decodeURIComponent(part[1])]);
271 // XMLHttpRequest Level 2 file uploads (big hat tip to francois2metz)
272 function fileUploadXhr(a) {
273 var formdata = new FormData();
275 for (var i=0; i < a.length; i++) {
276 formdata.append(a[i].name, a[i].value);
279 if (options.extraData) {
280 var serializedData = deepSerialize(options.extraData);
281 for (i=0; i < serializedData.length; i++)
282 if (serializedData[i])
283 formdata.append(serializedData[i][0], serializedData[i][1]);
288 var s = $.extend(true, {}, $.ajaxSettings, options, {
292 type: method || 'POST'
295 if (options.uploadProgress) {
296 // workaround because jqXHR does not expose upload property
298 var xhr = $.ajaxSettings.xhr();
300 xhr.upload.addEventListener('progress', function(event) {
302 var position = event.loaded || event.position; /*event.position is deprecated*/
303 var total = event.total;
304 if (event.lengthComputable) {
305 percent = Math.ceil(position / total * 100);
307 options.uploadProgress(event, position, total, percent);
315 var beforeSend = s.beforeSend;
316 s.beforeSend = function(xhr, o) {
319 beforeSend.call(this, xhr, o);
324 // private function for handling file uploads (hat tip to YAHOO!)
325 function fileUploadIframe(a) {
326 var form = $form[0], el, i, s, g, id, $io, io, xhr, sub, n, timedOut, timeoutHandle;
327 var deferred = $.Deferred();
330 // ensure that every serialized input is still enabled
331 for (i=0; i < elements.length; i++) {
334 el.prop('disabled', false);
336 el.removeAttr('disabled');
340 s = $.extend(true, {}, $.ajaxSettings, options);
341 s.context = s.context || s;
342 id = 'jqFormIO' + (new Date().getTime());
343 if (s.iframeTarget) {
344 $io = $(s.iframeTarget);
345 n = $io.attr2('name');
347 $io.attr2('name', id);
352 $io = $('<iframe name="' + id + '" src="'+ s.iframeSrc +'" />');
353 $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });
358 xhr = { // mock object
364 getAllResponseHeaders: function() {},
365 getResponseHeader: function() {},
366 setRequestHeader: function() {},
367 abort: function(status) {
368 var e = (status === 'timeout' ? 'timeout' : 'aborted');
369 log('aborting upload... ' + e);
373 if (io.contentWindow.document.execCommand) {
374 io.contentWindow.document.execCommand('Stop');
379 $io.attr('src', s.iframeSrc); // abort op in progress
382 s.error.call(s.context, xhr, e, status);
384 $.event.trigger("ajaxError", [xhr, s, e]);
386 s.complete.call(s.context, xhr, e);
391 // trigger ajax global events so that activity/block indicators work like normal
392 if (g && 0 === $.active++) {
393 $.event.trigger("ajaxStart");
396 $.event.trigger("ajaxSend", [xhr, s]);
399 if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) {
411 // add submitting element to data if we know it
415 if (n && !sub.disabled) {
416 s.extraData = s.extraData || {};
417 s.extraData[n] = sub.value;
418 if (sub.type == "image") {
419 s.extraData[n+'.x'] = form.clk_x;
420 s.extraData[n+'.y'] = form.clk_y;
425 var CLIENT_TIMEOUT_ABORT = 1;
426 var SERVER_ABORT = 2;
428 function getDoc(frame) {
429 /* it looks like contentWindow or contentDocument do not
430 * carry the protocol property in ie8, when running under ssl
431 * frame.document is the only valid response document, since
432 * the protocol is know but not on the other two objects. strange?
433 * "Same origin policy" http://en.wikipedia.org/wiki/Same_origin_policy
438 // IE8 cascading access check
440 if (frame.contentWindow) {
441 doc = frame.contentWindow.document;
444 // IE8 access denied under ssl & missing protocol
445 log('cannot get iframe.contentWindow document: ' + err);
448 if (doc) { // successful getting content
452 try { // simply checking may throw in ie8 under ssl or mismatched protocol
453 doc = frame.contentDocument ? frame.contentDocument : frame.document;
456 log('cannot get iframe.contentDocument: ' + err);
457 doc = frame.document;
462 // Rails CSRF hack (thanks to Yvan Barthelemy)
463 var csrf_token = $('meta[name=csrf-token]').attr('content');
464 var csrf_param = $('meta[name=csrf-param]').attr('content');
465 if (csrf_param && csrf_token) {
466 s.extraData = s.extraData || {};
467 s.extraData[csrf_param] = csrf_token;
470 // take a breath so that pending repaints get some cpu time before the upload starts
471 function doSubmit() {
472 // make sure form attrs are set
473 var t = $form.attr2('target'), a = $form.attr2('action');
475 // update form attrs in IE friendly way
476 form.setAttribute('target',id);
478 form.setAttribute('method', 'POST');
481 form.setAttribute('action', s.url);
484 // ie borks in some cases when setting encoding
485 if (! s.skipEncodingOverride && (!method || /post/i.test(method))) {
487 encoding: 'multipart/form-data',
488 enctype: 'multipart/form-data'
494 timeoutHandle = setTimeout(function() { timedOut = true; cb(CLIENT_TIMEOUT_ABORT); }, s.timeout);
497 // look for server aborts
498 function checkState() {
500 var state = getDoc(io).readyState;
501 log('state = ' + state);
502 if (state && state.toLowerCase() == 'uninitialized')
503 setTimeout(checkState,50);
506 log('Server abort: ' , e, ' (', e.name, ')');
509 clearTimeout(timeoutHandle);
510 timeoutHandle = undefined;
514 // add "extra" data to form if provided in options
515 var extraInputs = [];
518 for (var n in s.extraData) {
519 if (s.extraData.hasOwnProperty(n)) {
520 // if using the $.param format that allows for multiple values with the same name
521 if($.isPlainObject(s.extraData[n]) && s.extraData[n].hasOwnProperty('name') && s.extraData[n].hasOwnProperty('value')) {
523 $('<input type="hidden" name="'+s.extraData[n].name+'">').val(s.extraData[n].value)
527 $('<input type="hidden" name="'+n+'">').val(s.extraData[n])
534 if (!s.iframeTarget) {
535 // add iframe to doc and submit the form
536 $io.appendTo('body');
538 io.attachEvent('onload', cb);
540 io.addEventListener('load', cb, false);
542 setTimeout(checkState,15);
547 // just in case form has element with name/id of 'submit'
548 var submitFn = document.createElement('form').submit;
549 submitFn.apply(form);
553 // reset attrs and remove "extra" input elements
554 form.setAttribute('action',a);
556 form.setAttribute('target', t);
558 $form.removeAttr('target');
560 $(extraInputs).remove();
568 setTimeout(doSubmit, 10); // this lets dom updates render
571 var data, doc, domCheckCount = 50, callbackProcessed;
574 if (xhr.aborted || callbackProcessed) {
580 log('cannot access response document');
583 if (e === CLIENT_TIMEOUT_ABORT && xhr) {
584 xhr.abort('timeout');
585 deferred.reject(xhr, 'timeout');
588 else if (e == SERVER_ABORT && xhr) {
589 xhr.abort('server abort');
590 deferred.reject(xhr, 'error', 'server abort');
594 if (!doc || doc.location.href == s.iframeSrc) {
595 // response not received yet
600 io.detachEvent('onload', cb);
602 io.removeEventListener('load', cb, false);
604 var status = 'success', errMsg;
610 var isXml = s.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc);
612 if (!isXml && window.opera && (doc.body === null || !doc.body.innerHTML)) {
613 if (--domCheckCount) {
614 // in some browsers (Opera) the iframe DOM is not always traversable when
615 // the onload callback fires, so we loop a bit to accommodate
616 log('requeing onLoad callback, DOM not available');
620 // let this fall through because server response could be an empty document
621 //log('Could not access iframe DOM after mutiple tries.');
622 //throw 'DOMException: not available';
625 //log('response detected');
626 var docRoot = doc.body ? doc.body : doc.documentElement;
627 xhr.responseText = docRoot ? docRoot.innerHTML : null;
628 xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
631 xhr.getResponseHeader = function(header){
632 var headers = {'content-type': s.dataType};
633 return headers[header];
635 // support for XHR 'status' & 'statusText' emulation :
637 xhr.status = Number( docRoot.getAttribute('status') ) || xhr.status;
638 xhr.statusText = docRoot.getAttribute('statusText') || xhr.statusText;
641 var dt = (s.dataType || '').toLowerCase();
642 var scr = /(json|script|text)/.test(dt);
643 if (scr || s.textarea) {
644 // see if user embedded response in textarea
645 var ta = doc.getElementsByTagName('textarea')[0];
647 xhr.responseText = ta.value;
648 // support for XHR 'status' & 'statusText' emulation :
649 xhr.status = Number( ta.getAttribute('status') ) || xhr.status;
650 xhr.statusText = ta.getAttribute('statusText') || xhr.statusText;
653 // account for browsers injecting pre around json response
654 var pre = doc.getElementsByTagName('pre')[0];
655 var b = doc.getElementsByTagName('body')[0];
657 xhr.responseText = pre.textContent ? pre.textContent : pre.innerText;
660 xhr.responseText = b.textContent ? b.textContent : b.innerText;
664 else if (dt == 'xml' && !xhr.responseXML && xhr.responseText) {
665 xhr.responseXML = toXml(xhr.responseText);
669 data = httpData(xhr, dt, s);
672 status = 'parsererror';
673 xhr.error = errMsg = (err || status);
677 log('error caught: ',err);
679 xhr.error = errMsg = (err || status);
683 log('upload aborted');
687 if (xhr.status) { // we've set xhr.status
688 status = (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) ? 'success' : 'error';
691 // ordering of these callbacks/triggers is odd, but that's how $.ajax does it
692 if (status === 'success') {
694 s.success.call(s.context, data, 'success', xhr);
695 deferred.resolve(xhr.responseText, 'success', xhr);
697 $.event.trigger("ajaxSuccess", [xhr, s]);
700 if (errMsg === undefined)
701 errMsg = xhr.statusText;
703 s.error.call(s.context, xhr, status, errMsg);
704 deferred.reject(xhr, 'error', errMsg);
706 $.event.trigger("ajaxError", [xhr, s, errMsg]);
710 $.event.trigger("ajaxComplete", [xhr, s]);
712 if (g && ! --$.active) {
713 $.event.trigger("ajaxStop");
717 s.complete.call(s.context, xhr, status);
719 callbackProcessed = true;
721 clearTimeout(timeoutHandle);
724 setTimeout(function() {
727 xhr.responseXML = null;
731 var toXml = $.parseXML || function(s, doc) { // use parseXML if available (jQuery 1.5+)
732 if (window.ActiveXObject) {
733 doc = new ActiveXObject('Microsoft.XMLDOM');
738 doc = (new DOMParser()).parseFromString(s, 'text/xml');
740 return (doc && doc.documentElement && doc.documentElement.nodeName != 'parsererror') ? doc : null;
742 var parseJSON = $.parseJSON || function(s) {
743 /*jslint evil:true */
744 return window['eval']('(' + s + ')');
747 var httpData = function( xhr, type, s ) { // mostly lifted from jq1.4.4
749 var ct = xhr.getResponseHeader('content-type') || '',
750 xml = type === 'xml' || !type && ct.indexOf('xml') >= 0,
751 data = xml ? xhr.responseXML : xhr.responseText;
753 if (xml && data.documentElement.nodeName === 'parsererror') {
755 $.error('parsererror');
757 if (s && s.dataFilter) {
758 data = s.dataFilter(data, type);
760 if (typeof data === 'string') {
761 if (type === 'json' || !type && ct.indexOf('json') >= 0) {
762 data = parseJSON(data);
763 } else if (type === "script" || !type && ct.indexOf("javascript") >= 0) {
775 * ajaxForm() provides a mechanism for fully automating form submission.
777 * The advantages of using this method instead of ajaxSubmit() are:
779 * 1: This method will include coordinates for <input type="image" /> elements (if the element
780 * is used to submit the form).
781 * 2. This method will include the submit element's name/value data (for the element that was
782 * used to submit the form).
783 * 3. This method binds the submit() method to the form for you.
785 * The options argument for ajaxForm works exactly as it does for ajaxSubmit. ajaxForm merely
786 * passes the options argument along after properly binding events for submit elements and
789 $.fn.ajaxForm = function(options) {
790 options = options || {};
791 options.delegation = options.delegation && $.isFunction($.fn.on);
793 // in jQuery 1.3+ we can fix mistakes with the ready state
794 if (!options.delegation && this.length === 0) {
795 var o = { s: this.selector, c: this.context };
796 if (!$.isReady && o.s) {
797 log('DOM not ready, queuing ajaxForm');
799 $(o.s,o.c).ajaxForm(options);
803 // is your DOM ready? http://docs.jquery.com/Tutorials:Introducing_$(document).ready()
804 log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)'));
808 if ( options.delegation ) {
810 .off('submit.form-plugin', this.selector, doAjaxSubmit)
811 .off('click.form-plugin', this.selector, captureSubmittingElement)
812 .on('submit.form-plugin', this.selector, options, doAjaxSubmit)
813 .on('click.form-plugin', this.selector, options, captureSubmittingElement);
817 return this.ajaxFormUnbind()
818 .bind('submit.form-plugin', options, doAjaxSubmit)
819 .bind('click.form-plugin', options, captureSubmittingElement);
822 // private event handlers
823 function doAjaxSubmit(e) {
824 /*jshint validthis:true */
825 var options = e.data;
826 if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed
828 $(this).ajaxSubmit(options);
832 function captureSubmittingElement(e) {
833 /*jshint validthis:true */
834 var target = e.target;
836 if (!($el.is("[type=submit],[type=image]"))) {
837 // is this a child element of the submit el? (ex: a span within a button)
838 var t = $el.closest('[type=submit]');
839 if (t.length === 0) {
846 if (target.type == 'image') {
847 if (e.offsetX !== undefined) {
848 form.clk_x = e.offsetX;
849 form.clk_y = e.offsetY;
850 } else if (typeof $.fn.offset == 'function') {
851 var offset = $el.offset();
852 form.clk_x = e.pageX - offset.left;
853 form.clk_y = e.pageY - offset.top;
855 form.clk_x = e.pageX - target.offsetLeft;
856 form.clk_y = e.pageY - target.offsetTop;
860 setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 100);
864 // ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
865 $.fn.ajaxFormUnbind = function() {
866 return this.unbind('submit.form-plugin click.form-plugin');
870 * formToArray() gathers form element data into an array of objects that can
871 * be passed to any of the following ajax functions: $.get, $.post, or load.
872 * Each object in the array has both a 'name' and 'value' property. An example of
873 * an array for a simple login form might be:
875 * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ]
877 * It is this array that is passed to pre-submit callback functions provided to the
878 * ajaxSubmit() and ajaxForm() methods.
880 $.fn.formToArray = function(semantic, elements) {
882 if (this.length === 0) {
887 var els = semantic ? form.getElementsByTagName('*') : form.elements;
892 var i,j,n,v,el,max,jmax;
893 for(i=0, max=els.length; i < max; i++) {
896 if (!n || el.disabled) {
900 if (semantic && form.clk && el.type == "image") {
901 // handle image inputs on the fly when semantic == true
903 a.push({name: n, value: $(el).val(), type: el.type });
904 a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
909 v = $.fieldValue(el, true);
910 if (v && v.constructor == Array) {
913 for(j=0, jmax=v.length; j < jmax; j++) {
914 a.push({name: n, value: v[j]});
917 else if (feature.fileapi && el.type == 'file') {
920 var files = el.files;
922 for (j=0; j < files.length; j++) {
923 a.push({name: n, value: files[j], type: el.type});
928 a.push({ name: n, value: '', type: el.type });
931 else if (v !== null && typeof v != 'undefined') {
934 a.push({name: n, value: v, type: el.type, required: el.required});
938 if (!semantic && form.clk) {
939 // input type=='image' are not found in elements array! handle it here
940 var $input = $(form.clk), input = $input[0];
942 if (n && !input.disabled && input.type == 'image') {
943 a.push({name: n, value: $input.val()});
944 a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
951 * Serializes form data into a 'submittable' string. This method will return a string
952 * in the format: name1=value1&name2=value2
954 $.fn.formSerialize = function(semantic) {
955 //hand off to jQuery.param for proper encoding
956 return $.param(this.formToArray(semantic));
960 * Serializes all field elements in the jQuery object into a query string.
961 * This method will return a string in the format: name1=value1&name2=value2
963 $.fn.fieldSerialize = function(successful) {
965 this.each(function() {
970 var v = $.fieldValue(this, successful);
971 if (v && v.constructor == Array) {
972 for (var i=0,max=v.length; i < max; i++) {
973 a.push({name: n, value: v[i]});
976 else if (v !== null && typeof v != 'undefined') {
977 a.push({name: this.name, value: v});
980 //hand off to jQuery.param for proper encoding
985 * Returns the value(s) of the element in the matched set. For example, consider the following form:
988 * <input name="A" type="text" />
989 * <input name="A" type="text" />
990 * <input name="B" type="checkbox" value="B1" />
991 * <input name="B" type="checkbox" value="B2"/>
992 * <input name="C" type="radio" value="C1" />
993 * <input name="C" type="radio" value="C2" />
996 * var v = $('input[type=text]').fieldValue();
997 * // if no values are entered into the text inputs
999 * // if values entered into the text inputs are 'foo' and 'bar'
1000 * v == ['foo','bar']
1002 * var v = $('input[type=checkbox]').fieldValue();
1003 * // if neither checkbox is checked
1005 * // if both checkboxes are checked
1008 * var v = $('input[type=radio]').fieldValue();
1009 * // if neither radio is checked
1011 * // if first radio is checked
1014 * The successful argument controls whether or not the field element must be 'successful'
1015 * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls).
1016 * The default value of the successful argument is true. If this value is false the value(s)
1017 * for each element is returned.
1019 * Note: This method *always* returns an array. If no valid value can be determined the
1020 * array will be empty, otherwise it will contain one or more values.
1022 $.fn.fieldValue = function(successful) {
1023 for (var val=[], i=0, max=this.length; i < max; i++) {
1025 var v = $.fieldValue(el, successful);
1026 if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) {
1029 if (v.constructor == Array)
1038 * Returns the value of the field element.
1040 $.fieldValue = function(el, successful) {
1041 var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
1042 if (successful === undefined) {
1046 if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
1047 (t == 'checkbox' || t == 'radio') && !el.checked ||
1048 (t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
1049 tag == 'select' && el.selectedIndex == -1)) {
1053 if (tag == 'select') {
1054 var index = el.selectedIndex;
1058 var a = [], ops = el.options;
1059 var one = (t == 'select-one');
1060 var max = (one ? index+1 : ops.length);
1061 for(var i=(one ? index : 0); i < max; i++) {
1065 if (!v) { // extra pain for IE...
1066 v = (op.attributes && op.attributes['value'] && !(op.attributes['value'].specified)) ? op.text : op.value;
1080 * Clears the form data. Takes the following actions on the form's input fields:
1081 * - input text fields will have their 'value' property set to the empty string
1082 * - select elements will have their 'selectedIndex' property set to -1
1083 * - checkbox and radio inputs will have their 'checked' property set to false
1084 * - inputs of type submit, button, reset, and hidden will *not* be effected
1085 * - button elements will *not* be effected
1087 $.fn.clearForm = function(includeHidden) {
1088 return this.each(function() {
1089 $('input,select,textarea', this).clearFields(includeHidden);
1094 * Clears the selected form elements.
1096 $.fn.clearFields = $.fn.clearInputs = function(includeHidden) {
1097 var re = /^(?:color|date|datetime|email|month|number|password|range|search|tel|text|time|url|week)$/i; // 'hidden' is not in this list
1098 return this.each(function() {
1099 var t = this.type, tag = this.tagName.toLowerCase();
1100 if (re.test(t) || tag == 'textarea') {
1103 else if (t == 'checkbox' || t == 'radio') {
1104 this.checked = false;
1106 else if (tag == 'select') {
1107 this.selectedIndex = -1;
1109 else if (t == "file") {
1110 if (/MSIE/.test(navigator.userAgent)) {
1111 $(this).replaceWith($(this).clone(true));
1116 else if (includeHidden) {
1117 // includeHidden can be the value true, or it can be a selector string
1118 // indicating a special test; for example:
1119 // $('#myForm').clearForm('.special:hidden')
1120 // the above would clean hidden inputs that have the class of 'special'
1121 if ( (includeHidden === true && /hidden/.test(t)) ||
1122 (typeof includeHidden == 'string' && $(this).is(includeHidden)) )
1129 * Resets the form data. Causes all form elements to be reset to their original value.
1131 $.fn.resetForm = function() {
1132 return this.each(function() {
1133 // guard against an input with the name of 'reset'
1134 // note that IE reports the reset function as an 'object'
1135 if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType)) {
1142 * Enables or disables any matching elements.
1144 $.fn.enable = function(b) {
1145 if (b === undefined) {
1148 return this.each(function() {
1154 * Checks/unchecks any matching checkboxes or radio buttons and
1155 * selects/deselects and matching option elements.
1157 $.fn.selected = function(select) {
1158 if (select === undefined) {
1161 return this.each(function() {
1163 if (t == 'checkbox' || t == 'radio') {
1164 this.checked = select;
1166 else if (this.tagName.toLowerCase() == 'option') {
1167 var $sel = $(this).parent('select');
1168 if (select && $sel[0] && $sel[0].type == 'select-one') {
1169 // deselect all other options
1170 $sel.find('option').selected(false);
1172 this.selected = select;
1178 $.fn.ajaxSubmit.debug = false;
1180 // helper fn for console logging
1182 if (!$.fn.ajaxSubmit.debug)
1184 var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,'');
1185 if (window.console && window.console.log) {
1186 window.console.log(msg);
1188 else if (window.opera && window.opera.postError) {
1189 window.opera.postError(msg);