WordPress.org

Make WordPress Core

Ticket #23944: 23944.diff

File 23944.diff, 60.4 KB (added by JustinSainton, 13 months ago)
  • wp-includes/js/jquery/jquery.form.js

     
    11/*! 
    22 * jQuery Form Plugin 
    3  * version: 2.73 (03-MAY-2011) 
    4  * @requires jQuery v1.3.2 or later 
     3 * version: 3.32.0-2013.04.03 
     4 * @requires jQuery v1.5 or later 
    55 * 
    66 * Examples and documentation at: http://malsup.com/jquery/form/ 
     7 * Project repository: https://github.com/malsup/form 
    78 * 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 
     9 *    http://malsup.github.com/mit-license.txt 
     10 *    http://malsup.github.com/gpl-license-v2.txt 
    1011 */ 
     12/*global ActiveXObject */ 
    1113;(function($) { 
     14"use strict"; 
    1215 
    1316/* 
    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, 
     17    Usage Note: 
     18    ----------- 
     19    Do not use both ajaxSubmit and ajaxForm on the same form.  These 
     20    functions are mutually exclusive.  Use ajaxSubmit if you want 
     21    to bind your own submit handler to the form.  For example, 
    1922 
    20         $(document).ready(function() { 
    21                 $('#myForm').bind('submit', function(e) { 
    22                         e.preventDefault(); // <-- important 
    23                         $(this).ajaxSubmit({ 
    24                                 target: '#output' 
    25                         }); 
    26                 }); 
    27         }); 
     23    $(document).ready(function() { 
     24        $('#myForm').on('submit', function(e) { 
     25            e.preventDefault(); // <-- important 
     26            $(this).ajaxSubmit({ 
     27                target: '#output' 
     28            }); 
     29        }); 
     30    }); 
    2831 
    29         Use ajaxForm when you want the plugin to manage all the event binding 
    30         for you.  For example, 
     32    Use ajaxForm when you want the plugin to manage all the event binding 
     33    for you.  For example, 
    3134 
    32         $(document).ready(function() { 
    33                 $('#myForm').ajaxForm({ 
    34                         target: '#output' 
    35                 }); 
    36         }); 
     35    $(document).ready(function() { 
     36        $('#myForm').ajaxForm({ 
     37            target: '#output' 
     38        }); 
     39    }); 
    3740 
    38         When using ajaxForm, the ajaxSubmit function will be invoked for you 
    39         at the appropriate time. 
     41    You can also use ajaxForm with delegation (requires jQuery v1.7+), so the 
     42    form does not have to exist when you invoke ajaxForm: 
     43 
     44    $('#myForm').ajaxForm({ 
     45        delegation: true, 
     46        target: '#output' 
     47    }); 
     48 
     49    When using ajaxForm, the ajaxSubmit function will be invoked for you 
     50    at the appropriate time. 
    4051*/ 
    4152 
    4253/** 
     54 * Feature detection 
     55 */ 
     56var feature = {}; 
     57feature.fileapi = $("<input type='file'/>").get(0).files !== undefined; 
     58feature.formdata = window.FormData !== undefined; 
     59 
     60var hasProp = !!$.fn.prop; 
     61 
     62// attr2 uses prop when it can but checks the return type for 
     63// an expected string.  this accounts for the case where a form 
     64// contains inputs with names like "action" or "method"; in those 
     65// cases "prop" returns the element 
     66$.fn.attr2 = function() { 
     67    if ( ! hasProp ) 
     68        return this.attr.apply(this, arguments); 
     69    var val = this.prop.apply(this, arguments); 
     70    if ( ( val && val.jquery ) || typeof val === 'string' ) 
     71        return val; 
     72    return this.attr.apply(this, arguments); 
     73}; 
     74 
     75/** 
    4376 * ajaxSubmit() provides a mechanism for immediately submitting 
    4477 * an HTML form using AJAX. 
    4578 */ 
    4679$.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         } 
     80    /*jshint scripturl:true */ 
    5281 
    53         if (typeof options == 'function') { 
    54                 options = { success: options }; 
    55         } 
     82    // fast fail if nothing selected (http://dev.jquery.com/ticket/2752) 
     83    if (!this.length) { 
     84        log('ajaxSubmit: skipping submit process - no element selected'); 
     85        return this; 
     86    } 
    5687 
    57         var action = this.attr('action'); 
    58         var url = (typeof action === 'string') ? $.trim(action) : ''; 
    59         if (url) { 
    60                 // clean url (don't include hash vaue) 
    61                 url = (url.match(/^([^#]+)/)||[])[1]; 
    62         } 
    63         url = url || window.location.href || ''; 
     88    var method, action, url, $form = this; 
    6489 
    65         options = $.extend(true, { 
    66                 url:  url, 
    67                 success: $.ajaxSettings.success, 
    68                 type: this[0].getAttribute('method') || 'GET', // IE7 massage (see issue 57) 
    69                 iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank' 
    70         }, options); 
     90    if (typeof options == 'function') { 
     91        options = { success: options }; 
     92    } 
    7193 
    72         // hook for manipulating the form data before it is extracted; 
    73         // convenient for use with rich editors like tinyMCE or FCKEditor 
    74         var veto = {}; 
    75         this.trigger('form-pre-serialize', [this, options, veto]); 
    76         if (veto.veto) { 
    77                 log('ajaxSubmit: submit vetoed via form-pre-serialize trigger'); 
    78                 return this; 
    79         } 
     94    method = this.attr2('method'); 
     95    action = this.attr2('action'); 
    8096 
    81         // provide opportunity to alter form data before it is serialized 
    82         if (options.beforeSerialize && options.beforeSerialize(this, options) === false) { 
    83                 log('ajaxSubmit: submit aborted via beforeSerialize callback'); 
    84                 return this; 
    85         } 
     97    url = (typeof action === 'string') ? $.trim(action) : ''; 
     98    url = url || window.location.href || ''; 
     99    if (url) { 
     100        // clean url (don't include hash vaue) 
     101        url = (url.match(/^([^#]+)/)||[])[1]; 
     102    } 
    86103 
    87         var n,v,a = this.formToArray(options.semantic); 
    88         if (options.data) { 
    89                 options.extraData = options.data; 
    90                 for (n in options.data) { 
    91                         if(options.data[n] instanceof Array) { 
    92                                 for (var k in options.data[n]) { 
    93                                         a.push( { name: n, value: options.data[n][k] } ); 
    94                                 } 
    95                         } 
    96                         else { 
    97                                 v = options.data[n]; 
    98                                 v = $.isFunction(v) ? v() : v; // if value is fn, invoke it 
    99                                 a.push( { name: n, value: v } ); 
    100                         } 
    101                 } 
    102         } 
     104    options = $.extend(true, { 
     105        url:  url, 
     106        success: $.ajaxSettings.success, 
     107        type: method || 'GET', 
     108        iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank' 
     109    }, options); 
    103110 
    104         // give pre-submit callback an opportunity to abort the submit 
    105         if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) { 
    106                 log('ajaxSubmit: submit aborted via beforeSubmit callback'); 
    107                 return this; 
    108         } 
     111    // hook for manipulating the form data before it is extracted; 
     112    // convenient for use with rich editors like tinyMCE or FCKEditor 
     113    var veto = {}; 
     114    this.trigger('form-pre-serialize', [this, options, veto]); 
     115    if (veto.veto) { 
     116        log('ajaxSubmit: submit vetoed via form-pre-serialize trigger'); 
     117        return this; 
     118    } 
    109119 
    110         // fire vetoable 'validate' event 
    111         this.trigger('form-submit-validate', [a, this, options, veto]); 
    112         if (veto.veto) { 
    113                 log('ajaxSubmit: submit vetoed via form-submit-validate trigger'); 
    114                 return this; 
    115         } 
     120    // provide opportunity to alter form data before it is serialized 
     121    if (options.beforeSerialize && options.beforeSerialize(this, options) === false) { 
     122        log('ajaxSubmit: submit aborted via beforeSerialize callback'); 
     123        return this; 
     124    } 
    116125 
    117         var q = $.param(a); 
     126    var traditional = options.traditional; 
     127    if ( traditional === undefined ) { 
     128        traditional = $.ajaxSettings.traditional; 
     129    } 
    118130 
    119         if (options.type.toUpperCase() == 'GET') { 
    120                 options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q; 
    121                 options.data = null;  // data is null for 'get' 
    122         } 
    123         else { 
    124                 options.data = q; // data is the query string for 'post' 
    125         } 
     131    var elements = []; 
     132    var qx, a = this.formToArray(options.semantic, elements); 
     133    if (options.data) { 
     134        options.extraData = options.data; 
     135        qx = $.param(options.data, traditional); 
     136    } 
    126137 
    127         var $form = this, callbacks = []; 
    128         if (options.resetForm) { 
    129                 callbacks.push(function() { $form.resetForm(); }); 
    130         } 
    131         if (options.clearForm) { 
    132                 callbacks.push(function() { $form.clearForm(); }); 
    133         } 
     138    // give pre-submit callback an opportunity to abort the submit 
     139    if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) { 
     140        log('ajaxSubmit: submit aborted via beforeSubmit callback'); 
     141        return this; 
     142    } 
    134143 
    135         // perform a load on the target only if dataType is not provided 
    136         if (!options.dataType && options.target) { 
    137                 var oldSuccess = options.success || function(){}; 
    138                 callbacks.push(function(data) { 
    139                         var fn = options.replaceTarget ? 'replaceWith' : 'html'; 
    140                         $(options.target)[fn](data).each(oldSuccess, arguments); 
    141                 }); 
    142         } 
    143         else if (options.success) { 
    144                 callbacks.push(options.success); 
    145         } 
     144    // fire vetoable 'validate' event 
     145    this.trigger('form-submit-validate', [a, this, options, veto]); 
     146    if (veto.veto) { 
     147        log('ajaxSubmit: submit vetoed via form-submit-validate trigger'); 
     148        return this; 
     149    } 
    146150 
    147         options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg 
    148                 var context = options.context || options;   // jQuery 1.4+ supports scope context 
    149                 for (var i=0, max=callbacks.length; i < max; i++) { 
    150                         callbacks[i].apply(context, [data, status, xhr || $form, $form]); 
    151                 } 
    152         }; 
     151    var q = $.param(a, traditional); 
     152    if (qx) { 
     153        q = ( q ? (q + '&' + qx) : qx ); 
     154    } 
     155    if (options.type.toUpperCase() == 'GET') { 
     156        options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q; 
     157        options.data = null;  // data is null for 'get' 
     158    } 
     159    else { 
     160        options.data = q; // data is the query string for 'post' 
     161    } 
    153162 
    154         // are there files to upload? 
    155         var fileInputs = $('input:file', this).length > 0; 
    156         var mp = 'multipart/form-data'; 
    157         var multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp); 
     163    var callbacks = []; 
     164    if (options.resetForm) { 
     165        callbacks.push(function() { $form.resetForm(); }); 
     166    } 
     167    if (options.clearForm) { 
     168        callbacks.push(function() { $form.clearForm(options.includeHidden); }); 
     169    } 
    158170 
    159         // options.iframe allows user to force iframe mode 
    160         // 06-NOV-09: now defaulting to iframe mode if file input is detected 
    161    if (options.iframe !== false && (fileInputs || options.iframe || multipart)) { 
    162            // hack to fix Safari hang (thanks to Tim Molendijk for this) 
    163            // see:  http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d 
    164            if (options.closeKeepAlive) { 
    165                    $.get(options.closeKeepAlive, fileUpload); 
    166                 } 
    167            else { 
    168                    fileUpload(); 
    169                 } 
    170    } 
    171    else { 
    172                 $.ajax(options); 
    173    } 
     171    // perform a load on the target only if dataType is not provided 
     172    if (!options.dataType && options.target) { 
     173        var oldSuccess = options.success || function(){}; 
     174        callbacks.push(function(data) { 
     175            var fn = options.replaceTarget ? 'replaceWith' : 'html'; 
     176            $(options.target)[fn](data).each(oldSuccess, arguments); 
     177        }); 
     178    } 
     179    else if (options.success) { 
     180        callbacks.push(options.success); 
     181    } 
    174182 
    175         // fire 'notify' event 
    176         this.trigger('form-submit-notify', [this, options]); 
    177         return this; 
     183    options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg 
     184        var context = options.context || this ;    // jQuery 1.4+ supports scope context 
     185        for (var i=0, max=callbacks.length; i < max; i++) { 
     186            callbacks[i].apply(context, [data, status, xhr || $form, $form]); 
     187        } 
     188    }; 
    178189 
     190    // are there files to upload? 
    179191 
    180         // private function for handling file uploads (hat tip to YAHOO!) 
    181         function fileUpload() { 
    182                 var form = $form[0]; 
     192    // [value] (issue #113), also see comment: 
     193    // https://github.com/malsup/form/commit/588306aedba1de01388032d5f42a60159eea9228#commitcomment-2180219 
     194    var fileInputs = $('input[type=file]:enabled[value!=""]', this); 
    183195 
    184                 if ($(':input[name=submit],:input[id=submit]', form).length) { 
    185                         // if there is an input with a name or id of 'submit' then we won't be 
    186                         // able to invoke the submit fn on the form (at least not x-browser) 
    187                         alert('Error: Form elements must not have name or id of "submit".'); 
    188                         return; 
    189                 } 
     196    var hasFileInputs = fileInputs.length > 0; 
     197    var mp = 'multipart/form-data'; 
     198    var multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp); 
    190199 
    191                 var s = $.extend(true, {}, $.ajaxSettings, options); 
    192                 s.context = s.context || s; 
    193                 var id = 'jqFormIO' + (new Date().getTime()), fn = '_'+id; 
    194                 var $io = $('<iframe id="' + id + '" name="' + id + '" src="'+ s.iframeSrc +'" />'); 
    195                 var io = $io[0]; 
     200    var fileAPI = feature.fileapi && feature.formdata; 
     201    log("fileAPI :" + fileAPI); 
     202    var shouldUseFrame = (hasFileInputs || multipart) && !fileAPI; 
    196203 
    197                 $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' }); 
     204    var jqxhr; 
    198205 
    199                 var xhr = { // mock object 
    200                         aborted: 0, 
    201                         responseText: null, 
    202                         responseXML: null, 
    203                         status: 0, 
    204                         statusText: 'n/a', 
    205                         getAllResponseHeaders: function() {}, 
    206                         getResponseHeader: function() {}, 
    207                         setRequestHeader: function() {}, 
    208                         abort: function(status) { 
    209                                 var e = (status === 'timeout' ? 'timeout' : 'aborted'); 
    210                                 log('aborting upload... ' + e); 
    211                                 this.aborted = 1; 
    212                                 $io.attr('src', s.iframeSrc); // abort op in progress 
    213                                 xhr.error = e; 
    214                                 s.error && s.error.call(s.context, xhr, e, e); 
    215                                 g && $.event.trigger("ajaxError", [xhr, s, e]); 
    216                                 s.complete && s.complete.call(s.context, xhr, e); 
    217                         } 
    218                 }; 
     206    // options.iframe allows user to force iframe mode 
     207    // 06-NOV-09: now defaulting to iframe mode if file input is detected 
     208    if (options.iframe !== false && (options.iframe || shouldUseFrame)) { 
     209        // hack to fix Safari hang (thanks to Tim Molendijk for this) 
     210        // see:  http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d 
     211        if (options.closeKeepAlive) { 
     212            $.get(options.closeKeepAlive, function() { 
     213                jqxhr = fileUploadIframe(a); 
     214            }); 
     215        } 
     216        else { 
     217            jqxhr = fileUploadIframe(a); 
     218        } 
     219    } 
     220    else if ((hasFileInputs || multipart) && fileAPI) { 
     221        jqxhr = fileUploadXhr(a); 
     222    } 
     223    else { 
     224        jqxhr = $.ajax(options); 
     225    } 
    219226 
    220                 var g = s.global; 
    221                 // trigger ajax global events so that activity/block indicators work like normal 
    222                 if (g && ! $.active++) { 
    223                         $.event.trigger("ajaxStart"); 
    224                 } 
    225                 if (g) { 
    226                         $.event.trigger("ajaxSend", [xhr, s]); 
    227                 } 
     227    $form.removeData('jqxhr').data('jqxhr', jqxhr); 
    228228 
    229                 if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) { 
    230                         if (s.global) { 
    231                                 $.active--; 
    232                         } 
    233                         return; 
    234                 } 
    235                 if (xhr.aborted) { 
    236                         return; 
    237                 } 
     229    // clear element array 
     230    for (var k=0; k < elements.length; k++) 
     231        elements[k] = null; 
    238232 
    239                 var timedOut = 0, timeoutHandle; 
     233    // fire 'notify' event 
     234    this.trigger('form-submit-notify', [this, options]); 
     235    return this; 
    240236 
    241                 // add submitting element to data if we know it 
    242                 var sub = form.clk; 
    243                 if (sub) { 
    244                         var n = sub.name; 
    245                         if (n && !sub.disabled) { 
    246                                 s.extraData = s.extraData || {}; 
    247                                 s.extraData[n] = sub.value; 
    248                                 if (sub.type == "image") { 
    249                                         s.extraData[n+'.x'] = form.clk_x; 
    250                                         s.extraData[n+'.y'] = form.clk_y; 
    251                                 } 
    252                         } 
    253                 } 
     237    // utility fn for deep serialization 
     238    function deepSerialize(extraData){ 
     239        var serialized = $.param(extraData).split('&'); 
     240        var len = serialized.length; 
     241        var result = []; 
     242        var i, part; 
     243        for (i=0; i < len; i++) { 
     244            // #252; undo param space replacement 
     245            serialized[i] = serialized[i].replace(/\+/g,' '); 
     246            part = serialized[i].split('='); 
     247            // #278; use array instead of object storage, favoring array serializations 
     248            result.push([decodeURIComponent(part[0]), decodeURIComponent(part[1])]); 
     249        } 
     250        return result; 
     251    } 
    254252 
    255                 // take a breath so that pending repaints get some cpu time before the upload starts 
    256                 function doSubmit() { 
    257                         // make sure form attrs are set 
    258                         var t = $form.attr('target'), a = $form.attr('action'); 
     253     // XMLHttpRequest Level 2 file uploads (big hat tip to francois2metz) 
     254    function fileUploadXhr(a) { 
     255        var formdata = new FormData(); 
    259256 
    260                         // update form attrs in IE friendly way 
    261                         form.setAttribute('target',id); 
    262                         if (form.getAttribute('method') != 'POST') { 
    263                                 form.setAttribute('method', 'POST'); 
    264                         } 
    265                         if (form.getAttribute('action') != s.url) { 
    266                                 form.setAttribute('action', s.url); 
    267                         } 
     257        for (var i=0; i < a.length; i++) { 
     258            formdata.append(a[i].name, a[i].value); 
     259        } 
    268260 
    269                         // ie borks in some cases when setting encoding 
    270                         if (! s.skipEncodingOverride) { 
    271                                 $form.attr({ 
    272                                         encoding: 'multipart/form-data', 
    273                                         enctype:  'multipart/form-data' 
    274                                 }); 
    275                         } 
     261        if (options.extraData) { 
     262            var serializedData = deepSerialize(options.extraData); 
     263            for (i=0; i < serializedData.length; i++) 
     264                if (serializedData[i]) 
     265                    formdata.append(serializedData[i][0], serializedData[i][1]); 
     266        } 
    276267 
    277                         // support timout 
    278                         if (s.timeout) { 
    279                                 timeoutHandle = setTimeout(function() { timedOut = true; cb(true); }, s.timeout); 
    280                         } 
     268        options.data = null; 
    281269 
    282                         // add "extra" data to form if provided in options 
    283                         var extraInputs = []; 
    284                         try { 
    285                                 if (s.extraData) { 
    286                                         for (var n in s.extraData) { 
    287                                                 extraInputs.push( 
    288                                                         $('<input type="hidden" name="'+n+'" value="'+s.extraData[n]+'" />') 
    289                                                                 .appendTo(form)[0]); 
    290                                         } 
    291                                 } 
     270        var s = $.extend(true, {}, $.ajaxSettings, options, { 
     271            contentType: false, 
     272            processData: false, 
     273            cache: false, 
     274            type: method || 'POST' 
     275        }); 
    292276 
    293                                 // add iframe to doc and submit the form 
    294                                 $io.appendTo('body'); 
    295                 io.attachEvent ? io.attachEvent('onload', cb) : io.addEventListener('load', cb, false); 
    296                                 form.submit(); 
    297                         } 
    298                         finally { 
    299                                 // reset attrs and remove "extra" input elements 
    300                                 form.setAttribute('action',a); 
    301                                 if(t) { 
    302                                         form.setAttribute('target', t); 
    303                                 } else { 
    304                                         $form.removeAttr('target'); 
    305                                 } 
    306                                 $(extraInputs).remove(); 
    307                         } 
    308                 } 
     277        if (options.uploadProgress) { 
     278            // workaround because jqXHR does not expose upload property 
     279            s.xhr = function() { 
     280                var xhr = jQuery.ajaxSettings.xhr(); 
     281                if (xhr.upload) { 
     282                    xhr.upload.addEventListener('progress', function(event) { 
     283                        var percent = 0; 
     284                        var position = event.loaded || event.position; /*event.position is deprecated*/ 
     285                        var total = event.total; 
     286                        if (event.lengthComputable) { 
     287                            percent = Math.ceil(position / total * 100); 
     288                        } 
     289                        options.uploadProgress(event, position, total, percent); 
     290                    }, false); 
     291                } 
     292                return xhr; 
     293            }; 
     294        } 
    309295 
    310                 if (s.forceSync) { 
    311                         doSubmit(); 
    312                 } 
    313                 else { 
    314                         setTimeout(doSubmit, 10); // this lets dom updates render 
    315                 } 
     296        s.data = null; 
     297            var beforeSend = s.beforeSend; 
     298            s.beforeSend = function(xhr, o) { 
     299                o.data = formdata; 
     300                if(beforeSend) 
     301                    beforeSend.call(this, xhr, o); 
     302        }; 
     303        return $.ajax(s); 
     304    } 
    316305 
    317                 var data, doc, domCheckCount = 50, callbackProcessed; 
     306    // private function for handling file uploads (hat tip to YAHOO!) 
     307    function fileUploadIframe(a) { 
     308        var form = $form[0], el, i, s, g, id, $io, io, xhr, sub, n, timedOut, timeoutHandle; 
     309        var deferred = $.Deferred(); 
    318310 
    319                 function cb(e) { 
    320                         if (xhr.aborted || callbackProcessed) { 
    321                                 return; 
    322                         } 
    323                         if (e === true && xhr) { 
    324                                 xhr.abort('timeout'); 
    325                                 return; 
    326                         } 
     311        if (a) { 
     312            // ensure that every serialized input is still enabled 
     313            for (i=0; i < elements.length; i++) { 
     314                el = $(elements[i]); 
     315                if ( hasProp ) 
     316                    el.prop('disabled', false); 
     317                else 
     318                    el.removeAttr('disabled'); 
     319            } 
     320        } 
    327321 
    328                         var doc = io.contentWindow ? io.contentWindow.document : io.contentDocument ? io.contentDocument : io.document; 
    329                         if (!doc || doc.location.href == s.iframeSrc) { 
    330                                 // response not received yet 
    331                                 if (!timedOut) 
    332                                         return; 
    333                         } 
    334             io.detachEvent ? io.detachEvent('onload', cb) : io.removeEventListener('load', cb, false); 
     322        s = $.extend(true, {}, $.ajaxSettings, options); 
     323        s.context = s.context || s; 
     324        id = 'jqFormIO' + (new Date().getTime()); 
     325        if (s.iframeTarget) { 
     326            $io = $(s.iframeTarget); 
     327            n = $io.attr2('name'); 
     328            if (!n) 
     329                 $io.attr2('name', id); 
     330            else 
     331                id = n; 
     332        } 
     333        else { 
     334            $io = $('<iframe name="' + id + '" src="'+ s.iframeSrc +'" />'); 
     335            $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' }); 
     336        } 
     337        io = $io[0]; 
    335338 
    336                         var ok = true; 
    337                         try { 
    338                                 if (timedOut) { 
    339                                         throw 'timeout'; 
    340                                 } 
    341339 
    342                                 var isXml = s.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc); 
    343                                 log('isXml='+isXml); 
    344                                 if (!isXml && window.opera && (doc.body == null || doc.body.innerHTML == '')) { 
    345                                         if (--domCheckCount) { 
    346                                                 // in some browsers (Opera) the iframe DOM is not always traversable when 
    347                                                 // the onload callback fires, so we loop a bit to accommodate 
    348                                                 log('requeing onLoad callback, DOM not available'); 
    349                                                 setTimeout(cb, 250); 
    350                                                 return; 
    351                                         } 
    352                                         // let this fall through because server response could be an empty document 
    353                                         //log('Could not access iframe DOM after mutiple tries.'); 
    354                                         //throw 'DOMException: not available'; 
    355                                 } 
     340        xhr = { // mock object 
     341            aborted: 0, 
     342            responseText: null, 
     343            responseXML: null, 
     344            status: 0, 
     345            statusText: 'n/a', 
     346            getAllResponseHeaders: function() {}, 
     347            getResponseHeader: function() {}, 
     348            setRequestHeader: function() {}, 
     349            abort: function(status) { 
     350                var e = (status === 'timeout' ? 'timeout' : 'aborted'); 
     351                log('aborting upload... ' + e); 
     352                this.aborted = 1; 
    356353 
    357                                 //log('response detected'); 
    358                                 xhr.responseText = doc.body ? doc.body.innerHTML : doc.documentElement ? doc.documentElement.innerHTML : null; 
    359                                 xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc; 
    360                                 if (isXml) 
    361                                         s.dataType = 'xml'; 
    362                                 xhr.getResponseHeader = function(header){ 
    363                                         var headers = {'content-type': s.dataType}; 
    364                                         return headers[header]; 
    365                                 }; 
     354                try { // #214, #257 
     355                    if (io.contentWindow.document.execCommand) { 
     356                        io.contentWindow.document.execCommand('Stop'); 
     357                    } 
     358                } 
     359                catch(ignore) {} 
    366360 
    367                                 var scr = /(json|script|text)/.test(s.dataType); 
    368                                 if (scr || s.textarea) { 
    369                                         // see if user embedded response in textarea 
    370                                         var ta = doc.getElementsByTagName('textarea')[0]; 
    371                                         if (ta) { 
    372                                                 xhr.responseText = ta.value; 
    373                                         } 
    374                                         else if (scr) { 
    375                                                 // account for browsers injecting pre around json response 
    376                                                 var pre = doc.getElementsByTagName('pre')[0]; 
    377                                                 var b = doc.getElementsByTagName('body')[0]; 
    378                                                 if (pre) { 
    379                                                         xhr.responseText = pre.textContent; 
    380                                                 } 
    381                                                 else if (b) { 
    382                                                         xhr.responseText = b.innerHTML; 
    383                                                 } 
    384                                         } 
    385                                 } 
    386                                 else if (s.dataType == 'xml' && !xhr.responseXML && xhr.responseText != null) { 
    387                                         xhr.responseXML = toXml(xhr.responseText); 
    388                                 } 
     361                $io.attr('src', s.iframeSrc); // abort op in progress 
     362                xhr.error = e; 
     363                if (s.error) 
     364                    s.error.call(s.context, xhr, e, status); 
     365                if (g) 
     366                    $.event.trigger("ajaxError", [xhr, s, e]); 
     367                if (s.complete) 
     368                    s.complete.call(s.context, xhr, e); 
     369            } 
     370        }; 
    389371 
    390                                 data = httpData(xhr, s.dataType, s); 
    391                         } 
    392                         catch(e){ 
    393                                 log('error caught:',e); 
    394                                 ok = false; 
    395                                 xhr.error = e; 
    396                                 s.error && s.error.call(s.context, xhr, 'error', e); 
    397                                 g && $.event.trigger("ajaxError", [xhr, s, e]); 
    398                         } 
     372        g = s.global; 
     373        // trigger ajax global events so that activity/block indicators work like normal 
     374        if (g && 0 === $.active++) { 
     375            $.event.trigger("ajaxStart"); 
     376        } 
     377        if (g) { 
     378            $.event.trigger("ajaxSend", [xhr, s]); 
     379        } 
    399380 
    400                         if (xhr.aborted) { 
    401                                 log('upload aborted'); 
    402                                 ok = false; 
    403                         } 
     381        if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) { 
     382            if (s.global) { 
     383                $.active--; 
     384            } 
     385            deferred.reject(); 
     386            return deferred; 
     387        } 
     388        if (xhr.aborted) { 
     389            deferred.reject(); 
     390            return deferred; 
     391        } 
    404392 
    405                         // ordering of these callbacks/triggers is odd, but that's how $.ajax does it 
    406                         if (ok) { 
    407                                 s.success && s.success.call(s.context, data, 'success', xhr); 
    408                                 g && $.event.trigger("ajaxSuccess", [xhr, s]); 
    409                         } 
     393        // add submitting element to data if we know it 
     394        sub = form.clk; 
     395        if (sub) { 
     396            n = sub.name; 
     397            if (n && !sub.disabled) { 
     398                s.extraData = s.extraData || {}; 
     399                s.extraData[n] = sub.value; 
     400                if (sub.type == "image") { 
     401                    s.extraData[n+'.x'] = form.clk_x; 
     402                    s.extraData[n+'.y'] = form.clk_y; 
     403                } 
     404            } 
     405        } 
    410406 
    411                         g && $.event.trigger("ajaxComplete", [xhr, s]); 
     407        var CLIENT_TIMEOUT_ABORT = 1; 
     408        var SERVER_ABORT = 2; 
    412409 
    413                         if (g && ! --$.active) { 
    414                                 $.event.trigger("ajaxStop"); 
    415                         } 
     410        function getDoc(frame) { 
     411            /* it looks like contentWindow or contentDocument do not 
     412             * carry the protocol property in ie8, when running under ssl 
     413             * frame.document is the only valid response document, since 
     414             * the protocol is know but not on the other two objects. strange? 
     415             * "Same origin policy" http://en.wikipedia.org/wiki/Same_origin_policy 
     416             */ 
    416417 
    417                         s.complete && s.complete.call(s.context, xhr, ok ? 'success' : 'error'); 
     418            var doc = null; 
    418419 
    419                         callbackProcessed = true; 
    420                         if (s.timeout) 
    421                                 clearTimeout(timeoutHandle); 
     420            // IE8 cascading access check 
     421            try { 
     422                if (frame.contentWindow) { 
     423                    doc = frame.contentWindow.document; 
     424                } 
     425            } catch(err) { 
     426                // IE8 access denied under ssl & missing protocol 
     427                log('cannot get iframe.contentWindow document: ' + err); 
     428            } 
    422429 
    423                         // clean up 
    424                         setTimeout(function() { 
    425                                 $io.removeData('form-plugin-onload'); 
    426                                 $io.remove(); 
    427                                 xhr.responseXML = null; 
    428                         }, 100); 
    429                 } 
     430            if (doc) { // successful getting content 
     431                return doc; 
     432            } 
    430433 
    431                 var toXml = $.parseXML || function(s, doc) { // use parseXML if available (jQuery 1.5+) 
    432                         if (window.ActiveXObject) { 
    433                                 doc = new ActiveXObject('Microsoft.XMLDOM'); 
    434                                 doc.async = 'false'; 
    435                                 doc.loadXML(s); 
    436                         } 
    437                         else { 
    438                                 doc = (new DOMParser()).parseFromString(s, 'text/xml'); 
    439                         } 
    440                         return (doc && doc.documentElement && doc.documentElement.nodeName != 'parsererror') ? doc : null; 
    441                 }; 
    442                 var parseJSON = $.parseJSON || function(s) { 
    443                         return window['eval']('(' + s + ')'); 
    444                 }; 
     434            try { // simply checking may throw in ie8 under ssl or mismatched protocol 
     435                doc = frame.contentDocument ? frame.contentDocument : frame.document; 
     436            } catch(err) { 
     437                // last attempt 
     438                log('cannot get iframe.contentDocument: ' + err); 
     439                doc = frame.document; 
     440            } 
     441            return doc; 
     442        } 
    445443 
    446                 var httpData = function( xhr, type, s ) { // mostly lifted from jq1.4.4 
    447                         var ct = xhr.getResponseHeader('content-type') || '', 
    448                                 xml = type === 'xml' || !type && ct.indexOf('xml') >= 0, 
    449                                 data = xml ? xhr.responseXML : xhr.responseText; 
     444        // Rails CSRF hack (thanks to Yvan Barthelemy) 
     445        var csrf_token = $('meta[name=csrf-token]').attr('content'); 
     446        var csrf_param = $('meta[name=csrf-param]').attr('content'); 
     447        if (csrf_param && csrf_token) { 
     448            s.extraData = s.extraData || {}; 
     449            s.extraData[csrf_param] = csrf_token; 
     450        } 
    450451 
    451                         if (xml && data.documentElement.nodeName === 'parsererror') { 
    452                                 $.error && $.error('parsererror'); 
    453                         } 
    454                         if (s && s.dataFilter) { 
    455                                 data = s.dataFilter(data, type); 
    456                         } 
    457                         if (typeof data === 'string') { 
    458                                 if (type === 'json' || !type && ct.indexOf('json') >= 0) { 
    459                                         data = parseJSON(data); 
    460                                 } else if (type === "script" || !type && ct.indexOf("javascript") >= 0) { 
    461                                         $.globalEval(data); 
    462                                 } 
    463                         } 
    464                         return data; 
    465                 }; 
    466         } 
     452        // take a breath so that pending repaints get some cpu time before the upload starts 
     453        function doSubmit() { 
     454            // make sure form attrs are set 
     455            var t = $form.attr2('target'), a = $form.attr2('action'); 
     456 
     457            // update form attrs in IE friendly way 
     458            form.setAttribute('target',id); 
     459            if (!method) { 
     460                form.setAttribute('method', 'POST'); 
     461            } 
     462            if (a != s.url) { 
     463                form.setAttribute('action', s.url); 
     464            } 
     465 
     466            // ie borks in some cases when setting encoding 
     467            if (! s.skipEncodingOverride && (!method || /post/i.test(method))) { 
     468                $form.attr({ 
     469                    encoding: 'multipart/form-data', 
     470                    enctype:  'multipart/form-data' 
     471                }); 
     472            } 
     473 
     474            // support timout 
     475            if (s.timeout) { 
     476                timeoutHandle = setTimeout(function() { timedOut = true; cb(CLIENT_TIMEOUT_ABORT); }, s.timeout); 
     477            } 
     478 
     479            // look for server aborts 
     480            function checkState() { 
     481                try { 
     482                    var state = getDoc(io).readyState; 
     483                    log('state = ' + state); 
     484                    if (state && state.toLowerCase() == 'uninitialized') 
     485                        setTimeout(checkState,50); 
     486                } 
     487                catch(e) { 
     488                    log('Server abort: ' , e, ' (', e.name, ')'); 
     489                    cb(SERVER_ABORT); 
     490                    if (timeoutHandle) 
     491                        clearTimeout(timeoutHandle); 
     492                    timeoutHandle = undefined; 
     493                } 
     494            } 
     495 
     496            // add "extra" data to form if provided in options 
     497            var extraInputs = []; 
     498            try { 
     499                if (s.extraData) { 
     500                    for (var n in s.extraData) { 
     501                        if (s.extraData.hasOwnProperty(n)) { 
     502                           // if using the $.param format that allows for multiple values with the same name 
     503                           if($.isPlainObject(s.extraData[n]) && s.extraData[n].hasOwnProperty('name') && s.extraData[n].hasOwnProperty('value')) { 
     504                               extraInputs.push( 
     505                               $('<input type="hidden" name="'+s.extraData[n].name+'">').val(s.extraData[n].value) 
     506                                   .appendTo(form)[0]); 
     507                           } else { 
     508                               extraInputs.push( 
     509                               $('<input type="hidden" name="'+n+'">').val(s.extraData[n]) 
     510                                   .appendTo(form)[0]); 
     511                           } 
     512                        } 
     513                    } 
     514                } 
     515 
     516                if (!s.iframeTarget) { 
     517                    // add iframe to doc and submit the form 
     518                    $io.appendTo('body'); 
     519                    if (io.attachEvent) 
     520                        io.attachEvent('onload', cb); 
     521                    else 
     522                        io.addEventListener('load', cb, false); 
     523                } 
     524                setTimeout(checkState,15); 
     525 
     526                try { 
     527                    form.submit(); 
     528                } catch(err) { 
     529                    // just in case form has element with name/id of 'submit' 
     530                    var submitFn = document.createElement('form').submit; 
     531                    submitFn.apply(form); 
     532                } 
     533            } 
     534            finally { 
     535                // reset attrs and remove "extra" input elements 
     536                form.setAttribute('action',a); 
     537                if(t) { 
     538                    form.setAttribute('target', t); 
     539                } else { 
     540                    $form.removeAttr('target'); 
     541                } 
     542                $(extraInputs).remove(); 
     543            } 
     544        } 
     545 
     546        if (s.forceSync) { 
     547            doSubmit(); 
     548        } 
     549        else { 
     550            setTimeout(doSubmit, 10); // this lets dom updates render 
     551        } 
     552 
     553        var data, doc, domCheckCount = 50, callbackProcessed; 
     554 
     555        function cb(e) { 
     556            if (xhr.aborted || callbackProcessed) { 
     557                return; 
     558            } 
     559 
     560            doc = getDoc(io); 
     561            if(!doc) { 
     562                log('cannot access response document'); 
     563                e = SERVER_ABORT; 
     564            } 
     565            if (e === CLIENT_TIMEOUT_ABORT && xhr) { 
     566                xhr.abort('timeout'); 
     567                deferred.reject(xhr, 'timeout'); 
     568                return; 
     569            } 
     570            else if (e == SERVER_ABORT && xhr) { 
     571                xhr.abort('server abort'); 
     572                deferred.reject(xhr, 'error', 'server abort'); 
     573                return; 
     574            } 
     575 
     576            if (!doc || doc.location.href == s.iframeSrc) { 
     577                // response not received yet 
     578                if (!timedOut) 
     579                    return; 
     580            } 
     581            if (io.detachEvent) 
     582                io.detachEvent('onload', cb); 
     583            else 
     584                io.removeEventListener('load', cb, false); 
     585 
     586            var status = 'success', errMsg; 
     587            try { 
     588                if (timedOut) { 
     589                    throw 'timeout'; 
     590                } 
     591 
     592                var isXml = s.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc); 
     593                log('isXml='+isXml); 
     594                if (!isXml && window.opera && (doc.body === null || !doc.body.innerHTML)) { 
     595                    if (--domCheckCount) { 
     596                        // in some browsers (Opera) the iframe DOM is not always traversable when 
     597                        // the onload callback fires, so we loop a bit to accommodate 
     598                        log('requeing onLoad callback, DOM not available'); 
     599                        setTimeout(cb, 250); 
     600                        return; 
     601                    } 
     602                    // let this fall through because server response could be an empty document 
     603                    //log('Could not access iframe DOM after mutiple tries.'); 
     604                    //throw 'DOMException: not available'; 
     605                } 
     606 
     607                //log('response detected'); 
     608                var docRoot = doc.body ? doc.body : doc.documentElement; 
     609                xhr.responseText = docRoot ? docRoot.innerHTML : null; 
     610                xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc; 
     611                if (isXml) 
     612                    s.dataType = 'xml'; 
     613                xhr.getResponseHeader = function(header){ 
     614                    var headers = {'content-type': s.dataType}; 
     615                    return headers[header]; 
     616                }; 
     617                // support for XHR 'status' & 'statusText' emulation : 
     618                if (docRoot) { 
     619                    xhr.status = Number( docRoot.getAttribute('status') ) || xhr.status; 
     620                    xhr.statusText = docRoot.getAttribute('statusText') || xhr.statusText; 
     621                } 
     622 
     623                var dt = (s.dataType || '').toLowerCase(); 
     624                var scr = /(json|script|text)/.test(dt); 
     625                if (scr || s.textarea) { 
     626                    // see if user embedded response in textarea 
     627                    var ta = doc.getElementsByTagName('textarea')[0]; 
     628                    if (ta) { 
     629                        xhr.responseText = ta.value; 
     630                        // support for XHR 'status' & 'statusText' emulation : 
     631                        xhr.status = Number( ta.getAttribute('status') ) || xhr.status; 
     632                        xhr.statusText = ta.getAttribute('statusText') || xhr.statusText; 
     633                    } 
     634                    else if (scr) { 
     635                        // account for browsers injecting pre around json response 
     636                        var pre = doc.getElementsByTagName('pre')[0]; 
     637                        var b = doc.getElementsByTagName('body')[0]; 
     638                        if (pre) { 
     639                            xhr.responseText = pre.textContent ? pre.textContent : pre.innerText; 
     640                        } 
     641                        else if (b) { 
     642                            xhr.responseText = b.textContent ? b.textContent : b.innerText; 
     643                        } 
     644                    } 
     645                } 
     646                else if (dt == 'xml' && !xhr.responseXML && xhr.responseText) { 
     647                    xhr.responseXML = toXml(xhr.responseText); 
     648                } 
     649 
     650                try { 
     651                    data = httpData(xhr, dt, s); 
     652                } 
     653                catch (err) { 
     654                    status = 'parsererror'; 
     655                    xhr.error = errMsg = (err || status); 
     656                } 
     657            } 
     658            catch (err) { 
     659                log('error caught: ',err); 
     660                status = 'error'; 
     661                xhr.error = errMsg = (err || status); 
     662            } 
     663 
     664            if (xhr.aborted) { 
     665                log('upload aborted'); 
     666                status = null; 
     667            } 
     668 
     669            if (xhr.status) { // we've set xhr.status 
     670                status = (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) ? 'success' : 'error'; 
     671            } 
     672 
     673            // ordering of these callbacks/triggers is odd, but that's how $.ajax does it 
     674            if (status === 'success') { 
     675                if (s.success) 
     676                    s.success.call(s.context, data, 'success', xhr); 
     677                deferred.resolve(xhr.responseText, 'success', xhr); 
     678                if (g) 
     679                    $.event.trigger("ajaxSuccess", [xhr, s]); 
     680            } 
     681            else if (status) { 
     682                if (errMsg === undefined) 
     683                    errMsg = xhr.statusText; 
     684                if (s.error) 
     685                    s.error.call(s.context, xhr, status, errMsg); 
     686                deferred.reject(xhr, 'error', errMsg); 
     687                if (g) 
     688                    $.event.trigger("ajaxError", [xhr, s, errMsg]); 
     689            } 
     690 
     691            if (g) 
     692                $.event.trigger("ajaxComplete", [xhr, s]); 
     693 
     694            if (g && ! --$.active) { 
     695                $.event.trigger("ajaxStop"); 
     696            } 
     697 
     698            if (s.complete) 
     699                s.complete.call(s.context, xhr, status); 
     700 
     701            callbackProcessed = true; 
     702            if (s.timeout) 
     703                clearTimeout(timeoutHandle); 
     704 
     705            // clean up 
     706            setTimeout(function() { 
     707                if (!s.iframeTarget) 
     708                    $io.remove(); 
     709                xhr.responseXML = null; 
     710            }, 100); 
     711        } 
     712 
     713        var toXml = $.parseXML || function(s, doc) { // use parseXML if available (jQuery 1.5+) 
     714            if (window.ActiveXObject) { 
     715                doc = new ActiveXObject('Microsoft.XMLDOM'); 
     716                doc.async = 'false'; 
     717                doc.loadXML(s); 
     718            } 
     719            else { 
     720                doc = (new DOMParser()).parseFromString(s, 'text/xml'); 
     721            } 
     722            return (doc && doc.documentElement && doc.documentElement.nodeName != 'parsererror') ? doc : null; 
     723        }; 
     724        var parseJSON = $.parseJSON || function(s) { 
     725            /*jslint evil:true */ 
     726            return window['eval']('(' + s + ')'); 
     727        }; 
     728 
     729        var httpData = function( xhr, type, s ) { // mostly lifted from jq1.4.4 
     730 
     731            var ct = xhr.getResponseHeader('content-type') || '', 
     732                xml = type === 'xml' || !type && ct.indexOf('xml') >= 0, 
     733                data = xml ? xhr.responseXML : xhr.responseText; 
     734 
     735            if (xml && data.documentElement.nodeName === 'parsererror') { 
     736                if ($.error) 
     737                    $.error('parsererror'); 
     738            } 
     739            if (s && s.dataFilter) { 
     740                data = s.dataFilter(data, type); 
     741            } 
     742            if (typeof data === 'string') { 
     743                if (type === 'json' || !type && ct.indexOf('json') >= 0) { 
     744                    data = parseJSON(data); 
     745                } else if (type === "script" || !type && ct.indexOf("javascript") >= 0) { 
     746                    $.globalEval(data); 
     747                } 
     748            } 
     749            return data; 
     750        }; 
     751 
     752        return deferred; 
     753    } 
    467754}; 
    468755 
    469756/** 
     
    472759 * The advantages of using this method instead of ajaxSubmit() are: 
    473760 * 
    474761 * 1: This method will include coordinates for <input type="image" /> elements (if the element 
    475  *      is used to submit the form). 
     762 *    is used to submit the form). 
    476763 * 2. This method will include the submit element's name/value data (for the element that was 
    477  *      used to submit the form). 
     764 *    used to submit the form). 
    478765 * 3. This method binds the submit() method to the form for you. 
    479766 * 
    480767 * The options argument for ajaxForm works exactly as it does for ajaxSubmit.  ajaxForm merely 
     
    482769 * the form itself. 
    483770 */ 
    484771$.fn.ajaxForm = function(options) { 
    485         // in jQuery 1.3+ we can fix mistakes with the ready state 
    486         if (this.length === 0) { 
    487                 var o = { s: this.selector, c: this.context }; 
    488                 if (!$.isReady && o.s) { 
    489                         log('DOM not ready, queuing ajaxForm'); 
    490                         $(function() { 
    491                                 $(o.s,o.c).ajaxForm(options); 
    492                         }); 
    493                         return this; 
    494                 } 
    495                 // is your DOM ready?  http://docs.jquery.com/Tutorials:Introducing_$(document).ready() 
    496                 log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)')); 
    497                 return this; 
    498         } 
     772    options = options || {}; 
     773    options.delegation = options.delegation && $.isFunction($.fn.on); 
    499774 
    500         return this.ajaxFormUnbind().bind('submit.form-plugin', function(e) { 
    501                 if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed 
    502                         e.preventDefault(); 
    503                         $(this).ajaxSubmit(options); 
    504                 } 
    505         }).bind('click.form-plugin', function(e) { 
    506                 var target = e.target; 
    507                 var $el = $(target); 
    508                 if (!($el.is(":submit,input:image"))) { 
    509                         // is this a child element of the submit el?  (ex: a span within a button) 
    510                         var t = $el.closest(':submit'); 
    511                         if (t.length == 0) { 
    512                                 return; 
    513                         } 
    514                         target = t[0]; 
    515                 } 
    516                 var form = this; 
    517                 form.clk = target; 
    518                 if (target.type == 'image') { 
    519                         if (e.offsetX != undefined) { 
    520                                 form.clk_x = e.offsetX; 
    521                                 form.clk_y = e.offsetY; 
    522                         } else if (typeof $.fn.offset == 'function') { // try to use dimensions plugin 
    523                                 var offset = $el.offset(); 
    524                                 form.clk_x = e.pageX - offset.left; 
    525                                 form.clk_y = e.pageY - offset.top; 
    526                         } else { 
    527                                 form.clk_x = e.pageX - target.offsetLeft; 
    528                                 form.clk_y = e.pageY - target.offsetTop; 
    529                         } 
    530                 } 
    531                 // clear form vars 
    532                 setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 100); 
    533         }); 
     775    // in jQuery 1.3+ we can fix mistakes with the ready state 
     776    if (!options.delegation && this.length === 0) { 
     777        var o = { s: this.selector, c: this.context }; 
     778        if (!$.isReady && o.s) { 
     779            log('DOM not ready, queuing ajaxForm'); 
     780            $(function() { 
     781                $(o.s,o.c).ajaxForm(options); 
     782            }); 
     783            return this; 
     784        } 
     785        // is your DOM ready?  http://docs.jquery.com/Tutorials:Introducing_$(document).ready() 
     786        log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)')); 
     787        return this; 
     788    } 
     789 
     790    if ( options.delegation ) { 
     791        $(document) 
     792            .off('submit.form-plugin', this.selector, doAjaxSubmit) 
     793            .off('click.form-plugin', this.selector, captureSubmittingElement) 
     794            .on('submit.form-plugin', this.selector, options, doAjaxSubmit) 
     795            .on('click.form-plugin', this.selector, options, captureSubmittingElement); 
     796        return this; 
     797    } 
     798 
     799    return this.ajaxFormUnbind() 
     800        .bind('submit.form-plugin', options, doAjaxSubmit) 
     801        .bind('click.form-plugin', options, captureSubmittingElement); 
    534802}; 
    535803 
     804// private event handlers 
     805function doAjaxSubmit(e) { 
     806    /*jshint validthis:true */ 
     807    var options = e.data; 
     808    if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed 
     809        e.preventDefault(); 
     810        $(this).ajaxSubmit(options); 
     811    } 
     812} 
     813 
     814function captureSubmittingElement(e) { 
     815    /*jshint validthis:true */ 
     816    var target = e.target; 
     817    var $el = $(target); 
     818    if (!($el.is("[type=submit],[type=image]"))) { 
     819        // is this a child element of the submit el?  (ex: a span within a button) 
     820        var t = $el.closest('[type=submit]'); 
     821        if (t.length === 0) { 
     822            return; 
     823        } 
     824        target = t[0]; 
     825    } 
     826    var form = this; 
     827    form.clk = target; 
     828    if (target.type == 'image') { 
     829        if (e.offsetX !== undefined) { 
     830            form.clk_x = e.offsetX; 
     831            form.clk_y = e.offsetY; 
     832        } else if (typeof $.fn.offset == 'function') { 
     833            var offset = $el.offset(); 
     834            form.clk_x = e.pageX - offset.left; 
     835            form.clk_y = e.pageY - offset.top; 
     836        } else { 
     837            form.clk_x = e.pageX - target.offsetLeft; 
     838            form.clk_y = e.pageY - target.offsetTop; 
     839        } 
     840    } 
     841    // clear form vars 
     842    setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 100); 
     843} 
     844 
     845 
    536846// ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm 
    537847$.fn.ajaxFormUnbind = function() { 
    538         return this.unbind('submit.form-plugin click.form-plugin'); 
     848    return this.unbind('submit.form-plugin click.form-plugin'); 
    539849}; 
    540850 
    541851/** 
     
    549859 * It is this array that is passed to pre-submit callback functions provided to the 
    550860 * ajaxSubmit() and ajaxForm() methods. 
    551861 */ 
    552 $.fn.formToArray = function(semantic) { 
    553         var a = []; 
    554         if (this.length === 0) { 
    555                 return a; 
    556         } 
     862$.fn.formToArray = function(semantic, elements) { 
     863    var a = []; 
     864    if (this.length === 0) { 
     865        return a; 
     866    } 
    557867 
    558         var form = this[0]; 
    559         var els = semantic ? form.getElementsByTagName('*') : form.elements; 
    560         if (!els) { 
    561                 return a; 
    562         } 
     868    var form = this[0]; 
     869    var els = semantic ? form.getElementsByTagName('*') : form.elements; 
     870    if (!els) { 
     871        return a; 
     872    } 
    563873 
    564         var i,j,n,v,el,max,jmax; 
    565         for(i=0, max=els.length; i < max; i++) { 
    566                 el = els[i]; 
    567                 n = el.name; 
    568                 if (!n) { 
    569                         continue; 
    570                 } 
     874    var i,j,n,v,el,max,jmax; 
     875    for(i=0, max=els.length; i < max; i++) { 
     876        el = els[i]; 
     877        n = el.name; 
     878        if (!n || el.disabled) { 
     879            continue; 
     880        } 
    571881 
    572                 if (semantic && form.clk && el.type == "image") { 
    573                         // handle image inputs on the fly when semantic == true 
    574                         if(!el.disabled && form.clk == el) { 
    575                                 a.push({name: n, value: $(el).val()}); 
    576                                 a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y}); 
    577                         } 
    578                         continue; 
    579                 } 
     882        if (semantic && form.clk && el.type == "image") { 
     883            // handle image inputs on the fly when semantic == true 
     884            if(form.clk == el) { 
     885                a.push({name: n, value: $(el).val(), type: el.type }); 
     886                a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y}); 
     887            } 
     888            continue; 
     889        } 
    580890 
    581                 v = $.fieldValue(el, true); 
    582                 if (v && v.constructor == Array) { 
    583                         for(j=0, jmax=v.length; j < jmax; j++) { 
    584                                 a.push({name: n, value: v[j]}); 
    585                         } 
    586                 } 
    587                 else if (v !== null && typeof v != 'undefined') { 
    588                         a.push({name: n, value: v}); 
    589                 } 
    590         } 
     891        v = $.fieldValue(el, true); 
     892        if (v && v.constructor == Array) { 
     893            if (elements) 
     894                elements.push(el); 
     895            for(j=0, jmax=v.length; j < jmax; j++) { 
     896                a.push({name: n, value: v[j]}); 
     897            } 
     898        } 
     899        else if (feature.fileapi && el.type == 'file') { 
     900            if (elements) 
     901                elements.push(el); 
     902            var files = el.files; 
     903            if (files.length) { 
     904                for (j=0; j < files.length; j++) { 
     905                    a.push({name: n, value: files[j], type: el.type}); 
     906                } 
     907            } 
     908            else { 
     909                // #180 
     910                a.push({ name: n, value: '', type: el.type }); 
     911            } 
     912        } 
     913        else if (v !== null && typeof v != 'undefined') { 
     914            if (elements) 
     915                elements.push(el); 
     916            a.push({name: n, value: v, type: el.type, required: el.required}); 
     917        } 
     918    } 
    591919 
    592         if (!semantic && form.clk) { 
    593                 // input type=='image' are not found in elements array! handle it here 
    594                 var $input = $(form.clk), input = $input[0]; 
    595                 n = input.name; 
    596                 if (n && !input.disabled && input.type == 'image') { 
    597                         a.push({name: n, value: $input.val()}); 
    598                         a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y}); 
    599                 } 
    600         } 
    601         return a; 
     920    if (!semantic && form.clk) { 
     921        // input type=='image' are not found in elements array! handle it here 
     922        var $input = $(form.clk), input = $input[0]; 
     923        n = input.name; 
     924        if (n && !input.disabled && input.type == 'image') { 
     925            a.push({name: n, value: $input.val()}); 
     926            a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y}); 
     927        } 
     928    } 
     929    return a; 
    602930}; 
    603931 
    604932/** 
     
    606934 * in the format: name1=value1&amp;name2=value2 
    607935 */ 
    608936$.fn.formSerialize = function(semantic) { 
    609         //hand off to jQuery.param for proper encoding 
    610         return $.param(this.formToArray(semantic)); 
     937    //hand off to jQuery.param for proper encoding 
     938    return $.param(this.formToArray(semantic)); 
    611939}; 
    612940 
    613941/** 
     
    615943 * This method will return a string in the format: name1=value1&amp;name2=value2 
    616944 */ 
    617945$.fn.fieldSerialize = function(successful) { 
    618         var a = []; 
    619         this.each(function() { 
    620                 var n = this.name; 
    621                 if (!n) { 
    622                         return; 
    623                 } 
    624                 var v = $.fieldValue(this, successful); 
    625                 if (v && v.constructor == Array) { 
    626                         for (var i=0,max=v.length; i < max; i++) { 
    627                                 a.push({name: n, value: v[i]}); 
    628                         } 
    629                 } 
    630                 else if (v !== null && typeof v != 'undefined') { 
    631                         a.push({name: this.name, value: v}); 
    632                 } 
    633         }); 
    634         //hand off to jQuery.param for proper encoding 
    635         return $.param(a); 
     946    var a = []; 
     947    this.each(function() { 
     948        var n = this.name; 
     949        if (!n) { 
     950            return; 
     951        } 
     952        var v = $.fieldValue(this, successful); 
     953        if (v && v.constructor == Array) { 
     954            for (var i=0,max=v.length; i < max; i++) { 
     955                a.push({name: n, value: v[i]}); 
     956            } 
     957        } 
     958        else if (v !== null && typeof v != 'undefined') { 
     959            a.push({name: this.name, value: v}); 
     960        } 
     961    }); 
     962    //hand off to jQuery.param for proper encoding 
     963    return $.param(a); 
    636964}; 
    637965 
    638966/** 
    639967 * Returns the value(s) of the element in the matched set.  For example, consider the following form: 
    640968 * 
    641969 *  <form><fieldset> 
    642  *        <input name="A" type="text" /> 
    643  *        <input name="A" type="text" /> 
    644  *        <input name="B" type="checkbox" value="B1" /> 
    645  *        <input name="B" type="checkbox" value="B2"/> 
    646  *        <input name="C" type="radio" value="C1" /> 
    647  *        <input name="C" type="radio" value="C2" /> 
     970 *      <input name="A" type="text" /> 
     971 *      <input name="A" type="text" /> 
     972 *      <input name="B" type="checkbox" value="B1" /> 
     973 *      <input name="B" type="checkbox" value="B2"/> 
     974 *      <input name="C" type="radio" value="C1" /> 
     975 *      <input name="C" type="radio" value="C2" /> 
    648976 *  </fieldset></form> 
    649977 * 
    650  *  var v = $(':text').fieldValue(); 
     978 *  var v = $('input[type=text]').fieldValue(); 
    651979 *  // if no values are entered into the text inputs 
    652980 *  v == ['',''] 
    653981 *  // if values entered into the text inputs are 'foo' and 'bar' 
    654982 *  v == ['foo','bar'] 
    655983 * 
    656  *  var v = $(':checkbox').fieldValue(); 
     984 *  var v = $('input[type=checkbox]').fieldValue(); 
    657985 *  // if neither checkbox is checked 
    658986 *  v === undefined 
    659987 *  // if both checkboxes are checked 
    660988 *  v == ['B1', 'B2'] 
    661989 * 
    662  *  var v = $(':radio').fieldValue(); 
     990 *  var v = $('input[type=radio]').fieldValue(); 
    663991 *  // if neither radio is checked 
    664992 *  v === undefined 
    665993 *  // if first radio is checked 
     
    671999 * for each element is returned. 
    6721000 * 
    6731001 * Note: This method *always* returns an array.  If no valid value can be determined the 
    674  *         array will be empty, otherwise it will contain one or more values. 
     1002 *    array will be empty, otherwise it will contain one or more values. 
    6751003 */ 
    6761004$.fn.fieldValue = function(successful) { 
    677         for (var val=[], i=0, max=this.length; i < max; i++) { 
    678                 var el = this[i]; 
    679                 var v = $.fieldValue(el, successful); 
    680                 if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) { 
    681                         continue; 
    682                 } 
    683                 v.constructor == Array ? $.merge(val, v) : val.push(v); 
    684         } 
    685         return val; 
     1005    for (var val=[], i=0, max=this.length; i < max; i++) { 
     1006        var el = this[i]; 
     1007        var v = $.fieldValue(el, successful); 
     1008        if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) { 
     1009            continue; 
     1010        } 
     1011        if (v.constructor == Array) 
     1012            $.merge(val, v); 
     1013        else 
     1014            val.push(v); 
     1015    } 
     1016    return val; 
    6861017}; 
    6871018 
    6881019/** 
    6891020 * Returns the value of the field element. 
    6901021 */ 
    6911022$.fieldValue = function(el, successful) { 
    692         var n = el.name, t = el.type, tag = el.tagName.toLowerCase(); 
    693         if (successful === undefined) { 
    694                 successful = true; 
    695         } 
     1023    var n = el.name, t = el.type, tag = el.tagName.toLowerCase(); 
     1024    if (successful === undefined) { 
     1025        successful = true; 
     1026    } 
    6961027 
    697         if (successful && (!n || el.disabled || t == 'reset' || t == 'button' || 
    698                 (t == 'checkbox' || t == 'radio') && !el.checked || 
    699                 (t == 'submit' || t == 'image') && el.form && el.form.clk != el || 
    700                 tag == 'select' && el.selectedIndex == -1)) { 
    701                         return null; 
    702         } 
     1028    if (successful && (!n || el.disabled || t == 'reset' || t == 'button' || 
     1029        (t == 'checkbox' || t == 'radio') && !el.checked || 
     1030        (t == 'submit' || t == 'image') && el.form && el.form.clk != el || 
     1031        tag == 'select' && el.selectedIndex == -1)) { 
     1032            return null; 
     1033    } 
    7031034 
    704         if (tag == 'select') { 
    705                 var index = el.selectedIndex; 
    706                 if (index < 0) { 
    707                         return null; 
    708                 } 
    709                 var a = [], ops = el.options; 
    710                 var one = (t == 'select-one'); 
    711                 var max = (one ? index+1 : ops.length); 
    712                 for(var i=(one ? index : 0); i < max; i++) { 
    713                         var op = ops[i]; 
    714                         if (op.selected) { 
    715                                 var v = op.value; 
    716                                 if (!v) { // extra pain for IE... 
    717                                         v = (op.attributes && op.attributes['value'] && !(op.attributes['value'].specified)) ? op.text : op.value; 
    718                                 } 
    719                                 if (one) { 
    720                                         return v; 
    721                                 } 
    722                                 a.push(v); 
    723                         } 
    724                 } 
    725                 return a; 
    726         } 
    727         return $(el).val(); 
     1035    if (tag == 'select') { 
     1036        var index = el.selectedIndex; 
     1037        if (index < 0) { 
     1038            return null; 
     1039        } 
     1040        var a = [], ops = el.options; 
     1041        var one = (t == 'select-one'); 
     1042        var max = (one ? index+1 : ops.length); 
     1043        for(var i=(one ? index : 0); i < max; i++) { 
     1044            var op = ops[i]; 
     1045            if (op.selected) { 
     1046                var v = op.value; 
     1047                if (!v) { // extra pain for IE... 
     1048                    v = (op.attributes && op.attributes['value'] && !(op.attributes['value'].specified)) ? op.text : op.value; 
     1049                } 
     1050                if (one) { 
     1051                    return v; 
     1052                } 
     1053                a.push(v); 
     1054            } 
     1055        } 
     1056        return a; 
     1057    } 
     1058    return $(el).val(); 
    7281059}; 
    7291060 
    7301061/** 
     
    7351066 *  - inputs of type submit, button, reset, and hidden will *not* be effected 
    7361067 *  - button elements will *not* be effected 
    7371068 */ 
    738 $.fn.clearForm = function() { 
    739         return this.each(function() { 
    740                 $('input,select,textarea', this).clearFields(); 
    741         }); 
     1069$.fn.clearForm = function(includeHidden) { 
     1070    return this.each(function() { 
     1071        $('input,select,textarea', this).clearFields(includeHidden); 
     1072    }); 
    7421073}; 
    7431074 
    7441075/** 
    7451076 * Clears the selected form elements. 
    7461077 */ 
    747 $.fn.clearFields = $.fn.clearInputs = function() { 
    748         return this.each(function() { 
    749                 var t = this.type, tag = this.tagName.toLowerCase(); 
    750                 if (t == 'text' || t == 'password' || tag == 'textarea') { 
    751                         this.value = ''; 
     1078$.fn.clearFields = $.fn.clearInputs = function(includeHidden) { 
     1079    var re = /^(?:color|date|datetime|email|month|number|password|range|search|tel|text|time|url|week)$/i; // 'hidden' is not in this list 
     1080    return this.each(function() { 
     1081        var t = this.type, tag = this.tagName.toLowerCase(); 
     1082        if (re.test(t) || tag == 'textarea') { 
     1083            this.value = ''; 
     1084        } 
     1085        else if (t == 'checkbox' || t == 'radio') { 
     1086            this.checked = false; 
     1087        } 
     1088        else if (tag == 'select') { 
     1089            this.selectedIndex = -1; 
     1090        } 
     1091                else if (t == "file") { 
     1092                        if (/MSIE/.test(navigator.userAgent)) { 
     1093                                $(this).replaceWith($(this).clone(true)); 
     1094                        } else { 
     1095                                $(this).val(''); 
     1096                        } 
    7521097                } 
    753                 else if (t == 'checkbox' || t == 'radio') { 
    754                         this.checked = false; 
    755                 } 
    756                 else if (tag == 'select') { 
    757                         this.selectedIndex = -1; 
    758                 } 
    759         }); 
     1098        else if (includeHidden) { 
     1099            // includeHidden can be the value true, or it can be a selector string 
     1100            // indicating a special test; for example: 
     1101            //  $('#myForm').clearForm('.special:hidden') 
     1102            // the above would clean hidden inputs that have the class of 'special' 
     1103            if ( (includeHidden === true && /hidden/.test(t)) || 
     1104                 (typeof includeHidden == 'string' && $(this).is(includeHidden)) ) 
     1105                this.value = ''; 
     1106        } 
     1107    }); 
    7601108}; 
    7611109 
    7621110/** 
    7631111 * Resets the form data.  Causes all form elements to be reset to their original value. 
    7641112 */ 
    7651113$.fn.resetForm = function() { 
    766         return this.each(function() { 
    767                 // guard against an input with the name of 'reset' 
    768                 // note that IE reports the reset function as an 'object' 
    769                 if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType)) { 
    770                         this.reset(); 
    771                 } 
    772         }); 
     1114    return this.each(function() { 
     1115        // guard against an input with the name of 'reset' 
     1116        // note that IE reports the reset function as an 'object' 
     1117        if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType)) { 
     1118            this.reset(); 
     1119        } 
     1120    }); 
    7731121}; 
    7741122 
    7751123/** 
    7761124 * Enables or disables any matching elements. 
    7771125 */ 
    7781126$.fn.enable = function(b) { 
    779         if (b === undefined) { 
    780                 b = true; 
    781         } 
    782         return this.each(function() { 
    783                 this.disabled = !b; 
    784         }); 
     1127    if (b === undefined) { 
     1128        b = true; 
     1129    } 
     1130    return this.each(function() { 
     1131        this.disabled = !b; 
     1132    }); 
    7851133}; 
    7861134 
    7871135/** 
     
    7891137 * selects/deselects and matching option elements. 
    7901138 */ 
    7911139$.fn.selected = function(select) { 
    792         if (select === undefined) { 
    793                 select = true; 
    794         } 
    795         return this.each(function() { 
    796                 var t = this.type; 
    797                 if (t == 'checkbox' || t == 'radio') { 
    798                         this.checked = select; 
    799                 } 
    800                 else if (this.tagName.toLowerCase() == 'option') { 
    801                         var $sel = $(this).parent('select'); 
    802                         if (select && $sel[0] && $sel[0].type == 'select-one') { 
    803                                 // deselect all other options 
    804                                 $sel.find('option').selected(false); 
    805                         } 
    806                         this.selected = select; 
    807                 } 
    808         }); 
     1140    if (select === undefined) { 
     1141        select = true; 
     1142    } 
     1143    return this.each(function() { 
     1144        var t = this.type; 
     1145        if (t == 'checkbox' || t == 'radio') { 
     1146            this.checked = select; 
     1147        } 
     1148        else if (this.tagName.toLowerCase() == 'option') { 
     1149            var $sel = $(this).parent('select'); 
     1150            if (select && $sel[0] && $sel[0].type == 'select-one') { 
     1151                // deselect all other options 
     1152                $sel.find('option').selected(false); 
     1153            } 
     1154            this.selected = select; 
     1155        } 
     1156    }); 
    8091157}; 
    8101158 
     1159// expose debug var 
     1160$.fn.ajaxSubmit.debug = false; 
     1161 
    8111162// helper fn for console logging 
    812 // set $.fn.ajaxSubmit.debug to true to enable debug logging 
    8131163function log() { 
    814         if ($.fn.ajaxSubmit.debug) { 
    815                 var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,''); 
    816                 if (window.console && window.console.log) { 
    817                         window.console.log(msg); 
    818                 } 
    819                 else if (window.opera && window.opera.postError) { 
    820                         window.opera.postError(msg); 
    821                 } 
    822         } 
    823 }; 
     1164    if (!$.fn.ajaxSubmit.debug) 
     1165        return; 
     1166    var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,''); 
     1167    if (window.console && window.console.log) { 
     1168        window.console.log(msg); 
     1169    } 
     1170    else if (window.opera && window.opera.postError) { 
     1171        window.opera.postError(msg); 
     1172    } 
     1173} 
    8241174 
    825 })(jQuery); 
     1175})(jQuery); 
     1176 No newline at end of file