3  * version: 2.52 (07-DEC-2010)
 
   4  * @requires jQuery v1.3.2 or later
 
   6  * Examples and documentation at: http://malsup.com/jquery/form/
 
   7  * Dual licensed under the MIT and GPL licenses:
 
   8  *   http://www.opensource.org/licenses/mit-license.php
 
   9  *   http://www.gnu.org/licenses/gpl.html
 
  16         Do not use both ajaxSubmit and ajaxForm on the same form.  These
 
  17         functions are intended to be exclusive.  Use ajaxSubmit if you want
 
  18         to bind your own submit handler to the form.  For example,
 
  20         $(document).ready(function() {
 
  21                 $('#myForm').bind('submit', function(e) {
 
  22                         e.preventDefault(); // <-- important
 
  29         Use ajaxForm when you want the plugin to manage all the event binding
 
  32         $(document).ready(function() {
 
  33                 $('#myForm').ajaxForm({
 
  38         When using ajaxForm, the ajaxSubmit function will be invoked for you
 
  39         at the appropriate time.
 
  43  * ajaxSubmit() provides a mechanism for immediately submitting
 
  44  * an HTML form using AJAX.
 
  46 $.fn.ajaxSubmit = function(options) {
 
  47         // fast fail if nothing selected (http://dev.jquery.com/ticket/2752)
 
  49                 log('ajaxSubmit: skipping submit process - no element selected');
 
  53         if (typeof options == 'function') {
 
  54                 options = { success: options };
 
  57         var action = this.attr('action');
 
  58         var url = (typeof action === 'string') ? $.trim(action) : '';
 
  60                 // clean url (don't include hash vaue)
 
  61                 url = (url.match(/^([^#]+)/)||[])[1];
 
  63         url = url || window.location.href || '';
 
  65         options = $.extend(true, {
 
  67                 type: this.attr('method') || 'GET',
 
  68                 iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank'
 
  71         // hook for manipulating the form data before it is extracted;
 
  72         // convenient for use with rich editors like tinyMCE or FCKEditor
 
  74         this.trigger('form-pre-serialize', [this, options, veto]);
 
  76                 log('ajaxSubmit: submit vetoed via form-pre-serialize trigger');
 
  80         // provide opportunity to alter form data before it is serialized
 
  81         if (options.beforeSerialize && options.beforeSerialize(this, options) === false) {
 
  82                 log('ajaxSubmit: submit aborted via beforeSerialize callback');
 
  86         var n,v,a = this.formToArray(options.semantic);
 
  88                 options.extraData = options.data;
 
  89                 for (n in options.data) {
 
  90                         if(options.data[n] instanceof Array) {
 
  91                                 for (var k in options.data[n]) {
 
  92                                         a.push( { name: n, value: options.data[n][k] } );
 
  97                                 v = $.isFunction(v) ? v() : v; // if value is fn, invoke it
 
  98                                 a.push( { name: n, value: v } );
 
 103         // give pre-submit callback an opportunity to abort the submit
 
 104         if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) {
 
 105                 log('ajaxSubmit: submit aborted via beforeSubmit callback');
 
 109         // fire vetoable 'validate' event
 
 110         this.trigger('form-submit-validate', [a, this, options, veto]);
 
 112                 log('ajaxSubmit: submit vetoed via form-submit-validate trigger');
 
 118         if (options.type.toUpperCase() == 'GET') {
 
 119                 options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
 
 120                 options.data = null;  // data is null for 'get'
 
 123                 options.data = q; // data is the query string for 'post'
 
 126         var $form = this, callbacks = [];
 
 127         if (options.resetForm) {
 
 128                 callbacks.push(function() { $form.resetForm(); });
 
 130         if (options.clearForm) {
 
 131                 callbacks.push(function() { $form.clearForm(); });
 
 134         // perform a load on the target only if dataType is not provided
 
 135         if (!options.dataType && options.target) {
 
 136                 var oldSuccess = options.success || function(){};
 
 137                 callbacks.push(function(data) {
 
 138                         var fn = options.replaceTarget ? 'replaceWith' : 'html';
 
 139                         $(options.target)[fn](data).each(oldSuccess, arguments);
 
 142         else if (options.success) {
 
 143                 callbacks.push(options.success);
 
 146         options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg
 
 147                 var context = options.context || options;   // jQuery 1.4+ supports scope context 
 
 148                 for (var i=0, max=callbacks.length; i < max; i++) {
 
 149                         callbacks[i].apply(context, [data, status, xhr || $form, $form]);
 
 153         // are there files to upload?
 
 154         var fileInputs = $('input:file', this).length > 0;
 
 155         var mp = 'multipart/form-data';
 
 156         var multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp);
 
 158         // options.iframe allows user to force iframe mode
 
 159         // 06-NOV-09: now defaulting to iframe mode if file input is detected
 
 160    if (options.iframe !== false && (fileInputs || options.iframe || multipart)) {
 
 161            // hack to fix Safari hang (thanks to Tim Molendijk for this)
 
 162            // see:  http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
 
 163            if (options.closeKeepAlive) {
 
 164                    $.get(options.closeKeepAlive, fileUpload);
 
 174         // fire 'notify' event
 
 175         this.trigger('form-submit-notify', [this, options]);
 
 179         // private function for handling file uploads (hat tip to YAHOO!)
 
 180         function fileUpload() {
 
 183                 if ($(':input[name=submit],:input[id=submit]', form).length) {
 
 184                         // if there is an input with a name or id of 'submit' then we won't be
 
 185                         // able to invoke the submit fn on the form (at least not x-browser)
 
 186                         alert('Error: Form elements must not have name or id of "submit".');
 
 190                 var s = $.extend(true, {}, $.ajaxSettings, options);
 
 191                 s.context = s.context || s;
 
 192                 var id = 'jqFormIO' + (new Date().getTime()), fn = '_'+id;
 
 193                 window[fn] = function() {
 
 194                         var f = $io.data('form-plugin-onload');
 
 197                                 window[fn] = undefined;
 
 198                                 try { delete window[fn]; } catch(e){}
 
 201                 var $io = $('<iframe id="' + id + '" name="' + id + '" src="'+ s.iframeSrc +'" onload="window[\'_\'+this.id]()" />');
 
 204                 $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });
 
 206                 var xhr = { // mock object
 
 212                         getAllResponseHeaders: function() {},
 
 213                         getResponseHeader: function() {},
 
 214                         setRequestHeader: function() {},
 
 217                                 $io.attr('src', s.iframeSrc); // abort op in progress
 
 222                 // trigger ajax global events so that activity/block indicators work like normal
 
 223                 if (g && ! $.active++) {
 
 224                         $.event.trigger("ajaxStart");
 
 227                         $.event.trigger("ajaxSend", [xhr, s]);
 
 230                 if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) {
 
 240                 var cbInvoked = false;
 
 243                 // add submitting element to data if we know it
 
 247                         if (n && !sub.disabled) {
 
 248                                 s.extraData = s.extraData || {};
 
 249                                 s.extraData[n] = sub.value;
 
 250                                 if (sub.type == "image") {
 
 251                                         s.extraData[n+'.x'] = form.clk_x;
 
 252                                         s.extraData[n+'.y'] = form.clk_y;
 
 257                 // take a breath so that pending repaints get some cpu time before the upload starts
 
 258                 function doSubmit() {
 
 259                         // make sure form attrs are set
 
 260                         var t = $form.attr('target'), a = $form.attr('action');
 
 262                         // update form attrs in IE friendly way
 
 263                         form.setAttribute('target',id);
 
 264                         if (form.getAttribute('method') != 'POST') {
 
 265                                 form.setAttribute('method', 'POST');
 
 267                         if (form.getAttribute('action') != s.url) {
 
 268                                 form.setAttribute('action', s.url);
 
 271                         // ie borks in some cases when setting encoding
 
 272                         if (! s.skipEncodingOverride) {
 
 274                                         encoding: 'multipart/form-data',
 
 275                                         enctype:  'multipart/form-data'
 
 281                                 setTimeout(function() { timedOut = true; cb(); }, s.timeout);
 
 284                         // add "extra" data to form if provided in options
 
 285                         var extraInputs = [];
 
 288                                         for (var n in s.extraData) {
 
 290                                                         $('<input type="hidden" name="'+n+'" value="'+s.extraData[n]+'" />')
 
 295                                 // add iframe to doc and submit the form
 
 296                                 $io.appendTo('body');
 
 297                                 $io.data('form-plugin-onload', cb);
 
 301                                 // reset attrs and remove "extra" input elements
 
 302                                 form.setAttribute('action',a);
 
 304                                         form.setAttribute('target', t);
 
 306                                         $form.removeAttr('target');
 
 308                                 $(extraInputs).remove();
 
 316                         setTimeout(doSubmit, 10); // this lets dom updates render
 
 319                 var data, doc, domCheckCount = 50;
 
 326                         $io.removeData('form-plugin-onload');
 
 333                                 // extract the server response from the iframe
 
 334                                 doc = io.contentWindow ? io.contentWindow.document : io.contentDocument ? io.contentDocument : io.document;
 
 336                                 var isXml = s.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc);
 
 338                                 if (!isXml && window.opera && (doc.body == null || doc.body.innerHTML == '')) {
 
 339                                         if (--domCheckCount) {
 
 340                                                 // in some browsers (Opera) the iframe DOM is not always traversable when
 
 341                                                 // the onload callback fires, so we loop a bit to accommodate
 
 342                                                 log('requeing onLoad callback, DOM not available');
 
 346                                         // let this fall through because server response could be an empty document
 
 347                                         //log('Could not access iframe DOM after mutiple tries.');
 
 348                                         //throw 'DOMException: not available';
 
 351                                 //log('response detected');
 
 353                                 xhr.responseText = doc.documentElement ? doc.documentElement.innerHTML : null; 
 
 354                                 xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
 
 355                                 xhr.getResponseHeader = function(header){
 
 356                                         var headers = {'content-type': s.dataType};
 
 357                                         return headers[header];
 
 360                                 var scr = /(json|script)/.test(s.dataType);
 
 361                                 if (scr || s.textarea) {
 
 362                                         // see if user embedded response in textarea
 
 363                                         var ta = doc.getElementsByTagName('textarea')[0];
 
 365                                                 xhr.responseText = ta.value;
 
 368                                                 // account for browsers injecting pre around json response
 
 369                                                 var pre = doc.getElementsByTagName('pre')[0];
 
 370                                                 var b = doc.getElementsByTagName('body')[0];
 
 372                                                         xhr.responseText = pre.textContent;
 
 375                                                         xhr.responseText = b.innerHTML;
 
 379                                 else if (s.dataType == 'xml' && !xhr.responseXML && xhr.responseText != null) {
 
 380                                         xhr.responseXML = toXml(xhr.responseText);
 
 382                                 data = $.httpData(xhr, s.dataType);
 
 385                                 log('error caught:',e);
 
 388                                 $.handleError(s, xhr, 'error', e);
 
 392                                 log('upload aborted');
 
 396                         // ordering of these callbacks/triggers is odd, but that's how $.ajax does it
 
 398                                 s.success.call(s.context, data, 'success', xhr);
 
 400                                         $.event.trigger("ajaxSuccess", [xhr, s]);
 
 404                                 $.event.trigger("ajaxComplete", [xhr, s]);
 
 406                         if (g && ! --$.active) {
 
 407                                 $.event.trigger("ajaxStop");
 
 410                                 s.complete.call(s.context, xhr, ok ? 'success' : 'error');
 
 414                         setTimeout(function() {
 
 415                                 $io.removeData('form-plugin-onload');
 
 417                                 xhr.responseXML = null;
 
 421                 function toXml(s, doc) {
 
 422                         if (window.ActiveXObject) {
 
 423                                 doc = new ActiveXObject('Microsoft.XMLDOM');
 
 428                                 doc = (new DOMParser()).parseFromString(s, 'text/xml');
 
 430                         return (doc && doc.documentElement && doc.documentElement.tagName != 'parsererror') ? doc : null;
 
 436  * ajaxForm() provides a mechanism for fully automating form submission.
 
 438  * The advantages of using this method instead of ajaxSubmit() are:
 
 440  * 1: This method will include coordinates for <input type="image" /> elements (if the element
 
 441  *      is used to submit the form).
 
 442  * 2. This method will include the submit element's name/value data (for the element that was
 
 443  *      used to submit the form).
 
 444  * 3. This method binds the submit() method to the form for you.
 
 446  * The options argument for ajaxForm works exactly as it does for ajaxSubmit.  ajaxForm merely
 
 447  * passes the options argument along after properly binding events for submit elements and
 
 450 $.fn.ajaxForm = function(options) {
 
 451         // in jQuery 1.3+ we can fix mistakes with the ready state
 
 452         if (this.length === 0) {
 
 453                 var o = { s: this.selector, c: this.context };
 
 454                 if (!$.isReady && o.s) {
 
 455                         log('DOM not ready, queuing ajaxForm');
 
 457                                 $(o.s,o.c).ajaxForm(options);
 
 461                 // is your DOM ready?  http://docs.jquery.com/Tutorials:Introducing_$(document).ready()
 
 462                 log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)'));
 
 466         return this.ajaxFormUnbind().bind('submit.form-plugin', function(e) {
 
 467                 if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed
 
 469                         $(this).ajaxSubmit(options);
 
 471         }).bind('click.form-plugin', function(e) {
 
 472                 var target = e.target;
 
 474                 if (!($el.is(":submit,input:image"))) {
 
 475                         // is this a child element of the submit el?  (ex: a span within a button)
 
 476                         var t = $el.closest(':submit');
 
 484                 if (target.type == 'image') {
 
 485                         if (e.offsetX != undefined) {
 
 486                                 form.clk_x = e.offsetX;
 
 487                                 form.clk_y = e.offsetY;
 
 488                         } else if (typeof $.fn.offset == 'function') { // try to use dimensions plugin
 
 489                                 var offset = $el.offset();
 
 490                                 form.clk_x = e.pageX - offset.left;
 
 491                                 form.clk_y = e.pageY - offset.top;
 
 493                                 form.clk_x = e.pageX - target.offsetLeft;
 
 494                                 form.clk_y = e.pageY - target.offsetTop;
 
 498                 setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 100);
 
 502 // ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
 
 503 $.fn.ajaxFormUnbind = function() {
 
 504         return this.unbind('submit.form-plugin click.form-plugin');
 
 508  * formToArray() gathers form element data into an array of objects that can
 
 509  * be passed to any of the following ajax functions: $.get, $.post, or load.
 
 510  * Each object in the array has both a 'name' and 'value' property.  An example of
 
 511  * an array for a simple login form might be:
 
 513  * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ]
 
 515  * It is this array that is passed to pre-submit callback functions provided to the
 
 516  * ajaxSubmit() and ajaxForm() methods.
 
 518 $.fn.formToArray = function(semantic) {
 
 520         if (this.length === 0) {
 
 525         var els = semantic ? form.getElementsByTagName('*') : form.elements;
 
 530         var i,j,n,v,el,max,jmax;
 
 531         for(i=0, max=els.length; i < max; i++) {
 
 538                 if (semantic && form.clk && el.type == "image") {
 
 539                         // handle image inputs on the fly when semantic == true
 
 540                         if(!el.disabled && form.clk == el) {
 
 541                                 a.push({name: n, value: $(el).val()});
 
 542                                 a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
 
 547                 v = $.fieldValue(el, true);
 
 548                 if (v && v.constructor == Array) {
 
 549                         for(j=0, jmax=v.length; j < jmax; j++) {
 
 550                                 a.push({name: n, value: v[j]});
 
 553                 else if (v !== null && typeof v != 'undefined') {
 
 554                         a.push({name: n, value: v});
 
 558         if (!semantic && form.clk) {
 
 559                 // input type=='image' are not found in elements array! handle it here
 
 560                 var $input = $(form.clk), input = $input[0];
 
 562                 if (n && !input.disabled && input.type == 'image') {
 
 563                         a.push({name: n, value: $input.val()});
 
 564                         a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
 
 571  * Serializes form data into a 'submittable' string. This method will return a string
 
 572  * in the format: name1=value1&name2=value2
 
 574 $.fn.formSerialize = function(semantic) {
 
 575         //hand off to jQuery.param for proper encoding
 
 576         return $.param(this.formToArray(semantic));
 
 580  * Serializes all field elements in the jQuery object into a query string.
 
 581  * This method will return a string in the format: name1=value1&name2=value2
 
 583 $.fn.fieldSerialize = function(successful) {
 
 585         this.each(function() {
 
 590                 var v = $.fieldValue(this, successful);
 
 591                 if (v && v.constructor == Array) {
 
 592                         for (var i=0,max=v.length; i < max; i++) {
 
 593                                 a.push({name: n, value: v[i]});
 
 596                 else if (v !== null && typeof v != 'undefined') {
 
 597                         a.push({name: this.name, value: v});
 
 600         //hand off to jQuery.param for proper encoding
 
 605  * Returns the value(s) of the element in the matched set.  For example, consider the following form:
 
 608  *        <input name="A" type="text" />
 
 609  *        <input name="A" type="text" />
 
 610  *        <input name="B" type="checkbox" value="B1" />
 
 611  *        <input name="B" type="checkbox" value="B2"/>
 
 612  *        <input name="C" type="radio" value="C1" />
 
 613  *        <input name="C" type="radio" value="C2" />
 
 616  *  var v = $(':text').fieldValue();
 
 617  *  // if no values are entered into the text inputs
 
 619  *  // if values entered into the text inputs are 'foo' and 'bar'
 
 622  *  var v = $(':checkbox').fieldValue();
 
 623  *  // if neither checkbox is checked
 
 625  *  // if both checkboxes are checked
 
 628  *  var v = $(':radio').fieldValue();
 
 629  *  // if neither radio is checked
 
 631  *  // if first radio is checked
 
 634  * The successful argument controls whether or not the field element must be 'successful'
 
 635  * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls).
 
 636  * The default value of the successful argument is true.  If this value is false the value(s)
 
 637  * for each element is returned.
 
 639  * Note: This method *always* returns an array.  If no valid value can be determined the
 
 640  *         array will be empty, otherwise it will contain one or more values.
 
 642 $.fn.fieldValue = function(successful) {
 
 643         for (var val=[], i=0, max=this.length; i < max; i++) {
 
 645                 var v = $.fieldValue(el, successful);
 
 646                 if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) {
 
 649                 v.constructor == Array ? $.merge(val, v) : val.push(v);
 
 655  * Returns the value of the field element.
 
 657 $.fieldValue = function(el, successful) {
 
 658         var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
 
 659         if (successful === undefined) {
 
 663         if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
 
 664                 (t == 'checkbox' || t == 'radio') && !el.checked ||
 
 665                 (t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
 
 666                 tag == 'select' && el.selectedIndex == -1)) {
 
 670         if (tag == 'select') {
 
 671                 var index = el.selectedIndex;
 
 675                 var a = [], ops = el.options;
 
 676                 var one = (t == 'select-one');
 
 677                 var max = (one ? index+1 : ops.length);
 
 678                 for(var i=(one ? index : 0); i < max; i++) {
 
 682                                 if (!v) { // extra pain for IE...
 
 683                                         v = (op.attributes && op.attributes['value'] && !(op.attributes['value'].specified)) ? op.text : op.value;
 
 697  * Clears the form data.  Takes the following actions on the form's input fields:
 
 698  *  - input text fields will have their 'value' property set to the empty string
 
 699  *  - select elements will have their 'selectedIndex' property set to -1
 
 700  *  - checkbox and radio inputs will have their 'checked' property set to false
 
 701  *  - inputs of type submit, button, reset, and hidden will *not* be effected
 
 702  *  - button elements will *not* be effected
 
 704 $.fn.clearForm = function() {
 
 705         return this.each(function() {
 
 706                 $('input,select,textarea', this).clearFields();
 
 711  * Clears the selected form elements.
 
 713 $.fn.clearFields = $.fn.clearInputs = function() {
 
 714         return this.each(function() {
 
 715                 var t = this.type, tag = this.tagName.toLowerCase();
 
 716                 if (t == 'text' || t == 'password' || tag == 'textarea') {
 
 719                 else if (t == 'checkbox' || t == 'radio') {
 
 720                         this.checked = false;
 
 722                 else if (tag == 'select') {
 
 723                         this.selectedIndex = -1;
 
 729  * Resets the form data.  Causes all form elements to be reset to their original value.
 
 731 $.fn.resetForm = function() {
 
 732         return this.each(function() {
 
 733                 // guard against an input with the name of 'reset'
 
 734                 // note that IE reports the reset function as an 'object'
 
 735                 if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType)) {
 
 742  * Enables or disables any matching elements.
 
 744 $.fn.enable = function(b) {
 
 745         if (b === undefined) {
 
 748         return this.each(function() {
 
 754  * Checks/unchecks any matching checkboxes or radio buttons and
 
 755  * selects/deselects and matching option elements.
 
 757 $.fn.selected = function(select) {
 
 758         if (select === undefined) {
 
 761         return this.each(function() {
 
 763                 if (t == 'checkbox' || t == 'radio') {
 
 764                         this.checked = select;
 
 766                 else if (this.tagName.toLowerCase() == 'option') {
 
 767                         var $sel = $(this).parent('select');
 
 768                         if (select && $sel[0] && $sel[0].type == 'select-one') {
 
 769                                 // deselect all other options
 
 770                                 $sel.find('option').selected(false);
 
 772                         this.selected = select;
 
 777 // helper fn for console logging
 
 778 // set $.fn.ajaxSubmit.debug to true to enable debug logging
 
 780         if ($.fn.ajaxSubmit.debug) {
 
 781                 var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,'');
 
 782                 if (window.console && window.console.log) {
 
 783                         window.console.log(msg);
 
 785                 else if (window.opera && window.opera.postError) {
 
 786                         window.opera.postError(msg);