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