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