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