Bug Tracker

Changes between Initial Version and Version 1 of Ticket #13879


Ignore:
Timestamp:
May 10, 2013, 7:38:56 AM (10 years ago)
Author:
scottgonzalez
Comment:

Legend:

Unmodified
Added
Removed
Modified
  • Ticket #13879

    • Property Status changed from new to closed
    • Property Resolution changed from to notabug
  • Ticket #13879 – Description

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