web/wp-includes/js/jquery/jquery.form.dev.js
changeset 194 32102edaa81b
parent 136 bde1974c263b
equal deleted inserted replaced
193:2f6f6f7551ca 194:32102edaa81b
     1 /*
     1 /*!
     2  * jQuery Form Plugin
     2  * jQuery Form Plugin
     3  * version: 2.02 (12/16/2007)
     3  * version: 2.73 (03-MAY-2011)
     4  * @requires jQuery v1.1 or later
     4  * @requires jQuery v1.3.2 or later
     5  *
     5  *
     6  * Examples at: http://malsup.com/jquery/form/
     6  * Examples and documentation at: http://malsup.com/jquery/form/
     7  * Dual licensed under the MIT and GPL licenses:
     7  * Dual licensed under the MIT and GPL licenses:
     8  *   http://www.opensource.org/licenses/mit-license.php
     8  *   http://www.opensource.org/licenses/mit-license.php
     9  *   http://www.gnu.org/licenses/gpl.html
     9  *   http://www.gnu.org/licenses/gpl.html
    10  *
    10  */
    11  * Revision: $Id$
    11 ;(function($) {
    12  */
    12 
    13  (function($) {
    13 /*
    14 /**
    14 	Usage Note:
    15  * ajaxSubmit() provides a mechanism for submitting an HTML form using AJAX.
    15 	-----------
    16  *
    16 	Do not use both ajaxSubmit and ajaxForm on the same form.  These
    17  * ajaxSubmit accepts a single argument which can be either a success callback function
    17 	functions are intended to be exclusive.  Use ajaxSubmit if you want
    18  * or an options Object.  If a function is provided it will be invoked upon successful
    18 	to bind your own submit handler to the form.  For example,
    19  * completion of the submit and will be passed the response from the server.
    19 
    20  * If an options Object is provided, the following attributes are supported:
    20 	$(document).ready(function() {
    21  *
    21 		$('#myForm').bind('submit', function(e) {
    22  *  target:   Identifies the element(s) in the page to be updated with the server response.
    22 			e.preventDefault(); // <-- important
    23  *            This value may be specified as a jQuery selection string, a jQuery object,
    23 			$(this).ajaxSubmit({
    24  *            or a DOM element.
    24 				target: '#output'
    25  *            default value: null
    25 			});
    26  *
    26 		});
    27  *  url:      URL to which the form data will be submitted.
    27 	});
    28  *            default value: value of form's 'action' attribute
    28 
    29  *
    29 	Use ajaxForm when you want the plugin to manage all the event binding
    30  *  type:     The method in which the form data should be submitted, 'GET' or 'POST'.
    30 	for you.  For example,
    31  *            default value: value of form's 'method' attribute (or 'GET' if none found)
    31 
    32  *
    32 	$(document).ready(function() {
    33  *  data:     Additional data to add to the request, specified as key/value pairs (see $.ajax).
    33 		$('#myForm').ajaxForm({
    34  *
    34 			target: '#output'
    35  *  beforeSubmit:  Callback method to be invoked before the form is submitted.
    35 		});
    36  *            default value: null
    36 	});
    37  *
    37 
    38  *  success:  Callback method to be invoked after the form has been successfully submitted
    38 	When using ajaxForm, the ajaxSubmit function will be invoked for you
    39  *            and the response has been returned from the server
    39 	at the appropriate time.
    40  *            default value: null
    40 */
    41  *
    41 
    42  *  dataType: Expected dataType of the response.  One of: null, 'xml', 'script', or 'json'
    42 /**
    43  *            default value: null
    43  * ajaxSubmit() provides a mechanism for immediately submitting
    44  *
    44  * an HTML form using AJAX.
    45  *  semantic: Boolean flag indicating whether data must be submitted in semantic order (slower).
       
    46  *            default value: false
       
    47  *
       
    48  *  resetForm: Boolean flag indicating whether the form should be reset if the submit is successful
       
    49  *
       
    50  *  clearForm: Boolean flag indicating whether the form should be cleared if the submit is successful
       
    51  *
       
    52  *
       
    53  * The 'beforeSubmit' callback can be provided as a hook for running pre-submit logic or for
       
    54  * validating the form data.  If the 'beforeSubmit' callback returns false then the form will
       
    55  * not be submitted. The 'beforeSubmit' callback is invoked with three arguments: the form data
       
    56  * in array format, the jQuery object, and the options object passed into ajaxSubmit.
       
    57  * The form data array takes the following form:
       
    58  *
       
    59  *     [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ]
       
    60  *
       
    61  * If a 'success' callback method is provided it is invoked after the response has been returned
       
    62  * from the server.  It is passed the responseText or responseXML value (depending on dataType).
       
    63  * See jQuery.ajax for further details.
       
    64  *
       
    65  *
       
    66  * The dataType option provides a means for specifying how the server response should be handled.
       
    67  * This maps directly to the jQuery.httpData method.  The following values are supported:
       
    68  *
       
    69  *      'xml':    if dataType == 'xml' the server response is treated as XML and the 'success'
       
    70  *                   callback method, if specified, will be passed the responseXML value
       
    71  *      'json':   if dataType == 'json' the server response will be evaluted and passed to
       
    72  *                   the 'success' callback, if specified
       
    73  *      'script': if dataType == 'script' the server response is evaluated in the global context
       
    74  *
       
    75  *
       
    76  * Note that it does not make sense to use both the 'target' and 'dataType' options.  If both
       
    77  * are provided the target will be ignored.
       
    78  *
       
    79  * The semantic argument can be used to force form serialization in semantic order.
       
    80  * This is normally true anyway, unless the form contains input elements of type='image'.
       
    81  * If your form must be submitted with name/value pairs in semantic order and your form
       
    82  * contains an input of type='image" then pass true for this arg, otherwise pass false
       
    83  * (or nothing) to avoid the overhead for this logic.
       
    84  *
       
    85  *
       
    86  * When used on its own, ajaxSubmit() is typically bound to a form's submit event like this:
       
    87  *
       
    88  * $("#form-id").submit(function() {
       
    89  *     $(this).ajaxSubmit(options);
       
    90  *     return false; // cancel conventional submit
       
    91  * });
       
    92  *
       
    93  * When using ajaxForm(), however, this is done for you.
       
    94  *
       
    95  * @example
       
    96  * $('#myForm').ajaxSubmit(function(data) {
       
    97  *     alert('Form submit succeeded! Server returned: ' + data);
       
    98  * });
       
    99  * @desc Submit form and alert server response
       
   100  *
       
   101  *
       
   102  * @example
       
   103  * var options = {
       
   104  *     target: '#myTargetDiv'
       
   105  * };
       
   106  * $('#myForm').ajaxSubmit(options);
       
   107  * @desc Submit form and update page element with server response
       
   108  *
       
   109  *
       
   110  * @example
       
   111  * var options = {
       
   112  *     success: function(responseText) {
       
   113  *         alert(responseText);
       
   114  *     }
       
   115  * };
       
   116  * $('#myForm').ajaxSubmit(options);
       
   117  * @desc Submit form and alert the server response
       
   118  *
       
   119  *
       
   120  * @example
       
   121  * var options = {
       
   122  *     beforeSubmit: function(formArray, jqForm) {
       
   123  *         if (formArray.length == 0) {
       
   124  *             alert('Please enter data.');
       
   125  *             return false;
       
   126  *         }
       
   127  *     }
       
   128  * };
       
   129  * $('#myForm').ajaxSubmit(options);
       
   130  * @desc Pre-submit validation which aborts the submit operation if form data is empty
       
   131  *
       
   132  *
       
   133  * @example
       
   134  * var options = {
       
   135  *     url: myJsonUrl.php,
       
   136  *     dataType: 'json',
       
   137  *     success: function(data) {
       
   138  *        // 'data' is an object representing the the evaluated json data
       
   139  *     }
       
   140  * };
       
   141  * $('#myForm').ajaxSubmit(options);
       
   142  * @desc json data returned and evaluated
       
   143  *
       
   144  *
       
   145  * @example
       
   146  * var options = {
       
   147  *     url: myXmlUrl.php,
       
   148  *     dataType: 'xml',
       
   149  *     success: function(responseXML) {
       
   150  *        // responseXML is XML document object
       
   151  *        var data = $('myElement', responseXML).text();
       
   152  *     }
       
   153  * };
       
   154  * $('#myForm').ajaxSubmit(options);
       
   155  * @desc XML data returned from server
       
   156  *
       
   157  *
       
   158  * @example
       
   159  * var options = {
       
   160  *     resetForm: true
       
   161  * };
       
   162  * $('#myForm').ajaxSubmit(options);
       
   163  * @desc submit form and reset it if successful
       
   164  *
       
   165  * @example
       
   166  * $('#myForm).submit(function() {
       
   167  *    $(this).ajaxSubmit();
       
   168  *    return false;
       
   169  * });
       
   170  * @desc Bind form's submit event to use ajaxSubmit
       
   171  *
       
   172  *
       
   173  * @name ajaxSubmit
       
   174  * @type jQuery
       
   175  * @param options  object literal containing options which control the form submission process
       
   176  * @cat Plugins/Form
       
   177  * @return jQuery
       
   178  */
    45  */
   179 $.fn.ajaxSubmit = function(options) {
    46 $.fn.ajaxSubmit = function(options) {
   180     if (typeof options == 'function')
    47 	// fast fail if nothing selected (http://dev.jquery.com/ticket/2752)
   181         options = { success: options };
    48 	if (!this.length) {
   182 
    49 		log('ajaxSubmit: skipping submit process - no element selected');
   183     options = $.extend({
    50 		return this;
   184         url:  this.attr('action') || window.location.toString(),
    51 	}
   185         type: this.attr('method') || 'GET'
    52 
   186     }, options || {});
    53 	if (typeof options == 'function') {
   187 
    54 		options = { success: options };
   188     // hook for manipulating the form data before it is extracted;
    55 	}
   189     // convenient for use with rich editors like tinyMCE or FCKEditor
    56 
   190     var veto = {};
    57 	var action = this.attr('action');
   191     $.event.trigger('form.pre.serialize', [this, options, veto]);
    58 	var url = (typeof action === 'string') ? $.trim(action) : '';
   192     if (veto.veto) return this;
    59 	if (url) {
   193 
    60 		// clean url (don't include hash vaue)
   194     var a = this.formToArray(options.semantic);
    61 		url = (url.match(/^([^#]+)/)||[])[1];
       
    62 	}
       
    63 	url = url || window.location.href || '';
       
    64 
       
    65 	options = $.extend(true, {
       
    66 		url:  url,
       
    67 		success: $.ajaxSettings.success,
       
    68 		type: this[0].getAttribute('method') || 'GET', // IE7 massage (see issue 57)
       
    69 		iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank'
       
    70 	}, options);
       
    71 
       
    72 	// hook for manipulating the form data before it is extracted;
       
    73 	// convenient for use with rich editors like tinyMCE or FCKEditor
       
    74 	var veto = {};
       
    75 	this.trigger('form-pre-serialize', [this, options, veto]);
       
    76 	if (veto.veto) {
       
    77 		log('ajaxSubmit: submit vetoed via form-pre-serialize trigger');
       
    78 		return this;
       
    79 	}
       
    80 
       
    81 	// provide opportunity to alter form data before it is serialized
       
    82 	if (options.beforeSerialize && options.beforeSerialize(this, options) === false) {
       
    83 		log('ajaxSubmit: submit aborted via beforeSerialize callback');
       
    84 		return this;
       
    85 	}
       
    86 
       
    87 	var n,v,a = this.formToArray(options.semantic);
   195 	if (options.data) {
    88 	if (options.data) {
   196 	    for (var n in options.data)
    89 		options.extraData = options.data;
   197 	        a.push( { name: n, value: options.data[n] } );
    90 		for (n in options.data) {
   198 	}
    91 			if(options.data[n] instanceof Array) {
   199 
    92 				for (var k in options.data[n]) {
   200     // give pre-submit callback an opportunity to abort the submit
    93 					a.push( { name: n, value: options.data[n][k] } );
   201     if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) return this;
    94 				}
   202 
    95 			}
   203     // fire vetoable 'validate' event
    96 			else {
   204     $.event.trigger('form.submit.validate', [a, this, options, veto]);
    97 				v = options.data[n];
   205     if (veto.veto) return this;
    98 				v = $.isFunction(v) ? v() : v; // if value is fn, invoke it
   206 
    99 				a.push( { name: n, value: v } );
   207     var q = $.param(a);//.replace(/%20/g,'+');
   100 			}
   208 
   101 		}
   209     if (options.type.toUpperCase() == 'GET') {
   102 	}
   210         options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
   103 
   211         options.data = null;  // data is null for 'get'
   104 	// give pre-submit callback an opportunity to abort the submit
   212     }
   105 	if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) {
   213     else
   106 		log('ajaxSubmit: submit aborted via beforeSubmit callback');
   214         options.data = q; // data is the query string for 'post'
   107 		return this;
   215 
   108 	}
   216     var $form = this, callbacks = [];
   109 
   217     if (options.resetForm) callbacks.push(function() { $form.resetForm(); });
   110 	// fire vetoable 'validate' event
   218     if (options.clearForm) callbacks.push(function() { $form.clearForm(); });
   111 	this.trigger('form-submit-validate', [a, this, options, veto]);
   219 
   112 	if (veto.veto) {
   220     // perform a load on the target only if dataType is not provided
   113 		log('ajaxSubmit: submit vetoed via form-submit-validate trigger');
   221     if (!options.dataType && options.target) {
   114 		return this;
   222         var oldSuccess = options.success || function(){};
   115 	}
   223         callbacks.push(function(data) {
   116 
   224             if (this.evalScripts)
   117 	var q = $.param(a);
   225                 $(options.target).attr("innerHTML", data).evalScripts().each(oldSuccess, arguments);
   118 
   226             else // jQuery v1.1.4
   119 	if (options.type.toUpperCase() == 'GET') {
   227                 $(options.target).html(data).each(oldSuccess, arguments);
   120 		options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
   228         });
   121 		options.data = null;  // data is null for 'get'
   229     }
   122 	}
   230     else if (options.success)
   123 	else {
   231         callbacks.push(options.success);
   124 		options.data = q; // data is the query string for 'post'
   232 
   125 	}
   233     options.success = function(data, status) {
   126 
   234         for (var i=0, max=callbacks.length; i < max; i++)
   127 	var $form = this, callbacks = [];
   235             callbacks[i](data, status, $form);
   128 	if (options.resetForm) {
   236     };
   129 		callbacks.push(function() { $form.resetForm(); });
   237 
   130 	}
   238     // are there files to upload?
   131 	if (options.clearForm) {
   239     var files = $('input:file', this).fieldValue();
   132 		callbacks.push(function() { $form.clearForm(); });
   240     var found = false;
   133 	}
   241     for (var j=0; j < files.length; j++)
   134 
   242         if (files[j])
   135 	// perform a load on the target only if dataType is not provided
   243             found = true;
   136 	if (!options.dataType && options.target) {
   244 
   137 		var oldSuccess = options.success || function(){};
   245     // options.iframe allows user to force iframe mode
   138 		callbacks.push(function(data) {
   246    if (options.iframe || found) { 
   139 			var fn = options.replaceTarget ? 'replaceWith' : 'html';
   247        // hack to fix Safari hang (thanks to Tim Molendijk for this)
   140 			$(options.target)[fn](data).each(oldSuccess, arguments);
   248        // see:  http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
   141 		});
   249        if ($.browser.safari && options.closeKeepAlive)
   142 	}
   250            $.get(options.closeKeepAlive, fileUpload);
   143 	else if (options.success) {
   251        else
   144 		callbacks.push(options.success);
   252            fileUpload();
   145 	}
   253        }
   146 
   254    else
   147 	options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg
   255        $.ajax(options);
   148 		var context = options.context || options;   // jQuery 1.4+ supports scope context
   256 
   149 		for (var i=0, max=callbacks.length; i < max; i++) {
   257     // fire 'notify' event
   150 			callbacks[i].apply(context, [data, status, xhr || $form, $form]);
   258     $.event.trigger('form.submit.notify', [this, options]);
   151 		}
   259     return this;
   152 	};
   260 
   153 
   261 
   154 	// are there files to upload?
   262     // private function for handling file uploads (hat tip to YAHOO!)
   155 	var fileInputs = $('input:file', this).length > 0;
   263     function fileUpload() {
   156 	var mp = 'multipart/form-data';
   264         var form = $form[0];
   157 	var multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp);
   265         var opts = $.extend({}, $.ajaxSettings, options);
   158 
   266 
   159 	// options.iframe allows user to force iframe mode
   267         var id = 'jqFormIO' + $.fn.ajaxSubmit.counter++;
   160 	// 06-NOV-09: now defaulting to iframe mode if file input is detected
   268         var $io = $('<iframe id="' + id + '" name="' + id + '" />');
   161    if (options.iframe !== false && (fileInputs || options.iframe || multipart)) {
   269         var io = $io[0];
   162 	   // hack to fix Safari hang (thanks to Tim Molendijk for this)
   270         var op8 = $.browser.opera && window.opera.version() < 9;
   163 	   // see:  http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
   271         if ($.browser.msie || op8) io.src = 'javascript:false;document.write("");';
   164 	   if (options.closeKeepAlive) {
   272         $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });
   165 		   $.get(options.closeKeepAlive, fileUpload);
   273 
   166 		}
   274         var xhr = { // mock object
   167 	   else {
   275             responseText: null,
   168 		   fileUpload();
   276             responseXML: null,
   169 		}
   277             status: 0,
   170    }
   278             statusText: 'n/a',
   171    else {
   279             getAllResponseHeaders: function() {},
   172 		$.ajax(options);
   280             getResponseHeader: function() {},
   173    }
   281             setRequestHeader: function() {}
   174 
   282         };
   175 	// fire 'notify' event
   283 
   176 	this.trigger('form-submit-notify', [this, options]);
   284         var g = opts.global;
   177 	return this;
   285         // trigger ajax global events so that activity/block indicators work like normal
   178 
   286         if (g && ! $.active++) $.event.trigger("ajaxStart");
   179 
   287         if (g) $.event.trigger("ajaxSend", [xhr, opts]);
   180 	// private function for handling file uploads (hat tip to YAHOO!)
   288 
   181 	function fileUpload() {
   289         var cbInvoked = 0;
   182 		var form = $form[0];
   290         var timedOut = 0;
   183 
   291 
   184 		if ($(':input[name=submit],:input[id=submit]', form).length) {
   292         // take a breath so that pending repaints get some cpu time before the upload starts
   185 			// if there is an input with a name or id of 'submit' then we won't be
   293         setTimeout(function() {
   186 			// able to invoke the submit fn on the form (at least not x-browser)
   294             // make sure form attrs are set
   187 			alert('Error: Form elements must not have name or id of "submit".');
   295             var encAttr = form.encoding ? 'encoding' : 'enctype';
   188 			return;
   296             var t = $form.attr('target');
   189 		}
   297             $form.attr({
   190 
   298                 target:   id,
   191 		var s = $.extend(true, {}, $.ajaxSettings, options);
   299                 method:  'POST',
   192 		s.context = s.context || s;
   300                 action:   opts.url
   193 		var id = 'jqFormIO' + (new Date().getTime()), fn = '_'+id;
   301             });
   194 		var $io = $('<iframe id="' + id + '" name="' + id + '" src="'+ s.iframeSrc +'" />');
   302             form[encAttr] = 'multipart/form-data';
   195 		var io = $io[0];
   303 
   196 
   304             // support timout
   197 		$io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });
   305             if (opts.timeout)
   198 
   306                 setTimeout(function() { timedOut = true; cb(); }, opts.timeout);
   199 		var xhr = { // mock object
   307 
   200 			aborted: 0,
   308             // add iframe to doc and submit the form
   201 			responseText: null,
   309             $io.appendTo('body');
   202 			responseXML: null,
   310             io.attachEvent ? io.attachEvent('onload', cb) : io.addEventListener('load', cb, false);
   203 			status: 0,
   311             form.submit();
   204 			statusText: 'n/a',
   312             $form.attr('target', t); // reset target
   205 			getAllResponseHeaders: function() {},
   313         }, 10);
   206 			getResponseHeader: function() {},
   314 
   207 			setRequestHeader: function() {},
   315         function cb() {
   208 			abort: function(status) {
   316             if (cbInvoked++) return;
   209 				var e = (status === 'timeout' ? 'timeout' : 'aborted');
   317 
   210 				log('aborting upload... ' + e);
       
   211 				this.aborted = 1;
       
   212 				$io.attr('src', s.iframeSrc); // abort op in progress
       
   213 				xhr.error = e;
       
   214 				s.error && s.error.call(s.context, xhr, e, e);
       
   215 				g && $.event.trigger("ajaxError", [xhr, s, e]);
       
   216 				s.complete && s.complete.call(s.context, xhr, e);
       
   217 			}
       
   218 		};
       
   219 
       
   220 		var g = s.global;
       
   221 		// trigger ajax global events so that activity/block indicators work like normal
       
   222 		if (g && ! $.active++) {
       
   223 			$.event.trigger("ajaxStart");
       
   224 		}
       
   225 		if (g) {
       
   226 			$.event.trigger("ajaxSend", [xhr, s]);
       
   227 		}
       
   228 
       
   229 		if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) {
       
   230 			if (s.global) {
       
   231 				$.active--;
       
   232 			}
       
   233 			return;
       
   234 		}
       
   235 		if (xhr.aborted) {
       
   236 			return;
       
   237 		}
       
   238 
       
   239 		var timedOut = 0, timeoutHandle;
       
   240 
       
   241 		// add submitting element to data if we know it
       
   242 		var sub = form.clk;
       
   243 		if (sub) {
       
   244 			var n = sub.name;
       
   245 			if (n && !sub.disabled) {
       
   246 				s.extraData = s.extraData || {};
       
   247 				s.extraData[n] = sub.value;
       
   248 				if (sub.type == "image") {
       
   249 					s.extraData[n+'.x'] = form.clk_x;
       
   250 					s.extraData[n+'.y'] = form.clk_y;
       
   251 				}
       
   252 			}
       
   253 		}
       
   254 
       
   255 		// take a breath so that pending repaints get some cpu time before the upload starts
       
   256 		function doSubmit() {
       
   257 			// make sure form attrs are set
       
   258 			var t = $form.attr('target'), a = $form.attr('action');
       
   259 
       
   260 			// update form attrs in IE friendly way
       
   261 			form.setAttribute('target',id);
       
   262 			if (form.getAttribute('method') != 'POST') {
       
   263 				form.setAttribute('method', 'POST');
       
   264 			}
       
   265 			if (form.getAttribute('action') != s.url) {
       
   266 				form.setAttribute('action', s.url);
       
   267 			}
       
   268 
       
   269 			// ie borks in some cases when setting encoding
       
   270 			if (! s.skipEncodingOverride) {
       
   271 				$form.attr({
       
   272 					encoding: 'multipart/form-data',
       
   273 					enctype:  'multipart/form-data'
       
   274 				});
       
   275 			}
       
   276 
       
   277 			// support timout
       
   278 			if (s.timeout) {
       
   279 				timeoutHandle = setTimeout(function() { timedOut = true; cb(true); }, s.timeout);
       
   280 			}
       
   281 
       
   282 			// add "extra" data to form if provided in options
       
   283 			var extraInputs = [];
       
   284 			try {
       
   285 				if (s.extraData) {
       
   286 					for (var n in s.extraData) {
       
   287 						extraInputs.push(
       
   288 							$('<input type="hidden" name="'+n+'" value="'+s.extraData[n]+'" />')
       
   289 								.appendTo(form)[0]);
       
   290 					}
       
   291 				}
       
   292 
       
   293 				// add iframe to doc and submit the form
       
   294 				$io.appendTo('body');
       
   295                 io.attachEvent ? io.attachEvent('onload', cb) : io.addEventListener('load', cb, false);
       
   296 				form.submit();
       
   297 			}
       
   298 			finally {
       
   299 				// reset attrs and remove "extra" input elements
       
   300 				form.setAttribute('action',a);
       
   301 				if(t) {
       
   302 					form.setAttribute('target', t);
       
   303 				} else {
       
   304 					$form.removeAttr('target');
       
   305 				}
       
   306 				$(extraInputs).remove();
       
   307 			}
       
   308 		}
       
   309 
       
   310 		if (s.forceSync) {
       
   311 			doSubmit();
       
   312 		}
       
   313 		else {
       
   314 			setTimeout(doSubmit, 10); // this lets dom updates render
       
   315 		}
       
   316 
       
   317 		var data, doc, domCheckCount = 50, callbackProcessed;
       
   318 
       
   319 		function cb(e) {
       
   320 			if (xhr.aborted || callbackProcessed) {
       
   321 				return;
       
   322 			}
       
   323 			if (e === true && xhr) {
       
   324 				xhr.abort('timeout');
       
   325 				return;
       
   326 			}
       
   327 
       
   328 			var doc = io.contentWindow ? io.contentWindow.document : io.contentDocument ? io.contentDocument : io.document;
       
   329 			if (!doc || doc.location.href == s.iframeSrc) {
       
   330 				// response not received yet
       
   331 				if (!timedOut)
       
   332 					return;
       
   333 			}
   318             io.detachEvent ? io.detachEvent('onload', cb) : io.removeEventListener('load', cb, false);
   334             io.detachEvent ? io.detachEvent('onload', cb) : io.removeEventListener('load', cb, false);
   319 
   335 
   320             var ok = true;
   336 			var ok = true;
   321             try {
   337 			try {
   322                 if (timedOut) throw 'timeout';
   338 				if (timedOut) {
   323                 // extract the server response from the iframe
   339 					throw 'timeout';
   324                 var data, doc;
   340 				}
   325                 doc = io.contentWindow ? io.contentWindow.document : io.contentDocument ? io.contentDocument : io.document;
   341 
   326                 xhr.responseText = doc.body ? doc.body.innerHTML : null;
   342 				var isXml = s.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc);
   327                 xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
   343 				log('isXml='+isXml);
   328 
   344 				if (!isXml && window.opera && (doc.body == null || doc.body.innerHTML == '')) {
   329                 if (opts.dataType == 'json' || opts.dataType == 'script') {
   345 					if (--domCheckCount) {
   330                     var ta = doc.getElementsByTagName('textarea')[0];
   346 						// in some browsers (Opera) the iframe DOM is not always traversable when
   331                     data = ta ? ta.value : xhr.responseText;
   347 						// the onload callback fires, so we loop a bit to accommodate
   332                     if (opts.dataType == 'json')
   348 						log('requeing onLoad callback, DOM not available');
   333                         eval("data = " + data);
   349 						setTimeout(cb, 250);
   334                     else
   350 						return;
   335                         $.globalEval(data);
   351 					}
   336                 }
   352 					// let this fall through because server response could be an empty document
   337                 else if (opts.dataType == 'xml') {
   353 					//log('Could not access iframe DOM after mutiple tries.');
   338                     data = xhr.responseXML;
   354 					//throw 'DOMException: not available';
   339                     if (!data && xhr.responseText != null)
   355 				}
   340                         data = toXml(xhr.responseText);
   356 
   341                 }
   357 				//log('response detected');
   342                 else {
   358 				xhr.responseText = doc.body ? doc.body.innerHTML : doc.documentElement ? doc.documentElement.innerHTML : null;
   343                     data = xhr.responseText;
   359 				xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
   344                 }
   360 				if (isXml)
   345             }
   361 					s.dataType = 'xml';
   346             catch(e){
   362 				xhr.getResponseHeader = function(header){
   347                 ok = false;
   363 					var headers = {'content-type': s.dataType};
   348                 $.handleError(opts, xhr, 'error', e);
   364 					return headers[header];
   349             }
   365 				};
   350 
   366 
   351             // ordering of these callbacks/triggers is odd, but that's how $.ajax does it
   367 				var scr = /(json|script|text)/.test(s.dataType);
   352             if (ok) {
   368 				if (scr || s.textarea) {
   353                 opts.success(data, 'success');
   369 					// see if user embedded response in textarea
   354                 if (g) $.event.trigger("ajaxSuccess", [xhr, opts]);
   370 					var ta = doc.getElementsByTagName('textarea')[0];
   355             }
   371 					if (ta) {
   356             if (g) $.event.trigger("ajaxComplete", [xhr, opts]);
   372 						xhr.responseText = ta.value;
   357             if (g && ! --$.active) $.event.trigger("ajaxStop");
   373 					}
   358             if (opts.complete) opts.complete(xhr, ok ? 'success' : 'error');
   374 					else if (scr) {
   359 
   375 						// account for browsers injecting pre around json response
   360             // clean up
   376 						var pre = doc.getElementsByTagName('pre')[0];
   361             setTimeout(function() {
   377 						var b = doc.getElementsByTagName('body')[0];
   362                 $io.remove();
   378 						if (pre) {
   363                 xhr.responseXML = null;
   379 							xhr.responseText = pre.textContent;
   364             }, 100);
   380 						}
   365         };
   381 						else if (b) {
   366 
   382 							xhr.responseText = b.innerHTML;
   367         function toXml(s, doc) {
   383 						}
   368             if (window.ActiveXObject) {
   384 					}
   369                 doc = new ActiveXObject('Microsoft.XMLDOM');
   385 				}
   370                 doc.async = 'false';
   386 				else if (s.dataType == 'xml' && !xhr.responseXML && xhr.responseText != null) {
   371                 doc.loadXML(s);
   387 					xhr.responseXML = toXml(xhr.responseText);
   372             }
   388 				}
   373             else
   389 
   374                 doc = (new DOMParser()).parseFromString(s, 'text/xml');
   390 				data = httpData(xhr, s.dataType, s);
   375             return (doc && doc.documentElement && doc.documentElement.tagName != 'parsererror') ? doc : null;
   391 			}
   376         };
   392 			catch(e){
   377     };
   393 				log('error caught:',e);
   378 };
   394 				ok = false;
   379 $.fn.ajaxSubmit.counter = 0; // used to create unique iframe ids
   395 				xhr.error = e;
       
   396 				s.error && s.error.call(s.context, xhr, 'error', e);
       
   397 				g && $.event.trigger("ajaxError", [xhr, s, e]);
       
   398 			}
       
   399 
       
   400 			if (xhr.aborted) {
       
   401 				log('upload aborted');
       
   402 				ok = false;
       
   403 			}
       
   404 
       
   405 			// ordering of these callbacks/triggers is odd, but that's how $.ajax does it
       
   406 			if (ok) {
       
   407 				s.success && s.success.call(s.context, data, 'success', xhr);
       
   408 				g && $.event.trigger("ajaxSuccess", [xhr, s]);
       
   409 			}
       
   410 
       
   411 			g && $.event.trigger("ajaxComplete", [xhr, s]);
       
   412 
       
   413 			if (g && ! --$.active) {
       
   414 				$.event.trigger("ajaxStop");
       
   415 			}
       
   416 
       
   417 			s.complete && s.complete.call(s.context, xhr, ok ? 'success' : 'error');
       
   418 
       
   419 			callbackProcessed = true;
       
   420 			if (s.timeout)
       
   421 				clearTimeout(timeoutHandle);
       
   422 
       
   423 			// clean up
       
   424 			setTimeout(function() {
       
   425 				$io.removeData('form-plugin-onload');
       
   426 				$io.remove();
       
   427 				xhr.responseXML = null;
       
   428 			}, 100);
       
   429 		}
       
   430 
       
   431 		var toXml = $.parseXML || function(s, doc) { // use parseXML if available (jQuery 1.5+)
       
   432 			if (window.ActiveXObject) {
       
   433 				doc = new ActiveXObject('Microsoft.XMLDOM');
       
   434 				doc.async = 'false';
       
   435 				doc.loadXML(s);
       
   436 			}
       
   437 			else {
       
   438 				doc = (new DOMParser()).parseFromString(s, 'text/xml');
       
   439 			}
       
   440 			return (doc && doc.documentElement && doc.documentElement.nodeName != 'parsererror') ? doc : null;
       
   441 		};
       
   442 		var parseJSON = $.parseJSON || function(s) {
       
   443 			return window['eval']('(' + s + ')');
       
   444 		};
       
   445 
       
   446 		var httpData = function( xhr, type, s ) { // mostly lifted from jq1.4.4
       
   447 			var ct = xhr.getResponseHeader('content-type') || '',
       
   448 				xml = type === 'xml' || !type && ct.indexOf('xml') >= 0,
       
   449 				data = xml ? xhr.responseXML : xhr.responseText;
       
   450 
       
   451 			if (xml && data.documentElement.nodeName === 'parsererror') {
       
   452 				$.error && $.error('parsererror');
       
   453 			}
       
   454 			if (s && s.dataFilter) {
       
   455 				data = s.dataFilter(data, type);
       
   456 			}
       
   457 			if (typeof data === 'string') {
       
   458 				if (type === 'json' || !type && ct.indexOf('json') >= 0) {
       
   459 					data = parseJSON(data);
       
   460 				} else if (type === "script" || !type && ct.indexOf("javascript") >= 0) {
       
   461 					$.globalEval(data);
       
   462 				}
       
   463 			}
       
   464 			return data;
       
   465 		};
       
   466 	}
       
   467 };
   380 
   468 
   381 /**
   469 /**
   382  * ajaxForm() provides a mechanism for fully automating form submission.
   470  * ajaxForm() provides a mechanism for fully automating form submission.
   383  *
   471  *
   384  * The advantages of using this method instead of ajaxSubmit() are:
   472  * The advantages of using this method instead of ajaxSubmit() are:
   385  *
   473  *
   386  * 1: This method will include coordinates for <input type="image" /> elements (if the element
   474  * 1: This method will include coordinates for <input type="image" /> elements (if the element
   387  *    is used to submit the form).
   475  *	is used to submit the form).
   388  * 2. This method will include the submit element's name/value data (for the element that was
   476  * 2. This method will include the submit element's name/value data (for the element that was
   389  *    used to submit the form).
   477  *	used to submit the form).
   390  * 3. This method binds the submit() method to the form for you.
   478  * 3. This method binds the submit() method to the form for you.
   391  *
       
   392  * Note that for accurate x/y coordinates of image submit elements in all browsers
       
   393  * you need to also use the "dimensions" plugin (this method will auto-detect its presence).
       
   394  *
   479  *
   395  * The options argument for ajaxForm works exactly as it does for ajaxSubmit.  ajaxForm merely
   480  * The options argument for ajaxForm works exactly as it does for ajaxSubmit.  ajaxForm merely
   396  * passes the options argument along after properly binding events for submit elements and
   481  * passes the options argument along after properly binding events for submit elements and
   397  * the form itself.  See ajaxSubmit for a full description of the options argument.
   482  * the form itself.
   398  *
       
   399  *
       
   400  * @example
       
   401  * var options = {
       
   402  *     target: '#myTargetDiv'
       
   403  * };
       
   404  * $('#myForm').ajaxSForm(options);
       
   405  * @desc Bind form's submit event so that 'myTargetDiv' is updated with the server response
       
   406  *       when the form is submitted.
       
   407  *
       
   408  *
       
   409  * @example
       
   410  * var options = {
       
   411  *     success: function(responseText) {
       
   412  *         alert(responseText);
       
   413  *     }
       
   414  * };
       
   415  * $('#myForm').ajaxSubmit(options);
       
   416  * @desc Bind form's submit event so that server response is alerted after the form is submitted.
       
   417  *
       
   418  *
       
   419  * @example
       
   420  * var options = {
       
   421  *     beforeSubmit: function(formArray, jqForm) {
       
   422  *         if (formArray.length == 0) {
       
   423  *             alert('Please enter data.');
       
   424  *             return false;
       
   425  *         }
       
   426  *     }
       
   427  * };
       
   428  * $('#myForm').ajaxSubmit(options);
       
   429  * @desc Bind form's submit event so that pre-submit callback is invoked before the form
       
   430  *       is submitted.
       
   431  *
       
   432  *
       
   433  * @name   ajaxForm
       
   434  * @param  options  object literal containing options which control the form submission process
       
   435  * @return jQuery
       
   436  * @cat    Plugins/Form
       
   437  * @type   jQuery
       
   438  */
   483  */
   439 $.fn.ajaxForm = function(options) {
   484 $.fn.ajaxForm = function(options) {
   440     return this.ajaxFormUnbind().submit(submitHandler).each(function() {
   485 	// in jQuery 1.3+ we can fix mistakes with the ready state
   441         // store options in hash
   486 	if (this.length === 0) {
   442         this.formPluginId = $.fn.ajaxForm.counter++;
   487 		var o = { s: this.selector, c: this.context };
   443         $.fn.ajaxForm.optionHash[this.formPluginId] = options;
   488 		if (!$.isReady && o.s) {
   444         $(":submit,input:image", this).click(clickHandler);
   489 			log('DOM not ready, queuing ajaxForm');
   445     });
   490 			$(function() {
   446 };
   491 				$(o.s,o.c).ajaxForm(options);
   447 
   492 			});
   448 $.fn.ajaxForm.counter = 1;
   493 			return this;
   449 $.fn.ajaxForm.optionHash = {};
   494 		}
   450 
   495 		// is your DOM ready?  http://docs.jquery.com/Tutorials:Introducing_$(document).ready()
   451 function clickHandler(e) {
   496 		log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)'));
   452     var $form = this.form;
   497 		return this;
   453     $form.clk = this;
   498 	}
   454     if (this.type == 'image') {
   499 
   455         if (e.offsetX != undefined) {
   500 	return this.ajaxFormUnbind().bind('submit.form-plugin', function(e) {
   456             $form.clk_x = e.offsetX;
   501 		if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed
   457             $form.clk_y = e.offsetY;
   502 			e.preventDefault();
   458         } else if (typeof $.fn.offset == 'function') { // try to use dimensions plugin
   503 			$(this).ajaxSubmit(options);
   459             var offset = $(this).offset();
   504 		}
   460             $form.clk_x = e.pageX - offset.left;
   505 	}).bind('click.form-plugin', function(e) {
   461             $form.clk_y = e.pageY - offset.top;
   506 		var target = e.target;
   462         } else {
   507 		var $el = $(target);
   463             $form.clk_x = e.pageX - this.offsetLeft;
   508 		if (!($el.is(":submit,input:image"))) {
   464             $form.clk_y = e.pageY - this.offsetTop;
   509 			// is this a child element of the submit el?  (ex: a span within a button)
   465         }
   510 			var t = $el.closest(':submit');
   466     }
   511 			if (t.length == 0) {
   467     // clear form vars
   512 				return;
   468     setTimeout(function() { $form.clk = $form.clk_x = $form.clk_y = null; }, 10);
   513 			}
   469 };
   514 			target = t[0];
   470 
   515 		}
   471 function submitHandler() {
   516 		var form = this;
   472     // retrieve options from hash
   517 		form.clk = target;
   473     var id = this.formPluginId;
   518 		if (target.type == 'image') {
   474     var options = $.fn.ajaxForm.optionHash[id];
   519 			if (e.offsetX != undefined) {
   475     $(this).ajaxSubmit(options);
   520 				form.clk_x = e.offsetX;
   476     return false;
   521 				form.clk_y = e.offsetY;
   477 };
   522 			} else if (typeof $.fn.offset == 'function') { // try to use dimensions plugin
   478 
   523 				var offset = $el.offset();
   479 /**
   524 				form.clk_x = e.pageX - offset.left;
   480  * ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
   525 				form.clk_y = e.pageY - offset.top;
   481  *
   526 			} else {
   482  * @name   ajaxFormUnbind
   527 				form.clk_x = e.pageX - target.offsetLeft;
   483  * @return jQuery
   528 				form.clk_y = e.pageY - target.offsetTop;
   484  * @cat    Plugins/Form
   529 			}
   485  * @type   jQuery
   530 		}
   486  */
   531 		// clear form vars
       
   532 		setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 100);
       
   533 	});
       
   534 };
       
   535 
       
   536 // ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
   487 $.fn.ajaxFormUnbind = function() {
   537 $.fn.ajaxFormUnbind = function() {
   488     this.unbind('submit', submitHandler);
   538 	return this.unbind('submit.form-plugin click.form-plugin');
   489     return this.each(function() {
       
   490         $(":submit,input:image", this).unbind('click', clickHandler);
       
   491     });
       
   492 
       
   493 };
   539 };
   494 
   540 
   495 /**
   541 /**
   496  * formToArray() gathers form element data into an array of objects that can
   542  * formToArray() gathers form element data into an array of objects that can
   497  * be passed to any of the following ajax functions: $.get, $.post, or load.
   543  * be passed to any of the following ajax functions: $.get, $.post, or load.
   500  *
   546  *
   501  * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ]
   547  * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ]
   502  *
   548  *
   503  * It is this array that is passed to pre-submit callback functions provided to the
   549  * It is this array that is passed to pre-submit callback functions provided to the
   504  * ajaxSubmit() and ajaxForm() methods.
   550  * ajaxSubmit() and ajaxForm() methods.
   505  *
       
   506  * The semantic argument can be used to force form serialization in semantic order.
       
   507  * This is normally true anyway, unless the form contains input elements of type='image'.
       
   508  * If your form must be submitted with name/value pairs in semantic order and your form
       
   509  * contains an input of type='image" then pass true for this arg, otherwise pass false
       
   510  * (or nothing) to avoid the overhead for this logic.
       
   511  *
       
   512  * @example var data = $("#myForm").formToArray();
       
   513  * $.post( "myscript.cgi", data );
       
   514  * @desc Collect all the data from a form and submit it to the server.
       
   515  *
       
   516  * @name formToArray
       
   517  * @param semantic true if serialization must maintain strict semantic ordering of elements (slower)
       
   518  * @type Array<Object>
       
   519  * @cat Plugins/Form
       
   520  */
   551  */
   521 $.fn.formToArray = function(semantic) {
   552 $.fn.formToArray = function(semantic) {
   522     var a = [];
   553 	var a = [];
   523     if (this.length == 0) return a;
   554 	if (this.length === 0) {
   524 
   555 		return a;
   525     var form = this[0];
   556 	}
   526     var els = semantic ? form.getElementsByTagName('*') : form.elements;
   557 
   527     if (!els) return a;
   558 	var form = this[0];
   528     for(var i=0, max=els.length; i < max; i++) {
   559 	var els = semantic ? form.getElementsByTagName('*') : form.elements;
   529         var el = els[i];
   560 	if (!els) {
   530         var n = el.name;
   561 		return a;
   531         if (!n) continue;
   562 	}
   532 
   563 
   533         if (semantic && form.clk && el.type == "image") {
   564 	var i,j,n,v,el,max,jmax;
   534             // handle image inputs on the fly when semantic == true
   565 	for(i=0, max=els.length; i < max; i++) {
   535             if(!el.disabled && form.clk == el)
   566 		el = els[i];
   536                 a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
   567 		n = el.name;
   537             continue;
   568 		if (!n) {
   538         }
   569 			continue;
   539 
   570 		}
   540         var v = $.fieldValue(el, true);
   571 
   541         if (v && v.constructor == Array) {
   572 		if (semantic && form.clk && el.type == "image") {
   542             for(var j=0, jmax=v.length; j < jmax; j++)
   573 			// handle image inputs on the fly when semantic == true
   543                 a.push({name: n, value: v[j]});
   574 			if(!el.disabled && form.clk == el) {
   544         }
   575 				a.push({name: n, value: $(el).val()});
   545         else if (v !== null && typeof v != 'undefined')
   576 				a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
   546             a.push({name: n, value: v});
   577 			}
   547     }
   578 			continue;
   548 
   579 		}
   549     if (!semantic && form.clk) {
   580 
   550         // input type=='image' are not found in elements array! handle them here
   581 		v = $.fieldValue(el, true);
   551         var inputs = form.getElementsByTagName("input");
   582 		if (v && v.constructor == Array) {
   552         for(var i=0, max=inputs.length; i < max; i++) {
   583 			for(j=0, jmax=v.length; j < jmax; j++) {
   553             var input = inputs[i];
   584 				a.push({name: n, value: v[j]});
   554             var n = input.name;
   585 			}
   555             if(n && !input.disabled && input.type == "image" && form.clk == input)
   586 		}
   556                 a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
   587 		else if (v !== null && typeof v != 'undefined') {
   557         }
   588 			a.push({name: n, value: v});
   558     }
   589 		}
   559     return a;
   590 	}
   560 };
   591 
   561 
   592 	if (!semantic && form.clk) {
       
   593 		// input type=='image' are not found in elements array! handle it here
       
   594 		var $input = $(form.clk), input = $input[0];
       
   595 		n = input.name;
       
   596 		if (n && !input.disabled && input.type == 'image') {
       
   597 			a.push({name: n, value: $input.val()});
       
   598 			a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
       
   599 		}
       
   600 	}
       
   601 	return a;
       
   602 };
   562 
   603 
   563 /**
   604 /**
   564  * Serializes form data into a 'submittable' string. This method will return a string
   605  * Serializes form data into a 'submittable' string. This method will return a string
   565  * in the format: name1=value1&amp;name2=value2
   606  * in the format: name1=value1&amp;name2=value2
   566  *
       
   567  * The semantic argument can be used to force form serialization in semantic order.
       
   568  * If your form must be submitted with name/value pairs in semantic order then pass
       
   569  * true for this arg, otherwise pass false (or nothing) to avoid the overhead for
       
   570  * this logic (which can be significant for very large forms).
       
   571  *
       
   572  * @example var data = $("#myForm").formSerialize();
       
   573  * $.ajax('POST', "myscript.cgi", data);
       
   574  * @desc Collect all the data from a form into a single string
       
   575  *
       
   576  * @name formSerialize
       
   577  * @param semantic true if serialization must maintain strict semantic ordering of elements (slower)
       
   578  * @type String
       
   579  * @cat Plugins/Form
       
   580  */
   607  */
   581 $.fn.formSerialize = function(semantic) {
   608 $.fn.formSerialize = function(semantic) {
   582     //hand off to jQuery.param for proper encoding
   609 	//hand off to jQuery.param for proper encoding
   583     return $.param(this.formToArray(semantic));
   610 	return $.param(this.formToArray(semantic));
   584 };
   611 };
   585 
       
   586 
   612 
   587 /**
   613 /**
   588  * Serializes all field elements in the jQuery object into a query string.
   614  * Serializes all field elements in the jQuery object into a query string.
   589  * This method will return a string in the format: name1=value1&amp;name2=value2
   615  * This method will return a string in the format: name1=value1&amp;name2=value2
   590  *
       
   591  * The successful argument controls whether or not serialization is limited to
       
   592  * 'successful' controls (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls).
       
   593  * The default value of the successful argument is true.
       
   594  *
       
   595  * @example var data = $("input").formSerialize();
       
   596  * @desc Collect the data from all successful input elements into a query string
       
   597  *
       
   598  * @example var data = $(":radio").formSerialize();
       
   599  * @desc Collect the data from all successful radio input elements into a query string
       
   600  *
       
   601  * @example var data = $("#myForm :checkbox").formSerialize();
       
   602  * @desc Collect the data from all successful checkbox input elements in myForm into a query string
       
   603  *
       
   604  * @example var data = $("#myForm :checkbox").formSerialize(false);
       
   605  * @desc Collect the data from all checkbox elements in myForm (even the unchecked ones) into a query string
       
   606  *
       
   607  * @example var data = $(":input").formSerialize();
       
   608  * @desc Collect the data from all successful input, select, textarea and button elements into a query string
       
   609  *
       
   610  * @name fieldSerialize
       
   611  * @param successful true if only successful controls should be serialized (default is true)
       
   612  * @type String
       
   613  * @cat Plugins/Form
       
   614  */
   616  */
   615 $.fn.fieldSerialize = function(successful) {
   617 $.fn.fieldSerialize = function(successful) {
   616     var a = [];
   618 	var a = [];
   617     this.each(function() {
   619 	this.each(function() {
   618         var n = this.name;
   620 		var n = this.name;
   619         if (!n) return;
   621 		if (!n) {
   620         var v = $.fieldValue(this, successful);
   622 			return;
   621         if (v && v.constructor == Array) {
   623 		}
   622             for (var i=0,max=v.length; i < max; i++)
   624 		var v = $.fieldValue(this, successful);
   623                 a.push({name: n, value: v[i]});
   625 		if (v && v.constructor == Array) {
   624         }
   626 			for (var i=0,max=v.length; i < max; i++) {
   625         else if (v !== null && typeof v != 'undefined')
   627 				a.push({name: n, value: v[i]});
   626             a.push({name: this.name, value: v});
   628 			}
   627     });
   629 		}
   628     //hand off to jQuery.param for proper encoding
   630 		else if (v !== null && typeof v != 'undefined') {
   629     return $.param(a);
   631 			a.push({name: this.name, value: v});
   630 };
   632 		}
   631 
   633 	});
       
   634 	//hand off to jQuery.param for proper encoding
       
   635 	return $.param(a);
       
   636 };
   632 
   637 
   633 /**
   638 /**
   634  * Returns the value(s) of the element in the matched set.  For example, consider the following form:
   639  * Returns the value(s) of the element in the matched set.  For example, consider the following form:
   635  *
   640  *
   636  *  <form><fieldset>
   641  *  <form><fieldset>
   637  *      <input name="A" type="text" />
   642  *	  <input name="A" type="text" />
   638  *      <input name="A" type="text" />
   643  *	  <input name="A" type="text" />
   639  *      <input name="B" type="checkbox" value="B1" />
   644  *	  <input name="B" type="checkbox" value="B1" />
   640  *      <input name="B" type="checkbox" value="B2"/>
   645  *	  <input name="B" type="checkbox" value="B2"/>
   641  *      <input name="C" type="radio" value="C1" />
   646  *	  <input name="C" type="radio" value="C1" />
   642  *      <input name="C" type="radio" value="C2" />
   647  *	  <input name="C" type="radio" value="C2" />
   643  *  </fieldset></form>
   648  *  </fieldset></form>
   644  *
   649  *
   645  *  var v = $(':text').fieldValue();
   650  *  var v = $(':text').fieldValue();
   646  *  // if no values are entered into the text inputs
   651  *  // if no values are entered into the text inputs
   647  *  v == ['','']
   652  *  v == ['','']
   664  * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls).
   669  * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls).
   665  * The default value of the successful argument is true.  If this value is false the value(s)
   670  * The default value of the successful argument is true.  If this value is false the value(s)
   666  * for each element is returned.
   671  * for each element is returned.
   667  *
   672  *
   668  * Note: This method *always* returns an array.  If no valid value can be determined the
   673  * Note: This method *always* returns an array.  If no valid value can be determined the
   669  *       array will be empty, otherwise it will contain one or more values.
   674  *	   array will be empty, otherwise it will contain one or more values.
   670  *
       
   671  * @example var data = $("#myPasswordElement").fieldValue();
       
   672  * alert(data[0]);
       
   673  * @desc Alerts the current value of the myPasswordElement element
       
   674  *
       
   675  * @example var data = $("#myForm :input").fieldValue();
       
   676  * @desc Get the value(s) of the form elements in myForm
       
   677  *
       
   678  * @example var data = $("#myForm :checkbox").fieldValue();
       
   679  * @desc Get the value(s) for the successful checkbox element(s) in the jQuery object.
       
   680  *
       
   681  * @example var data = $("#mySingleSelect").fieldValue();
       
   682  * @desc Get the value(s) of the select control
       
   683  *
       
   684  * @example var data = $(':text').fieldValue();
       
   685  * @desc Get the value(s) of the text input or textarea elements
       
   686  *
       
   687  * @example var data = $("#myMultiSelect").fieldValue();
       
   688  * @desc Get the values for the select-multiple control
       
   689  *
       
   690  * @name fieldValue
       
   691  * @param Boolean successful true if only the values for successful controls should be returned (default is true)
       
   692  * @type Array<String>
       
   693  * @cat Plugins/Form
       
   694  */
   675  */
   695 $.fn.fieldValue = function(successful) {
   676 $.fn.fieldValue = function(successful) {
   696     for (var val=[], i=0, max=this.length; i < max; i++) {
   677 	for (var val=[], i=0, max=this.length; i < max; i++) {
   697         var el = this[i];
   678 		var el = this[i];
   698         var v = $.fieldValue(el, successful);
   679 		var v = $.fieldValue(el, successful);
   699         if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length))
   680 		if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) {
   700             continue;
   681 			continue;
   701         v.constructor == Array ? $.merge(val, v) : val.push(v);
   682 		}
   702     }
   683 		v.constructor == Array ? $.merge(val, v) : val.push(v);
   703     return val;
   684 	}
       
   685 	return val;
   704 };
   686 };
   705 
   687 
   706 /**
   688 /**
   707  * Returns the value of the field element.
   689  * Returns the value of the field element.
   708  *
       
   709  * The successful argument controls whether or not the field element must be 'successful'
       
   710  * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls).
       
   711  * The default value of the successful argument is true.  If the given element is not
       
   712  * successful and the successful arg is not false then the returned value will be null.
       
   713  *
       
   714  * Note: If the successful flag is true (default) but the element is not successful, the return will be null
       
   715  * Note: The value returned for a successful select-multiple element will always be an array.
       
   716  * Note: If the element has no value the return value will be undefined.
       
   717  *
       
   718  * @example var data = jQuery.fieldValue($("#myPasswordElement")[0]);
       
   719  * @desc Gets the current value of the myPasswordElement element
       
   720  *
       
   721  * @name fieldValue
       
   722  * @param Element el The DOM element for which the value will be returned
       
   723  * @param Boolean successful true if value returned must be for a successful controls (default is true)
       
   724  * @type String or Array<String> or null or undefined
       
   725  * @cat Plugins/Form
       
   726  */
   690  */
   727 $.fieldValue = function(el, successful) {
   691 $.fieldValue = function(el, successful) {
   728     var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
   692 	var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
   729     if (typeof successful == 'undefined') successful = true;
   693 	if (successful === undefined) {
   730 
   694 		successful = true;
   731     if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
   695 	}
   732         (t == 'checkbox' || t == 'radio') && !el.checked ||
   696 
   733         (t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
   697 	if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
   734         tag == 'select' && el.selectedIndex == -1))
   698 		(t == 'checkbox' || t == 'radio') && !el.checked ||
   735             return null;
   699 		(t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
   736 
   700 		tag == 'select' && el.selectedIndex == -1)) {
   737     if (tag == 'select') {
   701 			return null;
   738         var index = el.selectedIndex;
   702 	}
   739         if (index < 0) return null;
   703 
   740         var a = [], ops = el.options;
   704 	if (tag == 'select') {
   741         var one = (t == 'select-one');
   705 		var index = el.selectedIndex;
   742         var max = (one ? index+1 : ops.length);
   706 		if (index < 0) {
   743         for(var i=(one ? index : 0); i < max; i++) {
   707 			return null;
   744             var op = ops[i];
   708 		}
   745             if (op.selected) {
   709 		var a = [], ops = el.options;
   746                 // extra pain for IE...
   710 		var one = (t == 'select-one');
   747                 var v = $.browser.msie && !(op.attributes['value'].specified) ? op.text : op.value;
   711 		var max = (one ? index+1 : ops.length);
   748                 if (one) return v;
   712 		for(var i=(one ? index : 0); i < max; i++) {
   749                 a.push(v);
   713 			var op = ops[i];
   750             }
   714 			if (op.selected) {
   751         }
   715 				var v = op.value;
   752         return a;
   716 				if (!v) { // extra pain for IE...
   753     }
   717 					v = (op.attributes && op.attributes['value'] && !(op.attributes['value'].specified)) ? op.text : op.value;
   754     return el.value;
   718 				}
   755 };
   719 				if (one) {
   756 
   720 					return v;
       
   721 				}
       
   722 				a.push(v);
       
   723 			}
       
   724 		}
       
   725 		return a;
       
   726 	}
       
   727 	return $(el).val();
       
   728 };
   757 
   729 
   758 /**
   730 /**
   759  * Clears the form data.  Takes the following actions on the form's input fields:
   731  * Clears the form data.  Takes the following actions on the form's input fields:
   760  *  - input text fields will have their 'value' property set to the empty string
   732  *  - input text fields will have their 'value' property set to the empty string
   761  *  - select elements will have their 'selectedIndex' property set to -1
   733  *  - select elements will have their 'selectedIndex' property set to -1
   762  *  - checkbox and radio inputs will have their 'checked' property set to false
   734  *  - checkbox and radio inputs will have their 'checked' property set to false
   763  *  - inputs of type submit, button, reset, and hidden will *not* be effected
   735  *  - inputs of type submit, button, reset, and hidden will *not* be effected
   764  *  - button elements will *not* be effected
   736  *  - button elements will *not* be effected
   765  *
       
   766  * @example $('form').clearForm();
       
   767  * @desc Clears all forms on the page.
       
   768  *
       
   769  * @name clearForm
       
   770  * @type jQuery
       
   771  * @cat Plugins/Form
       
   772  */
   737  */
   773 $.fn.clearForm = function() {
   738 $.fn.clearForm = function() {
   774     return this.each(function() {
   739 	return this.each(function() {
   775         $('input,select,textarea', this).clearFields();
   740 		$('input,select,textarea', this).clearFields();
   776     });
   741 	});
   777 };
   742 };
   778 
   743 
   779 /**
   744 /**
   780  * Clears the selected form elements.  Takes the following actions on the matched elements:
   745  * Clears the selected form elements.
   781  *  - input text fields will have their 'value' property set to the empty string
       
   782  *  - select elements will have their 'selectedIndex' property set to -1
       
   783  *  - checkbox and radio inputs will have their 'checked' property set to false
       
   784  *  - inputs of type submit, button, reset, and hidden will *not* be effected
       
   785  *  - button elements will *not* be effected
       
   786  *
       
   787  * @example $('.myInputs').clearFields();
       
   788  * @desc Clears all inputs with class myInputs
       
   789  *
       
   790  * @name clearFields
       
   791  * @type jQuery
       
   792  * @cat Plugins/Form
       
   793  */
   746  */
   794 $.fn.clearFields = $.fn.clearInputs = function() {
   747 $.fn.clearFields = $.fn.clearInputs = function() {
   795     return this.each(function() {
   748 	return this.each(function() {
   796         var t = this.type, tag = this.tagName.toLowerCase();
   749 		var t = this.type, tag = this.tagName.toLowerCase();
   797         if (t == 'text' || t == 'password' || tag == 'textarea')
   750 		if (t == 'text' || t == 'password' || tag == 'textarea') {
   798             this.value = '';
   751 			this.value = '';
   799         else if (t == 'checkbox' || t == 'radio')
   752 		}
   800             this.checked = false;
   753 		else if (t == 'checkbox' || t == 'radio') {
   801         else if (tag == 'select')
   754 			this.checked = false;
   802             this.selectedIndex = -1;
   755 		}
   803     });
   756 		else if (tag == 'select') {
   804 };
   757 			this.selectedIndex = -1;
   805 
   758 		}
       
   759 	});
       
   760 };
   806 
   761 
   807 /**
   762 /**
   808  * Resets the form data.  Causes all form elements to be reset to their original value.
   763  * Resets the form data.  Causes all form elements to be reset to their original value.
   809  *
       
   810  * @example $('form').resetForm();
       
   811  * @desc Resets all forms on the page.
       
   812  *
       
   813  * @name resetForm
       
   814  * @type jQuery
       
   815  * @cat Plugins/Form
       
   816  */
   764  */
   817 $.fn.resetForm = function() {
   765 $.fn.resetForm = function() {
   818     return this.each(function() {
   766 	return this.each(function() {
   819         // guard against an input with the name of 'reset'
   767 		// guard against an input with the name of 'reset'
   820         // note that IE reports the reset function as an 'object'
   768 		// note that IE reports the reset function as an 'object'
   821         if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType))
   769 		if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType)) {
   822             this.reset();
   770 			this.reset();
   823     });
   771 		}
   824 };
   772 	});
   825 
   773 };
   826 
   774 
   827 /**
   775 /**
   828  * Enables or disables any matching elements.
   776  * Enables or disables any matching elements.
   829  *
   777  */
   830  * @example $(':radio').enabled(false);
   778 $.fn.enable = function(b) {
   831  * @desc Disables all radio buttons
   779 	if (b === undefined) {
   832  *
   780 		b = true;
   833  * @name select
   781 	}
   834  * @type jQuery
   782 	return this.each(function() {
   835  * @cat Plugins/Form
   783 		this.disabled = !b;
   836  */
   784 	});
   837 $.fn.enable = function(b) { 
       
   838     if (b == undefined) b = true;
       
   839     return this.each(function() { 
       
   840         this.disabled = !b 
       
   841     });
       
   842 };
   785 };
   843 
   786 
   844 /**
   787 /**
   845  * Checks/unchecks any matching checkboxes or radio buttons and
   788  * Checks/unchecks any matching checkboxes or radio buttons and
   846  * selects/deselects and matching option elements.
   789  * selects/deselects and matching option elements.
   847  *
   790  */
   848  * @example $(':checkbox').selected();
   791 $.fn.selected = function(select) {
   849  * @desc Checks all checkboxes
   792 	if (select === undefined) {
   850  *
   793 		select = true;
   851  * @name select
   794 	}
   852  * @type jQuery
   795 	return this.each(function() {
   853  * @cat Plugins/Form
   796 		var t = this.type;
   854  */
   797 		if (t == 'checkbox' || t == 'radio') {
   855 $.fn.select = function(select) {
   798 			this.checked = select;
   856     if (select == undefined) select = true;
   799 		}
   857     return this.each(function() { 
   800 		else if (this.tagName.toLowerCase() == 'option') {
   858         var t = this.type;
   801 			var $sel = $(this).parent('select');
   859         if (t == 'checkbox' || t == 'radio')
   802 			if (select && $sel[0] && $sel[0].type == 'select-one') {
   860             this.checked = select;
   803 				// deselect all other options
   861         else if (this.tagName.toLowerCase() == 'option') {
   804 				$sel.find('option').selected(false);
   862             var $sel = $(this).parent('select');
   805 			}
   863             if (select && $sel[0] && $sel[0].type == 'select-one') {
   806 			this.selected = select;
   864                 // deselect all other options
   807 		}
   865                 $sel.find('option').select(false);
   808 	});
   866             }
   809 };
   867             this.selected = select;
   810 
   868         }
   811 // helper fn for console logging
   869     });
   812 // set $.fn.ajaxSubmit.debug to true to enable debug logging
       
   813 function log() {
       
   814 	if ($.fn.ajaxSubmit.debug) {
       
   815 		var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,'');
       
   816 		if (window.console && window.console.log) {
       
   817 			window.console.log(msg);
       
   818 		}
       
   819 		else if (window.opera && window.opera.postError) {
       
   820 			window.opera.postError(msg);
       
   821 		}
       
   822 	}
   870 };
   823 };
   871 
   824 
   872 })(jQuery);
   825 })(jQuery);