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