1/* 2 * jQuery Form Plugin 3 * version: 2.33 (22-SEP-2009) 4 * @requires jQuery v1.2.6 or later 5 * 6 * Examples and documentation at: http://malsup.com/jquery/form/ 7 * Dual licensed under the MIT and GPL licenses: 8 * http://www.opensource.org/licenses/mit-license.php 9 * http://www.gnu.org/licenses/gpl.html 10 */ 11;(function($) { 12 13/* 14 Usage Note: 15 ----------- 16 Do not use both ajaxSubmit and ajaxForm on the same form. These 17 functions are intended to be exclusive. Use ajaxSubmit if you want 18 to bind your own submit handler to the form. For example, 19 20 $(document).ready(function() { 21 $('#myForm').bind('submit', function() { 22 $(this).ajaxSubmit({ 23 target: '#output' 24 }); 25 return false; // <-- important! 26 }); 27 }); 28 29 Use ajaxForm when you want the plugin to manage all the event binding 30 for you. For example, 31 32 $(document).ready(function() { 33 $('#myForm').ajaxForm({ 34 target: '#output' 35 }); 36 }); 37 38 When using ajaxForm, the ajaxSubmit function will be invoked for you 39 at the appropriate time. 40*/ 41 42/** 43 * ajaxSubmit() provides a mechanism for immediately submitting 44 * an HTML form using AJAX. 45 */ 46$.fn.ajaxSubmit = function(options) { 47 // fast fail if nothing selected (http://dev.jquery.com/ticket/2752) 48 if (!this.length) { 49 log('ajaxSubmit: skipping submit process - no element selected'); 50 return this; 51 } 52 53 if (typeof options == 'function') 54 options = { success: options }; 55 56 var url = $.trim(this.attr('action')); 57 if (url) { 58 // clean url (don't include hash vaue) 59 url = (url.match(/^([^#]+)/)||[])[1]; 60 } 61 url = url || window.location.href || ''; 62 63 options = $.extend({ 64 url: url, 65 type: this.attr('method') || 'GET' 66 }, options || {}); 67 68 // hook for manipulating the form data before it is extracted; 69 // convenient for use with rich editors like tinyMCE or FCKEditor 70 var veto = {}; 71 this.trigger('form-pre-serialize', [this, options, veto]); 72 if (veto.veto) { 73 log('ajaxSubmit: submit vetoed via form-pre-serialize trigger'); 74 return this; 75 } 76 77 // provide opportunity to alter form data before it is serialized 78 if (options.beforeSerialize && options.beforeSerialize(this, options) === false) { 79 log('ajaxSubmit: submit aborted via beforeSerialize callback'); 80 return this; 81 } 82 83 var a = this.formToArray(options.semantic); 84 if (options.data) { 85 options.extraData = options.data; 86 for (var n in options.data) { 87 if(options.data[n] instanceof Array) { 88 for (var k in options.data[n]) 89 a.push( { name: n, value: options.data[n][k] } ); 90 } 91 else 92 a.push( { name: n, value: options.data[n] } ); 93 } 94 } 95 96 // give pre-submit callback an opportunity to abort the submit 97 if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) { 98 log('ajaxSubmit: submit aborted via beforeSubmit callback'); 99 return this; 100 } 101 102 // fire vetoable 'validate' event 103 this.trigger('form-submit-validate', [a, this, options, veto]); 104 if (veto.veto) { 105 log('ajaxSubmit: submit vetoed via form-submit-validate trigger'); 106 return this; 107 } 108 109 var q = $.param(a); 110 111 if (options.type.toUpperCase() == 'GET') { 112 options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q; 113 options.data = null; // data is null for 'get' 114 } 115 else 116 options.data = q; // data is the query string for 'post' 117 118 var $form = this, callbacks = []; 119 if (options.resetForm) callbacks.push(function() { $form.resetForm(); }); 120 if (options.clearForm) callbacks.push(function() { $form.clearForm(); }); 121 122 // perform a load on the target only if dataType is not provided 123 if (!options.dataType && options.target) { 124 var oldSuccess = options.success || function(){}; 125 callbacks.push(function(data) { 126 $(options.target).html(data).each(oldSuccess, arguments); 127 }); 128 } 129 else if (options.success) 130 callbacks.push(options.success); 131 132 options.success = function(data, status) { 133 for (var i=0, max=callbacks.length; i < max; i++) 134 callbacks[i].apply(options, [data, status, $form]); 135 }; 136 137 // are there files to upload? 138 var files = $('input:file', this).fieldValue(); 139 var found = false; 140 for (var j=0; j < files.length; j++) 141 if (files[j]) 142 found = true; 143 144 var multipart = false;
145// var mp = 'multipart/form-data'; 146// multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp); 147 148 // options.iframe allows user to force iframe mode 149 if (options.iframe || found || multipart) { 150 // hack to fix Safari hang (thanks to Tim Molendijk for this) 151 // see: http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d 152 if (options.closeKeepAlive) 153 $.get(options.closeKeepAlive, fileUpload); 154 else 155 fileUpload(); 156 } 157 else 158 $.ajax(options); 159 160 // fire 'notify' event 161 this.trigger('form-submit-notify', [this, options]); 162 return this; 163 164 165 // private function for handling file uploads (hat tip to YAHOO!) 166 function fileUpload() { 167 var form = $form[0]; 168 169 if ($(':input[name=submit]', form).length) { 170 alert('Error: Form elements must not be named "submit".'); 171 return; 172 } 173 174 var opts = $.extend({}, $.ajaxSettings, options); 175 var s = $.extend(true, {}, $.extend(true, {}, $.ajaxSettings), opts); 176 177 var id = 'jqFormIO' + (new Date().getTime()); 178 var $io = $('<iframe id="' + id + '" name="' + id + '" src="about:blank" />'); 179 var io = $io[0]; 180 181 $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' }); 182 183 var xhr = { // mock object 184 aborted: 0, 185 responseText: null, 186 responseXML: null, 187 status: 0, 188 statusText: 'n/a', 189 getAllResponseHeaders: function() {}, 190 getResponseHeader: function() {}, 191 setRequestHeader: function() {}, 192 abort: function() { 193 this.aborted = 1; 194 $io.attr('src','about:blank'); // abort op in progress 195 } 196 }; 197 198 var g = opts.global; 199 // trigger ajax global events so that activity/block indicators work like normal 200 if (g && ! $.active++) $.event.trigger("ajaxStart"); 201 if (g) $.event.trigger("ajaxSend", [xhr, opts]); 202 203 if (s.beforeSend && s.beforeSend(xhr, s) === false) { 204 s.global && $.active--; 205 return; 206 } 207 if (xhr.aborted) 208 return; 209 210 var cbInvoked = 0; 211 var timedOut = 0; 212 213 // add submitting element to data if we know it 214 var sub = form.clk; 215 if (sub) { 216 var n = sub.name; 217 if (n && !sub.disabled) { 218 options.extraData = options.extraData || {}; 219 options.extraData[n] = sub.value; 220 if (sub.type == "image") { 221 options.extraData[name+'.x'] = form.clk_x; 222 options.extraData[name+'.y'] = form.clk_y; 223 } 224 } 225 } 226 227 // take a breath so that pending repaints get some cpu time before the upload starts 228 setTimeout(function() { 229 // make sure form attrs are set 230 var t = $form.attr('target'), a = $form.attr('action'); 231 232 // update form attrs in IE friendly way 233 form.setAttribute('target',id); 234 if (form.getAttribute('method') != 'POST') 235 form.setAttribute('method', 'POST'); 236 if (form.getAttribute('action') != opts.url) 237 form.setAttribute('action', opts.url); 238 239 // ie borks in some cases when setting encoding 240 if (! options.skipEncodingOverride) { 241 $form.attr({ 242 encoding: 'multipart/form-data', 243 enctype: 'multipart/form-data' 244 }); 245 } 246 247 // support timout 248 if (opts.timeout) 249 setTimeout(function() { timedOut = true; cb(); }, opts.timeout); 250 251 // add "extra" data to form if provided in options 252 var extraInputs = []; 253 try { 254 if (options.extraData) 255 for (var n in options.extraData) 256 extraInputs.push( 257 $('<input type="hidden" name="'+n+'" value="'+options.extraData[n]+'" />') 258 .appendTo(form)[0]); 259 260 // add iframe to doc and submit the form 261 $io.appendTo('body'); 262 io.attachEvent ? io.attachEvent('onload', cb) : io.addEventListener('load', cb, false); 263 form.submit(); 264 } 265 finally { 266 // reset attrs and remove "extra" input elements 267 form.setAttribute('action',a); 268 t ? form.setAttribute('target', t) : $form.removeAttr('target'); 269 $(extraInputs).remove(); 270 } 271 }, 10); 272 273 var domCheckCount = 50; 274 275 function cb() { 276 if (cbInvoked++) return; 277 278 io.detachEvent ? io.detachEvent('onload', cb) : io.removeEventListener('load', cb, false); 279 280 var ok = true; 281 try { 282 if (timedOut) throw 'timeout'; 283 // extract the server response from the iframe 284 var data, doc; 285 286 doc = io.contentWindow ? io.contentWindow.document : io.contentDocument ? io.contentDocument : io.document; 287 288 var isXml = opts.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc); 289 log('isXml='+isXml); 290 if (!isXml && (doc.body == null || doc.body.innerHTML == '')) { 291 if (--domCheckCount) { 292 // in some browsers (Opera) the iframe DOM is not always traversable when 293 // the onload callback fires, so we loop a bit to accommodate 294 cbInvoked = 0; 295 setTimeout(cb, 100); 296 return; 297 } 298 log('Could not access iframe DOM after 50 tries.'); 299 return; 300 } 301 302 xhr.responseText = doc.body ? doc.body.innerHTML : null; 303 xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc; 304 xhr.getResponseHeader = function(header){ 305 var headers = {'content-type': opts.dataType}; 306 return headers[header]; 307 }; 308 309 if (opts.dataType == 'json' || opts.dataType == 'script') { 310 // see if user embedded response in textarea 311 var ta = doc.getElementsByTagName('textarea')[0]; 312 if (ta) 313 xhr.responseText = ta.value; 314 else { 315 // account for browsers injecting pre around json response 316 var pre = doc.getElementsByTagName('pre')[0]; 317 if (pre) 318 xhr.responseText = pre.innerHTML; 319 } 320 } 321 else if (opts.dataType == 'xml' && !xhr.responseXML && xhr.responseText != null) { 322 xhr.responseXML = toXml(xhr.responseText); 323 } 324 data = $.httpData(xhr, opts.dataType); 325 } 326 catch(e){ 327 ok = false;
328 $.handleError(opts, xhr, 'error', e); 329 } 330 331 // ordering of these callbacks/triggers is odd, but that's how $.ajax does it 332 if (ok) { 333 opts.success(data, 'success'); 334 if (g) $.event.trigger("ajaxSuccess", [xhr, opts]); 335 } 336 if (g) $.event.trigger("ajaxComplete", [xhr, opts]); 337 if (g && ! --$.active) $.event.trigger("ajaxStop"); 338 if (opts.complete) opts.complete(xhr, ok ? 'success' : 'error'); 339 340 // clean up 341 setTimeout(function() { 342 $io.remove(); 343 xhr.responseXML = null; 344 }, 100); 345 }; 346 347 function toXml(s, doc) { 348 if (window.ActiveXObject) { 349 doc = new ActiveXObject('Microsoft.XMLDOM'); 350 doc.async = 'false'; 351 doc.loadXML(s); 352 } 353 else 354 doc = (new DOMParser()).parseFromString(s, 'text/xml'); 355 return (doc && doc.documentElement && doc.documentElement.tagName != 'parsererror') ? doc : null; 356 }; 357 }; 358}; 359 360/** 361 * ajaxForm() provides a mechanism for fully automating form submission. 362 * 363 * The advantages of using this method instead of ajaxSubmit() are: 364 * 365 * 1: This method will include coordinates for <input type="image" /> elements (if the element 366 * is used to submit the form). 367 * 2. This method will include the submit element's name/value data (for the element that was 368 * used to submit the form). 369 * 3. This method binds the submit() method to the form for you. 370 * 371 * The options argument for ajaxForm works exactly as it does for ajaxSubmit. ajaxForm merely 372 * passes the options argument along after properly binding events for submit elements and 373 * the form itself. 374 */ 375$.fn.ajaxForm = function(options) { 376 return this.ajaxFormUnbind().bind('submit.form-plugin', function() { 377 $(this).ajaxSubmit(options); 378 return false; 379 }).bind('click.form-plugin', function(e) { 380 var $el = $(e.target); 381 if (!($el.is(":submit,input:image"))) { 382 return; 383 } 384 var form = this; 385 form.clk = e.target; 386 if (e.target.type == 'image') { 387 if (e.offsetX != undefined) { 388 form.clk_x = e.offsetX; 389 form.clk_y = e.offsetY; 390 } else if (typeof $.fn.offset == 'function') { // try to use dimensions plugin 391 var offset = $el.offset(); 392 form.clk_x = e.pageX - offset.left; 393 form.clk_y = e.pageY - offset.top; 394 } else { 395 form.clk_x = e.pageX - e.target.offsetLeft; 396 form.clk_y = e.pageY - e.target.offsetTop; 397 } 398 } 399 // clear form vars 400 setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 10); 401 }); 402}; 403 404// ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm 405$.fn.ajaxFormUnbind = function() { 406 return this.unbind('submit.form-plugin click.form-plugin'); 407}; 408 409/** 410 * formToArray() gathers form element data into an array of objects that can 411 * be passed to any of the following ajax functions: $.get, $.post, or load. 412 * Each object in the array has both a 'name' and 'value' property. An example of 413 * an array for a simple login form might be: 414 * 415 * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ] 416 * 417 * It is this array that is passed to pre-submit callback functions provided to the 418 * ajaxSubmit() and ajaxForm() methods. 419 */ 420$.fn.formToArray = function(semantic) { 421 var a = []; 422 if (this.length == 0) return a; 423 424 var form = this[0]; 425 var els = semantic ? form.getElementsByTagName('*') : form.elements; 426 if (!els) return a; 427 for(var i=0, max=els.length; i < max; i++) { 428 var el = els[i]; 429 var n = el.name; 430 if (!n) continue; 431 432 if (semantic && form.clk && el.type == "image") { 433 // handle image inputs on the fly when semantic == true 434 if(!el.disabled && form.clk == el) { 435 a.push({name: n, value: $(el).val()}); 436 a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y}); 437 } 438 continue; 439 } 440 441 var v = $.fieldValue(el, true); 442 if (v && v.constructor == Array) { 443 for(var j=0, jmax=v.length; j < jmax; j++) 444 a.push({name: n, value: v[j]}); 445 } 446 else if (v !== null && typeof v != 'undefined') 447 a.push({name: n, value: v}); 448 } 449 450 if (!semantic && form.clk) { 451 // input type=='image' are not found in elements array! handle it here 452 var $input = $(form.clk), input = $input[0], n = input.name; 453 if (n && !input.disabled && input.type == 'image') { 454 a.push({name: n, value: $input.val()}); 455 a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y}); 456 } 457 } 458 return a; 459}; 460 461/** 462 * Serializes form data into a 'submittable' string. This method will return a string 463 * in the format: name1=value1&name2=value2 464 */
465$.fn.formSerialize = function(semantic) { 466 //hand off to jQuery.param for proper encoding 467 return $.param(this.formToArray(semantic)); 468}; 469 470/** 471 * Serializes all field elements in the jQuery object into a query string. 472 * This method will return a string in the format: name1=value1&name2=value2 473 */ 474$.fn.fieldSerialize = function(successful) { 475 var a = []; 476 this.each(function() { 477 var n = this.name; 478 if (!n) return; 479 var v = $.fieldValue(this, successful); 480 if (v && v.constructor == Array) { 481 for (var i=0,max=v.length; i < max; i++) 482 a.push({name: n, value: v[i]}); 483 } 484 else if (v !== null && typeof v != 'undefined') 485 a.push({name: this.name, value: v}); 486 }); 487 //hand off to jQuery.param for proper encoding 488 return $.param(a); 489}; 490 491/** 492 * Returns the value(s) of the element in the matched set. For example, consider the following form: 493 * 494 * <form><fieldset> 495 * <input name="A" type="text" /> 496 * <input name="A" type="text" /> 497 * <input name="B" type="checkbox" value="B1" /> 498 * <input name="B" type="checkbox" value="B2"/> 499 * <input name="C" type="radio" value="C1" /> 500 * <input name="C" type="radio" value="C2" /> 501 * </fieldset></form> 502 * 503 * var v = $(':text').fieldValue(); 504 * // if no values are entered into the text inputs 505 * v == ['',''] 506 * // if values entered into the text inputs are 'foo' and 'bar' 507 * v == ['foo','bar'] 508 * 509 * var v = $(':checkbox').fieldValue(); 510 * // if neither checkbox is checked 511 * v === undefined 512 * // if both checkboxes are checked 513 * v == ['B1', 'B2'] 514 * 515 * var v = $(':radio').fieldValue(); 516 * // if neither radio is checked 517 * v === undefined 518 * // if first radio is checked 519 * v == ['C1'] 520 * 521 * The successful argument controls whether or not the field element must be 'successful' 522 * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls). 523 * The default value of the successful argument is true. If this value is false the value(s) 524 * for each element is returned. 525 * 526 * Note: This method *always* returns an array. If no valid value can be determined the 527 * array will be empty, otherwise it will contain one or more values. 528 */ 529$.fn.fieldValue = function(successful) { 530 for (var val=[], i=0, max=this.length; i < max; i++) { 531 var el = this[i]; 532 var v = $.fieldValue(el, successful); 533 if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) 534 continue; 535 v.constructor == Array ? $.merge(val, v) : val.push(v); 536 } 537 return val; 538}; 539 540/** 541 * Returns the value of the field element. 542 */ 543$.fieldValue = function(el, successful) { 544 var n = el.name, t = el.type, tag = el.tagName.toLowerCase(); 545 if (typeof successful == 'undefined') successful = true; 546 547 if (successful && (!n || el.disabled || t == 'reset' || t == 'button' || 548 (t == 'checkbox' || t == 'radio') && !el.checked || 549 (t == 'submit' || t == 'image') && el.form && el.form.clk != el || 550 tag == 'select' && el.selectedIndex == -1)) 551 return null; 552 553 if (tag == 'select') { 554 var index = el.selectedIndex; 555 if (index < 0) return null; 556 var a = [], ops = el.options; 557 var one = (t == 'select-one'); 558 var max = (one ? index+1 : ops.length); 559 for(var i=(one ? index : 0); i < max; i++) { 560 var op = ops[i]; 561 if (op.selected) { 562 var v = op.value; 563 if (!v) // extra pain for IE... 564 v = (op.attributes && op.attributes['value'] && !(op.attributes['value'].specified)) ? op.text : op.value; 565 if (one) return v; 566 a.push(v); 567 } 568 } 569 return a; 570 } 571 return el.value; 572}; 573 574/** 575 * Clears the form data. Takes the following actions on the form's input fields: 576 * - input text fields will have their 'value' property set to the empty string 577 * - select elements will have their 'selectedIndex' property set to -1 578 * - checkbox and radio inputs will have their 'checked' property set to false 579 * - inputs of type submit, button, reset, and hidden will *not* be effected 580 * - button elements will *not* be effected 581 */ 582$.fn.clearForm = function() { 583 return this.each(function() { 584 $('input,select,textarea', this).clearFields(); 585 }); 586}; 587 588/** 589 * Clears the selected form elements. 590 */ 591$.fn.clearFields = $.fn.clearInputs = function() { 592 return this.each(function() { 593 var t = this.type, tag = this.tagName.toLowerCase(); 594 if (t == 'text' || t == 'password' || tag == 'textarea') 595 this.value = ''; 596 else if (t == 'checkbox' || t == 'radio') 597 this.checked = false;
598 else if (tag == 'select') 599 this.selectedIndex = -1; 600 }); 601}; 602 603/** 604 * Resets the form data. Causes all form elements to be reset to their original value. 605 */ 606$.fn.resetForm = function() { 607 return this.each(function() { 608 // guard against an input with the name of 'reset' 609 // note that IE reports the reset function as an 'object' 610 if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType)) 611 this.reset(); 612 }); 613}; 614 615/** 616 * Enables or disables any matching elements. 617 */ 618$.fn.enable = function(b) { 619 if (b == undefined) b = true; 620 return this.each(function() { 621 this.disabled = !b; 622 }); 623}; 624 625/** 626 * Checks/unchecks any matching checkboxes or radio buttons and 627 * selects/deselects and matching option elements. 628 */ 629$.fn.selected = function(select) { 630 if (select == undefined) select = true; 631 return this.each(function() { 632 var t = this.type; 633 if (t == 'checkbox' || t == 'radio') 634 this.checked = select; 635 else if (this.tagName.toLowerCase() == 'option') { 636 var $sel = $(this).parent('select'); 637 if (select && $sel[0] && $sel[0].type == 'select-one') { 638 // deselect all other options 639 $sel.find('option').selected(false); 640 } 641 this.selected = select; 642 } 643 }); 644}; 645 646// helper fn for console logging 647// set $.fn.ajaxSubmit.debug to true to enable debug logging 648function log() { 649 if ($.fn.ajaxSubmit.debug && window.console && window.console.log) 650 window.console.log('[jquery.form] ' + Array.prototype.join.call(arguments,'')); 651}; 652 653})(jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.