~lss-team/lilsoftstats/trunk

« back to all changes in this revision

Viewing changes to js/jquery/jquery.form.js

  • Committer: Nick
  • Date: 2011-11-14 04:10:28 UTC
  • Revision ID: nick@little-apps.org-20111114041028-cvmpwq6z6hx3pkya
first commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*!
 
2
 * jQuery Form Plugin
 
3
 * version: 2.86 (08-OCT-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
        var method, action, url, $form = this;
 
54
 
 
55
        if (typeof options == 'function') {
 
56
                options = { success: options };
 
57
        }
 
58
 
 
59
        method = this.attr('method');
 
60
        action = this.attr('action');
 
61
        url = (typeof action === 'string') ? $.trim(action) : '';
 
62
        url = url || window.location.href || '';
 
63
        if (url) {
 
64
                // clean url (don't include hash vaue)
 
65
                url = (url.match(/^([^#]+)/)||[])[1];
 
66
        }
 
67
 
 
68
        options = $.extend(true, {
 
69
                url:  url,
 
70
                success: $.ajaxSettings.success,
 
71
                type: method || 'GET',
 
72
                iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank'
 
73
        }, options);
 
74
 
 
75
        // hook for manipulating the form data before it is extracted;
 
76
        // convenient for use with rich editors like tinyMCE or FCKEditor
 
77
        var veto = {};
 
78
        this.trigger('form-pre-serialize', [this, options, veto]);
 
79
        if (veto.veto) {
 
80
                log('ajaxSubmit: submit vetoed via form-pre-serialize trigger');
 
81
                return this;
 
82
        }
 
83
 
 
84
        // provide opportunity to alter form data before it is serialized
 
85
        if (options.beforeSerialize && options.beforeSerialize(this, options) === false) {
 
86
                log('ajaxSubmit: submit aborted via beforeSerialize callback');
 
87
                return this;
 
88
        }
 
89
 
 
90
   var traditional = options.traditional;
 
91
   if ( traditional === undefined ) {
 
92
      traditional = $.ajaxSettings.traditional;
 
93
   }
 
94
   
 
95
        var qx,n,v,a = this.formToArray(options.semantic);
 
96
        if (options.data) {
 
97
                options.extraData = options.data;
 
98
      qx = $.param(options.data, traditional);
 
99
        }
 
100
 
 
101
        // give pre-submit callback an opportunity to abort the submit
 
102
        if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) {
 
103
                log('ajaxSubmit: submit aborted via beforeSubmit callback');
 
104
                return this;
 
105
        }
 
106
 
 
107
        // fire vetoable 'validate' event
 
108
        this.trigger('form-submit-validate', [a, this, options, veto]);
 
109
        if (veto.veto) {
 
110
                log('ajaxSubmit: submit vetoed via form-submit-validate trigger');
 
111
                return this;
 
112
        }
 
113
 
 
114
        var q = $.param(a, traditional);
 
115
   if (qx)
 
116
      q = ( q ? (q + '&' + qx) : qx );
 
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 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, function() { fileUpload(a); });
 
165
                }
 
166
           else {
 
167
                   fileUpload(a);
 
168
                }
 
169
   }
 
170
   else {
 
171
                // IE7 massage (see issue 57)
 
172
                if ($.browser.msie && method == 'get' && typeof options.type === "undefined") {
 
173
                        var ieMeth = $form[0].getAttribute('method');
 
174
                        if (typeof ieMeth === 'string')
 
175
                                options.type = ieMeth;
 
176
                }
 
177
                $.ajax(options);
 
178
   }
 
179
 
 
180
        // fire 'notify' event
 
181
        this.trigger('form-submit-notify', [this, options]);
 
182
        return this;
 
183
 
 
184
 
 
185
        // private function for handling file uploads (hat tip to YAHOO!)
 
186
        function fileUpload(a) {
 
187
                var form = $form[0], el, i, s, g, id, $io, io, xhr, sub, n, timedOut, timeoutHandle;
 
188
        var useProp = !!$.fn.prop;
 
189
 
 
190
        if (a) {
 
191
            if ( useProp ) {
 
192
                // ensure that every serialized input is still enabled
 
193
                for (i=0; i < a.length; i++) {
 
194
                    el = $(form[a[i].name]);
 
195
                    el.prop('disabled', false);
 
196
                }
 
197
            } else {
 
198
                for (i=0; i < a.length; i++) {
 
199
                    el = $(form[a[i].name]);
 
200
                    el.removeAttr('disabled');
 
201
                }
 
202
            };
 
203
        }
 
204
 
 
205
                if ($(':input[name=submit],:input[id=submit]', form).length) {
 
206
                        // if there is an input with a name or id of 'submit' then we won't be
 
207
                        // able to invoke the submit fn on the form (at least not x-browser)
 
208
                        alert('Error: Form elements must not have name or id of "submit".');
 
209
                        return;
 
210
                }
 
211
                
 
212
                s = $.extend(true, {}, $.ajaxSettings, options);
 
213
                s.context = s.context || s;
 
214
                id = 'jqFormIO' + (new Date().getTime());
 
215
                if (s.iframeTarget) {
 
216
                        $io = $(s.iframeTarget);
 
217
                        n = $io.attr('name');
 
218
                        if (n == null)
 
219
                                $io.attr('name', id);
 
220
                        else
 
221
                                id = n;
 
222
                }
 
223
                else {
 
224
                        $io = $('<iframe name="' + id + '" src="'+ s.iframeSrc +'" />');
 
225
                        $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });
 
226
                }
 
227
                io = $io[0];
 
228
 
 
229
 
 
230
                xhr = { // mock object
 
231
                        aborted: 0,
 
232
                        responseText: null,
 
233
                        responseXML: null,
 
234
                        status: 0,
 
235
                        statusText: 'n/a',
 
236
                        getAllResponseHeaders: function() {},
 
237
                        getResponseHeader: function() {},
 
238
                        setRequestHeader: function() {},
 
239
                        abort: function(status) {
 
240
                                var e = (status === 'timeout' ? 'timeout' : 'aborted');
 
241
                                log('aborting upload... ' + e);
 
242
                                this.aborted = 1;
 
243
                                $io.attr('src', s.iframeSrc); // abort op in progress
 
244
                                xhr.error = e;
 
245
                                s.error && s.error.call(s.context, xhr, e, status);
 
246
                                g && $.event.trigger("ajaxError", [xhr, s, e]);
 
247
                                s.complete && s.complete.call(s.context, xhr, e);
 
248
                        }
 
249
                };
 
250
 
 
251
                g = s.global;
 
252
                // trigger ajax global events so that activity/block indicators work like normal
 
253
                if (g && ! $.active++) {
 
254
                        $.event.trigger("ajaxStart");
 
255
                }
 
256
                if (g) {
 
257
                        $.event.trigger("ajaxSend", [xhr, s]);
 
258
                }
 
259
 
 
260
                if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) {
 
261
                        if (s.global) {
 
262
                                $.active--;
 
263
                        }
 
264
                        return;
 
265
                }
 
266
                if (xhr.aborted) {
 
267
                        return;
 
268
                }
 
269
 
 
270
                // add submitting element to data if we know it
 
271
                sub = form.clk;
 
272
                if (sub) {
 
273
                        n = sub.name;
 
274
                        if (n && !sub.disabled) {
 
275
                                s.extraData = s.extraData || {};
 
276
                                s.extraData[n] = sub.value;
 
277
                                if (sub.type == "image") {
 
278
                                        s.extraData[n+'.x'] = form.clk_x;
 
279
                                        s.extraData[n+'.y'] = form.clk_y;
 
280
                                }
 
281
                        }
 
282
                }
 
283
                
 
284
                var CLIENT_TIMEOUT_ABORT = 1;
 
285
                var SERVER_ABORT = 2;
 
286
 
 
287
                function getDoc(frame) {
 
288
                        var doc = frame.contentWindow ? frame.contentWindow.document : frame.contentDocument ? frame.contentDocument : frame.document;
 
289
                        return doc;
 
290
                }
 
291
                
 
292
                // take a breath so that pending repaints get some cpu time before the upload starts
 
293
                function doSubmit() {
 
294
                        // make sure form attrs are set
 
295
                        var t = $form.attr('target'), a = $form.attr('action');
 
296
 
 
297
                        // update form attrs in IE friendly way
 
298
                        form.setAttribute('target',id);
 
299
                        if (!method) {
 
300
                                form.setAttribute('method', 'POST');
 
301
                        }
 
302
                        if (a != s.url) {
 
303
                                form.setAttribute('action', s.url);
 
304
                        }
 
305
 
 
306
                        // ie borks in some cases when setting encoding
 
307
                        if (! s.skipEncodingOverride && (!method || /post/i.test(method))) {
 
308
                                $form.attr({
 
309
                                        encoding: 'multipart/form-data',
 
310
                                        enctype:  'multipart/form-data'
 
311
                                });
 
312
                        }
 
313
 
 
314
                        // support timout
 
315
                        if (s.timeout) {
 
316
                                timeoutHandle = setTimeout(function() { timedOut = true; cb(CLIENT_TIMEOUT_ABORT); }, s.timeout);
 
317
                        }
 
318
                        
 
319
                        // look for server aborts
 
320
                        function checkState() {
 
321
                                try {
 
322
                                        var state = getDoc(io).readyState;
 
323
                                        log('state = ' + state);
 
324
                                        if (state.toLowerCase() == 'uninitialized')
 
325
                                                setTimeout(checkState,50);
 
326
                                }
 
327
                                catch(e) {
 
328
                                        log('Server abort: ' , e, ' (', e.name, ')');
 
329
                                        cb(SERVER_ABORT);
 
330
                                        timeoutHandle && clearTimeout(timeoutHandle);
 
331
                                        timeoutHandle = undefined;
 
332
                                }
 
333
                        }
 
334
 
 
335
                        // add "extra" data to form if provided in options
 
336
                        var extraInputs = [];
 
337
                        try {
 
338
                                if (s.extraData) {
 
339
                                        for (var n in s.extraData) {
 
340
                                                extraInputs.push(
 
341
                                                        $('<input type="hidden" name="'+n+'" />').attr('value',s.extraData[n])
 
342
                                                                .appendTo(form)[0]);
 
343
                                        }
 
344
                                }
 
345
 
 
346
                                if (!s.iframeTarget) {
 
347
                                        // add iframe to doc and submit the form
 
348
                                        $io.appendTo('body');
 
349
                        io.attachEvent ? io.attachEvent('onload', cb) : io.addEventListener('load', cb, false);
 
350
                                }
 
351
                                setTimeout(checkState,15);
 
352
                                form.submit();
 
353
                        }
 
354
                        finally {
 
355
                                // reset attrs and remove "extra" input elements
 
356
                                form.setAttribute('action',a);
 
357
                                if(t) {
 
358
                                        form.setAttribute('target', t);
 
359
                                } else {
 
360
                                        $form.removeAttr('target');
 
361
                                }
 
362
                                $(extraInputs).remove();
 
363
                        }
 
364
                }
 
365
 
 
366
                if (s.forceSync) {
 
367
                        doSubmit();
 
368
                }
 
369
                else {
 
370
                        setTimeout(doSubmit, 10); // this lets dom updates render
 
371
                }
 
372
 
 
373
                var data, doc, domCheckCount = 50, callbackProcessed;
 
374
 
 
375
                function cb(e) {
 
376
                        if (xhr.aborted || callbackProcessed) {
 
377
                                return;
 
378
                        }
 
379
                        try {
 
380
                                doc = getDoc(io);
 
381
                        }
 
382
                        catch(ex) {
 
383
                                log('cannot access response document: ', ex);
 
384
                                e = SERVER_ABORT;
 
385
                        }
 
386
                        if (e === CLIENT_TIMEOUT_ABORT && xhr) {
 
387
                                xhr.abort('timeout');
 
388
                                return;
 
389
                        }
 
390
                        else if (e == SERVER_ABORT && xhr) {
 
391
                                xhr.abort('server abort');
 
392
                                return;
 
393
                        }
 
394
 
 
395
                        if (!doc || doc.location.href == s.iframeSrc) {
 
396
                                // response not received yet
 
397
                                if (!timedOut)
 
398
                                        return;
 
399
                        }
 
400
            io.detachEvent ? io.detachEvent('onload', cb) : io.removeEventListener('load', cb, false);
 
401
 
 
402
                        var status = 'success', errMsg;
 
403
                        try {
 
404
                                if (timedOut) {
 
405
                                        throw 'timeout';
 
406
                                }
 
407
 
 
408
                                var isXml = s.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc);
 
409
                                log('isXml='+isXml);
 
410
                                if (!isXml && window.opera && (doc.body == null || doc.body.innerHTML == '')) {
 
411
                                        if (--domCheckCount) {
 
412
                                                // in some browsers (Opera) the iframe DOM is not always traversable when
 
413
                                                // the onload callback fires, so we loop a bit to accommodate
 
414
                                                log('requeing onLoad callback, DOM not available');
 
415
                                                setTimeout(cb, 250);
 
416
                                                return;
 
417
                                        }
 
418
                                        // let this fall through because server response could be an empty document
 
419
                                        //log('Could not access iframe DOM after mutiple tries.');
 
420
                                        //throw 'DOMException: not available';
 
421
                                }
 
422
 
 
423
                                //log('response detected');
 
424
                var docRoot = doc.body ? doc.body : doc.documentElement;
 
425
                xhr.responseText = docRoot ? docRoot.innerHTML : null;
 
426
                                xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
 
427
                                if (isXml)
 
428
                                        s.dataType = 'xml';
 
429
                                xhr.getResponseHeader = function(header){
 
430
                                        var headers = {'content-type': s.dataType};
 
431
                                        return headers[header];
 
432
                                };
 
433
                // support for XHR 'status' & 'statusText' emulation :
 
434
                if (docRoot) {
 
435
                    xhr.status = Number( docRoot.getAttribute('status') ) || xhr.status;
 
436
                    xhr.statusText = docRoot.getAttribute('statusText') || xhr.statusText;
 
437
                }
 
438
 
 
439
                                var dt = (s.dataType || '').toLowerCase();
 
440
                                var scr = /(json|script|text)/.test(dt);
 
441
                                if (scr || s.textarea) {
 
442
                                        // see if user embedded response in textarea
 
443
                                        var ta = doc.getElementsByTagName('textarea')[0];
 
444
                                        if (ta) {
 
445
                                                xhr.responseText = ta.value;
 
446
                        // support for XHR 'status' & 'statusText' emulation :
 
447
                        xhr.status = Number( ta.getAttribute('status') ) || xhr.status;
 
448
                        xhr.statusText = ta.getAttribute('statusText') || xhr.statusText;
 
449
                                        }
 
450
                                        else if (scr) {
 
451
                                                // account for browsers injecting pre around json response
 
452
                                                var pre = doc.getElementsByTagName('pre')[0];
 
453
                                                var b = doc.getElementsByTagName('body')[0];
 
454
                                                if (pre) {
 
455
                                                        xhr.responseText = pre.textContent ? pre.textContent : pre.innerText;
 
456
                                                }
 
457
                                                else if (b) {
 
458
                                                        xhr.responseText = b.textContent ? b.textContent : b.innerText;
 
459
                                                }
 
460
                                        }
 
461
                                }
 
462
                                else if (dt == 'xml' && !xhr.responseXML && xhr.responseText != null) {
 
463
                                        xhr.responseXML = toXml(xhr.responseText);
 
464
                                }
 
465
 
 
466
                try {
 
467
                    data = httpData(xhr, dt, s);
 
468
                }
 
469
                catch (e) {
 
470
                    status = 'parsererror';
 
471
                    xhr.error = errMsg = (e || status);
 
472
                }
 
473
                        }
 
474
                        catch (e) {
 
475
                                log('error caught: ',e);
 
476
                                status = 'error';
 
477
                xhr.error = errMsg = (e || status);
 
478
                        }
 
479
 
 
480
                        if (xhr.aborted) {
 
481
                                log('upload aborted');
 
482
                                status = null;
 
483
                        }
 
484
 
 
485
            if (xhr.status) { // we've set xhr.status
 
486
                status = (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) ? 'success' : 'error';
 
487
            }
 
488
 
 
489
                        // ordering of these callbacks/triggers is odd, but that's how $.ajax does it
 
490
                        if (status === 'success') {
 
491
                                s.success && s.success.call(s.context, data, 'success', xhr);
 
492
                                g && $.event.trigger("ajaxSuccess", [xhr, s]);
 
493
                        }
 
494
            else if (status) {
 
495
                                if (errMsg == undefined)
 
496
                                        errMsg = xhr.statusText;
 
497
                                s.error && s.error.call(s.context, xhr, status, errMsg);
 
498
                                g && $.event.trigger("ajaxError", [xhr, s, errMsg]);
 
499
            }
 
500
 
 
501
                        g && $.event.trigger("ajaxComplete", [xhr, s]);
 
502
 
 
503
                        if (g && ! --$.active) {
 
504
                                $.event.trigger("ajaxStop");
 
505
                        }
 
506
 
 
507
                        s.complete && s.complete.call(s.context, xhr, status);
 
508
 
 
509
                        callbackProcessed = true;
 
510
                        if (s.timeout)
 
511
                                clearTimeout(timeoutHandle);
 
512
 
 
513
                        // clean up
 
514
                        setTimeout(function() {
 
515
                                if (!s.iframeTarget)
 
516
                                        $io.remove();
 
517
                                xhr.responseXML = null;
 
518
                        }, 100);
 
519
                }
 
520
 
 
521
                var toXml = $.parseXML || function(s, doc) { // use parseXML if available (jQuery 1.5+)
 
522
                        if (window.ActiveXObject) {
 
523
                                doc = new ActiveXObject('Microsoft.XMLDOM');
 
524
                                doc.async = 'false';
 
525
                                doc.loadXML(s);
 
526
                        }
 
527
                        else {
 
528
                                doc = (new DOMParser()).parseFromString(s, 'text/xml');
 
529
                        }
 
530
                        return (doc && doc.documentElement && doc.documentElement.nodeName != 'parsererror') ? doc : null;
 
531
                };
 
532
                var parseJSON = $.parseJSON || function(s) {
 
533
                        return window['eval']('(' + s + ')');
 
534
                };
 
535
 
 
536
                var httpData = function( xhr, type, s ) { // mostly lifted from jq1.4.4
 
537
 
 
538
                        var ct = xhr.getResponseHeader('content-type') || '',
 
539
                                xml = type === 'xml' || !type && ct.indexOf('xml') >= 0,
 
540
                                data = xml ? xhr.responseXML : xhr.responseText;
 
541
 
 
542
                        if (xml && data.documentElement.nodeName === 'parsererror') {
 
543
                                $.error && $.error('parsererror');
 
544
                        }
 
545
                        if (s && s.dataFilter) {
 
546
                                data = s.dataFilter(data, type);
 
547
                        }
 
548
                        if (typeof data === 'string') {
 
549
                                if (type === 'json' || !type && ct.indexOf('json') >= 0) {
 
550
                                        data = parseJSON(data);
 
551
                                } else if (type === "script" || !type && ct.indexOf("javascript") >= 0) {
 
552
                                        $.globalEval(data);
 
553
                                }
 
554
                        }
 
555
                        return data;
 
556
                };
 
557
        }
 
558
};
 
559
 
 
560
/**
 
561
 * ajaxForm() provides a mechanism for fully automating form submission.
 
562
 *
 
563
 * The advantages of using this method instead of ajaxSubmit() are:
 
564
 *
 
565
 * 1: This method will include coordinates for <input type="image" /> elements (if the element
 
566
 *      is used to submit the form).
 
567
 * 2. This method will include the submit element's name/value data (for the element that was
 
568
 *      used to submit the form).
 
569
 * 3. This method binds the submit() method to the form for you.
 
570
 *
 
571
 * The options argument for ajaxForm works exactly as it does for ajaxSubmit.  ajaxForm merely
 
572
 * passes the options argument along after properly binding events for submit elements and
 
573
 * the form itself.
 
574
 */
 
575
$.fn.ajaxForm = function(options) {
 
576
        // in jQuery 1.3+ we can fix mistakes with the ready state
 
577
        if (this.length === 0) {
 
578
                var o = { s: this.selector, c: this.context };
 
579
                if (!$.isReady && o.s) {
 
580
                        log('DOM not ready, queuing ajaxForm');
 
581
                        $(function() {
 
582
                                $(o.s,o.c).ajaxForm(options);
 
583
                        });
 
584
                        return this;
 
585
                }
 
586
                // is your DOM ready?  http://docs.jquery.com/Tutorials:Introducing_$(document).ready()
 
587
                log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)'));
 
588
                return this;
 
589
        }
 
590
 
 
591
        return this.ajaxFormUnbind().bind('submit.form-plugin', function(e) {
 
592
                if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed
 
593
                        e.preventDefault();
 
594
                        $(this).ajaxSubmit(options);
 
595
                }
 
596
        }).bind('click.form-plugin', function(e) {
 
597
                var target = e.target;
 
598
                var $el = $(target);
 
599
                if (!($el.is(":submit,input:image"))) {
 
600
                        // is this a child element of the submit el?  (ex: a span within a button)
 
601
                        var t = $el.closest(':submit');
 
602
                        if (t.length == 0) {
 
603
                                return;
 
604
                        }
 
605
                        target = t[0];
 
606
                }
 
607
                var form = this;
 
608
                form.clk = target;
 
609
                if (target.type == 'image') {
 
610
                        if (e.offsetX != undefined) {
 
611
                                form.clk_x = e.offsetX;
 
612
                                form.clk_y = e.offsetY;
 
613
                        } else if (typeof $.fn.offset == 'function') { // try to use dimensions plugin
 
614
                                var offset = $el.offset();
 
615
                                form.clk_x = e.pageX - offset.left;
 
616
                                form.clk_y = e.pageY - offset.top;
 
617
                        } else {
 
618
                                form.clk_x = e.pageX - target.offsetLeft;
 
619
                                form.clk_y = e.pageY - target.offsetTop;
 
620
                        }
 
621
                }
 
622
                // clear form vars
 
623
                setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 100);
 
624
        });
 
625
};
 
626
 
 
627
// ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
 
628
$.fn.ajaxFormUnbind = function() {
 
629
        return this.unbind('submit.form-plugin click.form-plugin');
 
630
};
 
631
 
 
632
/**
 
633
 * formToArray() gathers form element data into an array of objects that can
 
634
 * be passed to any of the following ajax functions: $.get, $.post, or load.
 
635
 * Each object in the array has both a 'name' and 'value' property.  An example of
 
636
 * an array for a simple login form might be:
 
637
 *
 
638
 * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ]
 
639
 *
 
640
 * It is this array that is passed to pre-submit callback functions provided to the
 
641
 * ajaxSubmit() and ajaxForm() methods.
 
642
 */
 
643
$.fn.formToArray = function(semantic) {
 
644
        var a = [];
 
645
        if (this.length === 0) {
 
646
                return a;
 
647
        }
 
648
 
 
649
        var form = this[0];
 
650
        var els = semantic ? form.getElementsByTagName('*') : form.elements;
 
651
        if (!els) {
 
652
                return a;
 
653
        }
 
654
 
 
655
        var i,j,n,v,el,max,jmax;
 
656
        for(i=0, max=els.length; i < max; i++) {
 
657
                el = els[i];
 
658
                n = el.name;
 
659
                if (!n) {
 
660
                        continue;
 
661
                }
 
662
 
 
663
                if (semantic && form.clk && el.type == "image") {
 
664
                        // handle image inputs on the fly when semantic == true
 
665
                        if(!el.disabled && form.clk == el) {
 
666
                                a.push({name: n, value: $(el).val()});
 
667
                                a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
 
668
                        }
 
669
                        continue;
 
670
                }
 
671
 
 
672
                v = $.fieldValue(el, true);
 
673
                if (v && v.constructor == Array) {
 
674
                        for(j=0, jmax=v.length; j < jmax; j++) {
 
675
                                a.push({name: n, value: v[j]});
 
676
                        }
 
677
                }
 
678
                else if (v !== null && typeof v != 'undefined') {
 
679
                        a.push({name: n, value: v});
 
680
                }
 
681
        }
 
682
 
 
683
        if (!semantic && form.clk) {
 
684
                // input type=='image' are not found in elements array! handle it here
 
685
                var $input = $(form.clk), input = $input[0];
 
686
                n = input.name;
 
687
                if (n && !input.disabled && input.type == 'image') {
 
688
                        a.push({name: n, value: $input.val()});
 
689
                        a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
 
690
                }
 
691
        }
 
692
        return a;
 
693
};
 
694
 
 
695
/**
 
696
 * Serializes form data into a 'submittable' string. This method will return a string
 
697
 * in the format: name1=value1&amp;name2=value2
 
698
 */
 
699
$.fn.formSerialize = function(semantic) {
 
700
        //hand off to jQuery.param for proper encoding
 
701
        return $.param(this.formToArray(semantic));
 
702
};
 
703
 
 
704
/**
 
705
 * Serializes all field elements in the jQuery object into a query string.
 
706
 * This method will return a string in the format: name1=value1&amp;name2=value2
 
707
 */
 
708
$.fn.fieldSerialize = function(successful) {
 
709
        var a = [];
 
710
        this.each(function() {
 
711
                var n = this.name;
 
712
                if (!n) {
 
713
                        return;
 
714
                }
 
715
                var v = $.fieldValue(this, successful);
 
716
                if (v && v.constructor == Array) {
 
717
                        for (var i=0,max=v.length; i < max; i++) {
 
718
                                a.push({name: n, value: v[i]});
 
719
                        }
 
720
                }
 
721
                else if (v !== null && typeof v != 'undefined') {
 
722
                        a.push({name: this.name, value: v});
 
723
                }
 
724
        });
 
725
        //hand off to jQuery.param for proper encoding
 
726
        return $.param(a);
 
727
};
 
728
 
 
729
/**
 
730
 * Returns the value(s) of the element in the matched set.  For example, consider the following form:
 
731
 *
 
732
 *  <form><fieldset>
 
733
 *        <input name="A" type="text" />
 
734
 *        <input name="A" type="text" />
 
735
 *        <input name="B" type="checkbox" value="B1" />
 
736
 *        <input name="B" type="checkbox" value="B2"/>
 
737
 *        <input name="C" type="radio" value="C1" />
 
738
 *        <input name="C" type="radio" value="C2" />
 
739
 *  </fieldset></form>
 
740
 *
 
741
 *  var v = $(':text').fieldValue();
 
742
 *  // if no values are entered into the text inputs
 
743
 *  v == ['','']
 
744
 *  // if values entered into the text inputs are 'foo' and 'bar'
 
745
 *  v == ['foo','bar']
 
746
 *
 
747
 *  var v = $(':checkbox').fieldValue();
 
748
 *  // if neither checkbox is checked
 
749
 *  v === undefined
 
750
 *  // if both checkboxes are checked
 
751
 *  v == ['B1', 'B2']
 
752
 *
 
753
 *  var v = $(':radio').fieldValue();
 
754
 *  // if neither radio is checked
 
755
 *  v === undefined
 
756
 *  // if first radio is checked
 
757
 *  v == ['C1']
 
758
 *
 
759
 * The successful argument controls whether or not the field element must be 'successful'
 
760
 * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls).
 
761
 * The default value of the successful argument is true.  If this value is false the value(s)
 
762
 * for each element is returned.
 
763
 *
 
764
 * Note: This method *always* returns an array.  If no valid value can be determined the
 
765
 *         array will be empty, otherwise it will contain one or more values.
 
766
 */
 
767
$.fn.fieldValue = function(successful) {
 
768
        for (var val=[], i=0, max=this.length; i < max; i++) {
 
769
                var el = this[i];
 
770
                var v = $.fieldValue(el, successful);
 
771
                if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) {
 
772
                        continue;
 
773
                }
 
774
                v.constructor == Array ? $.merge(val, v) : val.push(v);
 
775
        }
 
776
        return val;
 
777
};
 
778
 
 
779
/**
 
780
 * Returns the value of the field element.
 
781
 */
 
782
$.fieldValue = function(el, successful) {
 
783
        var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
 
784
        if (successful === undefined) {
 
785
                successful = true;
 
786
        }
 
787
 
 
788
        if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
 
789
                (t == 'checkbox' || t == 'radio') && !el.checked ||
 
790
                (t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
 
791
                tag == 'select' && el.selectedIndex == -1)) {
 
792
                        return null;
 
793
        }
 
794
 
 
795
        if (tag == 'select') {
 
796
                var index = el.selectedIndex;
 
797
                if (index < 0) {
 
798
                        return null;
 
799
                }
 
800
                var a = [], ops = el.options;
 
801
                var one = (t == 'select-one');
 
802
                var max = (one ? index+1 : ops.length);
 
803
                for(var i=(one ? index : 0); i < max; i++) {
 
804
                        var op = ops[i];
 
805
                        if (op.selected) {
 
806
                                var v = op.value;
 
807
                                if (!v) { // extra pain for IE...
 
808
                                        v = (op.attributes && op.attributes['value'] && !(op.attributes['value'].specified)) ? op.text : op.value;
 
809
                                }
 
810
                                if (one) {
 
811
                                        return v;
 
812
                                }
 
813
                                a.push(v);
 
814
                        }
 
815
                }
 
816
                return a;
 
817
        }
 
818
        return $(el).val();
 
819
};
 
820
 
 
821
/**
 
822
 * Clears the form data.  Takes the following actions on the form's input fields:
 
823
 *  - input text fields will have their 'value' property set to the empty string
 
824
 *  - select elements will have their 'selectedIndex' property set to -1
 
825
 *  - checkbox and radio inputs will have their 'checked' property set to false
 
826
 *  - inputs of type submit, button, reset, and hidden will *not* be effected
 
827
 *  - button elements will *not* be effected
 
828
 */
 
829
$.fn.clearForm = function() {
 
830
        return this.each(function() {
 
831
                $('input,select,textarea', this).clearFields();
 
832
        });
 
833
};
 
834
 
 
835
/**
 
836
 * Clears the selected form elements.
 
837
 */
 
838
$.fn.clearFields = $.fn.clearInputs = function() {
 
839
        var re = /^(?:color|date|datetime|email|month|number|password|range|search|tel|text|time|url|week)$/i; // 'hidden' is not in this list
 
840
        return this.each(function() {
 
841
                var t = this.type, tag = this.tagName.toLowerCase();
 
842
                if (re.test(t) || tag == 'textarea') {
 
843
                        this.value = '';
 
844
                }
 
845
                else if (t == 'checkbox' || t == 'radio') {
 
846
                        this.checked = false;
 
847
                }
 
848
                else if (tag == 'select') {
 
849
                        this.selectedIndex = -1;
 
850
                }
 
851
        });
 
852
};
 
853
 
 
854
/**
 
855
 * Resets the form data.  Causes all form elements to be reset to their original value.
 
856
 */
 
857
$.fn.resetForm = function() {
 
858
        return this.each(function() {
 
859
                // guard against an input with the name of 'reset'
 
860
                // note that IE reports the reset function as an 'object'
 
861
                if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType)) {
 
862
                        this.reset();
 
863
                }
 
864
        });
 
865
};
 
866
 
 
867
/**
 
868
 * Enables or disables any matching elements.
 
869
 */
 
870
$.fn.enable = function(b) {
 
871
        if (b === undefined) {
 
872
                b = true;
 
873
        }
 
874
        return this.each(function() {
 
875
                this.disabled = !b;
 
876
        });
 
877
};
 
878
 
 
879
/**
 
880
 * Checks/unchecks any matching checkboxes or radio buttons and
 
881
 * selects/deselects and matching option elements.
 
882
 */
 
883
$.fn.selected = function(select) {
 
884
        if (select === undefined) {
 
885
                select = true;
 
886
        }
 
887
        return this.each(function() {
 
888
                var t = this.type;
 
889
                if (t == 'checkbox' || t == 'radio') {
 
890
                        this.checked = select;
 
891
                }
 
892
                else if (this.tagName.toLowerCase() == 'option') {
 
893
                        var $sel = $(this).parent('select');
 
894
                        if (select && $sel[0] && $sel[0].type == 'select-one') {
 
895
                                // deselect all other options
 
896
                                $sel.find('option').selected(false);
 
897
                        }
 
898
                        this.selected = select;
 
899
                }
 
900
        });
 
901
};
 
902
 
 
903
// expose debug var
 
904
$.fn.ajaxSubmit.debug = false;
 
905
 
 
906
// helper fn for console logging
 
907
function log() {
 
908
        if (!$.fn.ajaxSubmit.debug) 
 
909
                return;
 
910
        var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,'');
 
911
        if (window.console && window.console.log) {
 
912
                window.console.log(msg);
 
913
        }
 
914
        else if (window.opera && window.opera.postError) {
 
915
                window.opera.postError(msg);
 
916
        }
 
917
};
 
918
 
 
919
})(jQuery);