[ Index ]

PHP Cross Reference of MediaWiki-1.24.0

title

Body

[close]

/resources/lib/jquery/ -> jquery.form.js (source)

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


Generated: Fri Nov 28 14:03:12 2014 Cross-referenced by PHPXref 0.7.1