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