Changeset 13261


Ignore:
Timestamp:
Mar 22, 2012, 10:36:32 PM (10 years ago)
Author:
livings124
Message:

jQuery Form Plugin to v1.3.2

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/web/javascript/jquery/jquery.form.js

    r13073 r13261  
    11/*!
    22 * jQuery Form Plugin
    3  * version: 2.87 (20-OCT-2011)
     3 * version: 3.02 (07-MAR-2012)
    44 * @requires jQuery v1.3.2 or later
    55 *
    66 * Examples and documentation at: http://malsup.com/jquery/form/
    77 * 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  */
     8 *    http://www.opensource.org/licenses/mit-license.php
     9 *    http://www.gnu.org/licenses/gpl.html
     10 */
     11/*global ActiveXObject alert */
    1112;(function($) {
     13"use strict";
    1214
    1315/*
    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.
     16    Usage Note:
     17    -----------
     18    Do not use both ajaxSubmit and ajaxForm on the same form.  These
     19    functions are mutually exclusive.  Use ajaxSubmit if you want
     20    to bind your own submit handler to the form.  For example,
     21
     22    $(document).ready(function() {
     23        $('#myForm').bind('submit', function(e) {
     24            e.preventDefault(); // <-- important
     25            $(this).ajaxSubmit({
     26                target: '#output'
     27            });
     28        });
     29    });
     30
     31    Use ajaxForm when you want the plugin to manage all the event binding
     32    for you.  For example,
     33
     34    $(document).ready(function() {
     35        $('#myForm').ajaxForm({
     36            target: '#output'
     37        });
     38    });
     39   
     40    You can also use ajaxForm with delegation (requires jQuery v1.7+), so the
     41    form does not have to exist when you invoke ajaxForm:
     42
     43    $('#myForm').ajaxForm({
     44        delegation: true,
     45        target: '#output'
     46    });
     47   
     48    When using ajaxForm, the ajaxSubmit function will be invoked for you
     49    at the appropriate time.
    4050*/
     51
     52/**
     53 * Feature detection
     54 */
     55var feature = {};
     56feature.fileapi = $("<input type='file'/>").get(0).files !== undefined;
     57feature.formdata = window.FormData !== undefined;
    4158
    4259/**
     
    4562 */
    4663$.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 traditional = options.traditional;
    91    if ( traditional === undefined ) {
    92       traditional = $.ajaxSettings.traditional;
    93    }
    94    
    95         var qx,n,v,a = this.formToArray(options.semantic);
    96         if (options.data) {
    97                 options.extraData = options.data;
    98       qx = $.param(options.data, traditional);
    99         }
    100 
    101         // give pre-submit callback an opportunity to abort the submit
    102         if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) {
    103                 log('ajaxSubmit: submit aborted via beforeSubmit callback');
    104                 return this;
    105         }
    106 
    107         // fire vetoable 'validate' event
    108         this.trigger('form-submit-validate', [a, this, options, veto]);
    109         if (veto.veto) {
    110                 log('ajaxSubmit: submit vetoed via form-submit-validate trigger');
    111                 return this;
    112         }
    113 
    114         var q = $.param(a, traditional);
    115    if (qx)
    116       q = ( q ? (q + '&' + qx) : qx );
    117 
    118         if (options.type.toUpperCase() == 'GET') {
    119                 options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
    120                 options.data = null;  // data is null for 'get'
    121         }
    122         else {
    123                 options.data = q; // data is the query string for 'post'
    124         }
    125 
    126         var callbacks = [];
    127         if (options.resetForm) {
    128                 callbacks.push(function() { $form.resetForm(); });
    129         }
    130         if (options.clearForm) {
    131                 callbacks.push(function() { $form.clearForm(options.includeHidden); });
    132         }
    133 
    134         // perform a load on the target only if dataType is not provided
    135         if (!options.dataType && options.target) {
    136                 var oldSuccess = options.success || function(){};
    137                 callbacks.push(function(data) {
    138                         var fn = options.replaceTarget ? 'replaceWith' : 'html';
    139                         $(options.target)[fn](data).each(oldSuccess, arguments);
    140                 });
    141         }
    142         else if (options.success) {
    143                 callbacks.push(options.success);
    144         }
    145 
    146         options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg
    147                 var context = options.context || options;   // jQuery 1.4+ supports scope context
    148                 for (var i=0, max=callbacks.length; i < max; i++) {
    149                         callbacks[i].apply(context, [data, status, xhr || $form, $form]);
     64    /*jshint scripturl:true */
     65
     66    // fast fail if nothing selected (http://dev.jquery.com/ticket/2752)
     67    if (!this.length) {
     68        log('ajaxSubmit: skipping submit process - no element selected');
     69        return this;
     70    }
     71   
     72    var method, action, url, $form = this;
     73
     74    if (typeof options == 'function') {
     75        options = { success: options };
     76    }
     77
     78    method = this.attr('method');
     79    action = this.attr('action');
     80    url = (typeof action === 'string') ? $.trim(action) : '';
     81    url = url || window.location.href || '';
     82    if (url) {
     83        // clean url (don't include hash vaue)
     84        url = (url.match(/^([^#]+)/)||[])[1];
     85    }
     86
     87    options = $.extend(true, {
     88        url:  url,
     89        success: $.ajaxSettings.success,
     90        type: method || 'GET',
     91        iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank'
     92    }, options);
     93
     94    // hook for manipulating the form data before it is extracted;
     95    // convenient for use with rich editors like tinyMCE or FCKEditor
     96    var veto = {};
     97    this.trigger('form-pre-serialize', [this, options, veto]);
     98    if (veto.veto) {
     99        log('ajaxSubmit: submit vetoed via form-pre-serialize trigger');
     100        return this;
     101    }
     102
     103    // provide opportunity to alter form data before it is serialized
     104    if (options.beforeSerialize && options.beforeSerialize(this, options) === false) {
     105        log('ajaxSubmit: submit aborted via beforeSerialize callback');
     106        return this;
     107    }
     108
     109    var traditional = options.traditional;
     110    if ( traditional === undefined ) {
     111        traditional = $.ajaxSettings.traditional;
     112    }
     113   
     114    var qx, a = this.formToArray(options.semantic);
     115    if (options.data) {
     116        options.extraData = options.data;
     117        qx = $.param(options.data, traditional);
     118    }
     119
     120    // give pre-submit callback an opportunity to abort the submit
     121    if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) {
     122        log('ajaxSubmit: submit aborted via beforeSubmit callback');
     123        return this;
     124    }
     125
     126    // fire vetoable 'validate' event
     127    this.trigger('form-submit-validate', [a, this, options, veto]);
     128    if (veto.veto) {
     129        log('ajaxSubmit: submit vetoed via form-submit-validate trigger');
     130        return this;
     131    }
     132
     133    var q = $.param(a, traditional);
     134    if (qx) {
     135        q = ( q ? (q + '&' + qx) : qx );
     136    }   
     137    if (options.type.toUpperCase() == 'GET') {
     138        options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
     139        options.data = null;  // data is null for 'get'
     140    }
     141    else {
     142        options.data = q; // data is the query string for 'post'
     143    }
     144
     145    var callbacks = [];
     146    if (options.resetForm) {
     147        callbacks.push(function() { $form.resetForm(); });
     148    }
     149    if (options.clearForm) {
     150        callbacks.push(function() { $form.clearForm(options.includeHidden); });
     151    }
     152
     153    // perform a load on the target only if dataType is not provided
     154    if (!options.dataType && options.target) {
     155        var oldSuccess = options.success || function(){};
     156        callbacks.push(function(data) {
     157            var fn = options.replaceTarget ? 'replaceWith' : 'html';
     158            $(options.target)[fn](data).each(oldSuccess, arguments);
     159        });
     160    }
     161    else if (options.success) {
     162        callbacks.push(options.success);
     163    }
     164
     165    options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg
     166        var context = options.context || options;    // jQuery 1.4+ supports scope context
     167        for (var i=0, max=callbacks.length; i < max; i++) {
     168            callbacks[i].apply(context, [data, status, xhr || $form, $form]);
     169        }
     170    };
     171
     172    // are there files to upload?
     173    var fileInputs = $('input:file:enabled[value]', this); // [value] (issue #113)
     174    var hasFileInputs = fileInputs.length > 0;
     175    var mp = 'multipart/form-data';
     176    var multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp);
     177
     178    var fileAPI = feature.fileapi && feature.formdata;
     179    log("fileAPI :" + fileAPI);
     180    var shouldUseFrame = (hasFileInputs || multipart) && !fileAPI;
     181
     182    // options.iframe allows user to force iframe mode
     183    // 06-NOV-09: now defaulting to iframe mode if file input is detected
     184    if (options.iframe !== false && (options.iframe || shouldUseFrame)) {
     185        // hack to fix Safari hang (thanks to Tim Molendijk for this)
     186        // see:  http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
     187        if (options.closeKeepAlive) {
     188            $.get(options.closeKeepAlive, function() {
     189                fileUploadIframe(a);
     190            });
     191        }
     192          else {
     193            fileUploadIframe(a);
     194          }
     195    }
     196    else if ((hasFileInputs || multipart) && fileAPI) {
     197        fileUploadXhr(a);
     198    }
     199    else {
     200        $.ajax(options);
     201    }
     202
     203     // fire 'notify' event
     204     this.trigger('form-submit-notify', [this, options]);
     205     return this;
     206
     207     // XMLHttpRequest Level 2 file uploads (big hat tip to francois2metz)
     208    function fileUploadXhr(a) {
     209        var formdata = new FormData();
     210
     211        for (var i=0; i < a.length; i++) {
     212            formdata.append(a[i].name, a[i].value);
     213        }
     214
     215        if (options.extraData) {
     216            for (var k in options.extraData)
     217                if (options.extraData.hasOwnProperty(k))
     218                    formdata.append(k, options.extraData[k]);
     219        }
     220
     221        options.data = null;
     222
     223        var s = $.extend(true, {}, $.ajaxSettings, options, {
     224            contentType: false,
     225            processData: false,
     226            cache: false,
     227            type: 'POST'
     228        });
     229
     230                if (options.uploadProgress) {
     231                        // workaround because jqXHR does not expose upload property
     232                        s.xhr = function() {
     233                                var xhr = jQuery.ajaxSettings.xhr();
     234                                if (xhr.upload) {
     235                                        xhr.upload.onprogress = function(event) {
     236                                                var percent = 0;
     237                                                if (event.lengthComputable)
     238                                                        percent = parseInt((event.position / event.total) * 100, 10);
     239                                                options.uploadProgress(event, event.position, event.total, percent);
     240                                        }
     241                                }
     242                                return xhr;
     243                        }
    150244                }
    151         };
    152 
    153         // are there files to upload?
    154         var fileInputs = $('input:file', this).length > 0;
    155         var mp = 'multipart/form-data';
    156         var multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp);
    157 
    158         // options.iframe allows user to force iframe mode
    159         // 06-NOV-09: now defaulting to iframe mode if file input is detected
    160    if (options.iframe !== false && (fileInputs || options.iframe || multipart)) {
    161            // hack to fix Safari hang (thanks to Tim Molendijk for this)
    162            // see:  http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
    163            if (options.closeKeepAlive) {
    164                    $.get(options.closeKeepAlive, function() { fileUpload(a); });
    165                 }
    166            else {
    167                    fileUpload(a);
    168                 }
    169    }
    170    else {
    171                 // IE7 massage (see issue 57)
    172                 if ($.browser.msie && method == 'get' && typeof options.type === "undefined") {
    173                         var ieMeth = $form[0].getAttribute('method');
    174                         if (typeof ieMeth === 'string')
    175                                 options.type = ieMeth;
    176                 }
    177                 $.ajax(options);
    178    }
    179 
    180         // fire 'notify' event
    181         this.trigger('form-submit-notify', [this, options]);
    182         return this;
    183 
    184 
    185         // private function for handling file uploads (hat tip to YAHOO!)
    186         function fileUpload(a) {
    187                 var form = $form[0], el, i, s, g, id, $io, io, xhr, sub, n, timedOut, timeoutHandle;
     245
     246        s.data = null;
     247        var beforeSend = s.beforeSend;
     248        s.beforeSend = function(xhr, o) {
     249                o.data = formdata;
     250            if(beforeSend)
     251                beforeSend.call(o, xhr, options);
     252        };
     253        $.ajax(s);
     254         }
     255
     256    // private function for handling file uploads (hat tip to YAHOO!)
     257    function fileUploadIframe(a) {
     258        var form = $form[0], el, i, s, g, id, $io, io, xhr, sub, n, timedOut, timeoutHandle;
    188259        var useProp = !!$.fn.prop;
    189260
    190261        if (a) {
    191262            if ( useProp ) {
    192                 // ensure that every serialized input is still enabled
    193                 for (i=0; i < a.length; i++) {
     263                // ensure that every serialized input is still enabled
     264                for (i=0; i < a.length; i++) {
    194265                    el = $(form[a[i].name]);
    195266                    el.prop('disabled', false);
    196                 }
     267                }
    197268            } else {
    198                 for (i=0; i < a.length; i++) {
     269                for (i=0; i < a.length; i++) {
    199270                    el = $(form[a[i].name]);
    200271                    el.removeAttr('disabled');
    201                 }
    202             };
    203         }
    204 
    205                 if ($(':input[name=submit],:input[id=submit]', form).length) {
    206                         // if there is an input with a name or id of 'submit' then we won't be
    207                         // able to invoke the submit fn on the form (at least not x-browser)
    208                         alert('Error: Form elements must not have name or id of "submit".');
    209                         return;
    210                 }
    211                
    212                 s = $.extend(true, {}, $.ajaxSettings, options);
    213                 s.context = s.context || s;
    214                 id = 'jqFormIO' + (new Date().getTime());
    215                 if (s.iframeTarget) {
    216                         $io = $(s.iframeTarget);
    217                         n = $io.attr('name');
    218                         if (n == null)
    219                                 $io.attr('name', id);
    220                         else
    221                                 id = n;
    222                 }
    223                 else {
    224                         $io = $('<iframe name="' + id + '" src="'+ s.iframeSrc +'" />');
    225                         $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });
    226                 }
    227                 io = $io[0];
    228 
    229 
    230                 xhr = { // mock object
    231                         aborted: 0,
    232                         responseText: null,
    233                         responseXML: null,
    234                         status: 0,
    235                         statusText: 'n/a',
    236                         getAllResponseHeaders: function() {},
    237                         getResponseHeader: function() {},
    238                         setRequestHeader: function() {},
    239                         abort: function(status) {
    240                                 var e = (status === 'timeout' ? 'timeout' : 'aborted');
    241                                 log('aborting upload... ' + e);
    242                                 this.aborted = 1;
    243                                 $io.attr('src', s.iframeSrc); // abort op in progress
    244                                 xhr.error = e;
    245                                 s.error && s.error.call(s.context, xhr, e, status);
    246                                 g && $.event.trigger("ajaxError", [xhr, s, e]);
    247                                 s.complete && s.complete.call(s.context, xhr, e);
    248                         }
    249                 };
    250 
    251                 g = s.global;
    252                 // trigger ajax global events so that activity/block indicators work like normal
    253                 if (g && ! $.active++) {
    254                         $.event.trigger("ajaxStart");
    255                 }
    256                 if (g) {
    257                         $.event.trigger("ajaxSend", [xhr, s]);
    258                 }
    259 
    260                 if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) {
    261                         if (s.global) {
    262                                 $.active--;
    263                         }
    264                         return;
    265                 }
    266                 if (xhr.aborted) {
    267                         return;
    268                 }
    269 
    270                 // add submitting element to data if we know it
    271                 sub = form.clk;
    272                 if (sub) {
    273                         n = sub.name;
    274                         if (n && !sub.disabled) {
    275                                 s.extraData = s.extraData || {};
    276                                 s.extraData[n] = sub.value;
    277                                 if (sub.type == "image") {
    278                                         s.extraData[n+'.x'] = form.clk_x;
    279                                         s.extraData[n+'.y'] = form.clk_y;
    280                                 }
    281                         }
    282                 }
    283                
    284                 var CLIENT_TIMEOUT_ABORT = 1;
    285                 var SERVER_ABORT = 2;
    286 
    287                 function getDoc(frame) {
    288                         var doc = frame.contentWindow ? frame.contentWindow.document : frame.contentDocument ? frame.contentDocument : frame.document;
    289                         return doc;
    290                 }
    291                
    292                 // take a breath so that pending repaints get some cpu time before the upload starts
    293                 function doSubmit() {
    294                         // make sure form attrs are set
    295                         var t = $form.attr('target'), a = $form.attr('action');
    296 
    297                         // update form attrs in IE friendly way
    298                         form.setAttribute('target',id);
    299                         if (!method) {
    300                                 form.setAttribute('method', 'POST');
    301                         }
    302                         if (a != s.url) {
    303                                 form.setAttribute('action', s.url);
    304                         }
    305 
    306                         // ie borks in some cases when setting encoding
    307                         if (! s.skipEncodingOverride && (!method || /post/i.test(method))) {
    308                                 $form.attr({
    309                                         encoding: 'multipart/form-data',
    310                                         enctype:  'multipart/form-data'
    311                                 });
    312                         }
    313 
    314                         // support timout
    315                         if (s.timeout) {
    316                                 timeoutHandle = setTimeout(function() { timedOut = true; cb(CLIENT_TIMEOUT_ABORT); }, s.timeout);
    317                         }
    318                        
    319                         // look for server aborts
    320                         function checkState() {
    321                                 try {
    322                                         var state = getDoc(io).readyState;
    323                                         log('state = ' + state);
    324                                         if (state.toLowerCase() == 'uninitialized')
    325                                                 setTimeout(checkState,50);
    326                                 }
    327                                 catch(e) {
    328                                         log('Server abort: ' , e, ' (', e.name, ')');
    329                                         cb(SERVER_ABORT);
    330                                         timeoutHandle && clearTimeout(timeoutHandle);
    331                                         timeoutHandle = undefined;
    332                                 }
    333                         }
    334 
    335                         // add "extra" data to form if provided in options
    336                         var extraInputs = [];
    337                         try {
    338                                 if (s.extraData) {
    339                                         for (var n in s.extraData) {
    340                                                 extraInputs.push(
    341                                                         $('<input type="hidden" name="'+n+'" />').attr('value',s.extraData[n])
    342                                                                 .appendTo(form)[0]);
    343                                         }
    344                                 }
    345 
    346                                 if (!s.iframeTarget) {
    347                                         // add iframe to doc and submit the form
    348                                         $io.appendTo('body');
    349                         io.attachEvent ? io.attachEvent('onload', cb) : io.addEventListener('load', cb, false);
    350                                 }
    351                                 setTimeout(checkState,15);
    352                                 form.submit();
    353                         }
    354                         finally {
    355                                 // reset attrs and remove "extra" input elements
    356                                 form.setAttribute('action',a);
    357                                 if(t) {
    358                                         form.setAttribute('target', t);
    359                                 } else {
    360                                         $form.removeAttr('target');
    361                                 }
    362                                 $(extraInputs).remove();
    363                         }
    364                 }
    365 
    366                 if (s.forceSync) {
    367                         doSubmit();
    368                 }
    369                 else {
    370                         setTimeout(doSubmit, 10); // this lets dom updates render
    371                 }
    372 
    373                 var data, doc, domCheckCount = 50, callbackProcessed;
    374 
    375                 function cb(e) {
    376                         if (xhr.aborted || callbackProcessed) {
    377                                 return;
    378                         }
    379                         try {
    380                                 doc = getDoc(io);
    381                         }
    382                         catch(ex) {
    383                                 log('cannot access response document: ', ex);
    384                                 e = SERVER_ABORT;
    385                         }
    386                         if (e === CLIENT_TIMEOUT_ABORT && xhr) {
    387                                 xhr.abort('timeout');
    388                                 return;
    389                         }
    390                         else if (e == SERVER_ABORT && xhr) {
    391                                 xhr.abort('server abort');
    392                                 return;
    393                         }
    394 
    395                         if (!doc || doc.location.href == s.iframeSrc) {
    396                                 // response not received yet
    397                                 if (!timedOut)
    398                                         return;
    399                         }
    400             io.detachEvent ? io.detachEvent('onload', cb) : io.removeEventListener('load', cb, false);
    401 
    402                         var status = 'success', errMsg;
    403                         try {
    404                                 if (timedOut) {
    405                                         throw 'timeout';
    406                                 }
    407 
    408                                 var isXml = s.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc);
    409                                 log('isXml='+isXml);
    410                                 if (!isXml && window.opera && (doc.body == null || doc.body.innerHTML == '')) {
    411                                         if (--domCheckCount) {
    412                                                 // in some browsers (Opera) the iframe DOM is not always traversable when
    413                                                 // the onload callback fires, so we loop a bit to accommodate
    414                                                 log('requeing onLoad callback, DOM not available');
    415                                                 setTimeout(cb, 250);
    416                                                 return;
    417                                         }
    418                                         // let this fall through because server response could be an empty document
    419                                         //log('Could not access iframe DOM after mutiple tries.');
    420                                         //throw 'DOMException: not available';
    421                                 }
    422 
    423                                 //log('response detected');
     272                }
     273            }
     274        }
     275
     276        if ($(':input[name=submit],:input[id=submit]', form).length) {
     277            // if there is an input with a name or id of 'submit' then we won't be
     278            // able to invoke the submit fn on the form (at least not x-browser)
     279            alert('Error: Form elements must not have name or id of "submit".');
     280            return;
     281        }
     282       
     283        s = $.extend(true, {}, $.ajaxSettings, options);
     284        s.context = s.context || s;
     285        id = 'jqFormIO' + (new Date().getTime());
     286        if (s.iframeTarget) {
     287            $io = $(s.iframeTarget);
     288            n = $io.attr('name');
     289            if (!n)
     290                 $io.attr('name', id);
     291            else
     292                id = n;
     293        }
     294        else {
     295            $io = $('<iframe name="' + id + '" src="'+ s.iframeSrc +'" />');
     296            $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });
     297        }
     298        io = $io[0];
     299
     300
     301        xhr = { // mock object
     302            aborted: 0,
     303            responseText: null,
     304            responseXML: null,
     305            status: 0,
     306            statusText: 'n/a',
     307            getAllResponseHeaders: function() {},
     308            getResponseHeader: function() {},
     309            setRequestHeader: function() {},
     310            abort: function(status) {
     311                var e = (status === 'timeout' ? 'timeout' : 'aborted');
     312                log('aborting upload... ' + e);
     313                this.aborted = 1;
     314                $io.attr('src', s.iframeSrc); // abort op in progress
     315                xhr.error = e;
     316                if (s.error)
     317                    s.error.call(s.context, xhr, e, status);
     318                if (g)
     319                    $.event.trigger("ajaxError", [xhr, s, e]);
     320                if (s.complete)
     321                    s.complete.call(s.context, xhr, e);
     322            }
     323        };
     324
     325        g = s.global;
     326        // trigger ajax global events so that activity/block indicators work like normal
     327        if (g && 0 === $.active++) {
     328            $.event.trigger("ajaxStart");
     329        }
     330        if (g) {
     331            $.event.trigger("ajaxSend", [xhr, s]);
     332        }
     333
     334        if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) {
     335            if (s.global) {
     336                $.active--;
     337            }
     338            return;
     339        }
     340        if (xhr.aborted) {
     341            return;
     342        }
     343
     344        // add submitting element to data if we know it
     345        sub = form.clk;
     346        if (sub) {
     347            n = sub.name;
     348            if (n && !sub.disabled) {
     349                s.extraData = s.extraData || {};
     350                s.extraData[n] = sub.value;
     351                if (sub.type == "image") {
     352                    s.extraData[n+'.x'] = form.clk_x;
     353                    s.extraData[n+'.y'] = form.clk_y;
     354                }
     355            }
     356        }
     357       
     358        var CLIENT_TIMEOUT_ABORT = 1;
     359        var SERVER_ABORT = 2;
     360
     361        function getDoc(frame) {
     362            var doc = frame.contentWindow ? frame.contentWindow.document : frame.contentDocument ? frame.contentDocument : frame.document;
     363            return doc;
     364        }
     365       
     366        // Rails CSRF hack (thanks to Yvan Barthelemy)
     367        var csrf_token = $('meta[name=csrf-token]').attr('content');
     368        var csrf_param = $('meta[name=csrf-param]').attr('content');
     369        if (csrf_param && csrf_token) {
     370            s.extraData = s.extraData || {};
     371            s.extraData[csrf_param] = csrf_token;
     372        }
     373
     374        // take a breath so that pending repaints get some cpu time before the upload starts
     375        function doSubmit() {
     376            // make sure form attrs are set
     377            var t = $form.attr('target'), a = $form.attr('action');
     378
     379            // update form attrs in IE friendly way
     380            form.setAttribute('target',id);
     381            if (!method) {
     382                form.setAttribute('method', 'POST');
     383            }
     384            if (a != s.url) {
     385                form.setAttribute('action', s.url);
     386            }
     387
     388            // ie borks in some cases when setting encoding
     389            if (! s.skipEncodingOverride && (!method || /post/i.test(method))) {
     390                $form.attr({
     391                    encoding: 'multipart/form-data',
     392                    enctype:  'multipart/form-data'
     393                });
     394            }
     395
     396            // support timout
     397            if (s.timeout) {
     398                timeoutHandle = setTimeout(function() { timedOut = true; cb(CLIENT_TIMEOUT_ABORT); }, s.timeout);
     399            }
     400           
     401            // look for server aborts
     402            function checkState() {
     403                try {
     404                    var state = getDoc(io).readyState;
     405                    log('state = ' + state);
     406                    if (state && state.toLowerCase() == 'uninitialized')
     407                        setTimeout(checkState,50);
     408                }
     409                catch(e) {
     410                    log('Server abort: ' , e, ' (', e.name, ')');
     411                    cb(SERVER_ABORT);
     412                    if (timeoutHandle)
     413                        clearTimeout(timeoutHandle);
     414                    timeoutHandle = undefined;
     415                }
     416            }
     417
     418            // add "extra" data to form if provided in options
     419            var extraInputs = [];
     420            try {
     421                if (s.extraData) {
     422                    for (var n in s.extraData) {
     423                        if (s.extraData.hasOwnProperty(n)) {
     424                            extraInputs.push(
     425                                $('<input type="hidden" name="'+n+'">').attr('value',s.extraData[n])
     426                                    .appendTo(form)[0]);
     427                        }
     428                    }
     429                }
     430
     431                if (!s.iframeTarget) {
     432                    // add iframe to doc and submit the form
     433                    $io.appendTo('body');
     434                    if (io.attachEvent)
     435                        io.attachEvent('onload', cb);
     436                    else
     437                        io.addEventListener('load', cb, false);
     438                }
     439                setTimeout(checkState,15);
     440                form.submit();
     441            }
     442            finally {
     443                // reset attrs and remove "extra" input elements
     444                form.setAttribute('action',a);
     445                if(t) {
     446                    form.setAttribute('target', t);
     447                } else {
     448                    $form.removeAttr('target');
     449                }
     450                $(extraInputs).remove();
     451            }
     452        }
     453
     454        if (s.forceSync) {
     455            doSubmit();
     456        }
     457        else {
     458            setTimeout(doSubmit, 10); // this lets dom updates render
     459        }
     460
     461        var data, doc, domCheckCount = 50, callbackProcessed;
     462
     463        function cb(e) {
     464            if (xhr.aborted || callbackProcessed) {
     465                return;
     466            }
     467            try {
     468                doc = getDoc(io);
     469            }
     470            catch(ex) {
     471                log('cannot access response document: ', ex);
     472                e = SERVER_ABORT;
     473            }
     474            if (e === CLIENT_TIMEOUT_ABORT && xhr) {
     475                xhr.abort('timeout');
     476                return;
     477            }
     478            else if (e == SERVER_ABORT && xhr) {
     479                xhr.abort('server abort');
     480                return;
     481            }
     482
     483            if (!doc || doc.location.href == s.iframeSrc) {
     484                // response not received yet
     485                if (!timedOut)
     486                    return;
     487            }
     488            if (io.detachEvent)
     489                io.detachEvent('onload', cb);
     490            else   
     491                io.removeEventListener('load', cb, false);
     492
     493            var status = 'success', errMsg;
     494            try {
     495                if (timedOut) {
     496                    throw 'timeout';
     497                }
     498
     499                var isXml = s.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc);
     500                log('isXml='+isXml);
     501                if (!isXml && window.opera && (doc.body === null || !doc.body.innerHTML)) {
     502                    if (--domCheckCount) {
     503                        // in some browsers (Opera) the iframe DOM is not always traversable when
     504                        // the onload callback fires, so we loop a bit to accommodate
     505                        log('requeing onLoad callback, DOM not available');
     506                        setTimeout(cb, 250);
     507                        return;
     508                    }
     509                    // let this fall through because server response could be an empty document
     510                    //log('Could not access iframe DOM after mutiple tries.');
     511                    //throw 'DOMException: not available';
     512                }
     513
     514                //log('response detected');
    424515                var docRoot = doc.body ? doc.body : doc.documentElement;
    425516                xhr.responseText = docRoot ? docRoot.innerHTML : null;
    426                                 xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
    427                                 if (isXml)
    428                                         s.dataType = 'xml';
    429                                 xhr.getResponseHeader = function(header){
    430                                         var headers = {'content-type': s.dataType};
    431                                         return headers[header];
    432                                 };
     517                xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
     518                if (isXml)
     519                    s.dataType = 'xml';
     520                xhr.getResponseHeader = function(header){
     521                    var headers = {'content-type': s.dataType};
     522                    return headers[header];
     523                };
    433524                // support for XHR 'status' & 'statusText' emulation :
    434525                if (docRoot) {
     
    437528                }
    438529
    439                                 var dt = (s.dataType || '').toLowerCase();
    440                                 var scr = /(json|script|text)/.test(dt);
    441                                 if (scr || s.textarea) {
    442                                         // see if user embedded response in textarea
    443                                         var ta = doc.getElementsByTagName('textarea')[0];
    444                                         if (ta) {
    445                                                 xhr.responseText = ta.value;
     530                var dt = (s.dataType || '').toLowerCase();
     531                var scr = /(json|script|text)/.test(dt);
     532                if (scr || s.textarea) {
     533                    // see if user embedded response in textarea
     534                    var ta = doc.getElementsByTagName('textarea')[0];
     535                    if (ta) {
     536                        xhr.responseText = ta.value;
    446537                        // support for XHR 'status' & 'statusText' emulation :
    447538                        xhr.status = Number( ta.getAttribute('status') ) || xhr.status;
    448539                        xhr.statusText = ta.getAttribute('statusText') || xhr.statusText;
    449                                         }
    450                                         else if (scr) {
    451                                                 // account for browsers injecting pre around json response
    452                                                 var pre = doc.getElementsByTagName('pre')[0];
    453                                                 var b = doc.getElementsByTagName('body')[0];
    454                                                 if (pre) {
    455                                                         xhr.responseText = pre.textContent ? pre.textContent : pre.innerText;
    456                                                 }
    457                                                 else if (b) {
    458                                                         xhr.responseText = b.textContent ? b.textContent : b.innerText;
    459                                                 }
    460                                         }
    461                                 }
    462                                 else if (dt == 'xml' && !xhr.responseXML && xhr.responseText != null) {
    463                                         xhr.responseXML = toXml(xhr.responseText);
    464                                 }
     540                    }
     541                    else if (scr) {
     542                        // account for browsers injecting pre around json response
     543                        var pre = doc.getElementsByTagName('pre')[0];
     544                        var b = doc.getElementsByTagName('body')[0];
     545                        if (pre) {
     546                            xhr.responseText = pre.textContent ? pre.textContent : pre.innerText;
     547                        }
     548                        else if (b) {
     549                            xhr.responseText = b.textContent ? b.textContent : b.innerText;
     550                        }
     551                    }
     552                }
     553                else if (dt == 'xml' && !xhr.responseXML && xhr.responseText) {
     554                    xhr.responseXML = toXml(xhr.responseText);
     555                }
    465556
    466557                try {
     
    471562                    xhr.error = errMsg = (e || status);
    472563                }
    473                         }
    474                         catch (e) {
    475                                 log('error caught: ',e);
    476                                 status = 'error';
     564            }
     565            catch (e) {
     566                log('error caught: ',e);
     567                status = 'error';
    477568                xhr.error = errMsg = (e || status);
    478                         }
    479 
    480                         if (xhr.aborted) {
    481                                 log('upload aborted');
    482                                 status = null;
    483                         }
     569            }
     570
     571            if (xhr.aborted) {
     572                log('upload aborted');
     573                status = null;
     574            }
    484575
    485576            if (xhr.status) { // we've set xhr.status
     
    487578            }
    488579
    489                         // ordering of these callbacks/triggers is odd, but that's how $.ajax does it
    490                         if (status === 'success') {
    491                                 s.success && s.success.call(s.context, data, 'success', xhr);
    492                                 g && $.event.trigger("ajaxSuccess", [xhr, s]);
    493                         }
     580            // ordering of these callbacks/triggers is odd, but that's how $.ajax does it
     581            if (status === 'success') {
     582                if (s.success)
     583                    s.success.call(s.context, data, 'success', xhr);
     584                if (g)
     585                    $.event.trigger("ajaxSuccess", [xhr, s]);
     586            }
    494587            else if (status) {
    495                                 if (errMsg == undefined)
    496                                         errMsg = xhr.statusText;
    497                                 s.error && s.error.call(s.context, xhr, status, errMsg);
    498                                 g && $.event.trigger("ajaxError", [xhr, s, errMsg]);
    499             }
    500 
    501                         g && $.event.trigger("ajaxComplete", [xhr, s]);
    502 
    503                         if (g && ! --$.active) {
    504                                 $.event.trigger("ajaxStop");
    505                         }
    506 
    507                         s.complete && s.complete.call(s.context, xhr, status);
    508 
    509                         callbackProcessed = true;
    510                         if (s.timeout)
    511                                 clearTimeout(timeoutHandle);
    512 
    513                         // clean up
    514                         setTimeout(function() {
    515                                 if (!s.iframeTarget)
    516                                         $io.remove();
    517                                 xhr.responseXML = null;
    518                         }, 100);
    519                 }
    520 
    521                 var toXml = $.parseXML || function(s, doc) { // use parseXML if available (jQuery 1.5+)
    522                         if (window.ActiveXObject) {
    523                                 doc = new ActiveXObject('Microsoft.XMLDOM');
    524                                 doc.async = 'false';
    525                                 doc.loadXML(s);
    526                         }
    527                         else {
    528                                 doc = (new DOMParser()).parseFromString(s, 'text/xml');
    529                         }
    530                         return (doc && doc.documentElement && doc.documentElement.nodeName != 'parsererror') ? doc : null;
    531                 };
    532                 var parseJSON = $.parseJSON || function(s) {
    533                         return window['eval']('(' + s + ')');
    534                 };
    535 
    536                 var httpData = function( xhr, type, s ) { // mostly lifted from jq1.4.4
    537 
    538                         var ct = xhr.getResponseHeader('content-type') || '',
    539                                 xml = type === 'xml' || !type && ct.indexOf('xml') >= 0,
    540                                 data = xml ? xhr.responseXML : xhr.responseText;
    541 
    542                         if (xml && data.documentElement.nodeName === 'parsererror') {
    543                                 $.error && $.error('parsererror');
    544                         }
    545                         if (s && s.dataFilter) {
    546                                 data = s.dataFilter(data, type);
    547                         }
    548                         if (typeof data === 'string') {
    549                                 if (type === 'json' || !type && ct.indexOf('json') >= 0) {
    550                                         data = parseJSON(data);
    551                                 } else if (type === "script" || !type && ct.indexOf("javascript") >= 0) {
    552                                         $.globalEval(data);
    553                                 }
    554                         }
    555                         return data;
    556                 };
    557         }
     588                if (errMsg === undefined)
     589                    errMsg = xhr.statusText;
     590                if (s.error)
     591                    s.error.call(s.context, xhr, status, errMsg);
     592                if (g)
     593                    $.event.trigger("ajaxError", [xhr, s, errMsg]);
     594            }
     595
     596            if (g)
     597                $.event.trigger("ajaxComplete", [xhr, s]);
     598
     599            if (g && ! --$.active) {
     600                $.event.trigger("ajaxStop");
     601            }
     602
     603            if (s.complete)
     604                s.complete.call(s.context, xhr, status);
     605
     606            callbackProcessed = true;
     607            if (s.timeout)
     608                clearTimeout(timeoutHandle);
     609
     610            // clean up
     611            setTimeout(function() {
     612                if (!s.iframeTarget)
     613                    $io.remove();
     614                xhr.responseXML = null;
     615            }, 100);
     616        }
     617
     618        var toXml = $.parseXML || function(s, doc) { // use parseXML if available (jQuery 1.5+)
     619            if (window.ActiveXObject) {
     620                doc = new ActiveXObject('Microsoft.XMLDOM');
     621                doc.async = 'false';
     622                doc.loadXML(s);
     623            }
     624            else {
     625                doc = (new DOMParser()).parseFromString(s, 'text/xml');
     626            }
     627            return (doc && doc.documentElement && doc.documentElement.nodeName != 'parsererror') ? doc : null;
     628        };
     629        var parseJSON = $.parseJSON || function(s) {
     630            /*jslint evil:true */
     631            return window['eval']('(' + s + ')');
     632        };
     633
     634        var httpData = function( xhr, type, s ) { // mostly lifted from jq1.4.4
     635
     636            var ct = xhr.getResponseHeader('content-type') || '',
     637                xml = type === 'xml' || !type && ct.indexOf('xml') >= 0,
     638                data = xml ? xhr.responseXML : xhr.responseText;
     639
     640            if (xml && data.documentElement.nodeName === 'parsererror') {
     641                if ($.error)
     642                    $.error('parsererror');
     643            }
     644            if (s && s.dataFilter) {
     645                data = s.dataFilter(data, type);
     646            }
     647            if (typeof data === 'string') {
     648                if (type === 'json' || !type && ct.indexOf('json') >= 0) {
     649                    data = parseJSON(data);
     650                } else if (type === "script" || !type && ct.indexOf("javascript") >= 0) {
     651                    $.globalEval(data);
     652                }
     653            }
     654            return data;
     655        };
     656    }
    558657};
    559658
     
    564663 *
    565664 * 1: This method will include coordinates for <input type="image" /> elements (if the element
    566  *      is used to submit the form).
     665 *    is used to submit the form).
    567666 * 2. This method will include the submit element's name/value data (for the element that was
    568  *      used to submit the form).
     667 *    used to submit the form).
    569668 * 3. This method binds the submit() method to the form for you.
    570669 *
     
    574673 */
    575674$.fn.ajaxForm = function(options) {
    576         // in jQuery 1.3+ we can fix mistakes with the ready state
    577         if (this.length === 0) {
    578                 var o = { s: this.selector, c: this.context };
    579                 if (!$.isReady && o.s) {
    580                         log('DOM not ready, queuing ajaxForm');
    581                         $(function() {
    582                                 $(o.s,o.c).ajaxForm(options);
    583                         });
    584                         return this;
    585                 }
    586                 // is your DOM ready?  http://docs.jquery.com/Tutorials:Introducing_$(document).ready()
    587                 log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)'));
    588                 return this;
    589         }
    590 
    591         return this.ajaxFormUnbind().bind('submit.form-plugin', function(e) {
    592                 if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed
    593                         e.preventDefault();
    594                         $(this).ajaxSubmit(options);
    595                 }
    596         }).bind('click.form-plugin', function(e) {
    597                 var target = e.target;
    598                 var $el = $(target);
    599                 if (!($el.is(":submit,input:image"))) {
    600                         // is this a child element of the submit el?  (ex: a span within a button)
    601                         var t = $el.closest(':submit');
    602                         if (t.length == 0) {
    603                                 return;
    604                         }
    605                         target = t[0];
    606                 }
    607                 var form = this;
    608                 form.clk = target;
    609                 if (target.type == 'image') {
    610                         if (e.offsetX != undefined) {
    611                                 form.clk_x = e.offsetX;
    612                                 form.clk_y = e.offsetY;
    613                         } else if (typeof $.fn.offset == 'function') { // try to use dimensions plugin
    614                                 var offset = $el.offset();
    615                                 form.clk_x = e.pageX - offset.left;
    616                                 form.clk_y = e.pageY - offset.top;
    617                         } else {
    618                                 form.clk_x = e.pageX - target.offsetLeft;
    619                                 form.clk_y = e.pageY - target.offsetTop;
    620                         }
    621                 }
    622                 // clear form vars
    623                 setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 100);
    624         });
    625 };
     675    options = options || {};
     676    options.delegation = options.delegation && $.isFunction($.fn.on);
     677   
     678    // in jQuery 1.3+ we can fix mistakes with the ready state
     679    if (!options.delegation && this.length === 0) {
     680        var o = { s: this.selector, c: this.context };
     681        if (!$.isReady && o.s) {
     682            log('DOM not ready, queuing ajaxForm');
     683            $(function() {
     684                $(o.s,o.c).ajaxForm(options);
     685            });
     686            return this;
     687        }
     688        // is your DOM ready?  http://docs.jquery.com/Tutorials:Introducing_$(document).ready()
     689        log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)'));
     690        return this;
     691    }
     692
     693    if ( options.delegation ) {
     694        $(document)
     695            .off('submit.form-plugin', this.selector, doAjaxSubmit)
     696            .off('click.form-plugin', this.selector, captureSubmittingElement)
     697            .on('submit.form-plugin', this.selector, options, doAjaxSubmit)
     698            .on('click.form-plugin', this.selector, options, captureSubmittingElement);
     699        return this;
     700    }
     701
     702    return this.ajaxFormUnbind()
     703        .bind('submit.form-plugin', options, doAjaxSubmit)
     704        .bind('click.form-plugin', options, captureSubmittingElement);
     705};
     706
     707// private event handlers   
     708function doAjaxSubmit(e) {
     709    /*jshint validthis:true */
     710    var options = e.data;
     711    if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed
     712        e.preventDefault();
     713        $(this).ajaxSubmit(options);
     714    }
     715}
     716   
     717function captureSubmittingElement(e) {
     718    /*jshint validthis:true */
     719    var target = e.target;
     720    var $el = $(target);
     721    if (!($el.is(":submit,input:image"))) {
     722        // is this a child element of the submit el?  (ex: a span within a button)
     723        var t = $el.closest(':submit');
     724        if (t.length === 0) {
     725            return;
     726        }
     727        target = t[0];
     728    }
     729    var form = this;
     730    form.clk = target;
     731    if (target.type == 'image') {
     732        if (e.offsetX !== undefined) {
     733            form.clk_x = e.offsetX;
     734            form.clk_y = e.offsetY;
     735        } else if (typeof $.fn.offset == 'function') {
     736            var offset = $el.offset();
     737            form.clk_x = e.pageX - offset.left;
     738            form.clk_y = e.pageY - offset.top;
     739        } else {
     740            form.clk_x = e.pageX - target.offsetLeft;
     741            form.clk_y = e.pageY - target.offsetTop;
     742        }
     743    }
     744    // clear form vars
     745    setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 100);
     746}
     747
    626748
    627749// ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
    628750$.fn.ajaxFormUnbind = function() {
    629         return this.unbind('submit.form-plugin click.form-plugin');
     751    return this.unbind('submit.form-plugin click.form-plugin');
    630752};
    631753
     
    642764 */
    643765$.fn.formToArray = function(semantic) {
    644         var a = [];
    645         if (this.length === 0) {
    646                 return a;
    647         }
    648 
    649         var form = this[0];
    650         var els = semantic ? form.getElementsByTagName('*') : form.elements;
    651         if (!els) {
    652                 return a;
    653         }
    654 
    655         var i,j,n,v,el,max,jmax;
    656         for(i=0, max=els.length; i < max; i++) {
    657                 el = els[i];
    658                 n = el.name;
    659                 if (!n) {
    660                         continue;
    661                 }
    662 
    663                 if (semantic && form.clk && el.type == "image") {
    664                         // handle image inputs on the fly when semantic == true
    665                         if(!el.disabled && form.clk == el) {
    666                                 a.push({name: n, value: $(el).val()});
    667                                 a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
    668                         }
    669                         continue;
    670                 }
    671 
    672                 v = $.fieldValue(el, true);
    673                 if (v && v.constructor == Array) {
    674                         for(j=0, jmax=v.length; j < jmax; j++) {
    675                                 a.push({name: n, value: v[j]});
    676                         }
    677                 }
    678                 else if (v !== null && typeof v != 'undefined') {
    679                         a.push({name: n, value: v});
    680                 }
    681         }
    682 
    683         if (!semantic && form.clk) {
    684                 // input type=='image' are not found in elements array! handle it here
    685                 var $input = $(form.clk), input = $input[0];
    686                 n = input.name;
    687                 if (n && !input.disabled && input.type == 'image') {
    688                         a.push({name: n, value: $input.val()});
    689                         a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
    690                 }
    691         }
    692         return a;
     766    var a = [];
     767    if (this.length === 0) {
     768        return a;
     769    }
     770
     771    var form = this[0];
     772    var els = semantic ? form.getElementsByTagName('*') : form.elements;
     773    if (!els) {
     774        return a;
     775    }
     776
     777    var i,j,n,v,el,max,jmax;
     778    for(i=0, max=els.length; i < max; i++) {
     779        el = els[i];
     780        n = el.name;
     781        if (!n) {
     782            continue;
     783        }
     784
     785        if (semantic && form.clk && el.type == "image") {
     786            // handle image inputs on the fly when semantic == true
     787            if(!el.disabled && form.clk == el) {
     788                a.push({name: n, value: $(el).val(), type: el.type });
     789                a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
     790            }
     791            continue;
     792        }
     793
     794        v = $.fieldValue(el, true);
     795        if (v && v.constructor == Array) {
     796            for(j=0, jmax=v.length; j < jmax; j++) {
     797                a.push({name: n, value: v[j]});
     798            }
     799        }
     800        else if (feature.fileapi && el.type == 'file' && !el.disabled) {
     801            var files = el.files;
     802            for (j=0; j < files.length; j++) {
     803                a.push({name: n, value: files[j], type: el.type});
     804            }
     805        }
     806        else if (v !== null && typeof v != 'undefined') {
     807            a.push({name: n, value: v, type: el.type});
     808        }
     809    }
     810
     811    if (!semantic && form.clk) {
     812        // input type=='image' are not found in elements array! handle it here
     813        var $input = $(form.clk), input = $input[0];
     814        n = input.name;
     815        if (n && !input.disabled && input.type == 'image') {
     816            a.push({name: n, value: $input.val()});
     817            a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
     818        }
     819    }
     820    return a;
    693821};
    694822
     
    698826 */
    699827$.fn.formSerialize = function(semantic) {
    700         //hand off to jQuery.param for proper encoding
    701         return $.param(this.formToArray(semantic));
     828    //hand off to jQuery.param for proper encoding
     829    return $.param(this.formToArray(semantic));
    702830};
    703831
     
    707835 */
    708836$.fn.fieldSerialize = function(successful) {
    709         var a = [];
    710         this.each(function() {
    711                 var n = this.name;
    712                 if (!n) {
    713                         return;
    714                 }
    715                 var v = $.fieldValue(this, successful);
    716                 if (v && v.constructor == Array) {
    717                         for (var i=0,max=v.length; i < max; i++) {
    718                                 a.push({name: n, value: v[i]});
    719                         }
    720                 }
    721                 else if (v !== null && typeof v != 'undefined') {
    722                         a.push({name: this.name, value: v});
    723                 }
    724         });
    725         //hand off to jQuery.param for proper encoding
    726         return $.param(a);
     837    var a = [];
     838    this.each(function() {
     839        var n = this.name;
     840        if (!n) {
     841            return;
     842        }
     843        var v = $.fieldValue(this, successful);
     844        if (v && v.constructor == Array) {
     845            for (var i=0,max=v.length; i < max; i++) {
     846                a.push({name: n, value: v[i]});
     847            }
     848        }
     849        else if (v !== null && typeof v != 'undefined') {
     850            a.push({name: this.name, value: v});
     851        }
     852    });
     853    //hand off to jQuery.param for proper encoding
     854    return $.param(a);
    727855};
    728856
     
    731859 *
    732860 *  <form><fieldset>
    733  *        <input name="A" type="text" />
    734  *        <input name="A" type="text" />
    735  *        <input name="B" type="checkbox" value="B1" />
    736  *        <input name="B" type="checkbox" value="B2"/>
    737  *        <input name="C" type="radio" value="C1" />
    738  *        <input name="C" type="radio" value="C2" />
     861 *      <input name="A" type="text" />
     862 *      <input name="A" type="text" />
     863 *      <input name="B" type="checkbox" value="B1" />
     864 *      <input name="B" type="checkbox" value="B2"/>
     865 *      <input name="C" type="radio" value="C1" />
     866 *      <input name="C" type="radio" value="C2" />
    739867 *  </fieldset></form>
    740868 *
     
    763891 *
    764892 * Note: This method *always* returns an array.  If no valid value can be determined the
    765  *         array will be empty, otherwise it will contain one or more values.
     893 *    array will be empty, otherwise it will contain one or more values.
    766894 */
    767895$.fn.fieldValue = function(successful) {
    768         for (var val=[], i=0, max=this.length; i < max; i++) {
    769                 var el = this[i];
    770                 var v = $.fieldValue(el, successful);
    771                 if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) {
    772                         continue;
    773                 }
    774                 v.constructor == Array ? $.merge(val, v) : val.push(v);
    775         }
    776         return val;
     896    for (var val=[], i=0, max=this.length; i < max; i++) {
     897        var el = this[i];
     898        var v = $.fieldValue(el, successful);
     899        if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) {
     900            continue;
     901        }
     902        if (v.constructor == Array)
     903            $.merge(val, v);
     904        else
     905            val.push(v);
     906    }
     907    return val;
    777908};
    778909
     
    781912 */
    782913$.fieldValue = function(el, successful) {
    783         var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
    784         if (successful === undefined) {
    785                 successful = true;
    786         }
    787 
    788         if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
    789                 (t == 'checkbox' || t == 'radio') && !el.checked ||
    790                 (t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
    791                 tag == 'select' && el.selectedIndex == -1)) {
    792                         return null;
    793         }
    794 
    795         if (tag == 'select') {
    796                 var index = el.selectedIndex;
    797                 if (index < 0) {
    798                         return null;
    799                 }
    800                 var a = [], ops = el.options;
    801                 var one = (t == 'select-one');
    802                 var max = (one ? index+1 : ops.length);
    803                 for(var i=(one ? index : 0); i < max; i++) {
    804                         var op = ops[i];
    805                         if (op.selected) {
    806                                 var v = op.value;
    807                                 if (!v) { // extra pain for IE...
    808                                         v = (op.attributes && op.attributes['value'] && !(op.attributes['value'].specified)) ? op.text : op.value;
    809                                 }
    810                                 if (one) {
    811                                         return v;
    812                                 }
    813                                 a.push(v);
    814                         }
    815                 }
    816                 return a;
    817         }
    818         return $(el).val();
     914    var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
     915    if (successful === undefined) {
     916        successful = true;
     917    }
     918
     919    if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
     920        (t == 'checkbox' || t == 'radio') && !el.checked ||
     921        (t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
     922        tag == 'select' && el.selectedIndex == -1)) {
     923            return null;
     924    }
     925
     926    if (tag == 'select') {
     927        var index = el.selectedIndex;
     928        if (index < 0) {
     929            return null;
     930        }
     931        var a = [], ops = el.options;
     932        var one = (t == 'select-one');
     933        var max = (one ? index+1 : ops.length);
     934        for(var i=(one ? index : 0); i < max; i++) {
     935            var op = ops[i];
     936            if (op.selected) {
     937                var v = op.value;
     938                if (!v) { // extra pain for IE...
     939                    v = (op.attributes && op.attributes['value'] && !(op.attributes['value'].specified)) ? op.text : op.value;
     940                }
     941                if (one) {
     942                    return v;
     943                }
     944                a.push(v);
     945            }
     946        }
     947        return a;
     948    }
     949    return $(el).val();
    819950};
    820951
     
    828959 */
    829960$.fn.clearForm = function(includeHidden) {
    830         return this.each(function() {
    831                 $('input,select,textarea', this).clearFields(includeHidden);
    832         });
     961    return this.each(function() {
     962        $('input,select,textarea', this).clearFields(includeHidden);
     963    });
    833964};
    834965
     
    837968 */
    838969$.fn.clearFields = $.fn.clearInputs = function(includeHidden) {
    839         var re = /^(?:color|date|datetime|email|month|number|password|range|search|tel|text|time|url|week)$/i; // 'hidden' is not in this list
    840         return this.each(function() {
    841                 var t = this.type, tag = this.tagName.toLowerCase();
    842                 if (re.test(t) || tag == 'textarea' || (includeHidden && /hidden/.test(t)) ) {
    843                         this.value = '';
    844                 }
    845                 else if (t == 'checkbox' || t == 'radio') {
    846                         this.checked = false;
    847                 }
    848                 else if (tag == 'select') {
    849                         this.selectedIndex = -1;
    850                 }
    851         });
     970    var re = /^(?:color|date|datetime|email|month|number|password|range|search|tel|text|time|url|week)$/i; // 'hidden' is not in this list
     971    return this.each(function() {
     972        var t = this.type, tag = this.tagName.toLowerCase();
     973        if (re.test(t) || tag == 'textarea' || (includeHidden && /hidden/.test(t)) ) {
     974            this.value = '';
     975        }
     976        else if (t == 'checkbox' || t == 'radio') {
     977            this.checked = false;
     978        }
     979        else if (tag == 'select') {
     980            this.selectedIndex = -1;
     981        }
     982    });
    852983};
    853984
     
    856987 */
    857988$.fn.resetForm = function() {
    858         return this.each(function() {
    859                 // guard against an input with the name of 'reset'
    860                 // note that IE reports the reset function as an 'object'
    861                 if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType)) {
    862                         this.reset();
    863                 }
    864         });
     989    return this.each(function() {
     990        // guard against an input with the name of 'reset'
     991        // note that IE reports the reset function as an 'object'
     992        if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType)) {
     993            this.reset();
     994        }
     995    });
    865996};
    866997
     
    8691000 */
    8701001$.fn.enable = function(b) {
    871         if (b === undefined) {
    872                 b = true;
    873         }
    874         return this.each(function() {
    875                 this.disabled = !b;
    876         });
     1002    if (b === undefined) {
     1003        b = true;
     1004    }
     1005    return this.each(function() {
     1006        this.disabled = !b;
     1007    });
    8771008};
    8781009
     
    8821013 */
    8831014$.fn.selected = function(select) {
    884         if (select === undefined) {
    885                 select = true;
    886         }
    887         return this.each(function() {
    888                 var t = this.type;
    889                 if (t == 'checkbox' || t == 'radio') {
    890                         this.checked = select;
    891                 }
    892                 else if (this.tagName.toLowerCase() == 'option') {
    893                         var $sel = $(this).parent('select');
    894                         if (select && $sel[0] && $sel[0].type == 'select-one') {
    895                                 // deselect all other options
    896                                 $sel.find('option').selected(false);
    897                         }
    898                         this.selected = select;
    899                 }
    900         });
     1015    if (select === undefined) {
     1016        select = true;
     1017    }
     1018    return this.each(function() {
     1019        var t = this.type;
     1020        if (t == 'checkbox' || t == 'radio') {
     1021            this.checked = select;
     1022        }
     1023        else if (this.tagName.toLowerCase() == 'option') {
     1024            var $sel = $(this).parent('select');
     1025            if (select && $sel[0] && $sel[0].type == 'select-one') {
     1026                // deselect all other options
     1027                $sel.find('option').selected(false);
     1028            }
     1029            this.selected = select;
     1030        }
     1031    });
    9011032};
    9021033
     
    9061037// helper fn for console logging
    9071038function log() {
    908         if (!$.fn.ajaxSubmit.debug)
    909                 return;
    910         var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,'');
    911         if (window.console && window.console.log) {
    912                 window.console.log(msg);
    913         }
    914         else if (window.opera && window.opera.postError) {
    915                 window.opera.postError(msg);
    916         }
    917 };
     1039    if (!$.fn.ajaxSubmit.debug)
     1040        return;
     1041    var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,'');
     1042    if (window.console && window.console.log) {
     1043        window.console.log(msg);
     1044    }
     1045    else if (window.opera && window.opera.postError) {
     1046        window.opera.postError(msg);
     1047    }
     1048}
    9181049
    9191050})(jQuery);
Note: See TracChangeset for help on using the changeset viewer.