balmet.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

jquery.form.js (41911B)


      1 /*!
      2  * jQuery Form Plugin
      3  * version: 4.3.0
      4  * Requires jQuery v1.7.2 or later
      5  * Project repository: https://github.com/jquery-form/form
      6 
      7  * Copyright 2017 Kevin Morris
      8  * Copyright 2006 M. Alsup
      9 
     10  * Dual licensed under the LGPL-2.1+ or MIT licenses
     11  * https://github.com/jquery-form/form#license
     12 
     13  * This library is free software; you can redistribute it and/or
     14  * modify it under the terms of the GNU Lesser General Public
     15  * License as published by the Free Software Foundation; either
     16  * version 2.1 of the License, or (at your option) any later version.
     17  * This library is distributed in the hope that it will be useful,
     18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     20  * Lesser General Public License for more details.
     21  */
     22 /* global ActiveXObject */
     23 
     24 /* eslint-disable */
     25 (function (factory) {
     26 	if (typeof define === 'function' && define.amd) {
     27 		// AMD. Register as an anonymous module.
     28 		define(['jquery'], factory);
     29 	} else if (typeof module === 'object' && module.exports) {
     30 		// Node/CommonJS
     31 		module.exports = function( root, jQuery ) {
     32 			if (typeof jQuery === 'undefined') {
     33 				// require('jQuery') returns a factory that requires window to build a jQuery instance, we normalize how we use modules
     34 				// that require this pattern but the window provided is a noop if it's defined (how jquery works)
     35 				if (typeof window !== 'undefined') {
     36 					jQuery = require('jquery');
     37 				}
     38 				else {
     39 					jQuery = require('jquery')(root);
     40 				}
     41 			}
     42 			factory(jQuery);
     43 			return jQuery;
     44 		};
     45 	} else {
     46 		// Browser globals
     47 		factory(jQuery);
     48 	}
     49 
     50 }(function ($) {
     51 /* eslint-enable */
     52 	'use strict';
     53 
     54 	/*
     55 		Usage Note:
     56 		-----------
     57 		Do not use both ajaxSubmit and ajaxForm on the same form. These
     58 		functions are mutually exclusive. Use ajaxSubmit if you want
     59 		to bind your own submit handler to the form. For example,
     60 
     61 		$(document).ready(function() {
     62 			$('#myForm').on('submit', function(e) {
     63 				e.preventDefault(); // <-- important
     64 				$(this).ajaxSubmit({
     65 					target: '#output'
     66 				});
     67 			});
     68 		});
     69 
     70 		Use ajaxForm when you want the plugin to manage all the event binding
     71 		for you. For example,
     72 
     73 		$(document).ready(function() {
     74 			$('#myForm').ajaxForm({
     75 				target: '#output'
     76 			});
     77 		});
     78 
     79 		You can also use ajaxForm with delegation (requires jQuery v1.7+), so the
     80 		form does not have to exist when you invoke ajaxForm:
     81 
     82 		$('#myForm').ajaxForm({
     83 			delegation: true,
     84 			target: '#output'
     85 		});
     86 
     87 		When using ajaxForm, the ajaxSubmit function will be invoked for you
     88 		at the appropriate time.
     89 	*/
     90 
     91 	var rCRLF = /\r?\n/g;
     92 
     93 	/**
     94 	 * Feature detection
     95 	 */
     96 	var feature = {};
     97 
     98 	feature.fileapi = $('<input type="file">').get(0).files !== undefined;
     99 	feature.formdata = (typeof window.FormData !== 'undefined');
    100 
    101 	var hasProp = !!$.fn.prop;
    102 
    103 	// attr2 uses prop when it can but checks the return type for
    104 	// an expected string. This accounts for the case where a form
    105 	// contains inputs with names like "action" or "method"; in those
    106 	// cases "prop" returns the element
    107 	$.fn.attr2 = function() {
    108 		if (!hasProp) {
    109 			return this.attr.apply(this, arguments);
    110 		}
    111 
    112 		var val = this.prop.apply(this, arguments);
    113 
    114 		if ((val && val.jquery) || typeof val === 'string') {
    115 			return val;
    116 		}
    117 
    118 		return this.attr.apply(this, arguments);
    119 	};
    120 
    121 	/**
    122 	 * ajaxSubmit() provides a mechanism for immediately submitting
    123 	 * an HTML form using AJAX.
    124 	 *
    125 	 * @param	{object|string}	options		jquery.form.js parameters or custom url for submission
    126 	 * @param	{object}		data		extraData
    127 	 * @param	{string}		dataType	ajax dataType
    128 	 * @param	{function}		onSuccess	ajax success callback function
    129 	 */
    130 	$.fn.ajaxSubmit = function(options, data, dataType, onSuccess) {
    131 		// fast fail if nothing selected (http://dev.jquery.com/ticket/2752)
    132 		if (!this.length) {
    133 			log('ajaxSubmit: skipping submit process - no element selected');
    134 
    135 			return this;
    136 		}
    137 
    138 		/* eslint consistent-this: ["error", "$form"] */
    139 		var method, action, url, isMsie, iframeSrc, $form = this;
    140 
    141 		if (typeof options === 'function') {
    142 			options = {success: options};
    143 
    144 		} else if (typeof options === 'string' || (options === false && arguments.length > 0)) {
    145 			options = {
    146 				'url'      : options,
    147 				'data'     : data,
    148 				'dataType' : dataType
    149 			};
    150 
    151 			if (typeof onSuccess === 'function') {
    152 				options.success = onSuccess;
    153 			}
    154 
    155 		} else if (typeof options === 'undefined') {
    156 			options = {};
    157 		}
    158 
    159 		method = options.method || options.type || this.attr2('method');
    160 		action = options.url || this.attr2('action');
    161 
    162 		url = (typeof action === 'string') ? $.trim(action) : '';
    163 		url = url || window.location.href || '';
    164 		if (url) {
    165 			// clean url (don't include hash vaue)
    166 			url = (url.match(/^([^#]+)/) || [])[1];
    167 		}
    168 		// IE requires javascript:false in https, but this breaks chrome >83 and goes against spec.
    169 		// Instead of using javascript:false always, let's only apply it for IE.
    170 		isMsie = /(MSIE|Trident)/.test(navigator.userAgent || '');
    171 		iframeSrc = (isMsie && /^https/i.test(window.location.href || '')) ? 'javascript:false' : 'about:blank'; // eslint-disable-line no-script-url
    172 
    173 		options = $.extend(true, {
    174 			url       : url,
    175 			success   : $.ajaxSettings.success,
    176 			type      : method || $.ajaxSettings.type,
    177 			iframeSrc : iframeSrc
    178 		}, options);
    179 
    180 		// hook for manipulating the form data before it is extracted;
    181 		// convenient for use with rich editors like tinyMCE or FCKEditor
    182 		var veto = {};
    183 
    184 		this.trigger('form-pre-serialize', [this, options, veto]);
    185 
    186 		if (veto.veto) {
    187 			log('ajaxSubmit: submit vetoed via form-pre-serialize trigger');
    188 
    189 			return this;
    190 		}
    191 
    192 		// provide opportunity to alter form data before it is serialized
    193 		if (options.beforeSerialize && options.beforeSerialize(this, options) === false) {
    194 			log('ajaxSubmit: submit aborted via beforeSerialize callback');
    195 
    196 			return this;
    197 		}
    198 
    199 		var traditional = options.traditional;
    200 
    201 		if (typeof traditional === 'undefined') {
    202 			traditional = $.ajaxSettings.traditional;
    203 		}
    204 
    205 		var elements = [];
    206 		var qx, a = this.formToArray(options.semantic, elements, options.filtering);
    207 
    208 		if (options.data) {
    209 			var optionsData = $.isFunction(options.data) ? options.data(a) : options.data;
    210 
    211 			options.extraData = optionsData;
    212 			qx = $.param(optionsData, traditional);
    213 		}
    214 
    215 		// give pre-submit callback an opportunity to abort the submit
    216 		if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) {
    217 			log('ajaxSubmit: submit aborted via beforeSubmit callback');
    218 
    219 			return this;
    220 		}
    221 
    222 		// fire vetoable 'validate' event
    223 		this.trigger('form-submit-validate', [a, this, options, veto]);
    224 		if (veto.veto) {
    225 			log('ajaxSubmit: submit vetoed via form-submit-validate trigger');
    226 
    227 			return this;
    228 		}
    229 
    230 		var q = $.param(a, traditional);
    231 
    232 		if (qx) {
    233 			q = (q ? (q + '&' + qx) : qx);
    234 		}
    235 
    236 		if (options.type.toUpperCase() === 'GET') {
    237 			options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
    238 			options.data = null;	// data is null for 'get'
    239 		} else {
    240 			options.data = q;		// data is the query string for 'post'
    241 		}
    242 
    243 		var callbacks = [];
    244 
    245 		if (options.resetForm) {
    246 			callbacks.push(function() {
    247 				$form.resetForm();
    248 			});
    249 		}
    250 
    251 		if (options.clearForm) {
    252 			callbacks.push(function() {
    253 				$form.clearForm(options.includeHidden);
    254 			});
    255 		}
    256 
    257 		// perform a load on the target only if dataType is not provided
    258 		if (!options.dataType && options.target) {
    259 			var oldSuccess = options.success || function(){};
    260 
    261 			callbacks.push(function(data, textStatus, jqXHR) {
    262 				var successArguments = arguments,
    263 					fn = options.replaceTarget ? 'replaceWith' : 'html';
    264 
    265 				$(options.target)[fn](data).each(function(){
    266 					oldSuccess.apply(this, successArguments);
    267 				});
    268 			});
    269 
    270 		} else if (options.success) {
    271 			if ($.isArray(options.success)) {
    272 				$.merge(callbacks, options.success);
    273 			} else {
    274 				callbacks.push(options.success);
    275 			}
    276 		}
    277 
    278 		options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg
    279 			var context = options.context || this;		// jQuery 1.4+ supports scope context
    280 
    281 			for (var i = 0, max = callbacks.length; i < max; i++) {
    282 				callbacks[i].apply(context, [data, status, xhr || $form, $form]);
    283 			}
    284 		};
    285 
    286 		if (options.error) {
    287 			var oldError = options.error;
    288 
    289 			options.error = function(xhr, status, error) {
    290 				var context = options.context || this;
    291 
    292 				oldError.apply(context, [xhr, status, error, $form]);
    293 			};
    294 		}
    295 
    296 		if (options.complete) {
    297 			var oldComplete = options.complete;
    298 
    299 			options.complete = function(xhr, status) {
    300 				var context = options.context || this;
    301 
    302 				oldComplete.apply(context, [xhr, status, $form]);
    303 			};
    304 		}
    305 
    306 		// are there files to upload?
    307 
    308 		// [value] (issue #113), also see comment:
    309 		// https://github.com/malsup/form/commit/588306aedba1de01388032d5f42a60159eea9228#commitcomment-2180219
    310 		var fileInputs = $('input[type=file]:enabled', this).filter(function() {
    311 			return $(this).val() !== '';
    312 		});
    313 		var hasFileInputs = fileInputs.length > 0;
    314 		var mp = 'multipart/form-data';
    315 		var multipart = ($form.attr('enctype') === mp || $form.attr('encoding') === mp);
    316 		var fileAPI = feature.fileapi && feature.formdata;
    317 
    318 		log('fileAPI :' + fileAPI);
    319 
    320 		var shouldUseFrame = (hasFileInputs || multipart) && !fileAPI;
    321 		var jqxhr;
    322 
    323 		// options.iframe allows user to force iframe mode
    324 		// 06-NOV-09: now defaulting to iframe mode if file input is detected
    325 		if (options.iframe !== false && (options.iframe || shouldUseFrame)) {
    326 			// hack to fix Safari hang (thanks to Tim Molendijk for this)
    327 			// see: http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
    328 			if (options.closeKeepAlive) {
    329 				$.get(options.closeKeepAlive, function() {
    330 					jqxhr = fileUploadIframe(a);
    331 				});
    332 
    333 			} else {
    334 				jqxhr = fileUploadIframe(a);
    335 			}
    336 
    337 		} else if ((hasFileInputs || multipart) && fileAPI) {
    338 			jqxhr = fileUploadXhr(a);
    339 
    340 		} else {
    341 			jqxhr = $.ajax(options);
    342 		}
    343 
    344 		$form.removeData('jqxhr').data('jqxhr', jqxhr);
    345 
    346 		// clear element array
    347 		for (var k = 0; k < elements.length; k++) {
    348 			elements[k] = null;
    349 		}
    350 
    351 		// fire 'notify' event
    352 		this.trigger('form-submit-notify', [this, options]);
    353 
    354 		return this;
    355 
    356 		// utility fn for deep serialization
    357 		function deepSerialize(extraData) {
    358 			var serialized = $.param(extraData, options.traditional).split('&');
    359 			var len = serialized.length;
    360 			var result = [];
    361 			var i, part;
    362 
    363 			for (i = 0; i < len; i++) {
    364 				// #252; undo param space replacement
    365 				serialized[i] = serialized[i].replace(/\+/g, ' ');
    366 				part = serialized[i].split('=');
    367 				// #278; use array instead of object storage, favoring array serializations
    368 				result.push([decodeURIComponent(part[0]), decodeURIComponent(part[1])]);
    369 			}
    370 
    371 			return result;
    372 		}
    373 
    374 		// XMLHttpRequest Level 2 file uploads (big hat tip to francois2metz)
    375 		function fileUploadXhr(a) {
    376 			var formdata = new FormData();
    377 
    378 			for (var i = 0; i < a.length; i++) {
    379 				formdata.append(a[i].name, a[i].value);
    380 			}
    381 
    382 			if (options.extraData) {
    383 				var serializedData = deepSerialize(options.extraData);
    384 
    385 				for (i = 0; i < serializedData.length; i++) {
    386 					if (serializedData[i]) {
    387 						formdata.append(serializedData[i][0], serializedData[i][1]);
    388 					}
    389 				}
    390 			}
    391 
    392 			options.data = null;
    393 
    394 			var s = $.extend(true, {}, $.ajaxSettings, options, {
    395 				contentType : false,
    396 				processData : false,
    397 				cache       : false,
    398 				type        : method || 'POST'
    399 			});
    400 
    401 			if (options.uploadProgress) {
    402 				// workaround because jqXHR does not expose upload property
    403 				s.xhr = function() {
    404 					var xhr = $.ajaxSettings.xhr();
    405 
    406 					if (xhr.upload) {
    407 						xhr.upload.addEventListener('progress', function(event) {
    408 							var percent = 0;
    409 							var position = event.loaded || event.position;			/* event.position is deprecated */
    410 							var total = event.total;
    411 
    412 							if (event.lengthComputable) {
    413 								percent = Math.ceil(position / total * 100);
    414 							}
    415 
    416 							options.uploadProgress(event, position, total, percent);
    417 						}, false);
    418 					}
    419 
    420 					return xhr;
    421 				};
    422 			}
    423 
    424 			s.data = null;
    425 
    426 			var beforeSend = s.beforeSend;
    427 
    428 			s.beforeSend = function(xhr, o) {
    429 				// Send FormData() provided by user
    430 				if (options.formData) {
    431 					o.data = options.formData;
    432 				} else {
    433 					o.data = formdata;
    434 				}
    435 
    436 				if (beforeSend) {
    437 					beforeSend.call(this, xhr, o);
    438 				}
    439 			};
    440 
    441 			return $.ajax(s);
    442 		}
    443 
    444 		// private function for handling file uploads (hat tip to YAHOO!)
    445 		function fileUploadIframe(a) {
    446 			var form = $form[0], el, i, s, g, id, $io, io, xhr, sub, n, timedOut, timeoutHandle;
    447 			var deferred = $.Deferred();
    448 
    449 			// #341
    450 			deferred.abort = function(status) {
    451 				xhr.abort(status);
    452 			};
    453 
    454 			if (a) {
    455 				// ensure that every serialized input is still enabled
    456 				for (i = 0; i < elements.length; i++) {
    457 					el = $(elements[i]);
    458 					if (hasProp) {
    459 						el.prop('disabled', false);
    460 					} else {
    461 						el.removeAttr('disabled');
    462 					}
    463 				}
    464 			}
    465 
    466 			s = $.extend(true, {}, $.ajaxSettings, options);
    467 			s.context = s.context || s;
    468 			id = 'jqFormIO' + new Date().getTime();
    469 			var ownerDocument = form.ownerDocument;
    470 			var $body = $form.closest('body');
    471 
    472 			if (s.iframeTarget) {
    473 				$io = $(s.iframeTarget, ownerDocument);
    474 				n = $io.attr2('name');
    475 				if (!n) {
    476 					$io.attr2('name', id);
    477 				} else {
    478 					id = n;
    479 				}
    480 
    481 			} else {
    482 				$io = $('<iframe name="' + id + '" src="' + s.iframeSrc + '" />', ownerDocument);
    483 				$io.css({position: 'absolute', top: '-1000px', left: '-1000px'});
    484 			}
    485 			io = $io[0];
    486 
    487 
    488 			xhr = { // mock object
    489 				aborted               : 0,
    490 				responseText          : null,
    491 				responseXML           : null,
    492 				status                : 0,
    493 				statusText            : 'n/a',
    494 				getAllResponseHeaders : function() {},
    495 				getResponseHeader     : function() {},
    496 				setRequestHeader      : function() {},
    497 				abort                 : function(status) {
    498 					var e = (status === 'timeout' ? 'timeout' : 'aborted');
    499 
    500 					log('aborting upload... ' + e);
    501 					this.aborted = 1;
    502 
    503 					try { // #214, #257
    504 						if (io.contentWindow.document.execCommand) {
    505 							io.contentWindow.document.execCommand('Stop');
    506 						}
    507 					} catch (ignore) {}
    508 
    509 					$io.attr('src', s.iframeSrc); // abort op in progress
    510 					xhr.error = e;
    511 					if (s.error) {
    512 						s.error.call(s.context, xhr, e, status);
    513 					}
    514 
    515 					if (g) {
    516 						$.event.trigger('ajaxError', [xhr, s, e]);
    517 					}
    518 
    519 					if (s.complete) {
    520 						s.complete.call(s.context, xhr, e);
    521 					}
    522 				}
    523 			};
    524 
    525 			g = s.global;
    526 			// trigger ajax global events so that activity/block indicators work like normal
    527 			if (g && $.active++ === 0) {
    528 				$.event.trigger('ajaxStart');
    529 			}
    530 			if (g) {
    531 				$.event.trigger('ajaxSend', [xhr, s]);
    532 			}
    533 
    534 			if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) {
    535 				if (s.global) {
    536 					$.active--;
    537 				}
    538 				deferred.reject();
    539 
    540 				return deferred;
    541 			}
    542 
    543 			if (xhr.aborted) {
    544 				deferred.reject();
    545 
    546 				return deferred;
    547 			}
    548 
    549 			// add submitting element to data if we know it
    550 			sub = form.clk;
    551 			if (sub) {
    552 				n = sub.name;
    553 				if (n && !sub.disabled) {
    554 					s.extraData = s.extraData || {};
    555 					s.extraData[n] = sub.value;
    556 					if (sub.type === 'image') {
    557 						s.extraData[n + '.x'] = form.clk_x;
    558 						s.extraData[n + '.y'] = form.clk_y;
    559 					}
    560 				}
    561 			}
    562 
    563 			var CLIENT_TIMEOUT_ABORT = 1;
    564 			var SERVER_ABORT = 2;
    565 
    566 			function getDoc(frame) {
    567 				/* it looks like contentWindow or contentDocument do not
    568 				 * carry the protocol property in ie8, when running under ssl
    569 				 * frame.document is the only valid response document, since
    570 				 * the protocol is know but not on the other two objects. strange?
    571 				 * "Same origin policy" http://en.wikipedia.org/wiki/Same_origin_policy
    572 				 */
    573 
    574 				var doc = null;
    575 
    576 				// IE8 cascading access check
    577 				try {
    578 					if (frame.contentWindow) {
    579 						doc = frame.contentWindow.document;
    580 					}
    581 				} catch (err) {
    582 					// IE8 access denied under ssl & missing protocol
    583 					log('cannot get iframe.contentWindow document: ' + err);
    584 				}
    585 
    586 				if (doc) { // successful getting content
    587 					return doc;
    588 				}
    589 
    590 				try { // simply checking may throw in ie8 under ssl or mismatched protocol
    591 					doc = frame.contentDocument ? frame.contentDocument : frame.document;
    592 				} catch (err) {
    593 					// last attempt
    594 					log('cannot get iframe.contentDocument: ' + err);
    595 					doc = frame.document;
    596 				}
    597 
    598 				return doc;
    599 			}
    600 
    601 			// Rails CSRF hack (thanks to Yvan Barthelemy)
    602 			var csrf_token = $('meta[name=csrf-token]').attr('content');
    603 			var csrf_param = $('meta[name=csrf-param]').attr('content');
    604 
    605 			if (csrf_param && csrf_token) {
    606 				s.extraData = s.extraData || {};
    607 				s.extraData[csrf_param] = csrf_token;
    608 			}
    609 
    610 			// take a breath so that pending repaints get some cpu time before the upload starts
    611 			function doSubmit() {
    612 				// make sure form attrs are set
    613 				var t = $form.attr2('target'),
    614 					a = $form.attr2('action'),
    615 					mp = 'multipart/form-data',
    616 					et = $form.attr('enctype') || $form.attr('encoding') || mp;
    617 
    618 				// update form attrs in IE friendly way
    619 				form.setAttribute('target', id);
    620 				if (!method || /post/i.test(method)) {
    621 					form.setAttribute('method', 'POST');
    622 				}
    623 				if (a !== s.url) {
    624 					form.setAttribute('action', s.url);
    625 				}
    626 
    627 				// ie borks in some cases when setting encoding
    628 				if (!s.skipEncodingOverride && (!method || /post/i.test(method))) {
    629 					$form.attr({
    630 						encoding : 'multipart/form-data',
    631 						enctype  : 'multipart/form-data'
    632 					});
    633 				}
    634 
    635 				// support timout
    636 				if (s.timeout) {
    637 					timeoutHandle = setTimeout(function() {
    638 						timedOut = true; cb(CLIENT_TIMEOUT_ABORT);
    639 					}, s.timeout);
    640 				}
    641 
    642 				// look for server aborts
    643 				function checkState() {
    644 					try {
    645 						var state = getDoc(io).readyState;
    646 
    647 						log('state = ' + state);
    648 						if (state && state.toLowerCase() === 'uninitialized') {
    649 							setTimeout(checkState, 50);
    650 						}
    651 
    652 					} catch (e) {
    653 						log('Server abort: ', e, ' (', e.name, ')');
    654 						cb(SERVER_ABORT);				// eslint-disable-line callback-return
    655 						if (timeoutHandle) {
    656 							clearTimeout(timeoutHandle);
    657 						}
    658 						timeoutHandle = undefined;
    659 					}
    660 				}
    661 
    662 				// add "extra" data to form if provided in options
    663 				var extraInputs = [];
    664 
    665 				try {
    666 					if (s.extraData) {
    667 						for (var n in s.extraData) {
    668 							if (s.extraData.hasOwnProperty(n)) {
    669 								// if using the $.param format that allows for multiple values with the same name
    670 								if ($.isPlainObject(s.extraData[n]) && s.extraData[n].hasOwnProperty('name') && s.extraData[n].hasOwnProperty('value')) {
    671 									extraInputs.push(
    672 										$('<input type="hidden" name="' + s.extraData[n].name + '">', ownerDocument).val(s.extraData[n].value)
    673 											.appendTo(form)[0]);
    674 								} else {
    675 									extraInputs.push(
    676 										$('<input type="hidden" name="' + n + '">', ownerDocument).val(s.extraData[n])
    677 											.appendTo(form)[0]);
    678 								}
    679 							}
    680 						}
    681 					}
    682 
    683 					if (!s.iframeTarget) {
    684 						// add iframe to doc and submit the form
    685 						$io.appendTo($body);
    686 					}
    687 
    688 					if (io.attachEvent) {
    689 						io.attachEvent('onload', cb);
    690 					} else {
    691 						io.addEventListener('load', cb, false);
    692 					}
    693 
    694 					setTimeout(checkState, 15);
    695 
    696 					try {
    697 						form.submit();
    698 
    699 					} catch (err) {
    700 						// just in case form has element with name/id of 'submit'
    701 						var submitFn = document.createElement('form').submit;
    702 
    703 						submitFn.apply(form);
    704 					}
    705 
    706 				} finally {
    707 					// reset attrs and remove "extra" input elements
    708 					form.setAttribute('action', a);
    709 					form.setAttribute('enctype', et); // #380
    710 					if (t) {
    711 						form.setAttribute('target', t);
    712 					} else {
    713 						$form.removeAttr('target');
    714 					}
    715 					$(extraInputs).remove();
    716 				}
    717 			}
    718 
    719 			if (s.forceSync) {
    720 				doSubmit();
    721 			} else {
    722 				setTimeout(doSubmit, 10); // this lets dom updates render
    723 			}
    724 
    725 			var data, doc, domCheckCount = 50, callbackProcessed;
    726 
    727 			function cb(e) {
    728 				if (xhr.aborted || callbackProcessed) {
    729 					return;
    730 				}
    731 
    732 				doc = getDoc(io);
    733 				if (!doc) {
    734 					log('cannot access response document');
    735 					e = SERVER_ABORT;
    736 				}
    737 				if (e === CLIENT_TIMEOUT_ABORT && xhr) {
    738 					xhr.abort('timeout');
    739 					deferred.reject(xhr, 'timeout');
    740 
    741 					return;
    742 
    743 				}
    744 				if (e === SERVER_ABORT && xhr) {
    745 					xhr.abort('server abort');
    746 					deferred.reject(xhr, 'error', 'server abort');
    747 
    748 					return;
    749 				}
    750 
    751 				if (!doc || doc.location.href === s.iframeSrc) {
    752 					// response not received yet
    753 					if (!timedOut) {
    754 						return;
    755 					}
    756 				}
    757 
    758 				if (io.detachEvent) {
    759 					io.detachEvent('onload', cb);
    760 				} else {
    761 					io.removeEventListener('load', cb, false);
    762 				}
    763 
    764 				var status = 'success', errMsg;
    765 
    766 				try {
    767 					if (timedOut) {
    768 						throw 'timeout';
    769 					}
    770 
    771 					var isXml = s.dataType === 'xml' || doc.XMLDocument || $.isXMLDoc(doc);
    772 
    773 					log('isXml=' + isXml);
    774 
    775 					if (!isXml && window.opera && (doc.body === null || !doc.body.innerHTML)) {
    776 						if (--domCheckCount) {
    777 							// in some browsers (Opera) the iframe DOM is not always traversable when
    778 							// the onload callback fires, so we loop a bit to accommodate
    779 							log('requeing onLoad callback, DOM not available');
    780 							setTimeout(cb, 250);
    781 
    782 							return;
    783 						}
    784 						// let this fall through because server response could be an empty document
    785 						// log('Could not access iframe DOM after mutiple tries.');
    786 						// throw 'DOMException: not available';
    787 					}
    788 
    789 					// log('response detected');
    790 					var docRoot = doc.body ? doc.body : doc.documentElement;
    791 
    792 					xhr.responseText = docRoot ? docRoot.innerHTML : null;
    793 					xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
    794 					if (isXml) {
    795 						s.dataType = 'xml';
    796 					}
    797 					xhr.getResponseHeader = function(header){
    798 						var headers = {'content-type': s.dataType};
    799 
    800 						return headers[header.toLowerCase()];
    801 					};
    802 					// support for XHR 'status' & 'statusText' emulation :
    803 					if (docRoot) {
    804 						xhr.status = Number(docRoot.getAttribute('status')) || xhr.status;
    805 						xhr.statusText = docRoot.getAttribute('statusText') || xhr.statusText;
    806 					}
    807 
    808 					var dt = (s.dataType || '').toLowerCase();
    809 					var scr = /(json|script|text)/.test(dt);
    810 
    811 					if (scr || s.textarea) {
    812 						// see if user embedded response in textarea
    813 						var ta = doc.getElementsByTagName('textarea')[0];
    814 
    815 						if (ta) {
    816 							xhr.responseText = ta.value;
    817 							// support for XHR 'status' & 'statusText' emulation :
    818 							xhr.status = Number(ta.getAttribute('status')) || xhr.status;
    819 							xhr.statusText = ta.getAttribute('statusText') || xhr.statusText;
    820 
    821 						} else if (scr) {
    822 							// account for browsers injecting pre around json response
    823 							var pre = doc.getElementsByTagName('pre')[0];
    824 							var b = doc.getElementsByTagName('body')[0];
    825 
    826 							if (pre) {
    827 								xhr.responseText = pre.textContent ? pre.textContent : pre.innerText;
    828 							} else if (b) {
    829 								xhr.responseText = b.textContent ? b.textContent : b.innerText;
    830 							}
    831 						}
    832 
    833 					} else if (dt === 'xml' && !xhr.responseXML && xhr.responseText) {
    834 						xhr.responseXML = toXml(xhr.responseText);			// eslint-disable-line no-use-before-define
    835 					}
    836 
    837 					try {
    838 						data = httpData(xhr, dt, s);						// eslint-disable-line no-use-before-define
    839 
    840 					} catch (err) {
    841 						status = 'parsererror';
    842 						xhr.error = errMsg = (err || status);
    843 					}
    844 
    845 				} catch (err) {
    846 					log('error caught: ', err);
    847 					status = 'error';
    848 					xhr.error = errMsg = (err || status);
    849 				}
    850 
    851 				if (xhr.aborted) {
    852 					log('upload aborted');
    853 					status = null;
    854 				}
    855 
    856 				if (xhr.status) { // we've set xhr.status
    857 					status = ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) ? 'success' : 'error';
    858 				}
    859 
    860 				// ordering of these callbacks/triggers is odd, but that's how $.ajax does it
    861 				if (status === 'success') {
    862 					if (s.success) {
    863 						s.success.call(s.context, data, 'success', xhr);
    864 					}
    865 
    866 					deferred.resolve(xhr.responseText, 'success', xhr);
    867 
    868 					if (g) {
    869 						$.event.trigger('ajaxSuccess', [xhr, s]);
    870 					}
    871 
    872 				} else if (status) {
    873 					if (typeof errMsg === 'undefined') {
    874 						errMsg = xhr.statusText;
    875 					}
    876 					if (s.error) {
    877 						s.error.call(s.context, xhr, status, errMsg);
    878 					}
    879 					deferred.reject(xhr, 'error', errMsg);
    880 					if (g) {
    881 						$.event.trigger('ajaxError', [xhr, s, errMsg]);
    882 					}
    883 				}
    884 
    885 				if (g) {
    886 					$.event.trigger('ajaxComplete', [xhr, s]);
    887 				}
    888 
    889 				if (g && !--$.active) {
    890 					$.event.trigger('ajaxStop');
    891 				}
    892 
    893 				if (s.complete) {
    894 					s.complete.call(s.context, xhr, status);
    895 				}
    896 
    897 				callbackProcessed = true;
    898 				if (s.timeout) {
    899 					clearTimeout(timeoutHandle);
    900 				}
    901 
    902 				// clean up
    903 				setTimeout(function() {
    904 					if (!s.iframeTarget) {
    905 						$io.remove();
    906 					} else { // adding else to clean up existing iframe response.
    907 						$io.attr('src', s.iframeSrc);
    908 					}
    909 					xhr.responseXML = null;
    910 				}, 100);
    911 			}
    912 
    913 			var toXml = $.parseXML || function(s, doc) { // use parseXML if available (jQuery 1.5+)
    914 				if (window.ActiveXObject) {
    915 					doc = new ActiveXObject('Microsoft.XMLDOM');
    916 					doc.async = 'false';
    917 					doc.loadXML(s);
    918 
    919 				} else {
    920 					doc = (new DOMParser()).parseFromString(s, 'text/xml');
    921 				}
    922 
    923 				return (doc && doc.documentElement && doc.documentElement.nodeName !== 'parsererror') ? doc : null;
    924 			};
    925 			var parseJSON = $.parseJSON || function(s) {
    926 				/* jslint evil:true */
    927 				return window['eval']('(' + s + ')');			// eslint-disable-line dot-notation
    928 			};
    929 
    930 			var httpData = function(xhr, type, s) { // mostly lifted from jq1.4.4
    931 
    932 				var ct = xhr.getResponseHeader('content-type') || '',
    933 					xml = ((type === 'xml' || !type) && ct.indexOf('xml') >= 0),
    934 					data = xml ? xhr.responseXML : xhr.responseText;
    935 
    936 				if (xml && data.documentElement.nodeName === 'parsererror') {
    937 					if ($.error) {
    938 						$.error('parsererror');
    939 					}
    940 				}
    941 				if (s && s.dataFilter) {
    942 					data = s.dataFilter(data, type);
    943 				}
    944 				if (typeof data === 'string') {
    945 					if ((type === 'json' || !type) && ct.indexOf('json') >= 0) {
    946 						data = parseJSON(data);
    947 					} else if ((type === 'script' || !type) && ct.indexOf('javascript') >= 0) {
    948 						$.globalEval(data);
    949 					}
    950 				}
    951 
    952 				return data;
    953 			};
    954 
    955 			return deferred;
    956 		}
    957 	};
    958 
    959 	/**
    960 	 * ajaxForm() provides a mechanism for fully automating form submission.
    961 	 *
    962 	 * The advantages of using this method instead of ajaxSubmit() are:
    963 	 *
    964 	 * 1: This method will include coordinates for <input type="image"> elements (if the element
    965 	 *	is used to submit the form).
    966 	 * 2. This method will include the submit element's name/value data (for the element that was
    967 	 *	used to submit the form).
    968 	 * 3. This method binds the submit() method to the form for you.
    969 	 *
    970 	 * The options argument for ajaxForm works exactly as it does for ajaxSubmit. ajaxForm merely
    971 	 * passes the options argument along after properly binding events for submit elements and
    972 	 * the form itself.
    973 	 */
    974 	$.fn.ajaxForm = function(options, data, dataType, onSuccess) {
    975 		if (typeof options === 'string' || (options === false && arguments.length > 0)) {
    976 			options = {
    977 				'url'      : options,
    978 				'data'     : data,
    979 				'dataType' : dataType
    980 			};
    981 
    982 			if (typeof onSuccess === 'function') {
    983 				options.success = onSuccess;
    984 			}
    985 		}
    986 
    987 		options = options || {};
    988 		options.delegation = options.delegation && $.isFunction($.fn.on);
    989 
    990 		// in jQuery 1.3+ we can fix mistakes with the ready state
    991 		if (!options.delegation && this.length === 0) {
    992 			var o = {s: this.selector, c: this.context};
    993 
    994 			if (!$.isReady && o.s) {
    995 				log('DOM not ready, queuing ajaxForm');
    996 				$(function() {
    997 					$(o.s, o.c).ajaxForm(options);
    998 				});
    999 
   1000 				return this;
   1001 			}
   1002 
   1003 			// is your DOM ready?  http://docs.jquery.com/Tutorials:Introducing_$(document).ready()
   1004 			log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)'));
   1005 
   1006 			return this;
   1007 		}
   1008 
   1009 		if (options.delegation) {
   1010 			$(document)
   1011 				.off('submit.form-plugin', this.selector, doAjaxSubmit)
   1012 				.off('click.form-plugin', this.selector, captureSubmittingElement)
   1013 				.on('submit.form-plugin', this.selector, options, doAjaxSubmit)
   1014 				.on('click.form-plugin', this.selector, options, captureSubmittingElement);
   1015 
   1016 			return this;
   1017 		}
   1018 
   1019 		if (options.beforeFormUnbind) {
   1020 			options.beforeFormUnbind(this, options);
   1021 		}
   1022 
   1023 		return this.ajaxFormUnbind()
   1024 			.on('submit.form-plugin', options, doAjaxSubmit)
   1025 			.on('click.form-plugin', options, captureSubmittingElement);
   1026 	};
   1027 
   1028 	// private event handlers
   1029 	function doAjaxSubmit(e) {
   1030 		/* jshint validthis:true */
   1031 		var options = e.data;
   1032 
   1033 		if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed
   1034 			e.preventDefault();
   1035 			$(e.target).closest('form').ajaxSubmit(options); // #365
   1036 		}
   1037 	}
   1038 
   1039 	function captureSubmittingElement(e) {
   1040 		/* jshint validthis:true */
   1041 		var target = e.target;
   1042 		var $el = $(target);
   1043 
   1044 		if (!$el.is('[type=submit],[type=image]')) {
   1045 			// is this a child element of the submit el?  (ex: a span within a button)
   1046 			var t = $el.closest('[type=submit]');
   1047 
   1048 			if (t.length === 0) {
   1049 				return;
   1050 			}
   1051 			target = t[0];
   1052 		}
   1053 
   1054 		var form = target.form;
   1055 
   1056 		form.clk = target;
   1057 
   1058 		if (target.type === 'image') {
   1059 			if (typeof e.offsetX !== 'undefined') {
   1060 				form.clk_x = e.offsetX;
   1061 				form.clk_y = e.offsetY;
   1062 
   1063 			} else if (typeof $.fn.offset === 'function') {
   1064 				var offset = $el.offset();
   1065 
   1066 				form.clk_x = e.pageX - offset.left;
   1067 				form.clk_y = e.pageY - offset.top;
   1068 
   1069 			} else {
   1070 				form.clk_x = e.pageX - target.offsetLeft;
   1071 				form.clk_y = e.pageY - target.offsetTop;
   1072 			}
   1073 		}
   1074 		// clear form vars
   1075 		setTimeout(function() {
   1076 			form.clk = form.clk_x = form.clk_y = null;
   1077 		}, 100);
   1078 	}
   1079 
   1080 
   1081 	// ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
   1082 	$.fn.ajaxFormUnbind = function() {
   1083 		return this.off('submit.form-plugin click.form-plugin');
   1084 	};
   1085 
   1086 	/**
   1087 	 * formToArray() gathers form element data into an array of objects that can
   1088 	 * be passed to any of the following ajax functions: $.get, $.post, or load.
   1089 	 * Each object in the array has both a 'name' and 'value' property. An example of
   1090 	 * an array for a simple login form might be:
   1091 	 *
   1092 	 * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ]
   1093 	 *
   1094 	 * It is this array that is passed to pre-submit callback functions provided to the
   1095 	 * ajaxSubmit() and ajaxForm() methods.
   1096 	 */
   1097 	$.fn.formToArray = function(semantic, elements, filtering) {
   1098 		var a = [];
   1099 
   1100 		if (this.length === 0) {
   1101 			return a;
   1102 		}
   1103 
   1104 		var form = this[0];
   1105 		var formId = this.attr('id');
   1106 		var els = (semantic || typeof form.elements === 'undefined') ? form.getElementsByTagName('*') : form.elements;
   1107 		var els2;
   1108 
   1109 		if (els) {
   1110 			els = $.makeArray(els); // convert to standard array
   1111 		}
   1112 
   1113 		// #386; account for inputs outside the form which use the 'form' attribute
   1114 		// FinesseRus: in non-IE browsers outside fields are already included in form.elements.
   1115 		if (formId && (semantic || /(Edge|Trident)\//.test(navigator.userAgent))) {
   1116 			els2 = $(':input[form="' + formId + '"]').get(); // hat tip @thet
   1117 			if (els2.length) {
   1118 				els = (els || []).concat(els2);
   1119 			}
   1120 		}
   1121 
   1122 		if (!els || !els.length) {
   1123 			return a;
   1124 		}
   1125 
   1126 		if ($.isFunction(filtering)) {
   1127 			els = $.map(els, filtering);
   1128 		}
   1129 
   1130 		var i, j, n, v, el, max, jmax;
   1131 
   1132 		for (i = 0, max = els.length; i < max; i++) {
   1133 			el = els[i];
   1134 			n = el.name;
   1135 			if (!n || el.disabled) {
   1136 				continue;
   1137 			}
   1138 
   1139 			if (semantic && form.clk && el.type === 'image') {
   1140 				// handle image inputs on the fly when semantic == true
   1141 				if (form.clk === el) {
   1142 					a.push({name: n, value: $(el).val(), type: el.type});
   1143 					a.push({name: n + '.x', value: form.clk_x}, {name: n + '.y', value: form.clk_y});
   1144 				}
   1145 				continue;
   1146 			}
   1147 
   1148 			v = $.fieldValue(el, true);
   1149 			if (v && v.constructor === Array) {
   1150 				if (elements) {
   1151 					elements.push(el);
   1152 				}
   1153 				for (j = 0, jmax = v.length; j < jmax; j++) {
   1154 					a.push({name: n, value: v[j]});
   1155 				}
   1156 
   1157 			} else if (feature.fileapi && el.type === 'file') {
   1158 				if (elements) {
   1159 					elements.push(el);
   1160 				}
   1161 
   1162 				var files = el.files;
   1163 
   1164 				if (files.length) {
   1165 					for (j = 0; j < files.length; j++) {
   1166 						a.push({name: n, value: files[j], type: el.type});
   1167 					}
   1168 				} else {
   1169 					// #180
   1170 					a.push({name: n, value: '', type: el.type});
   1171 				}
   1172 
   1173 			} else if (v !== null && typeof v !== 'undefined') {
   1174 				if (elements) {
   1175 					elements.push(el);
   1176 				}
   1177 				a.push({name: n, value: v, type: el.type, required: el.required});
   1178 			}
   1179 		}
   1180 
   1181 		if (!semantic && form.clk) {
   1182 			// input type=='image' are not found in elements array! handle it here
   1183 			var $input = $(form.clk), input = $input[0];
   1184 
   1185 			n = input.name;
   1186 
   1187 			if (n && !input.disabled && input.type === 'image') {
   1188 				a.push({name: n, value: $input.val()});
   1189 				a.push({name: n + '.x', value: form.clk_x}, {name: n + '.y', value: form.clk_y});
   1190 			}
   1191 		}
   1192 
   1193 		return a;
   1194 	};
   1195 
   1196 	/**
   1197 	 * Serializes form data into a 'submittable' string. This method will return a string
   1198 	 * in the format: name1=value1&amp;name2=value2
   1199 	 */
   1200 	$.fn.formSerialize = function(semantic) {
   1201 		// hand off to jQuery.param for proper encoding
   1202 		return $.param(this.formToArray(semantic));
   1203 	};
   1204 
   1205 	/**
   1206 	 * Serializes all field elements in the jQuery object into a query string.
   1207 	 * This method will return a string in the format: name1=value1&amp;name2=value2
   1208 	 */
   1209 	$.fn.fieldSerialize = function(successful) {
   1210 		var a = [];
   1211 
   1212 		this.each(function() {
   1213 			var n = this.name;
   1214 
   1215 			if (!n) {
   1216 				return;
   1217 			}
   1218 
   1219 			var v = $.fieldValue(this, successful);
   1220 
   1221 			if (v && v.constructor === Array) {
   1222 				for (var i = 0, max = v.length; i < max; i++) {
   1223 					a.push({name: n, value: v[i]});
   1224 				}
   1225 
   1226 			} else if (v !== null && typeof v !== 'undefined') {
   1227 				a.push({name: this.name, value: v});
   1228 			}
   1229 		});
   1230 
   1231 		// hand off to jQuery.param for proper encoding
   1232 		return $.param(a);
   1233 	};
   1234 
   1235 	/**
   1236 	 * Returns the value(s) of the element in the matched set. For example, consider the following form:
   1237 	 *
   1238 	 *	<form><fieldset>
   1239 	 *		<input name="A" type="text">
   1240 	 *		<input name="A" type="text">
   1241 	 *		<input name="B" type="checkbox" value="B1">
   1242 	 *		<input name="B" type="checkbox" value="B2">
   1243 	 *		<input name="C" type="radio" value="C1">
   1244 	 *		<input name="C" type="radio" value="C2">
   1245 	 *	</fieldset></form>
   1246 	 *
   1247 	 *	var v = $('input[type=text]').fieldValue();
   1248 	 *	// if no values are entered into the text inputs
   1249 	 *	v === ['','']
   1250 	 *	// if values entered into the text inputs are 'foo' and 'bar'
   1251 	 *	v === ['foo','bar']
   1252 	 *
   1253 	 *	var v = $('input[type=checkbox]').fieldValue();
   1254 	 *	// if neither checkbox is checked
   1255 	 *	v === undefined
   1256 	 *	// if both checkboxes are checked
   1257 	 *	v === ['B1', 'B2']
   1258 	 *
   1259 	 *	var v = $('input[type=radio]').fieldValue();
   1260 	 *	// if neither radio is checked
   1261 	 *	v === undefined
   1262 	 *	// if first radio is checked
   1263 	 *	v === ['C1']
   1264 	 *
   1265 	 * The successful argument controls whether or not the field element must be 'successful'
   1266 	 * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls).
   1267 	 * The default value of the successful argument is true. If this value is false the value(s)
   1268 	 * for each element is returned.
   1269 	 *
   1270 	 * Note: This method *always* returns an array. If no valid value can be determined the
   1271 	 *	array will be empty, otherwise it will contain one or more values.
   1272 	 */
   1273 	$.fn.fieldValue = function(successful) {
   1274 		for (var val = [], i = 0, max = this.length; i < max; i++) {
   1275 			var el = this[i];
   1276 			var v = $.fieldValue(el, successful);
   1277 
   1278 			if (v === null || typeof v === 'undefined' || (v.constructor === Array && !v.length)) {
   1279 				continue;
   1280 			}
   1281 
   1282 			if (v.constructor === Array) {
   1283 				$.merge(val, v);
   1284 			} else {
   1285 				val.push(v);
   1286 			}
   1287 		}
   1288 
   1289 		return val;
   1290 	};
   1291 
   1292 	/**
   1293 	 * Returns the value of the field element.
   1294 	 */
   1295 	$.fieldValue = function(el, successful) {
   1296 		var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
   1297 
   1298 		if (typeof successful === 'undefined') {
   1299 			successful = true;
   1300 		}
   1301 
   1302 		/* eslint-disable no-mixed-operators */
   1303 		if (successful && (!n || el.disabled || t === 'reset' || t === 'button' ||
   1304 			(t === 'checkbox' || t === 'radio') && !el.checked ||
   1305 			(t === 'submit' || t === 'image') && el.form && el.form.clk !== el ||
   1306 			tag === 'select' && el.selectedIndex === -1)) {
   1307 		/* eslint-enable no-mixed-operators */
   1308 			return null;
   1309 		}
   1310 
   1311 		if (tag === 'select') {
   1312 			var index = el.selectedIndex;
   1313 
   1314 			if (index < 0) {
   1315 				return null;
   1316 			}
   1317 
   1318 			var a = [], ops = el.options;
   1319 			var one = (t === 'select-one');
   1320 			var max = (one ? index + 1 : ops.length);
   1321 
   1322 			for (var i = (one ? index : 0); i < max; i++) {
   1323 				var op = ops[i];
   1324 
   1325 				if (op.selected && !op.disabled) {
   1326 					var v = op.value;
   1327 
   1328 					if (!v) { // extra pain for IE...
   1329 						v = (op.attributes && op.attributes.value && !(op.attributes.value.specified)) ? op.text : op.value;
   1330 					}
   1331 
   1332 					if (one) {
   1333 						return v;
   1334 					}
   1335 
   1336 					a.push(v);
   1337 				}
   1338 			}
   1339 
   1340 			return a;
   1341 		}
   1342 
   1343 		return $(el).val().replace(rCRLF, '\r\n');
   1344 	};
   1345 
   1346 	/**
   1347 	 * Clears the form data. Takes the following actions on the form's input fields:
   1348 	 *  - input text fields will have their 'value' property set to the empty string
   1349 	 *  - select elements will have their 'selectedIndex' property set to -1
   1350 	 *  - checkbox and radio inputs will have their 'checked' property set to false
   1351 	 *  - inputs of type submit, button, reset, and hidden will *not* be effected
   1352 	 *  - button elements will *not* be effected
   1353 	 */
   1354 	$.fn.clearForm = function(includeHidden) {
   1355 		return this.each(function() {
   1356 			$('input,select,textarea', this).clearFields(includeHidden);
   1357 		});
   1358 	};
   1359 
   1360 	/**
   1361 	 * Clears the selected form elements.
   1362 	 */
   1363 	$.fn.clearFields = $.fn.clearInputs = function(includeHidden) {
   1364 		var re = /^(?:color|date|datetime|email|month|number|password|range|search|tel|text|time|url|week)$/i; // 'hidden' is not in this list
   1365 
   1366 		return this.each(function() {
   1367 			var t = this.type, tag = this.tagName.toLowerCase();
   1368 
   1369 			if (re.test(t) || tag === 'textarea') {
   1370 				this.value = '';
   1371 
   1372 			} else if (t === 'checkbox' || t === 'radio') {
   1373 				this.checked = false;
   1374 
   1375 			} else if (tag === 'select') {
   1376 				this.selectedIndex = -1;
   1377 
   1378 			} else if (t === 'file') {
   1379 				if (/MSIE/.test(navigator.userAgent)) {
   1380 					$(this).replaceWith($(this).clone(true));
   1381 				} else {
   1382 					$(this).val('');
   1383 				}
   1384 
   1385 			} else if (includeHidden) {
   1386 				// includeHidden can be the value true, or it can be a selector string
   1387 				// indicating a special test; for example:
   1388 				// $('#myForm').clearForm('.special:hidden')
   1389 				// the above would clean hidden inputs that have the class of 'special'
   1390 				if ((includeHidden === true && /hidden/.test(t)) ||
   1391 					(typeof includeHidden === 'string' && $(this).is(includeHidden))) {
   1392 					this.value = '';
   1393 				}
   1394 			}
   1395 		});
   1396 	};
   1397 
   1398 
   1399 	/**
   1400 	 * Resets the form data or individual elements. Takes the following actions
   1401 	 * on the selected tags:
   1402 	 * - all fields within form elements will be reset to their original value
   1403 	 * - input / textarea / select fields will be reset to their original value
   1404 	 * - option / optgroup fields (for multi-selects) will defaulted individually
   1405 	 * - non-multiple options will find the right select to default
   1406 	 * - label elements will be searched against its 'for' attribute
   1407 	 * - all others will be searched for appropriate children to default
   1408 	 */
   1409 	$.fn.resetForm = function() {
   1410 		return this.each(function() {
   1411 			var el = $(this);
   1412 			var tag = this.tagName.toLowerCase();
   1413 
   1414 			switch (tag) {
   1415 			case 'input':
   1416 				this.checked = this.defaultChecked;
   1417 				// fall through
   1418 
   1419 			case 'textarea':
   1420 				this.value = this.defaultValue;
   1421 
   1422 				return true;
   1423 
   1424 			case 'option':
   1425 			case 'optgroup':
   1426 				var select = el.parents('select');
   1427 
   1428 				if (select.length && select[0].multiple) {
   1429 					if (tag === 'option') {
   1430 						this.selected = this.defaultSelected;
   1431 					} else {
   1432 						el.find('option').resetForm();
   1433 					}
   1434 				} else {
   1435 					select.resetForm();
   1436 				}
   1437 
   1438 				return true;
   1439 
   1440 			case 'select':
   1441 				el.find('option').each(function(i) {				// eslint-disable-line consistent-return
   1442 					this.selected = this.defaultSelected;
   1443 					if (this.defaultSelected && !el[0].multiple) {
   1444 						el[0].selectedIndex = i;
   1445 
   1446 						return false;
   1447 					}
   1448 				});
   1449 
   1450 				return true;
   1451 
   1452 			case 'label':
   1453 				var forEl = $(el.attr('for'));
   1454 				var list = el.find('input,select,textarea');
   1455 
   1456 				if (forEl[0]) {
   1457 					list.unshift(forEl[0]);
   1458 				}
   1459 
   1460 				list.resetForm();
   1461 
   1462 				return true;
   1463 
   1464 			case 'form':
   1465 				// guard against an input with the name of 'reset'
   1466 				// note that IE reports the reset function as an 'object'
   1467 				if (typeof this.reset === 'function' || (typeof this.reset === 'object' && !this.reset.nodeType)) {
   1468 					this.reset();
   1469 				}
   1470 
   1471 				return true;
   1472 
   1473 			default:
   1474 				el.find('form,input,label,select,textarea').resetForm();
   1475 
   1476 				return true;
   1477 			}
   1478 		});
   1479 	};
   1480 
   1481 	/**
   1482 	 * Enables or disables any matching elements.
   1483 	 */
   1484 	$.fn.enable = function(b) {
   1485 		if (typeof b === 'undefined') {
   1486 			b = true;
   1487 		}
   1488 
   1489 		return this.each(function() {
   1490 			this.disabled = !b;
   1491 		});
   1492 	};
   1493 
   1494 	/**
   1495 	 * Checks/unchecks any matching checkboxes or radio buttons and
   1496 	 * selects/deselects and matching option elements.
   1497 	 */
   1498 	$.fn.selected = function(select) {
   1499 		if (typeof select === 'undefined') {
   1500 			select = true;
   1501 		}
   1502 
   1503 		return this.each(function() {
   1504 			var t = this.type;
   1505 
   1506 			if (t === 'checkbox' || t === 'radio') {
   1507 				this.checked = select;
   1508 
   1509 			} else if (this.tagName.toLowerCase() === 'option') {
   1510 				var $sel = $(this).parent('select');
   1511 
   1512 				if (select && $sel[0] && $sel[0].type === 'select-one') {
   1513 					// deselect all other options
   1514 					$sel.find('option').selected(false);
   1515 				}
   1516 
   1517 				this.selected = select;
   1518 			}
   1519 		});
   1520 	};
   1521 
   1522 	// expose debug var
   1523 	$.fn.ajaxSubmit.debug = false;
   1524 
   1525 	// helper fn for console logging
   1526 	function log() {
   1527 		if (!$.fn.ajaxSubmit.debug) {
   1528 			return;
   1529 		}
   1530 
   1531 		var msg = '[jquery.form] ' + Array.prototype.join.call(arguments, '');
   1532 
   1533 		if (window.console && window.console.log) {
   1534 			window.console.log(msg);
   1535 
   1536 		} else if (window.opera && window.opera.postError) {
   1537 			window.opera.postError(msg);
   1538 		}
   1539 	}
   1540 }));