Wrapping core modules (FIXME from r79929)
[lhc/web/wiklou.git] / resources / jquery / jquery.form.js
1 /*!
2 * jQuery Form Plugin
3 * version: 2.52 (07-DEC-2010)
4 * @requires jQuery v1.3.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 (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(e) {
22 e.preventDefault(); // <-- important
23 $(this).ajaxSubmit({
24 target: '#output'
25 });
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
57 var action = this.attr('action');
58 var url = (typeof action === 'string') ? $.trim(action) : '';
59 if (url) {
60 // clean url (don't include hash vaue)
61 url = (url.match(/^([^#]+)/)||[])[1];
62 }
63 url = url || window.location.href || '';
64
65 options = $.extend(true, {
66 url: url,
67 type: this.attr('method') || 'GET',
68 iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank'
69 }, options);
70
71 // hook for manipulating the form data before it is extracted;
72 // convenient for use with rich editors like tinyMCE or FCKEditor
73 var veto = {};
74 this.trigger('form-pre-serialize', [this, options, veto]);
75 if (veto.veto) {
76 log('ajaxSubmit: submit vetoed via form-pre-serialize trigger');
77 return this;
78 }
79
80 // provide opportunity to alter form data before it is serialized
81 if (options.beforeSerialize && options.beforeSerialize(this, options) === false) {
82 log('ajaxSubmit: submit aborted via beforeSerialize callback');
83 return this;
84 }
85
86 var n,v,a = this.formToArray(options.semantic);
87 if (options.data) {
88 options.extraData = options.data;
89 for (n in options.data) {
90 if(options.data[n] instanceof Array) {
91 for (var k in options.data[n]) {
92 a.push( { name: n, value: options.data[n][k] } );
93 }
94 }
95 else {
96 v = options.data[n];
97 v = $.isFunction(v) ? v() : v; // if value is fn, invoke it
98 a.push( { name: n, value: v } );
99 }
100 }
101 }
102
103 // give pre-submit callback an opportunity to abort the submit
104 if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) {
105 log('ajaxSubmit: submit aborted via beforeSubmit callback');
106 return this;
107 }
108
109 // fire vetoable 'validate' event
110 this.trigger('form-submit-validate', [a, this, options, veto]);
111 if (veto.veto) {
112 log('ajaxSubmit: submit vetoed via form-submit-validate trigger');
113 return this;
114 }
115
116 var q = $.param(a);
117
118 if (options.type.toUpperCase() == 'GET') {
119 options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
120 options.data = null; // data is null for 'get'
121 }
122 else {
123 options.data = q; // data is the query string for 'post'
124 }
125
126 var $form = this, callbacks = [];
127 if (options.resetForm) {
128 callbacks.push(function() { $form.resetForm(); });
129 }
130 if (options.clearForm) {
131 callbacks.push(function() { $form.clearForm(); });
132 }
133
134 // perform a load on the target only if dataType is not provided
135 if (!options.dataType && options.target) {
136 var oldSuccess = options.success || function(){};
137 callbacks.push(function(data) {
138 var fn = options.replaceTarget ? 'replaceWith' : 'html';
139 $(options.target)[fn](data).each(oldSuccess, arguments);
140 });
141 }
142 else if (options.success) {
143 callbacks.push(options.success);
144 }
145
146 options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg
147 var context = options.context || options; // jQuery 1.4+ supports scope context
148 for (var i=0, max=callbacks.length; i < max; i++) {
149 callbacks[i].apply(context, [data, status, xhr || $form, $form]);
150 }
151 };
152
153 // are there files to upload?
154 var fileInputs = $('input:file', this).length > 0;
155 var mp = 'multipart/form-data';
156 var multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp);
157
158 // options.iframe allows user to force iframe mode
159 // 06-NOV-09: now defaulting to iframe mode if file input is detected
160 if (options.iframe !== false && (fileInputs || options.iframe || multipart)) {
161 // hack to fix Safari hang (thanks to Tim Molendijk for this)
162 // see: http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
163 if (options.closeKeepAlive) {
164 $.get(options.closeKeepAlive, fileUpload);
165 }
166 else {
167 fileUpload();
168 }
169 }
170 else {
171 $.ajax(options);
172 }
173
174 // fire 'notify' event
175 this.trigger('form-submit-notify', [this, options]);
176 return this;
177
178
179 // private function for handling file uploads (hat tip to YAHOO!)
180 function fileUpload() {
181 var form = $form[0];
182
183 if ($(':input[name=submit],:input[id=submit]', form).length) {
184 // if there is an input with a name or id of 'submit' then we won't be
185 // able to invoke the submit fn on the form (at least not x-browser)
186 alert('Error: Form elements must not have name or id of "submit".');
187 return;
188 }
189
190 var s = $.extend(true, {}, $.ajaxSettings, options);
191 s.context = s.context || s;
192 var id = 'jqFormIO' + (new Date().getTime()), fn = '_'+id;
193 window[fn] = function() {
194 var f = $io.data('form-plugin-onload');
195 if (f) {
196 f();
197 window[fn] = undefined;
198 try { delete window[fn]; } catch(e){}
199 }
200 };
201 var $io = $('<iframe id="' + id + '" name="' + id + '" src="'+ s.iframeSrc +'" onload="window[\'_\'+this.id]()" />');
202 var io = $io[0];
203
204 $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });
205
206 var xhr = { // mock object
207 aborted: 0,
208 responseText: null,
209 responseXML: null,
210 status: 0,
211 statusText: 'n/a',
212 getAllResponseHeaders: function() {},
213 getResponseHeader: function() {},
214 setRequestHeader: function() {},
215 abort: function() {
216 this.aborted = 1;
217 $io.attr('src', s.iframeSrc); // abort op in progress
218 }
219 };
220
221 var g = s.global;
222 // trigger ajax global events so that activity/block indicators work like normal
223 if (g && ! $.active++) {
224 $.event.trigger("ajaxStart");
225 }
226 if (g) {
227 $.event.trigger("ajaxSend", [xhr, s]);
228 }
229
230 if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) {
231 if (s.global) {
232 $.active--;
233 }
234 return;
235 }
236 if (xhr.aborted) {
237 return;
238 }
239
240 var cbInvoked = false;
241 var timedOut = 0;
242
243 // add submitting element to data if we know it
244 var sub = form.clk;
245 if (sub) {
246 var n = sub.name;
247 if (n && !sub.disabled) {
248 s.extraData = s.extraData || {};
249 s.extraData[n] = sub.value;
250 if (sub.type == "image") {
251 s.extraData[n+'.x'] = form.clk_x;
252 s.extraData[n+'.y'] = form.clk_y;
253 }
254 }
255 }
256
257 // take a breath so that pending repaints get some cpu time before the upload starts
258 function doSubmit() {
259 // make sure form attrs are set
260 var t = $form.attr('target'), a = $form.attr('action');
261
262 // update form attrs in IE friendly way
263 form.setAttribute('target',id);
264 if (form.getAttribute('method') != 'POST') {
265 form.setAttribute('method', 'POST');
266 }
267 if (form.getAttribute('action') != s.url) {
268 form.setAttribute('action', s.url);
269 }
270
271 // ie borks in some cases when setting encoding
272 if (! s.skipEncodingOverride) {
273 $form.attr({
274 encoding: 'multipart/form-data',
275 enctype: 'multipart/form-data'
276 });
277 }
278
279 // support timout
280 if (s.timeout) {
281 setTimeout(function() { timedOut = true; cb(); }, s.timeout);
282 }
283
284 // add "extra" data to form if provided in options
285 var extraInputs = [];
286 try {
287 if (s.extraData) {
288 for (var n in s.extraData) {
289 extraInputs.push(
290 $('<input type="hidden" name="'+n+'" value="'+s.extraData[n]+'" />')
291 .appendTo(form)[0]);
292 }
293 }
294
295 // add iframe to doc and submit the form
296 $io.appendTo('body');
297 $io.data('form-plugin-onload', cb);
298 form.submit();
299 }
300 finally {
301 // reset attrs and remove "extra" input elements
302 form.setAttribute('action',a);
303 if(t) {
304 form.setAttribute('target', t);
305 } else {
306 $form.removeAttr('target');
307 }
308 $(extraInputs).remove();
309 }
310 }
311
312 if (s.forceSync) {
313 doSubmit();
314 }
315 else {
316 setTimeout(doSubmit, 10); // this lets dom updates render
317 }
318
319 var data, doc, domCheckCount = 50;
320
321 function cb() {
322 if (cbInvoked) {
323 return;
324 }
325
326 $io.removeData('form-plugin-onload');
327
328 var ok = true;
329 try {
330 if (timedOut) {
331 throw 'timeout';
332 }
333 // extract the server response from the iframe
334 doc = io.contentWindow ? io.contentWindow.document : io.contentDocument ? io.contentDocument : io.document;
335
336 var isXml = s.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc);
337 log('isXml='+isXml);
338 if (!isXml && window.opera && (doc.body == null || doc.body.innerHTML == '')) {
339 if (--domCheckCount) {
340 // in some browsers (Opera) the iframe DOM is not always traversable when
341 // the onload callback fires, so we loop a bit to accommodate
342 log('requeing onLoad callback, DOM not available');
343 setTimeout(cb, 250);
344 return;
345 }
346 // let this fall through because server response could be an empty document
347 //log('Could not access iframe DOM after mutiple tries.');
348 //throw 'DOMException: not available';
349 }
350
351 //log('response detected');
352 cbInvoked = true;
353 xhr.responseText = doc.documentElement ? doc.documentElement.innerHTML : null;
354 xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
355 xhr.getResponseHeader = function(header){
356 var headers = {'content-type': s.dataType};
357 return headers[header];
358 };
359
360 var scr = /(json|script)/.test(s.dataType);
361 if (scr || s.textarea) {
362 // see if user embedded response in textarea
363 var ta = doc.getElementsByTagName('textarea')[0];
364 if (ta) {
365 xhr.responseText = ta.value;
366 }
367 else if (scr) {
368 // account for browsers injecting pre around json response
369 var pre = doc.getElementsByTagName('pre')[0];
370 var b = doc.getElementsByTagName('body')[0];
371 if (pre) {
372 xhr.responseText = pre.textContent;
373 }
374 else if (b) {
375 xhr.responseText = b.innerHTML;
376 }
377 }
378 }
379 else if (s.dataType == 'xml' && !xhr.responseXML && xhr.responseText != null) {
380 xhr.responseXML = toXml(xhr.responseText);
381 }
382 data = $.httpData(xhr, s.dataType);
383 }
384 catch(e){
385 log('error caught:',e);
386 ok = false;
387 xhr.error = e;
388 $.handleError(s, xhr, 'error', e);
389 }
390
391 if (xhr.aborted) {
392 log('upload aborted');
393 ok = false;
394 }
395
396 // ordering of these callbacks/triggers is odd, but that's how $.ajax does it
397 if (ok) {
398 s.success.call(s.context, data, 'success', xhr);
399 if (g) {
400 $.event.trigger("ajaxSuccess", [xhr, s]);
401 }
402 }
403 if (g) {
404 $.event.trigger("ajaxComplete", [xhr, s]);
405 }
406 if (g && ! --$.active) {
407 $.event.trigger("ajaxStop");
408 }
409 if (s.complete) {
410 s.complete.call(s.context, xhr, ok ? 'success' : 'error');
411 }
412
413 // clean up
414 setTimeout(function() {
415 $io.removeData('form-plugin-onload');
416 $io.remove();
417 xhr.responseXML = null;
418 }, 100);
419 }
420
421 function toXml(s, doc) {
422 if (window.ActiveXObject) {
423 doc = new ActiveXObject('Microsoft.XMLDOM');
424 doc.async = 'false';
425 doc.loadXML(s);
426 }
427 else {
428 doc = (new DOMParser()).parseFromString(s, 'text/xml');
429 }
430 return (doc && doc.documentElement && doc.documentElement.tagName != 'parsererror') ? doc : null;
431 }
432 }
433 };
434
435 /**
436 * ajaxForm() provides a mechanism for fully automating form submission.
437 *
438 * The advantages of using this method instead of ajaxSubmit() are:
439 *
440 * 1: This method will include coordinates for <input type="image" /> elements (if the element
441 * is used to submit the form).
442 * 2. This method will include the submit element's name/value data (for the element that was
443 * used to submit the form).
444 * 3. This method binds the submit() method to the form for you.
445 *
446 * The options argument for ajaxForm works exactly as it does for ajaxSubmit. ajaxForm merely
447 * passes the options argument along after properly binding events for submit elements and
448 * the form itself.
449 */
450 $.fn.ajaxForm = function(options) {
451 // in jQuery 1.3+ we can fix mistakes with the ready state
452 if (this.length === 0) {
453 var o = { s: this.selector, c: this.context };
454 if (!$.isReady && o.s) {
455 log('DOM not ready, queuing ajaxForm');
456 $(function() {
457 $(o.s,o.c).ajaxForm(options);
458 });
459 return this;
460 }
461 // is your DOM ready? http://docs.jquery.com/Tutorials:Introducing_$(document).ready()
462 log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)'));
463 return this;
464 }
465
466 return this.ajaxFormUnbind().bind('submit.form-plugin', function(e) {
467 if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed
468 e.preventDefault();
469 $(this).ajaxSubmit(options);
470 }
471 }).bind('click.form-plugin', function(e) {
472 var target = e.target;
473 var $el = $(target);
474 if (!($el.is(":submit,input:image"))) {
475 // is this a child element of the submit el? (ex: a span within a button)
476 var t = $el.closest(':submit');
477 if (t.length == 0) {
478 return;
479 }
480 target = t[0];
481 }
482 var form = this;
483 form.clk = target;
484 if (target.type == 'image') {
485 if (e.offsetX != undefined) {
486 form.clk_x = e.offsetX;
487 form.clk_y = e.offsetY;
488 } else if (typeof $.fn.offset == 'function') { // try to use dimensions plugin
489 var offset = $el.offset();
490 form.clk_x = e.pageX - offset.left;
491 form.clk_y = e.pageY - offset.top;
492 } else {
493 form.clk_x = e.pageX - target.offsetLeft;
494 form.clk_y = e.pageY - target.offsetTop;
495 }
496 }
497 // clear form vars
498 setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 100);
499 });
500 };
501
502 // ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
503 $.fn.ajaxFormUnbind = function() {
504 return this.unbind('submit.form-plugin click.form-plugin');
505 };
506
507 /**
508 * formToArray() gathers form element data into an array of objects that can
509 * be passed to any of the following ajax functions: $.get, $.post, or load.
510 * Each object in the array has both a 'name' and 'value' property. An example of
511 * an array for a simple login form might be:
512 *
513 * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ]
514 *
515 * It is this array that is passed to pre-submit callback functions provided to the
516 * ajaxSubmit() and ajaxForm() methods.
517 */
518 $.fn.formToArray = function(semantic) {
519 var a = [];
520 if (this.length === 0) {
521 return a;
522 }
523
524 var form = this[0];
525 var els = semantic ? form.getElementsByTagName('*') : form.elements;
526 if (!els) {
527 return a;
528 }
529
530 var i,j,n,v,el,max,jmax;
531 for(i=0, max=els.length; i < max; i++) {
532 el = els[i];
533 n = el.name;
534 if (!n) {
535 continue;
536 }
537
538 if (semantic && form.clk && el.type == "image") {
539 // handle image inputs on the fly when semantic == true
540 if(!el.disabled && form.clk == el) {
541 a.push({name: n, value: $(el).val()});
542 a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
543 }
544 continue;
545 }
546
547 v = $.fieldValue(el, true);
548 if (v && v.constructor == Array) {
549 for(j=0, jmax=v.length; j < jmax; j++) {
550 a.push({name: n, value: v[j]});
551 }
552 }
553 else if (v !== null && typeof v != 'undefined') {
554 a.push({name: n, value: v});
555 }
556 }
557
558 if (!semantic && form.clk) {
559 // input type=='image' are not found in elements array! handle it here
560 var $input = $(form.clk), input = $input[0];
561 n = input.name;
562 if (n && !input.disabled && input.type == 'image') {
563 a.push({name: n, value: $input.val()});
564 a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
565 }
566 }
567 return a;
568 };
569
570 /**
571 * Serializes form data into a 'submittable' string. This method will return a string
572 * in the format: name1=value1&amp;name2=value2
573 */
574 $.fn.formSerialize = function(semantic) {
575 //hand off to jQuery.param for proper encoding
576 return $.param(this.formToArray(semantic));
577 };
578
579 /**
580 * Serializes all field elements in the jQuery object into a query string.
581 * This method will return a string in the format: name1=value1&amp;name2=value2
582 */
583 $.fn.fieldSerialize = function(successful) {
584 var a = [];
585 this.each(function() {
586 var n = this.name;
587 if (!n) {
588 return;
589 }
590 var v = $.fieldValue(this, successful);
591 if (v && v.constructor == Array) {
592 for (var i=0,max=v.length; i < max; i++) {
593 a.push({name: n, value: v[i]});
594 }
595 }
596 else if (v !== null && typeof v != 'undefined') {
597 a.push({name: this.name, value: v});
598 }
599 });
600 //hand off to jQuery.param for proper encoding
601 return $.param(a);
602 };
603
604 /**
605 * Returns the value(s) of the element in the matched set. For example, consider the following form:
606 *
607 * <form><fieldset>
608 * <input name="A" type="text" />
609 * <input name="A" type="text" />
610 * <input name="B" type="checkbox" value="B1" />
611 * <input name="B" type="checkbox" value="B2"/>
612 * <input name="C" type="radio" value="C1" />
613 * <input name="C" type="radio" value="C2" />
614 * </fieldset></form>
615 *
616 * var v = $(':text').fieldValue();
617 * // if no values are entered into the text inputs
618 * v == ['','']
619 * // if values entered into the text inputs are 'foo' and 'bar'
620 * v == ['foo','bar']
621 *
622 * var v = $(':checkbox').fieldValue();
623 * // if neither checkbox is checked
624 * v === undefined
625 * // if both checkboxes are checked
626 * v == ['B1', 'B2']
627 *
628 * var v = $(':radio').fieldValue();
629 * // if neither radio is checked
630 * v === undefined
631 * // if first radio is checked
632 * v == ['C1']
633 *
634 * The successful argument controls whether or not the field element must be 'successful'
635 * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls).
636 * The default value of the successful argument is true. If this value is false the value(s)
637 * for each element is returned.
638 *
639 * Note: This method *always* returns an array. If no valid value can be determined the
640 * array will be empty, otherwise it will contain one or more values.
641 */
642 $.fn.fieldValue = function(successful) {
643 for (var val=[], i=0, max=this.length; i < max; i++) {
644 var el = this[i];
645 var v = $.fieldValue(el, successful);
646 if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) {
647 continue;
648 }
649 v.constructor == Array ? $.merge(val, v) : val.push(v);
650 }
651 return val;
652 };
653
654 /**
655 * Returns the value of the field element.
656 */
657 $.fieldValue = function(el, successful) {
658 var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
659 if (successful === undefined) {
660 successful = true;
661 }
662
663 if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
664 (t == 'checkbox' || t == 'radio') && !el.checked ||
665 (t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
666 tag == 'select' && el.selectedIndex == -1)) {
667 return null;
668 }
669
670 if (tag == 'select') {
671 var index = el.selectedIndex;
672 if (index < 0) {
673 return null;
674 }
675 var a = [], ops = el.options;
676 var one = (t == 'select-one');
677 var max = (one ? index+1 : ops.length);
678 for(var i=(one ? index : 0); i < max; i++) {
679 var op = ops[i];
680 if (op.selected) {
681 var v = op.value;
682 if (!v) { // extra pain for IE...
683 v = (op.attributes && op.attributes['value'] && !(op.attributes['value'].specified)) ? op.text : op.value;
684 }
685 if (one) {
686 return v;
687 }
688 a.push(v);
689 }
690 }
691 return a;
692 }
693 return $(el).val();
694 };
695
696 /**
697 * Clears the form data. Takes the following actions on the form's input fields:
698 * - input text fields will have their 'value' property set to the empty string
699 * - select elements will have their 'selectedIndex' property set to -1
700 * - checkbox and radio inputs will have their 'checked' property set to false
701 * - inputs of type submit, button, reset, and hidden will *not* be effected
702 * - button elements will *not* be effected
703 */
704 $.fn.clearForm = function() {
705 return this.each(function() {
706 $('input,select,textarea', this).clearFields();
707 });
708 };
709
710 /**
711 * Clears the selected form elements.
712 */
713 $.fn.clearFields = $.fn.clearInputs = function() {
714 return this.each(function() {
715 var t = this.type, tag = this.tagName.toLowerCase();
716 if (t == 'text' || t == 'password' || tag == 'textarea') {
717 this.value = '';
718 }
719 else if (t == 'checkbox' || t == 'radio') {
720 this.checked = false;
721 }
722 else if (tag == 'select') {
723 this.selectedIndex = -1;
724 }
725 });
726 };
727
728 /**
729 * Resets the form data. Causes all form elements to be reset to their original value.
730 */
731 $.fn.resetForm = function() {
732 return this.each(function() {
733 // guard against an input with the name of 'reset'
734 // note that IE reports the reset function as an 'object'
735 if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType)) {
736 this.reset();
737 }
738 });
739 };
740
741 /**
742 * Enables or disables any matching elements.
743 */
744 $.fn.enable = function(b) {
745 if (b === undefined) {
746 b = true;
747 }
748 return this.each(function() {
749 this.disabled = !b;
750 });
751 };
752
753 /**
754 * Checks/unchecks any matching checkboxes or radio buttons and
755 * selects/deselects and matching option elements.
756 */
757 $.fn.selected = function(select) {
758 if (select === undefined) {
759 select = true;
760 }
761 return this.each(function() {
762 var t = this.type;
763 if (t == 'checkbox' || t == 'radio') {
764 this.checked = select;
765 }
766 else if (this.tagName.toLowerCase() == 'option') {
767 var $sel = $(this).parent('select');
768 if (select && $sel[0] && $sel[0].type == 'select-one') {
769 // deselect all other options
770 $sel.find('option').selected(false);
771 }
772 this.selected = select;
773 }
774 });
775 };
776
777 // helper fn for console logging
778 // set $.fn.ajaxSubmit.debug to true to enable debug logging
779 function log() {
780 if ($.fn.ajaxSubmit.debug) {
781 var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,'');
782 if (window.console && window.console.log) {
783 window.console.log(msg);
784 }
785 else if (window.opera && window.opera.postError) {
786 window.opera.postError(msg);
787 }
788 }
789 };
790
791 })(jQuery);