Projet

Général

Profil

Paste
Télécharger (23,2 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / jquery_update / replace / misc / jquery.form.js @ 503b3f7b

1
/*!
2
 * jQuery Form Plugin
3
 * version: 2.69 (06-APR-2011)
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
                success: $.ajaxSettings.success,
68
                type: this[0].getAttribute('method') || 'GET', // IE7 massage (see issue 57)
69
                iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank'
70
        }, options);
71

    
72
        // hook for manipulating the form data before it is extracted;
73
        // convenient for use with rich editors like tinyMCE or FCKEditor
74
        var veto = {};
75
        this.trigger('form-pre-serialize', [this, options, veto]);
76
        if (veto.veto) {
77
                log('ajaxSubmit: submit vetoed via form-pre-serialize trigger');
78
                return this;
79
        }
80

    
81
        // provide opportunity to alter form data before it is serialized
82
        if (options.beforeSerialize && options.beforeSerialize(this, options) === false) {
83
                log('ajaxSubmit: submit aborted via beforeSerialize callback');
84
                return this;
85
        }
86

    
87
        var n,v,a = this.formToArray(options.semantic);
88
        if (options.data) {
89
                options.extraData = options.data;
90
                for (n in options.data) {
91
                        if(options.data[n] instanceof Array) {
92
                                for (var k in options.data[n]) {
93
                                        a.push( { name: n, value: options.data[n][k] } );
94
                                }
95
                        }
96
                        else {
97
                                v = options.data[n];
98
                                v = $.isFunction(v) ? v() : v; // if value is fn, invoke it
99
                                a.push( { name: n, value: v } );
100
                        }
101
                }
102
        }
103

    
104
        // give pre-submit callback an opportunity to abort the submit
105
        if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) {
106
                log('ajaxSubmit: submit aborted via beforeSubmit callback');
107
                return this;
108
        }
109

    
110
        // fire vetoable 'validate' event
111
        this.trigger('form-submit-validate', [a, this, options, veto]);
112
        if (veto.veto) {
113
                log('ajaxSubmit: submit vetoed via form-submit-validate trigger');
114
                return this;
115
        }
116

    
117
        var q = $.param(a);
118

    
119
        if (options.type.toUpperCase() == 'GET') {
120
                options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
121
                options.data = null;  // data is null for 'get'
122
        }
123
        else {
124
                options.data = q; // data is the query string for 'post'
125
        }
126

    
127
        var $form = this, callbacks = [];
128
        if (options.resetForm) {
129
                callbacks.push(function() { $form.resetForm(); });
130
        }
131
        if (options.clearForm) {
132
                callbacks.push(function() { $form.clearForm(); });
133
        }
134

    
135
        // perform a load on the target only if dataType is not provided
136
        if (!options.dataType && options.target) {
137
                var oldSuccess = options.success || function(){};
138
                callbacks.push(function(data) {
139
                        var fn = options.replaceTarget ? 'replaceWith' : 'html';
140
                        $(options.target)[fn](data).each(oldSuccess, arguments);
141
                });
142
        }
143
        else if (options.success) {
144
                callbacks.push(options.success);
145
        }
146

    
147
        options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg
148
                var context = options.context || options;   // jQuery 1.4+ supports scope context 
149
                for (var i=0, max=callbacks.length; i < max; i++) {
150
                        callbacks[i].apply(context, [data, status, xhr || $form, $form]);
151
                }
152
        };
153

    
154
        // are there files to upload?
155
        var fileInputs = $('input:file', this).length > 0;
156
        var mp = 'multipart/form-data';
157
        var multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp);
158

    
159
        // options.iframe allows user to force iframe mode
160
        // 06-NOV-09: now defaulting to iframe mode if file input is detected
161
   if (options.iframe !== false && (fileInputs || options.iframe || multipart)) {
162
           // hack to fix Safari hang (thanks to Tim Molendijk for this)
163
           // see:  http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
164
           if (options.closeKeepAlive) {
165
                   $.get(options.closeKeepAlive, fileUpload);
166
                }
167
           else {
168
                   fileUpload();
169
                }
170
   }
171
   else {
172
                $.ajax(options);
173
   }
174

    
175
        // fire 'notify' event
176
        this.trigger('form-submit-notify', [this, options]);
177
        return this;
178

    
179

    
180
        // private function for handling file uploads (hat tip to YAHOO!)
181
        function fileUpload() {
182
                var form = $form[0];
183

    
184
                if ($(':input[name=submit],:input[id=submit]', form).length) {
185
                        // if there is an input with a name or id of 'submit' then we won't be
186
                        // able to invoke the submit fn on the form (at least not x-browser)
187
                        alert('Error: Form elements must not have name or id of "submit".');
188
                        return;
189
                }
190
                
191
                var s = $.extend(true, {}, $.ajaxSettings, options);
192
                s.context = s.context || s;
193
                var id = 'jqFormIO' + (new Date().getTime()), fn = '_'+id;
194
                var $io = $('<iframe id="' + id + '" name="' + id + '" src="'+ s.iframeSrc +'" />');
195
                var io = $io[0];
196

    
197
                $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });
198

    
199
                var xhr = { // mock object
200
                        aborted: 0,
201
                        responseText: null,
202
                        responseXML: null,
203
                        status: 0,
204
                        statusText: 'n/a',
205
                        getAllResponseHeaders: function() {},
206
                        getResponseHeader: function() {},
207
                        setRequestHeader: function() {},
208
                        abort: function() {
209
                                log('aborting upload...');
210
                                var e = 'aborted';
211
                                this.aborted = 1;
212
                                $io.attr('src', s.iframeSrc); // abort op in progress
213
                                xhr.error = e;
214
                                s.error && s.error.call(s.context, xhr, 'error', e);
215
                                g && $.event.trigger("ajaxError", [xhr, s, e]);
216
                                s.complete && s.complete.call(s.context, xhr, 'error');
217
                        }
218
                };
219

    
220
                var g = s.global;
221
                // trigger ajax global events so that activity/block indicators work like normal
222
                if (g && ! $.active++) {
223
                        $.event.trigger("ajaxStart");
224
                }
225
                if (g) {
226
                        $.event.trigger("ajaxSend", [xhr, s]);
227
                }
228

    
229
                if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) {
230
                        if (s.global) { 
231
                                $.active--;
232
                        }
233
                        return;
234
                }
235
                if (xhr.aborted) {
236
                        return;
237
                }
238

    
239
                var timedOut = 0;
240

    
241
                // add submitting element to data if we know it
242
                var sub = form.clk;
243
                if (sub) {
244
                        var n = sub.name;
245
                        if (n && !sub.disabled) {
246
                                s.extraData = s.extraData || {};
247
                                s.extraData[n] = sub.value;
248
                                if (sub.type == "image") {
249
                                        s.extraData[n+'.x'] = form.clk_x;
250
                                        s.extraData[n+'.y'] = form.clk_y;
251
                                }
252
                        }
253
                }
254

    
255
                // take a breath so that pending repaints get some cpu time before the upload starts
256
                function doSubmit() {
257
                        // make sure form attrs are set
258
                        var t = $form.attr('target'), a = $form.attr('action');
259

    
260
                        // update form attrs in IE friendly way
261
                        form.setAttribute('target',id);
262
                        if (form.getAttribute('method') != 'POST') {
263
                                form.setAttribute('method', 'POST');
264
                        }
265
                        if (form.getAttribute('action') != s.url) {
266
                                form.setAttribute('action', s.url);
267
                        }
268

    
269
                        // ie borks in some cases when setting encoding
270
                        if (! s.skipEncodingOverride) {
271
                                $form.attr({
272
                                        encoding: 'multipart/form-data',
273
                                        enctype:  'multipart/form-data'
274
                                });
275
                        }
276

    
277
                        // support timout
278
                        if (s.timeout) {
279
                                setTimeout(function() { timedOut = true; cb(); }, s.timeout);
280
                        }
281

    
282
                        // add "extra" data to form if provided in options
283
                        var extraInputs = [];
284
                        try {
285
                                if (s.extraData) {
286
                                        for (var n in s.extraData) {
287
                                                extraInputs.push(
288
                                                        $('<input type="hidden" name="'+n+'" value="'+s.extraData[n]+'" />')
289
                                                                .appendTo(form)[0]);
290
                                        }
291
                                }
292

    
293
                                // add iframe to doc and submit the form
294
                                $io.appendTo('body');
295
                io.attachEvent ? io.attachEvent('onload', cb) : io.addEventListener('load', cb, false);
296
                                form.submit();
297
                        }
298
                        finally {
299
                                // reset attrs and remove "extra" input elements
300
                                form.setAttribute('action',a);
301
                                if(t) {
302
                                        form.setAttribute('target', t);
303
                                } else {
304
                                        $form.removeAttr('target');
305
                                }
306
                                $(extraInputs).remove();
307
                        }
308
                }
309

    
310
                if (s.forceSync) {
311
                        doSubmit();
312
                }
313
                else {
314
                        setTimeout(doSubmit, 10); // this lets dom updates render
315
                }
316
        
317
                var data, doc, domCheckCount = 50;
318

    
319
                function cb() {
320
                        if (xhr.aborted) {
321
                                return;
322
                        }
323
                        
324
                        var doc = io.contentWindow ? io.contentWindow.document : io.contentDocument ? io.contentDocument : io.document;
325
                        if (!doc || doc.location.href == s.iframeSrc) {
326
                                // response not received yet
327
                                if (!timedOut)
328
                                        return;
329
                        }
330
            io.detachEvent ? io.detachEvent('onload', cb) : io.removeEventListener('load', cb, false);
331

    
332
                        var ok = true;
333
                        try {
334
                                if (timedOut) {
335
                                        throw 'timeout';
336
                                }
337

    
338
                                var isXml = s.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc);
339
                                log('isXml='+isXml);
340
                                if (!isXml && window.opera && (doc.body == null || doc.body.innerHTML == '')) {
341
                                        if (--domCheckCount) {
342
                                                // in some browsers (Opera) the iframe DOM is not always traversable when
343
                                                // the onload callback fires, so we loop a bit to accommodate
344
                                                log('requeing onLoad callback, DOM not available');
345
                                                setTimeout(cb, 250);
346
                                                return;
347
                                        }
348
                                        // let this fall through because server response could be an empty document
349
                                        //log('Could not access iframe DOM after mutiple tries.');
350
                                        //throw 'DOMException: not available';
351
                                }
352

    
353
                                //log('response detected');
354
                                xhr.responseText = doc.body ? doc.body.innerHTML : doc.documentElement ? doc.documentElement.innerHTML : null; 
355
                                xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
356
                                xhr.getResponseHeader = function(header){
357
                                        var headers = {'content-type': s.dataType};
358
                                        return headers[header];
359
                                };
360

    
361
                                var scr = /(json|script)/.test(s.dataType);
362
                                if (scr || s.textarea) {
363
                                        // see if user embedded response in textarea
364
                                        var ta = doc.getElementsByTagName('textarea')[0];
365
                                        if (ta) {
366
                                                xhr.responseText = ta.value;
367
                                        }
368
                                        else if (scr) {
369
                                                // account for browsers injecting pre around json response
370
                                                var pre = doc.getElementsByTagName('pre')[0];
371
                                                var b = doc.getElementsByTagName('body')[0];
372
                                                if (pre) {
373
                                                        xhr.responseText = pre.textContent;
374
                                                }
375
                                                else if (b) {
376
                                                        xhr.responseText = b.innerHTML;
377
                                                }
378
                                        }                          
379
                                }
380
                                else if (s.dataType == 'xml' && !xhr.responseXML && xhr.responseText != null) {
381
                                        xhr.responseXML = toXml(xhr.responseText);
382
                                }
383
                                
384
                                data = httpData(xhr, s.dataType, s);
385
                        }
386
                        catch(e){
387
                                log('error caught:',e);
388
                                ok = false;
389
                                xhr.error = e;
390
                                s.error && s.error.call(s.context, xhr, 'error', e);
391
                                g && $.event.trigger("ajaxError", [xhr, s, e]);
392
                        }
393
                        
394
                        if (xhr.aborted) {
395
                                log('upload aborted');
396
                                ok = false;
397
                        }
398

    
399
                        // ordering of these callbacks/triggers is odd, but that's how $.ajax does it
400
                        if (ok) {
401
                                s.success && s.success.call(s.context, data, 'success', xhr);
402
                                g && $.event.trigger("ajaxSuccess", [xhr, s]);
403
                        }
404
                        
405
                        g && $.event.trigger("ajaxComplete", [xhr, s]);
406

    
407
                        if (g && ! --$.active) {
408
                                $.event.trigger("ajaxStop");
409
                        }
410
                        
411
                        s.complete && s.complete.call(s.context, xhr, ok ? 'success' : 'error');
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
                var toXml = $.parseXML || function(s, doc) { // use parseXML if available (jQuery 1.5+)
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.nodeName != 'parsererror') ? doc : null;
431
                };
432
                var parseJSON = $.parseJSON || function(s) {
433
                        return window['eval']('(' + s + ')');
434
                };
435
                
436
                var httpData = function( xhr, type, s ) { // mostly lifted from jq1.4.4
437
                        var ct = xhr.getResponseHeader('content-type') || '',
438
                                xml = type === 'xml' || !type && ct.indexOf('xml') >= 0,
439
                                data = xml ? xhr.responseXML : xhr.responseText;
440

    
441
                        if (xml && data.documentElement.nodeName === 'parsererror') {
442
                                $.error && $.error('parsererror');
443
                        }
444
                        if (s && s.dataFilter) {
445
                                data = s.dataFilter(data, type);
446
                        }
447
                        if (typeof data === 'string') {
448
                                if (type === 'json' || !type && ct.indexOf('json') >= 0) {
449
                                        data = parseJSON(data);
450
                                } else if (type === "script" || !type && ct.indexOf("javascript") >= 0) {
451
                                        $.globalEval(data);
452
                                }
453
                        }
454
                        return data;
455
                };
456
        }
457
};
458

    
459
/**
460
 * ajaxForm() provides a mechanism for fully automating form submission.
461
 *
462
 * The advantages of using this method instead of ajaxSubmit() are:
463
 *
464
 * 1: This method will include coordinates for <input type="image" /> elements (if the element
465
 *        is used to submit the form).
466
 * 2. This method will include the submit element's name/value data (for the element that was
467
 *        used to submit the form).
468
 * 3. This method binds the submit() method to the form for you.
469
 *
470
 * The options argument for ajaxForm works exactly as it does for ajaxSubmit.  ajaxForm merely
471
 * passes the options argument along after properly binding events for submit elements and
472
 * the form itself.
473
 */
474
$.fn.ajaxForm = function(options) {
475
        // in jQuery 1.3+ we can fix mistakes with the ready state
476
        if (this.length === 0) {
477
                var o = { s: this.selector, c: this.context };
478
                if (!$.isReady && o.s) {
479
                        log('DOM not ready, queuing ajaxForm');
480
                        $(function() {
481
                                $(o.s,o.c).ajaxForm(options);
482
                        });
483
                        return this;
484
                }
485
                // is your DOM ready?  http://docs.jquery.com/Tutorials:Introducing_$(document).ready()
486
                log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)'));
487
                return this;
488
        }
489
        
490
        return this.ajaxFormUnbind().bind('submit.form-plugin', function(e) {
491
                if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed
492
                        e.preventDefault();
493
                        $(this).ajaxSubmit(options);
494
                }
495
        }).bind('click.form-plugin', function(e) {
496
                var target = e.target;
497
                var $el = $(target);
498
                if (!($el.is(":submit,input:image"))) {
499
                        // is this a child element of the submit el?  (ex: a span within a button)
500
                        var t = $el.closest(':submit');
501
                        if (t.length == 0) {
502
                                return;
503
                        }
504
                        target = t[0];
505
                }
506
                var form = this;
507
                form.clk = target;
508
                if (target.type == 'image') {
509
                        if (e.offsetX != undefined) {
510
                                form.clk_x = e.offsetX;
511
                                form.clk_y = e.offsetY;
512
                        } else if (typeof $.fn.offset == 'function') { // try to use dimensions plugin
513
                                var offset = $el.offset();
514
                                form.clk_x = e.pageX - offset.left;
515
                                form.clk_y = e.pageY - offset.top;
516
                        } else {
517
                                form.clk_x = e.pageX - target.offsetLeft;
518
                                form.clk_y = e.pageY - target.offsetTop;
519
                        }
520
                }
521
                // clear form vars
522
                setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 100);
523
        });
524
};
525

    
526
// ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
527
$.fn.ajaxFormUnbind = function() {
528
        return this.unbind('submit.form-plugin click.form-plugin');
529
};
530

    
531
/**
532
 * formToArray() gathers form element data into an array of objects that can
533
 * be passed to any of the following ajax functions: $.get, $.post, or load.
534
 * Each object in the array has both a 'name' and 'value' property.  An example of
535
 * an array for a simple login form might be:
536
 *
537
 * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ]
538
 *
539
 * It is this array that is passed to pre-submit callback functions provided to the
540
 * ajaxSubmit() and ajaxForm() methods.
541
 */
542
$.fn.formToArray = function(semantic) {
543
        var a = [];
544
        if (this.length === 0) {
545
                return a;
546
        }
547

    
548
        var form = this[0];
549
        var els = semantic ? form.getElementsByTagName('*') : form.elements;
550
        if (!els) {
551
                return a;
552
        }
553
        
554
        var i,j,n,v,el,max,jmax;
555
        for(i=0, max=els.length; i < max; i++) {
556
                el = els[i];
557
                n = el.name;
558
                if (!n) {
559
                        continue;
560
                }
561

    
562
                if (semantic && form.clk && el.type == "image") {
563
                        // handle image inputs on the fly when semantic == true
564
                        if(!el.disabled && form.clk == el) {
565
                                a.push({name: n, value: $(el).val()});
566
                                a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
567
                        }
568
                        continue;
569
                }
570

    
571
                v = $.fieldValue(el, true);
572
                if (v && v.constructor == Array) {
573
                        for(j=0, jmax=v.length; j < jmax; j++) {
574
                                a.push({name: n, value: v[j]});
575
                        }
576
                }
577
                else if (v !== null && typeof v != 'undefined') {
578
                        a.push({name: n, value: v});
579
                }
580
        }
581

    
582
        if (!semantic && form.clk) {
583
                // input type=='image' are not found in elements array! handle it here
584
                var $input = $(form.clk), input = $input[0];
585
                n = input.name;
586
                if (n && !input.disabled && input.type == 'image') {
587
                        a.push({name: n, value: $input.val()});
588
                        a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
589
                }
590
        }
591
        return a;
592
};
593

    
594
/**
595
 * Serializes form data into a 'submittable' string. This method will return a string
596
 * in the format: name1=value1&amp;name2=value2
597
 */
598
$.fn.formSerialize = function(semantic) {
599
        //hand off to jQuery.param for proper encoding
600
        return $.param(this.formToArray(semantic));
601
};
602

    
603
/**
604
 * Serializes all field elements in the jQuery object into a query string.
605
 * This method will return a string in the format: name1=value1&amp;name2=value2
606
 */
607
$.fn.fieldSerialize = function(successful) {
608
        var a = [];
609
        this.each(function() {
610
                var n = this.name;
611
                if (!n) {
612
                        return;
613
                }
614
                var v = $.fieldValue(this, successful);
615
                if (v && v.constructor == Array) {
616
                        for (var i=0,max=v.length; i < max; i++) {
617
                                a.push({name: n, value: v[i]});
618
                        }
619
                }
620
                else if (v !== null && typeof v != 'undefined') {
621
                        a.push({name: this.name, value: v});
622
                }
623
        });
624
        //hand off to jQuery.param for proper encoding
625
        return $.param(a);
626
};
627

    
628
/**
629
 * Returns the value(s) of the element in the matched set.  For example, consider the following form:
630
 *
631
 *  <form><fieldset>
632
 *          <input name="A" type="text" />
633
 *          <input name="A" type="text" />
634
 *          <input name="B" type="checkbox" value="B1" />
635
 *          <input name="B" type="checkbox" value="B2"/>
636
 *          <input name="C" type="radio" value="C1" />
637
 *          <input name="C" type="radio" value="C2" />
638
 *  </fieldset></form>
639
 *
640
 *  var v = $(':text').fieldValue();
641
 *  // if no values are entered into the text inputs
642
 *  v == ['','']
643
 *  // if values entered into the text inputs are 'foo' and 'bar'
644
 *  v == ['foo','bar']
645
 *
646
 *  var v = $(':checkbox').fieldValue();
647
 *  // if neither checkbox is checked
648
 *  v === undefined
649
 *  // if both checkboxes are checked
650
 *  v == ['B1', 'B2']
651
 *
652
 *  var v = $(':radio').fieldValue();
653
 *  // if neither radio is checked
654
 *  v === undefined
655
 *  // if first radio is checked
656
 *  v == ['C1']
657
 *
658
 * The successful argument controls whether or not the field element must be 'successful'
659
 * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls).
660
 * The default value of the successful argument is true.  If this value is false the value(s)
661
 * for each element is returned.
662
 *
663
 * Note: This method *always* returns an array.  If no valid value can be determined the
664
 *           array will be empty, otherwise it will contain one or more values.
665
 */
666
$.fn.fieldValue = function(successful) {
667
        for (var val=[], i=0, max=this.length; i < max; i++) {
668
                var el = this[i];
669
                var v = $.fieldValue(el, successful);
670
                if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) {
671
                        continue;
672
                }
673
                v.constructor == Array ? $.merge(val, v) : val.push(v);
674
        }
675
        return val;
676
};
677

    
678
/**
679
 * Returns the value of the field element.
680
 */
681
$.fieldValue = function(el, successful) {
682
        var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
683
        if (successful === undefined) {
684
                successful = true;
685
        }
686

    
687
        if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
688
                (t == 'checkbox' || t == 'radio') && !el.checked ||
689
                (t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
690
                tag == 'select' && el.selectedIndex == -1)) {
691
                        return null;
692
        }
693

    
694
        if (tag == 'select') {
695
                var index = el.selectedIndex;
696
                if (index < 0) {
697
                        return null;
698
                }
699
                var a = [], ops = el.options;
700
                var one = (t == 'select-one');
701
                var max = (one ? index+1 : ops.length);
702
                for(var i=(one ? index : 0); i < max; i++) {
703
                        var op = ops[i];
704
                        if (op.selected) {
705
                                var v = op.value;
706
                                if (!v) { // extra pain for IE...
707
                                        v = (op.attributes && op.attributes['value'] && !(op.attributes['value'].specified)) ? op.text : op.value;
708
                                }
709
                                if (one) {
710
                                        return v;
711
                                }
712
                                a.push(v);
713
                        }
714
                }
715
                return a;
716
        }
717
        return $(el).val();
718
};
719

    
720
/**
721
 * Clears the form data.  Takes the following actions on the form's input fields:
722
 *  - input text fields will have their 'value' property set to the empty string
723
 *  - select elements will have their 'selectedIndex' property set to -1
724
 *  - checkbox and radio inputs will have their 'checked' property set to false
725
 *  - inputs of type submit, button, reset, and hidden will *not* be effected
726
 *  - button elements will *not* be effected
727
 */
728
$.fn.clearForm = function() {
729
        return this.each(function() {
730
                $('input,select,textarea', this).clearFields();
731
        });
732
};
733

    
734
/**
735
 * Clears the selected form elements.
736
 */
737
$.fn.clearFields = $.fn.clearInputs = function() {
738
        return this.each(function() {
739
                var t = this.type, tag = this.tagName.toLowerCase();
740
                if (t == 'text' || t == 'password' || tag == 'textarea') {
741
                        this.value = '';
742
                }
743
                else if (t == 'checkbox' || t == 'radio') {
744
                        this.checked = false;
745
                }
746
                else if (tag == 'select') {
747
                        this.selectedIndex = -1;
748
                }
749
        });
750
};
751

    
752
/**
753
 * Resets the form data.  Causes all form elements to be reset to their original value.
754
 */
755
$.fn.resetForm = function() {
756
        return this.each(function() {
757
                // guard against an input with the name of 'reset'
758
                // note that IE reports the reset function as an 'object'
759
                if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType)) {
760
                        this.reset();
761
                }
762
        });
763
};
764

    
765
/**
766
 * Enables or disables any matching elements.
767
 */
768
$.fn.enable = function(b) {
769
        if (b === undefined) {
770
                b = true;
771
        }
772
        return this.each(function() {
773
                this.disabled = !b;
774
        });
775
};
776

    
777
/**
778
 * Checks/unchecks any matching checkboxes or radio buttons and
779
 * selects/deselects and matching option elements.
780
 */
781
$.fn.selected = function(select) {
782
        if (select === undefined) {
783
                select = true;
784
        }
785
        return this.each(function() {
786
                var t = this.type;
787
                if (t == 'checkbox' || t == 'radio') {
788
                        this.checked = select;
789
                }
790
                else if (this.tagName.toLowerCase() == 'option') {
791
                        var $sel = $(this).parent('select');
792
                        if (select && $sel[0] && $sel[0].type == 'select-one') {
793
                                // deselect all other options
794
                                $sel.find('option').selected(false);
795
                        }
796
                        this.selected = select;
797
                }
798
        });
799
};
800

    
801
// helper fn for console logging
802
// set $.fn.ajaxSubmit.debug to true to enable debug logging
803
function log() {
804
        if ($.fn.ajaxSubmit.debug) {
805
                var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,'');
806
                if (window.console && window.console.log) {
807
                        window.console.log(msg);
808
                }
809
                else if (window.opera && window.opera.postError) {
810
                        window.opera.postError(msg);
811
                }
812
        }
813
};
814

    
815
})(jQuery);