3 * version: 3.43.0-2013.09.03
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 || $.ajaxSettings.type,
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', this).filter(function() { return $(this).val() != ''; });
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 deferred.abort = function(status) {
335 // ensure that every serialized input is still enabled
336 for (i=0; i < elements.length; i++) {
339 el.prop('disabled', false);
341 el.removeAttr('disabled');
345 s = $.extend(true, {}, $.ajaxSettings, options);
346 s.context = s.context || s;
347 id = 'jqFormIO' + (new Date().getTime());
348 if (s.iframeTarget) {
349 $io = $(s.iframeTarget);
350 n = $io.attr2('name');
352 $io.attr2('name', id);
357 $io = $('<iframe name="' + id + '" src="'+ s.iframeSrc +'" />');
358 $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });
363 xhr = { // mock object
369 getAllResponseHeaders: function() {},
370 getResponseHeader: function() {},
371 setRequestHeader: function() {},
372 abort: function(status) {
373 var e = (status === 'timeout' ? 'timeout' : 'aborted');
374 log('aborting upload... ' + e);
378 if (io.contentWindow.document.execCommand) {
379 io.contentWindow.document.execCommand('Stop');
384 $io.attr('src', s.iframeSrc); // abort op in progress
387 s.error.call(s.context, xhr, e, status);
389 $.event.trigger("ajaxError", [xhr, s, e]);
391 s.complete.call(s.context, xhr, e);
396 // trigger ajax global events so that activity/block indicators work like normal
397 if (g && 0 === $.active++) {
398 $.event.trigger("ajaxStart");
401 $.event.trigger("ajaxSend", [xhr, s]);
404 if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) {
416 // add submitting element to data if we know it
420 if (n && !sub.disabled) {
421 s.extraData = s.extraData || {};
422 s.extraData[n] = sub.value;
423 if (sub.type == "image") {
424 s.extraData[n+'.x'] = form.clk_x;
425 s.extraData[n+'.y'] = form.clk_y;
430 var CLIENT_TIMEOUT_ABORT = 1;
431 var SERVER_ABORT = 2;
433 function getDoc(frame) {
434 /* it looks like contentWindow or contentDocument do not
435 * carry the protocol property in ie8, when running under ssl
436 * frame.document is the only valid response document, since
437 * the protocol is know but not on the other two objects. strange?
438 * "Same origin policy" http://en.wikipedia.org/wiki/Same_origin_policy
443 // IE8 cascading access check
445 if (frame.contentWindow) {
446 doc = frame.contentWindow.document;
449 // IE8 access denied under ssl & missing protocol
450 log('cannot get iframe.contentWindow document: ' + err);
453 if (doc) { // successful getting content
457 try { // simply checking may throw in ie8 under ssl or mismatched protocol
458 doc = frame.contentDocument ? frame.contentDocument : frame.document;
461 log('cannot get iframe.contentDocument: ' + err);
462 doc = frame.document;
467 // Rails CSRF hack (thanks to Yvan Barthelemy)
468 var csrf_token = $('meta[name=csrf-token]').attr('content');
469 var csrf_param = $('meta[name=csrf-param]').attr('content');
470 if (csrf_param && csrf_token) {
471 s.extraData = s.extraData || {};
472 s.extraData[csrf_param] = csrf_token;
475 // take a breath so that pending repaints get some cpu time before the upload starts
476 function doSubmit() {
477 // make sure form attrs are set
478 var t = $form.attr2('target'), a = $form.attr2('action');
480 // update form attrs in IE friendly way
481 form.setAttribute('target',id);
482 if (!method || /post/i.test(method) ) {
483 form.setAttribute('method', 'POST');
486 form.setAttribute('action', s.url);
489 // ie borks in some cases when setting encoding
490 if (! s.skipEncodingOverride && (!method || /post/i.test(method))) {
492 encoding: 'multipart/form-data',
493 enctype: 'multipart/form-data'
499 timeoutHandle = setTimeout(function() { timedOut = true; cb(CLIENT_TIMEOUT_ABORT); }, s.timeout);
502 // look for server aborts
503 function checkState() {
505 var state = getDoc(io).readyState;
506 log('state = ' + state);
507 if (state && state.toLowerCase() == 'uninitialized')
508 setTimeout(checkState,50);
511 log('Server abort: ' , e, ' (', e.name, ')');
514 clearTimeout(timeoutHandle);
515 timeoutHandle = undefined;
519 // add "extra" data to form if provided in options
520 var extraInputs = [];
523 for (var n in s.extraData) {
524 if (s.extraData.hasOwnProperty(n)) {
525 // if using the $.param format that allows for multiple values with the same name
526 if($.isPlainObject(s.extraData[n]) && s.extraData[n].hasOwnProperty('name') && s.extraData[n].hasOwnProperty('value')) {
528 $('<input type="hidden" name="'+s.extraData[n].name+'">').val(s.extraData[n].value)
532 $('<input type="hidden" name="'+n+'">').val(s.extraData[n])
539 if (!s.iframeTarget) {
540 // add iframe to doc and submit the form
541 $io.appendTo('body');
544 io.attachEvent('onload', cb);
546 io.addEventListener('load', cb, false);
547 setTimeout(checkState,15);
552 // just in case form has element with name/id of 'submit'
553 var submitFn = document.createElement('form').submit;
554 submitFn.apply(form);
558 // reset attrs and remove "extra" input elements
559 form.setAttribute('action',a);
561 form.setAttribute('target', t);
563 $form.removeAttr('target');
565 $(extraInputs).remove();
573 setTimeout(doSubmit, 10); // this lets dom updates render
576 var data, doc, domCheckCount = 50, callbackProcessed;
579 if (xhr.aborted || callbackProcessed) {
585 log('cannot access response document');
588 if (e === CLIENT_TIMEOUT_ABORT && xhr) {
589 xhr.abort('timeout');
590 deferred.reject(xhr, 'timeout');
593 else if (e == SERVER_ABORT && xhr) {
594 xhr.abort('server abort');
595 deferred.reject(xhr, 'error', 'server abort');
599 if (!doc || doc.location.href == s.iframeSrc) {
600 // response not received yet
605 io.detachEvent('onload', cb);
607 io.removeEventListener('load', cb, false);
609 var status = 'success', errMsg;
615 var isXml = s.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc);
617 if (!isXml && window.opera && (doc.body === null || !doc.body.innerHTML)) {
618 if (--domCheckCount) {
619 // in some browsers (Opera) the iframe DOM is not always traversable when
620 // the onload callback fires, so we loop a bit to accommodate
621 log('requeing onLoad callback, DOM not available');
625 // let this fall through because server response could be an empty document
626 //log('Could not access iframe DOM after mutiple tries.');
627 //throw 'DOMException: not available';
630 //log('response detected');
631 var docRoot = doc.body ? doc.body : doc.documentElement;
632 xhr.responseText = docRoot ? docRoot.innerHTML : null;
633 xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
636 xhr.getResponseHeader = function(header){
637 var headers = {'content-type': s.dataType};
638 return headers[header.toLowerCase()];
640 // support for XHR 'status' & 'statusText' emulation :
642 xhr.status = Number( docRoot.getAttribute('status') ) || xhr.status;
643 xhr.statusText = docRoot.getAttribute('statusText') || xhr.statusText;
646 var dt = (s.dataType || '').toLowerCase();
647 var scr = /(json|script|text)/.test(dt);
648 if (scr || s.textarea) {
649 // see if user embedded response in textarea
650 var ta = doc.getElementsByTagName('textarea')[0];
652 xhr.responseText = ta.value;
653 // support for XHR 'status' & 'statusText' emulation :
654 xhr.status = Number( ta.getAttribute('status') ) || xhr.status;
655 xhr.statusText = ta.getAttribute('statusText') || xhr.statusText;
658 // account for browsers injecting pre around json response
659 var pre = doc.getElementsByTagName('pre')[0];
660 var b = doc.getElementsByTagName('body')[0];
662 xhr.responseText = pre.textContent ? pre.textContent : pre.innerText;
665 xhr.responseText = b.textContent ? b.textContent : b.innerText;
669 else if (dt == 'xml' && !xhr.responseXML && xhr.responseText) {
670 xhr.responseXML = toXml(xhr.responseText);
674 data = httpData(xhr, dt, s);
677 status = 'parsererror';
678 xhr.error = errMsg = (err || status);
682 log('error caught: ',err);
684 xhr.error = errMsg = (err || status);
688 log('upload aborted');
692 if (xhr.status) { // we've set xhr.status
693 status = (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) ? 'success' : 'error';
696 // ordering of these callbacks/triggers is odd, but that's how $.ajax does it
697 if (status === 'success') {
699 s.success.call(s.context, data, 'success', xhr);
700 deferred.resolve(xhr.responseText, 'success', xhr);
702 $.event.trigger("ajaxSuccess", [xhr, s]);
705 if (errMsg === undefined)
706 errMsg = xhr.statusText;
708 s.error.call(s.context, xhr, status, errMsg);
709 deferred.reject(xhr, 'error', errMsg);
711 $.event.trigger("ajaxError", [xhr, s, errMsg]);
715 $.event.trigger("ajaxComplete", [xhr, s]);
717 if (g && ! --$.active) {
718 $.event.trigger("ajaxStop");
722 s.complete.call(s.context, xhr, status);
724 callbackProcessed = true;
726 clearTimeout(timeoutHandle);
729 setTimeout(function() {
732 else //adding else to clean up existing iframe response.
733 $io.attr('src', s.iframeSrc);
734 xhr.responseXML = null;
738 var toXml = $.parseXML || function(s, doc) { // use parseXML if available (jQuery 1.5+)
739 if (window.ActiveXObject) {
740 doc = new ActiveXObject('Microsoft.XMLDOM');
745 doc = (new DOMParser()).parseFromString(s, 'text/xml');
747 return (doc && doc.documentElement && doc.documentElement.nodeName != 'parsererror') ? doc : null;
749 var parseJSON = $.parseJSON || function(s) {
750 /*jslint evil:true */
751 return window['eval']('(' + s + ')');
754 var httpData = function( xhr, type, s ) { // mostly lifted from jq1.4.4
756 var ct = xhr.getResponseHeader('content-type') || '',
757 xml = type === 'xml' || !type && ct.indexOf('xml') >= 0,
758 data = xml ? xhr.responseXML : xhr.responseText;
760 if (xml && data.documentElement.nodeName === 'parsererror') {
762 $.error('parsererror');
764 if (s && s.dataFilter) {
765 data = s.dataFilter(data, type);
767 if (typeof data === 'string') {
768 if (type === 'json' || !type && ct.indexOf('json') >= 0) {
769 data = parseJSON(data);
770 } else if (type === "script" || !type && ct.indexOf("javascript") >= 0) {
782 * ajaxForm() provides a mechanism for fully automating form submission.
784 * The advantages of using this method instead of ajaxSubmit() are:
786 * 1: This method will include coordinates for <input type="image" /> elements (if the element
787 * is used to submit the form).
788 * 2. This method will include the submit element's name/value data (for the element that was
789 * used to submit the form).
790 * 3. This method binds the submit() method to the form for you.
792 * The options argument for ajaxForm works exactly as it does for ajaxSubmit. ajaxForm merely
793 * passes the options argument along after properly binding events for submit elements and
796 $.fn.ajaxForm = function(options) {
797 options = options || {};
798 options.delegation = options.delegation && $.isFunction($.fn.on);
800 // in jQuery 1.3+ we can fix mistakes with the ready state
801 if (!options.delegation && this.length === 0) {
802 var o = { s: this.selector, c: this.context };
803 if (!$.isReady && o.s) {
804 log('DOM not ready, queuing ajaxForm');
806 $(o.s,o.c).ajaxForm(options);
810 // is your DOM ready? http://docs.jquery.com/Tutorials:Introducing_$(document).ready()
811 log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)'));
815 if ( options.delegation ) {
817 .off('submit.form-plugin', this.selector, doAjaxSubmit)
818 .off('click.form-plugin', this.selector, captureSubmittingElement)
819 .on('submit.form-plugin', this.selector, options, doAjaxSubmit)
820 .on('click.form-plugin', this.selector, options, captureSubmittingElement);
824 return this.ajaxFormUnbind()
825 .bind('submit.form-plugin', options, doAjaxSubmit)
826 .bind('click.form-plugin', options, captureSubmittingElement);
829 // private event handlers
830 function doAjaxSubmit(e) {
831 /*jshint validthis:true */
832 var options = e.data;
833 if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed
835 $(this).ajaxSubmit(options);
839 function captureSubmittingElement(e) {
840 /*jshint validthis:true */
841 var target = e.target;
843 if (!($el.is("[type=submit],[type=image]"))) {
844 // is this a child element of the submit el? (ex: a span within a button)
845 var t = $el.closest('[type=submit]');
846 if (t.length === 0) {
853 if (target.type == 'image') {
854 if (e.offsetX !== undefined) {
855 form.clk_x = e.offsetX;
856 form.clk_y = e.offsetY;
857 } else if (typeof $.fn.offset == 'function') {
858 var offset = $el.offset();
859 form.clk_x = e.pageX - offset.left;
860 form.clk_y = e.pageY - offset.top;
862 form.clk_x = e.pageX - target.offsetLeft;
863 form.clk_y = e.pageY - target.offsetTop;
867 setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 100);
871 // ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
872 $.fn.ajaxFormUnbind = function() {
873 return this.unbind('submit.form-plugin click.form-plugin');
877 * formToArray() gathers form element data into an array of objects that can
878 * be passed to any of the following ajax functions: $.get, $.post, or load.
879 * Each object in the array has both a 'name' and 'value' property. An example of
880 * an array for a simple login form might be:
882 * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ]
884 * It is this array that is passed to pre-submit callback functions provided to the
885 * ajaxSubmit() and ajaxForm() methods.
887 $.fn.formToArray = function(semantic, elements) {
889 if (this.length === 0) {
894 var els = semantic ? form.getElementsByTagName('*') : form.elements;
899 var i,j,n,v,el,max,jmax;
900 for(i=0, max=els.length; i < max; i++) {
903 if (!n || el.disabled) {
907 if (semantic && form.clk && el.type == "image") {
908 // handle image inputs on the fly when semantic == true
910 a.push({name: n, value: $(el).val(), type: el.type });
911 a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
916 v = $.fieldValue(el, true);
917 if (v && v.constructor == Array) {
920 for(j=0, jmax=v.length; j < jmax; j++) {
921 a.push({name: n, value: v[j]});
924 else if (feature.fileapi && el.type == 'file') {
927 var files = el.files;
929 for (j=0; j < files.length; j++) {
930 a.push({name: n, value: files[j], type: el.type});
935 a.push({ name: n, value: '', type: el.type });
938 else if (v !== null && typeof v != 'undefined') {
941 a.push({name: n, value: v, type: el.type, required: el.required});
945 if (!semantic && form.clk) {
946 // input type=='image' are not found in elements array! handle it here
947 var $input = $(form.clk), input = $input[0];
949 if (n && !input.disabled && input.type == 'image') {
950 a.push({name: n, value: $input.val()});
951 a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
958 * Serializes form data into a 'submittable' string. This method will return a string
959 * in the format: name1=value1&name2=value2
961 $.fn.formSerialize = function(semantic) {
962 //hand off to jQuery.param for proper encoding
963 return $.param(this.formToArray(semantic));
967 * Serializes all field elements in the jQuery object into a query string.
968 * This method will return a string in the format: name1=value1&name2=value2
970 $.fn.fieldSerialize = function(successful) {
972 this.each(function() {
977 var v = $.fieldValue(this, successful);
978 if (v && v.constructor == Array) {
979 for (var i=0,max=v.length; i < max; i++) {
980 a.push({name: n, value: v[i]});
983 else if (v !== null && typeof v != 'undefined') {
984 a.push({name: this.name, value: v});
987 //hand off to jQuery.param for proper encoding
992 * Returns the value(s) of the element in the matched set. For example, consider the following form:
995 * <input name="A" type="text" />
996 * <input name="A" type="text" />
997 * <input name="B" type="checkbox" value="B1" />
998 * <input name="B" type="checkbox" value="B2"/>
999 * <input name="C" type="radio" value="C1" />
1000 * <input name="C" type="radio" value="C2" />
1001 * </fieldset></form>
1003 * var v = $('input[type=text]').fieldValue();
1004 * // if no values are entered into the text inputs
1006 * // if values entered into the text inputs are 'foo' and 'bar'
1007 * v == ['foo','bar']
1009 * var v = $('input[type=checkbox]').fieldValue();
1010 * // if neither checkbox is checked
1012 * // if both checkboxes are checked
1015 * var v = $('input[type=radio]').fieldValue();
1016 * // if neither radio is checked
1018 * // if first radio is checked
1021 * The successful argument controls whether or not the field element must be 'successful'
1022 * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls).
1023 * The default value of the successful argument is true. If this value is false the value(s)
1024 * for each element is returned.
1026 * Note: This method *always* returns an array. If no valid value can be determined the
1027 * array will be empty, otherwise it will contain one or more values.
1029 $.fn.fieldValue = function(successful) {
1030 for (var val=[], i=0, max=this.length; i < max; i++) {
1032 var v = $.fieldValue(el, successful);
1033 if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) {
1036 if (v.constructor == Array)
1045 * Returns the value of the field element.
1047 $.fieldValue = function(el, successful) {
1048 var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
1049 if (successful === undefined) {
1053 if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
1054 (t == 'checkbox' || t == 'radio') && !el.checked ||
1055 (t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
1056 tag == 'select' && el.selectedIndex == -1)) {
1060 if (tag == 'select') {
1061 var index = el.selectedIndex;
1065 var a = [], ops = el.options;
1066 var one = (t == 'select-one');
1067 var max = (one ? index+1 : ops.length);
1068 for(var i=(one ? index : 0); i < max; i++) {
1072 if (!v) { // extra pain for IE...
1073 v = (op.attributes && op.attributes['value'] && !(op.attributes['value'].specified)) ? op.text : op.value;
1087 * Clears the form data. Takes the following actions on the form's input fields:
1088 * - input text fields will have their 'value' property set to the empty string
1089 * - select elements will have their 'selectedIndex' property set to -1
1090 * - checkbox and radio inputs will have their 'checked' property set to false
1091 * - inputs of type submit, button, reset, and hidden will *not* be effected
1092 * - button elements will *not* be effected
1094 $.fn.clearForm = function(includeHidden) {
1095 return this.each(function() {
1096 $('input,select,textarea', this).clearFields(includeHidden);
1101 * Clears the selected form elements.
1103 $.fn.clearFields = $.fn.clearInputs = function(includeHidden) {
1104 var re = /^(?:color|date|datetime|email|month|number|password|range|search|tel|text|time|url|week)$/i; // 'hidden' is not in this list
1105 return this.each(function() {
1106 var t = this.type, tag = this.tagName.toLowerCase();
1107 if (re.test(t) || tag == 'textarea') {
1110 else if (t == 'checkbox' || t == 'radio') {
1111 this.checked = false;
1113 else if (tag == 'select') {
1114 this.selectedIndex = -1;
1116 else if (t == "file") {
1117 if (/MSIE/.test(navigator.userAgent)) {
1118 $(this).replaceWith($(this).clone(true));
1123 else if (includeHidden) {
1124 // includeHidden can be the value true, or it can be a selector string
1125 // indicating a special test; for example:
1126 // $('#myForm').clearForm('.special:hidden')
1127 // the above would clean hidden inputs that have the class of 'special'
1128 if ( (includeHidden === true && /hidden/.test(t)) ||
1129 (typeof includeHidden == 'string' && $(this).is(includeHidden)) )
1136 * Resets the form data. Causes all form elements to be reset to their original value.
1138 $.fn.resetForm = function() {
1139 return this.each(function() {
1140 // guard against an input with the name of 'reset'
1141 // note that IE reports the reset function as an 'object'
1142 if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType)) {
1149 * Enables or disables any matching elements.
1151 $.fn.enable = function(b) {
1152 if (b === undefined) {
1155 return this.each(function() {
1161 * Checks/unchecks any matching checkboxes or radio buttons and
1162 * selects/deselects and matching option elements.
1164 $.fn.selected = function(select) {
1165 if (select === undefined) {
1168 return this.each(function() {
1170 if (t == 'checkbox' || t == 'radio') {
1171 this.checked = select;
1173 else if (this.tagName.toLowerCase() == 'option') {
1174 var $sel = $(this).parent('select');
1175 if (select && $sel[0] && $sel[0].type == 'select-one') {
1176 // deselect all other options
1177 $sel.find('option').selected(false);
1179 this.selected = select;
1185 $.fn.ajaxSubmit.debug = false;
1187 // helper fn for console logging
1189 if (!$.fn.ajaxSubmit.debug)
1191 var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,'');
1192 if (window.console && window.console.log) {
1193 window.console.log(msg);
1195 else if (window.opera && window.opera.postError) {
1196 window.opera.postError(msg);
1200 })( (typeof(jQuery) != 'undefined') ? jQuery : window.Zepto );