3 * version: 3.45.0-2013.10.17
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) {
317 //Send FormData() provided by user
318 if (options.formData)
319 o.data = options.formData;
323 beforeSend.call(this, xhr, o);
328 // private function for handling file uploads (hat tip to YAHOO!)
329 function fileUploadIframe(a) {
330 var form = $form[0], el, i, s, g, id, $io, io, xhr, sub, n, timedOut, timeoutHandle;
331 var deferred = $.Deferred();
334 deferred.abort = function(status) {
339 // ensure that every serialized input is still enabled
340 for (i=0; i < elements.length; i++) {
343 el.prop('disabled', false);
345 el.removeAttr('disabled');
349 s = $.extend(true, {}, $.ajaxSettings, options);
350 s.context = s.context || s;
351 id = 'jqFormIO' + (new Date().getTime());
352 if (s.iframeTarget) {
353 $io = $(s.iframeTarget);
354 n = $io.attr2('name');
356 $io.attr2('name', id);
361 $io = $('<iframe name="' + id + '" src="'+ s.iframeSrc +'" />');
362 $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });
367 xhr = { // mock object
373 getAllResponseHeaders: function() {},
374 getResponseHeader: function() {},
375 setRequestHeader: function() {},
376 abort: function(status) {
377 var e = (status === 'timeout' ? 'timeout' : 'aborted');
378 log('aborting upload... ' + e);
382 if (io.contentWindow.document.execCommand) {
383 io.contentWindow.document.execCommand('Stop');
388 $io.attr('src', s.iframeSrc); // abort op in progress
391 s.error.call(s.context, xhr, e, status);
393 $.event.trigger("ajaxError", [xhr, s, e]);
395 s.complete.call(s.context, xhr, e);
400 // trigger ajax global events so that activity/block indicators work like normal
401 if (g && 0 === $.active++) {
402 $.event.trigger("ajaxStart");
405 $.event.trigger("ajaxSend", [xhr, s]);
408 if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) {
420 // add submitting element to data if we know it
424 if (n && !sub.disabled) {
425 s.extraData = s.extraData || {};
426 s.extraData[n] = sub.value;
427 if (sub.type == "image") {
428 s.extraData[n+'.x'] = form.clk_x;
429 s.extraData[n+'.y'] = form.clk_y;
434 var CLIENT_TIMEOUT_ABORT = 1;
435 var SERVER_ABORT = 2;
437 function getDoc(frame) {
438 /* it looks like contentWindow or contentDocument do not
439 * carry the protocol property in ie8, when running under ssl
440 * frame.document is the only valid response document, since
441 * the protocol is know but not on the other two objects. strange?
442 * "Same origin policy" http://en.wikipedia.org/wiki/Same_origin_policy
447 // IE8 cascading access check
449 if (frame.contentWindow) {
450 doc = frame.contentWindow.document;
453 // IE8 access denied under ssl & missing protocol
454 log('cannot get iframe.contentWindow document: ' + err);
457 if (doc) { // successful getting content
461 try { // simply checking may throw in ie8 under ssl or mismatched protocol
462 doc = frame.contentDocument ? frame.contentDocument : frame.document;
465 log('cannot get iframe.contentDocument: ' + err);
466 doc = frame.document;
471 // Rails CSRF hack (thanks to Yvan Barthelemy)
472 var csrf_token = $('meta[name=csrf-token]').attr('content');
473 var csrf_param = $('meta[name=csrf-param]').attr('content');
474 if (csrf_param && csrf_token) {
475 s.extraData = s.extraData || {};
476 s.extraData[csrf_param] = csrf_token;
479 // take a breath so that pending repaints get some cpu time before the upload starts
480 function doSubmit() {
481 // make sure form attrs are set
482 var t = $form.attr2('target'), a = $form.attr2('action');
484 // update form attrs in IE friendly way
485 form.setAttribute('target',id);
486 if (!method || /post/i.test(method) ) {
487 form.setAttribute('method', 'POST');
490 form.setAttribute('action', s.url);
493 // ie borks in some cases when setting encoding
494 if (! s.skipEncodingOverride && (!method || /post/i.test(method))) {
496 encoding: 'multipart/form-data',
497 enctype: 'multipart/form-data'
503 timeoutHandle = setTimeout(function() { timedOut = true; cb(CLIENT_TIMEOUT_ABORT); }, s.timeout);
506 // look for server aborts
507 function checkState() {
509 var state = getDoc(io).readyState;
510 log('state = ' + state);
511 if (state && state.toLowerCase() == 'uninitialized')
512 setTimeout(checkState,50);
515 log('Server abort: ' , e, ' (', e.name, ')');
518 clearTimeout(timeoutHandle);
519 timeoutHandle = undefined;
523 // add "extra" data to form if provided in options
524 var extraInputs = [];
527 for (var n in s.extraData) {
528 if (s.extraData.hasOwnProperty(n)) {
529 // if using the $.param format that allows for multiple values with the same name
530 if($.isPlainObject(s.extraData[n]) && s.extraData[n].hasOwnProperty('name') && s.extraData[n].hasOwnProperty('value')) {
532 $('<input type="hidden" name="'+s.extraData[n].name+'">').val(s.extraData[n].value)
536 $('<input type="hidden" name="'+n+'">').val(s.extraData[n])
543 if (!s.iframeTarget) {
544 // add iframe to doc and submit the form
545 $io.appendTo('body');
548 io.attachEvent('onload', cb);
550 io.addEventListener('load', cb, false);
551 setTimeout(checkState,15);
556 // just in case form has element with name/id of 'submit'
557 var submitFn = document.createElement('form').submit;
558 submitFn.apply(form);
562 // reset attrs and remove "extra" input elements
563 form.setAttribute('action',a);
565 form.setAttribute('target', t);
567 $form.removeAttr('target');
569 $(extraInputs).remove();
577 setTimeout(doSubmit, 10); // this lets dom updates render
580 var data, doc, domCheckCount = 50, callbackProcessed;
583 if (xhr.aborted || callbackProcessed) {
589 log('cannot access response document');
592 if (e === CLIENT_TIMEOUT_ABORT && xhr) {
593 xhr.abort('timeout');
594 deferred.reject(xhr, 'timeout');
597 else if (e == SERVER_ABORT && xhr) {
598 xhr.abort('server abort');
599 deferred.reject(xhr, 'error', 'server abort');
603 if (!doc || doc.location.href == s.iframeSrc) {
604 // response not received yet
609 io.detachEvent('onload', cb);
611 io.removeEventListener('load', cb, false);
613 var status = 'success', errMsg;
619 var isXml = s.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc);
621 if (!isXml && window.opera && (doc.body === null || !doc.body.innerHTML)) {
622 if (--domCheckCount) {
623 // in some browsers (Opera) the iframe DOM is not always traversable when
624 // the onload callback fires, so we loop a bit to accommodate
625 log('requeing onLoad callback, DOM not available');
629 // let this fall through because server response could be an empty document
630 //log('Could not access iframe DOM after mutiple tries.');
631 //throw 'DOMException: not available';
634 //log('response detected');
635 var docRoot = doc.body ? doc.body : doc.documentElement;
636 xhr.responseText = docRoot ? docRoot.innerHTML : null;
637 xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
640 xhr.getResponseHeader = function(header){
641 var headers = {'content-type': s.dataType};
642 return headers[header.toLowerCase()];
644 // support for XHR 'status' & 'statusText' emulation :
646 xhr.status = Number( docRoot.getAttribute('status') ) || xhr.status;
647 xhr.statusText = docRoot.getAttribute('statusText') || xhr.statusText;
650 var dt = (s.dataType || '').toLowerCase();
651 var scr = /(json|script|text)/.test(dt);
652 if (scr || s.textarea) {
653 // see if user embedded response in textarea
654 var ta = doc.getElementsByTagName('textarea')[0];
656 xhr.responseText = ta.value;
657 // support for XHR 'status' & 'statusText' emulation :
658 xhr.status = Number( ta.getAttribute('status') ) || xhr.status;
659 xhr.statusText = ta.getAttribute('statusText') || xhr.statusText;
662 // account for browsers injecting pre around json response
663 var pre = doc.getElementsByTagName('pre')[0];
664 var b = doc.getElementsByTagName('body')[0];
666 xhr.responseText = pre.textContent ? pre.textContent : pre.innerText;
669 xhr.responseText = b.textContent ? b.textContent : b.innerText;
673 else if (dt == 'xml' && !xhr.responseXML && xhr.responseText) {
674 xhr.responseXML = toXml(xhr.responseText);
678 data = httpData(xhr, dt, s);
681 status = 'parsererror';
682 xhr.error = errMsg = (err || status);
686 log('error caught: ',err);
688 xhr.error = errMsg = (err || status);
692 log('upload aborted');
696 if (xhr.status) { // we've set xhr.status
697 status = (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) ? 'success' : 'error';
700 // ordering of these callbacks/triggers is odd, but that's how $.ajax does it
701 if (status === 'success') {
703 s.success.call(s.context, data, 'success', xhr);
704 deferred.resolve(xhr.responseText, 'success', xhr);
706 $.event.trigger("ajaxSuccess", [xhr, s]);
709 if (errMsg === undefined)
710 errMsg = xhr.statusText;
712 s.error.call(s.context, xhr, status, errMsg);
713 deferred.reject(xhr, 'error', errMsg);
715 $.event.trigger("ajaxError", [xhr, s, errMsg]);
719 $.event.trigger("ajaxComplete", [xhr, s]);
721 if (g && ! --$.active) {
722 $.event.trigger("ajaxStop");
726 s.complete.call(s.context, xhr, status);
728 callbackProcessed = true;
730 clearTimeout(timeoutHandle);
733 setTimeout(function() {
736 else //adding else to clean up existing iframe response.
737 $io.attr('src', s.iframeSrc);
738 xhr.responseXML = null;
742 var toXml = $.parseXML || function(s, doc) { // use parseXML if available (jQuery 1.5+)
743 if (window.ActiveXObject) {
744 doc = new ActiveXObject('Microsoft.XMLDOM');
749 doc = (new DOMParser()).parseFromString(s, 'text/xml');
751 return (doc && doc.documentElement && doc.documentElement.nodeName != 'parsererror') ? doc : null;
753 var parseJSON = $.parseJSON || function(s) {
754 /*jslint evil:true */
755 return window['eval']('(' + s + ')');
758 var httpData = function( xhr, type, s ) { // mostly lifted from jq1.4.4
760 var ct = xhr.getResponseHeader('content-type') || '',
761 xml = type === 'xml' || !type && ct.indexOf('xml') >= 0,
762 data = xml ? xhr.responseXML : xhr.responseText;
764 if (xml && data.documentElement.nodeName === 'parsererror') {
766 $.error('parsererror');
768 if (s && s.dataFilter) {
769 data = s.dataFilter(data, type);
771 if (typeof data === 'string') {
772 if (type === 'json' || !type && ct.indexOf('json') >= 0) {
773 data = parseJSON(data);
774 } else if (type === "script" || !type && ct.indexOf("javascript") >= 0) {
786 * ajaxForm() provides a mechanism for fully automating form submission.
788 * The advantages of using this method instead of ajaxSubmit() are:
790 * 1: This method will include coordinates for <input type="image" /> elements (if the element
791 * is used to submit the form).
792 * 2. This method will include the submit element's name/value data (for the element that was
793 * used to submit the form).
794 * 3. This method binds the submit() method to the form for you.
796 * The options argument for ajaxForm works exactly as it does for ajaxSubmit. ajaxForm merely
797 * passes the options argument along after properly binding events for submit elements and
800 $.fn.ajaxForm = function(options) {
801 options = options || {};
802 options.delegation = options.delegation && $.isFunction($.fn.on);
804 // in jQuery 1.3+ we can fix mistakes with the ready state
805 if (!options.delegation && this.length === 0) {
806 var o = { s: this.selector, c: this.context };
807 if (!$.isReady && o.s) {
808 log('DOM not ready, queuing ajaxForm');
810 $(o.s,o.c).ajaxForm(options);
814 // is your DOM ready? http://docs.jquery.com/Tutorials:Introducing_$(document).ready()
815 log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)'));
819 if ( options.delegation ) {
821 .off('submit.form-plugin', this.selector, doAjaxSubmit)
822 .off('click.form-plugin', this.selector, captureSubmittingElement)
823 .on('submit.form-plugin', this.selector, options, doAjaxSubmit)
824 .on('click.form-plugin', this.selector, options, captureSubmittingElement);
828 return this.ajaxFormUnbind()
829 .bind('submit.form-plugin', options, doAjaxSubmit)
830 .bind('click.form-plugin', options, captureSubmittingElement);
833 // private event handlers
834 function doAjaxSubmit(e) {
835 /*jshint validthis:true */
836 var options = e.data;
837 if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed
839 $(e.target).ajaxSubmit(options); // #365
843 function captureSubmittingElement(e) {
844 /*jshint validthis:true */
845 var target = e.target;
847 if (!($el.is("[type=submit],[type=image]"))) {
848 // is this a child element of the submit el? (ex: a span within a button)
849 var t = $el.closest('[type=submit]');
850 if (t.length === 0) {
857 if (target.type == 'image') {
858 if (e.offsetX !== undefined) {
859 form.clk_x = e.offsetX;
860 form.clk_y = e.offsetY;
861 } else if (typeof $.fn.offset == 'function') {
862 var offset = $el.offset();
863 form.clk_x = e.pageX - offset.left;
864 form.clk_y = e.pageY - offset.top;
866 form.clk_x = e.pageX - target.offsetLeft;
867 form.clk_y = e.pageY - target.offsetTop;
871 setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 100);
875 // ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
876 $.fn.ajaxFormUnbind = function() {
877 return this.unbind('submit.form-plugin click.form-plugin');
881 * formToArray() gathers form element data into an array of objects that can
882 * be passed to any of the following ajax functions: $.get, $.post, or load.
883 * Each object in the array has both a 'name' and 'value' property. An example of
884 * an array for a simple login form might be:
886 * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ]
888 * It is this array that is passed to pre-submit callback functions provided to the
889 * ajaxSubmit() and ajaxForm() methods.
891 $.fn.formToArray = function(semantic, elements) {
893 if (this.length === 0) {
898 var els = semantic ? form.getElementsByTagName('*') : form.elements;
903 var i,j,n,v,el,max,jmax;
904 for(i=0, max=els.length; i < max; i++) {
907 if (!n || el.disabled) {
911 if (semantic && form.clk && el.type == "image") {
912 // handle image inputs on the fly when semantic == true
914 a.push({name: n, value: $(el).val(), type: el.type });
915 a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
920 v = $.fieldValue(el, true);
921 if (v && v.constructor == Array) {
924 for(j=0, jmax=v.length; j < jmax; j++) {
925 a.push({name: n, value: v[j]});
928 else if (feature.fileapi && el.type == 'file') {
931 var files = el.files;
933 for (j=0; j < files.length; j++) {
934 a.push({name: n, value: files[j], type: el.type});
939 a.push({ name: n, value: '', type: el.type });
942 else if (v !== null && typeof v != 'undefined') {
945 a.push({name: n, value: v, type: el.type, required: el.required});
949 if (!semantic && form.clk) {
950 // input type=='image' are not found in elements array! handle it here
951 var $input = $(form.clk), input = $input[0];
953 if (n && !input.disabled && input.type == 'image') {
954 a.push({name: n, value: $input.val()});
955 a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
962 * Serializes form data into a 'submittable' string. This method will return a string
963 * in the format: name1=value1&name2=value2
965 $.fn.formSerialize = function(semantic) {
966 //hand off to jQuery.param for proper encoding
967 return $.param(this.formToArray(semantic));
971 * Serializes all field elements in the jQuery object into a query string.
972 * This method will return a string in the format: name1=value1&name2=value2
974 $.fn.fieldSerialize = function(successful) {
976 this.each(function() {
981 var v = $.fieldValue(this, successful);
982 if (v && v.constructor == Array) {
983 for (var i=0,max=v.length; i < max; i++) {
984 a.push({name: n, value: v[i]});
987 else if (v !== null && typeof v != 'undefined') {
988 a.push({name: this.name, value: v});
991 //hand off to jQuery.param for proper encoding
996 * Returns the value(s) of the element in the matched set. For example, consider the following form:
999 * <input name="A" type="text" />
1000 * <input name="A" type="text" />
1001 * <input name="B" type="checkbox" value="B1" />
1002 * <input name="B" type="checkbox" value="B2"/>
1003 * <input name="C" type="radio" value="C1" />
1004 * <input name="C" type="radio" value="C2" />
1005 * </fieldset></form>
1007 * var v = $('input[type=text]').fieldValue();
1008 * // if no values are entered into the text inputs
1010 * // if values entered into the text inputs are 'foo' and 'bar'
1011 * v == ['foo','bar']
1013 * var v = $('input[type=checkbox]').fieldValue();
1014 * // if neither checkbox is checked
1016 * // if both checkboxes are checked
1019 * var v = $('input[type=radio]').fieldValue();
1020 * // if neither radio is checked
1022 * // if first radio is checked
1025 * The successful argument controls whether or not the field element must be 'successful'
1026 * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls).
1027 * The default value of the successful argument is true. If this value is false the value(s)
1028 * for each element is returned.
1030 * Note: This method *always* returns an array. If no valid value can be determined the
1031 * array will be empty, otherwise it will contain one or more values.
1033 $.fn.fieldValue = function(successful) {
1034 for (var val=[], i=0, max=this.length; i < max; i++) {
1036 var v = $.fieldValue(el, successful);
1037 if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) {
1040 if (v.constructor == Array)
1049 * Returns the value of the field element.
1051 $.fieldValue = function(el, successful) {
1052 var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
1053 if (successful === undefined) {
1057 if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
1058 (t == 'checkbox' || t == 'radio') && !el.checked ||
1059 (t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
1060 tag == 'select' && el.selectedIndex == -1)) {
1064 if (tag == 'select') {
1065 var index = el.selectedIndex;
1069 var a = [], ops = el.options;
1070 var one = (t == 'select-one');
1071 var max = (one ? index+1 : ops.length);
1072 for(var i=(one ? index : 0); i < max; i++) {
1076 if (!v) { // extra pain for IE...
1077 v = (op.attributes && op.attributes['value'] && !(op.attributes['value'].specified)) ? op.text : op.value;
1091 * Clears the form data. Takes the following actions on the form's input fields:
1092 * - input text fields will have their 'value' property set to the empty string
1093 * - select elements will have their 'selectedIndex' property set to -1
1094 * - checkbox and radio inputs will have their 'checked' property set to false
1095 * - inputs of type submit, button, reset, and hidden will *not* be effected
1096 * - button elements will *not* be effected
1098 $.fn.clearForm = function(includeHidden) {
1099 return this.each(function() {
1100 $('input,select,textarea', this).clearFields(includeHidden);
1105 * Clears the selected form elements.
1107 $.fn.clearFields = $.fn.clearInputs = function(includeHidden) {
1108 var re = /^(?:color|date|datetime|email|month|number|password|range|search|tel|text|time|url|week)$/i; // 'hidden' is not in this list
1109 return this.each(function() {
1110 var t = this.type, tag = this.tagName.toLowerCase();
1111 if (re.test(t) || tag == 'textarea') {
1114 else if (t == 'checkbox' || t == 'radio') {
1115 this.checked = false;
1117 else if (tag == 'select') {
1118 this.selectedIndex = -1;
1120 else if (t == "file") {
1121 if (/MSIE/.test(navigator.userAgent)) {
1122 $(this).replaceWith($(this).clone(true));
1127 else if (includeHidden) {
1128 // includeHidden can be the value true, or it can be a selector string
1129 // indicating a special test; for example:
1130 // $('#myForm').clearForm('.special:hidden')
1131 // the above would clean hidden inputs that have the class of 'special'
1132 if ( (includeHidden === true && /hidden/.test(t)) ||
1133 (typeof includeHidden == 'string' && $(this).is(includeHidden)) )
1140 * Resets the form data. Causes all form elements to be reset to their original value.
1142 $.fn.resetForm = function() {
1143 return this.each(function() {
1144 // guard against an input with the name of 'reset'
1145 // note that IE reports the reset function as an 'object'
1146 if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType)) {
1153 * Enables or disables any matching elements.
1155 $.fn.enable = function(b) {
1156 if (b === undefined) {
1159 return this.each(function() {
1165 * Checks/unchecks any matching checkboxes or radio buttons and
1166 * selects/deselects and matching option elements.
1168 $.fn.selected = function(select) {
1169 if (select === undefined) {
1172 return this.each(function() {
1174 if (t == 'checkbox' || t == 'radio') {
1175 this.checked = select;
1177 else if (this.tagName.toLowerCase() == 'option') {
1178 var $sel = $(this).parent('select');
1179 if (select && $sel[0] && $sel[0].type == 'select-one') {
1180 // deselect all other options
1181 $sel.find('option').selected(false);
1183 this.selected = select;
1189 $.fn.ajaxSubmit.debug = false;
1191 // helper fn for console logging
1193 if (!$.fn.ajaxSubmit.debug)
1195 var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,'');
1196 if (window.console && window.console.log) {
1197 window.console.log(msg);
1199 else if (window.opera && window.opera.postError) {
1200 window.opera.postError(msg);
1204 })( (typeof(jQuery) != 'undefined') ? jQuery : window.Zepto );