Projet

Général

Profil

Télécharger (42,8 ko) Statistiques
| Branche: | Tag: | Révision:

calebasse / calebasse / static / js / jquery.form.js @ b057fff0

1
/*!
2
 * jQuery Form Plugin
3
 * version: 3.50.0-2014.02.05
4
 * Requires jQuery v1.5 or later
5
 * Copyright (c) 2013 M. Alsup
6
 * Examples and documentation at: http://malsup.com/jquery/form/
7
 * Project repository: https://github.com/malsup/form
8
 * Dual licensed under the MIT and GPL licenses.
9
 * https://github.com/malsup/form#copyright-and-license
10
 */
11
/*global ActiveXObject */
12

    
13
// AMD support
14
(function (factory) {
15
    "use strict";
16
    if (typeof define === 'function' && define.amd) {
17
        // using AMD; register as anon module
18
        define(['jquery'], factory);
19
    } else {
20
        // no AMD; invoke directly
21
        factory( (typeof(jQuery) != 'undefined') ? jQuery : window.Zepto );
22
    }
23
}
24

    
25
(function($) {
26
"use strict";
27

    
28
/*
29
    Usage Note:
30
    -----------
31
    Do not use both ajaxSubmit and ajaxForm on the same form.  These
32
    functions are mutually exclusive.  Use ajaxSubmit if you want
33
    to bind your own submit handler to the form.  For example,
34

    
35
    $(document).ready(function() {
36
        $('#myForm').on('submit', function(e) {
37
            e.preventDefault(); // <-- important
38
            $(this).ajaxSubmit({
39
                target: '#output'
40
            });
41
        });
42
    });
43

    
44
    Use ajaxForm when you want the plugin to manage all the event binding
45
    for you.  For example,
46

    
47
    $(document).ready(function() {
48
        $('#myForm').ajaxForm({
49
            target: '#output'
50
        });
51
    });
52

    
53
    You can also use ajaxForm with delegation (requires jQuery v1.7+), so the
54
    form does not have to exist when you invoke ajaxForm:
55

    
56
    $('#myForm').ajaxForm({
57
        delegation: true,
58
        target: '#output'
59
    });
60

    
61
    When using ajaxForm, the ajaxSubmit function will be invoked for you
62
    at the appropriate time.
63
*/
64

    
65
/**
66
 * Feature detection
67
 */
68
var feature = {};
69
feature.fileapi = $("<input type='file'/>").get(0).files !== undefined;
70
feature.formdata = window.FormData !== undefined;
71

    
72
var hasProp = !!$.fn.prop;
73

    
74
// attr2 uses prop when it can but checks the return type for
75
// an expected string.  this accounts for the case where a form 
76
// contains inputs with names like "action" or "method"; in those
77
// cases "prop" returns the element
78
$.fn.attr2 = function() {
79
    if ( ! hasProp ) {
80
        return this.attr.apply(this, arguments);
81
    }
82
    var val = this.prop.apply(this, arguments);
83
    if ( ( val && val.jquery ) || typeof val === 'string' ) {
84
        return val;
85
    }
86
    return this.attr.apply(this, arguments);
87
};
88

    
89
/**
90
 * ajaxSubmit() provides a mechanism for immediately submitting
91
 * an HTML form using AJAX.
92
 */
93
$.fn.ajaxSubmit = function(options) {
94
    /*jshint scripturl:true */
95

    
96
    // fast fail if nothing selected (http://dev.jquery.com/ticket/2752)
97
    if (!this.length) {
98
        log('ajaxSubmit: skipping submit process - no element selected');
99
        return this;
100
    }
101

    
102
    var method, action, url, $form = this;
103

    
104
    if (typeof options == 'function') {
105
        options = { success: options };
106
    }
107
    else if ( options === undefined ) {
108
        options = {};
109
    }
110

    
111
    method = options.type || this.attr2('method');
112
    action = options.url  || this.attr2('action');
113

    
114
    url = (typeof action === 'string') ? $.trim(action) : '';
115
    url = url || window.location.href || '';
116
    if (url) {
117
        // clean url (don't include hash vaue)
118
        url = (url.match(/^([^#]+)/)||[])[1];
119
    }
120

    
121
    options = $.extend(true, {
122
        url:  url,
123
        success: $.ajaxSettings.success,
124
        type: method || $.ajaxSettings.type,
125
        iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank'
126
    }, options);
127

    
128
    // hook for manipulating the form data before it is extracted;
129
    // convenient for use with rich editors like tinyMCE or FCKEditor
130
    var veto = {};
131
    this.trigger('form-pre-serialize', [this, options, veto]);
132
    if (veto.veto) {
133
        log('ajaxSubmit: submit vetoed via form-pre-serialize trigger');
134
        return this;
135
    }
136

    
137
    // provide opportunity to alter form data before it is serialized
138
    if (options.beforeSerialize && options.beforeSerialize(this, options) === false) {
139
        log('ajaxSubmit: submit aborted via beforeSerialize callback');
140
        return this;
141
    }
142

    
143
    var traditional = options.traditional;
144
    if ( traditional === undefined ) {
145
        traditional = $.ajaxSettings.traditional;
146
    }
147

    
148
    var elements = [];
149
    var qx, a = this.formToArray(options.semantic, elements);
150
    if (options.data) {
151
        options.extraData = options.data;
152
        qx = $.param(options.data, traditional);
153
    }
154

    
155
    // give pre-submit callback an opportunity to abort the submit
156
    if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) {
157
        log('ajaxSubmit: submit aborted via beforeSubmit callback');
158
        return this;
159
    }
160

    
161
    // fire vetoable 'validate' event
162
    this.trigger('form-submit-validate', [a, this, options, veto]);
163
    if (veto.veto) {
164
        log('ajaxSubmit: submit vetoed via form-submit-validate trigger');
165
        return this;
166
    }
167

    
168
    var q = $.param(a, traditional);
169
    if (qx) {
170
        q = ( q ? (q + '&' + qx) : qx );
171
    }
172
    if (options.type.toUpperCase() == 'GET') {
173
        options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
174
        options.data = null;  // data is null for 'get'
175
    }
176
    else {
177
        options.data = q; // data is the query string for 'post'
178
    }
179

    
180
    var callbacks = [];
181
    if (options.resetForm) {
182
        callbacks.push(function() { $form.resetForm(); });
183
    }
184
    if (options.clearForm) {
185
        callbacks.push(function() { $form.clearForm(options.includeHidden); });
186
    }
187

    
188
    // perform a load on the target only if dataType is not provided
189
    if (!options.dataType && options.target) {
190
        var oldSuccess = options.success || function(){};
191
        callbacks.push(function(data) {
192
            var fn = options.replaceTarget ? 'replaceWith' : 'html';
193
            $(options.target)[fn](data).each(oldSuccess, arguments);
194
        });
195
    }
196
    else if (options.success) {
197
        callbacks.push(options.success);
198
    }
199

    
200
    options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg
201
        var context = options.context || this ;    // jQuery 1.4+ supports scope context
202
        for (var i=0, max=callbacks.length; i < max; i++) {
203
            callbacks[i].apply(context, [data, status, xhr || $form, $form]);
204
        }
205
    };
206

    
207
    if (options.error) {
208
        var oldError = options.error;
209
        options.error = function(xhr, status, error) {
210
            var context = options.context || this;
211
            oldError.apply(context, [xhr, status, error, $form]);
212
        };
213
    }
214

    
215
     if (options.complete) {
216
        var oldComplete = options.complete;
217
        options.complete = function(xhr, status) {
218
            var context = options.context || this;
219
            oldComplete.apply(context, [xhr, status, $form]);
220
        };
221
    }
222

    
223
    // are there files to upload?
224

    
225
    // [value] (issue #113), also see comment:
226
    // https://github.com/malsup/form/commit/588306aedba1de01388032d5f42a60159eea9228#commitcomment-2180219
227
    var fileInputs = $('input[type=file]:enabled', this).filter(function() { return $(this).val() !== ''; });
228

    
229
    var hasFileInputs = fileInputs.length > 0;
230
    var mp = 'multipart/form-data';
231
    var multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp);
232

    
233
    var fileAPI = feature.fileapi && feature.formdata;
234
    log("fileAPI :" + fileAPI);
235
    var shouldUseFrame = (hasFileInputs || multipart) && !fileAPI;
236

    
237
    var jqxhr;
238

    
239
    // options.iframe allows user to force iframe mode
240
    // 06-NOV-09: now defaulting to iframe mode if file input is detected
241
    if (options.iframe !== false && (options.iframe || shouldUseFrame)) {
242
        // hack to fix Safari hang (thanks to Tim Molendijk for this)
243
        // see:  http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
244
        if (options.closeKeepAlive) {
245
            $.get(options.closeKeepAlive, function() {
246
                jqxhr = fileUploadIframe(a);
247
            });
248
        }
249
        else {
250
            jqxhr = fileUploadIframe(a);
251
        }
252
    }
253
    else if ((hasFileInputs || multipart) && fileAPI) {
254
        jqxhr = fileUploadXhr(a);
255
    }
256
    else {
257
        jqxhr = $.ajax(options);
258
    }
259

    
260
    $form.removeData('jqxhr').data('jqxhr', jqxhr);
261

    
262
    // clear element array
263
    for (var k=0; k < elements.length; k++) {
264
        elements[k] = null;
265
    }
266

    
267
    // fire 'notify' event
268
    this.trigger('form-submit-notify', [this, options]);
269
    return this;
270

    
271
    // utility fn for deep serialization
272
    function deepSerialize(extraData){
273
        var serialized = $.param(extraData, options.traditional).split('&');
274
        var len = serialized.length;
275
        var result = [];
276
        var i, part;
277
        for (i=0; i < len; i++) {
278
            // #252; undo param space replacement
279
            serialized[i] = serialized[i].replace(/\+/g,' ');
280
            part = serialized[i].split('=');
281
            // #278; use array instead of object storage, favoring array serializations
282
            result.push([decodeURIComponent(part[0]), decodeURIComponent(part[1])]);
283
        }
284
        return result;
285
    }
286

    
287
     // XMLHttpRequest Level 2 file uploads (big hat tip to francois2metz)
288
    function fileUploadXhr(a) {
289
        var formdata = new FormData();
290

    
291
        for (var i=0; i < a.length; i++) {
292
            formdata.append(a[i].name, a[i].value);
293
        }
294

    
295
        if (options.extraData) {
296
            var serializedData = deepSerialize(options.extraData);
297
            for (i=0; i < serializedData.length; i++) {
298
                if (serializedData[i]) {
299
                    formdata.append(serializedData[i][0], serializedData[i][1]);
300
                }
301
            }
302
        }
303

    
304
        options.data = null;
305

    
306
        var s = $.extend(true, {}, $.ajaxSettings, options, {
307
            contentType: false,
308
            processData: false,
309
            cache: false,
310
            type: method || 'POST'
311
        });
312

    
313
        if (options.uploadProgress) {
314
            // workaround because jqXHR does not expose upload property
315
            s.xhr = function() {
316
                var xhr = $.ajaxSettings.xhr();
317
                if (xhr.upload) {
318
                    xhr.upload.addEventListener('progress', function(event) {
319
                        var percent = 0;
320
                        var position = event.loaded || event.position; /*event.position is deprecated*/
321
                        var total = event.total;
322
                        if (event.lengthComputable) {
323
                            percent = Math.ceil(position / total * 100);
324
                        }
325
                        options.uploadProgress(event, position, total, percent);
326
                    }, false);
327
                }
328
                return xhr;
329
            };
330
        }
331

    
332
        s.data = null;
333
        var beforeSend = s.beforeSend;
334
        s.beforeSend = function(xhr, o) {
335
            //Send FormData() provided by user
336
            if (options.formData) {
337
                o.data = options.formData;
338
            }
339
            else {
340
                o.data = formdata;
341
            }
342
            if(beforeSend) {
343
                beforeSend.call(this, xhr, o);
344
            }
345
        };
346
        return $.ajax(s);
347
    }
348

    
349
    // private function for handling file uploads (hat tip to YAHOO!)
350
    function fileUploadIframe(a) {
351
        var form = $form[0], el, i, s, g, id, $io, io, xhr, sub, n, timedOut, timeoutHandle;
352
        var deferred = $.Deferred();
353

    
354
        // #341
355
        deferred.abort = function(status) {
356
            xhr.abort(status);
357
        };
358

    
359
        if (a) {
360
            // ensure that every serialized input is still enabled
361
            for (i=0; i < elements.length; i++) {
362
                el = $(elements[i]);
363
                if ( hasProp ) {
364
                    el.prop('disabled', false);
365
                }
366
                else {
367
                    el.removeAttr('disabled');
368
                }
369
            }
370
        }
371

    
372
        s = $.extend(true, {}, $.ajaxSettings, options);
373
        s.context = s.context || s;
374
        id = 'jqFormIO' + (new Date().getTime());
375
        if (s.iframeTarget) {
376
            $io = $(s.iframeTarget);
377
            n = $io.attr2('name');
378
            if (!n) {
379
                $io.attr2('name', id);
380
            }
381
            else {
382
                id = n;
383
            }
384
        }
385
        else {
386
            $io = $('<iframe name="' + id + '" src="'+ s.iframeSrc +'" />');
387
            $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });
388
        }
389
        io = $io[0];
390

    
391

    
392
        xhr = { // mock object
393
            aborted: 0,
394
            responseText: null,
395
            responseXML: null,
396
            status: 0,
397
            statusText: 'n/a',
398
            getAllResponseHeaders: function() {},
399
            getResponseHeader: function() {},
400
            setRequestHeader: function() {},
401
            abort: function(status) {
402
                var e = (status === 'timeout' ? 'timeout' : 'aborted');
403
                log('aborting upload... ' + e);
404
                this.aborted = 1;
405

    
406
                try { // #214, #257
407
                    if (io.contentWindow.document.execCommand) {
408
                        io.contentWindow.document.execCommand('Stop');
409
                    }
410
                }
411
                catch(ignore) {}
412

    
413
                $io.attr('src', s.iframeSrc); // abort op in progress
414
                xhr.error = e;
415
                if (s.error) {
416
                    s.error.call(s.context, xhr, e, status);
417
                }
418
                if (g) {
419
                    $.event.trigger("ajaxError", [xhr, s, e]);
420
                }
421
                if (s.complete) {
422
                    s.complete.call(s.context, xhr, e);
423
                }
424
            }
425
        };
426

    
427
        g = s.global;
428
        // trigger ajax global events so that activity/block indicators work like normal
429
        if (g && 0 === $.active++) {
430
            $.event.trigger("ajaxStart");
431
        }
432
        if (g) {
433
            $.event.trigger("ajaxSend", [xhr, s]);
434
        }
435

    
436
        if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) {
437
            if (s.global) {
438
                $.active--;
439
            }
440
            deferred.reject();
441
            return deferred;
442
        }
443
        if (xhr.aborted) {
444
            deferred.reject();
445
            return deferred;
446
        }
447

    
448
        // add submitting element to data if we know it
449
        sub = form.clk;
450
        if (sub) {
451
            n = sub.name;
452
            if (n && !sub.disabled) {
453
                s.extraData = s.extraData || {};
454
                s.extraData[n] = sub.value;
455
                if (sub.type == "image") {
456
                    s.extraData[n+'.x'] = form.clk_x;
457
                    s.extraData[n+'.y'] = form.clk_y;
458
                }
459
            }
460
        }
461

    
462
        var CLIENT_TIMEOUT_ABORT = 1;
463
        var SERVER_ABORT = 2;
464
                
465
        function getDoc(frame) {
466
            /* it looks like contentWindow or contentDocument do not
467
             * carry the protocol property in ie8, when running under ssl
468
             * frame.document is the only valid response document, since
469
             * the protocol is know but not on the other two objects. strange?
470
             * "Same origin policy" http://en.wikipedia.org/wiki/Same_origin_policy
471
             */
472
            
473
            var doc = null;
474
            
475
            // IE8 cascading access check
476
            try {
477
                if (frame.contentWindow) {
478
                    doc = frame.contentWindow.document;
479
                }
480
            } catch(err) {
481
                // IE8 access denied under ssl & missing protocol
482
                log('cannot get iframe.contentWindow document: ' + err);
483
            }
484

    
485
            if (doc) { // successful getting content
486
                return doc;
487
            }
488

    
489
            try { // simply checking may throw in ie8 under ssl or mismatched protocol
490
                doc = frame.contentDocument ? frame.contentDocument : frame.document;
491
            } catch(err) {
492
                // last attempt
493
                log('cannot get iframe.contentDocument: ' + err);
494
                doc = frame.document;
495
            }
496
            return doc;
497
        }
498

    
499
        // Rails CSRF hack (thanks to Yvan Barthelemy)
500
        var csrf_token = $('meta[name=csrf-token]').attr('content');
501
        var csrf_param = $('meta[name=csrf-param]').attr('content');
502
        if (csrf_param && csrf_token) {
503
            s.extraData = s.extraData || {};
504
            s.extraData[csrf_param] = csrf_token;
505
        }
506

    
507
        // take a breath so that pending repaints get some cpu time before the upload starts
508
        function doSubmit() {
509
            // make sure form attrs are set
510
            var t = $form.attr2('target'), 
511
                a = $form.attr2('action'), 
512
                mp = 'multipart/form-data',
513
                et = $form.attr('enctype') || $form.attr('encoding') || mp;
514

    
515
            // update form attrs in IE friendly way
516
            form.setAttribute('target',id);
517
            if (!method || /post/i.test(method) ) {
518
                form.setAttribute('method', 'POST');
519
            }
520
            if (a != s.url) {
521
                form.setAttribute('action', s.url);
522
            }
523

    
524
            // ie borks in some cases when setting encoding
525
            if (! s.skipEncodingOverride && (!method || /post/i.test(method))) {
526
                $form.attr({
527
                    encoding: 'multipart/form-data',
528
                    enctype:  'multipart/form-data'
529
                });
530
            }
531

    
532
            // support timout
533
            if (s.timeout) {
534
                timeoutHandle = setTimeout(function() { timedOut = true; cb(CLIENT_TIMEOUT_ABORT); }, s.timeout);
535
            }
536

    
537
            // look for server aborts
538
            function checkState() {
539
                try {
540
                    var state = getDoc(io).readyState;
541
                    log('state = ' + state);
542
                    if (state && state.toLowerCase() == 'uninitialized') {
543
                        setTimeout(checkState,50);
544
                    }
545
                }
546
                catch(e) {
547
                    log('Server abort: ' , e, ' (', e.name, ')');
548
                    cb(SERVER_ABORT);
549
                    if (timeoutHandle) {
550
                        clearTimeout(timeoutHandle);
551
                    }
552
                    timeoutHandle = undefined;
553
                }
554
            }
555

    
556
            // add "extra" data to form if provided in options
557
            var extraInputs = [];
558
            try {
559
                if (s.extraData) {
560
                    for (var n in s.extraData) {
561
                        if (s.extraData.hasOwnProperty(n)) {
562
                           // if using the $.param format that allows for multiple values with the same name
563
                           if($.isPlainObject(s.extraData[n]) && s.extraData[n].hasOwnProperty('name') && s.extraData[n].hasOwnProperty('value')) {
564
                               extraInputs.push(
565
                               $('<input type="hidden" name="'+s.extraData[n].name+'">').val(s.extraData[n].value)
566
                                   .appendTo(form)[0]);
567
                           } else {
568
                               extraInputs.push(
569
                               $('<input type="hidden" name="'+n+'">').val(s.extraData[n])
570
                                   .appendTo(form)[0]);
571
                           }
572
                        }
573
                    }
574
                }
575

    
576
                if (!s.iframeTarget) {
577
                    // add iframe to doc and submit the form
578
                    $io.appendTo('body');
579
                }
580
                if (io.attachEvent) {
581
                    io.attachEvent('onload', cb);
582
                }
583
                else {
584
                    io.addEventListener('load', cb, false);
585
                }
586
                setTimeout(checkState,15);
587

    
588
                try {
589
                    form.submit();
590
                } catch(err) {
591
                    // just in case form has element with name/id of 'submit'
592
                    var submitFn = document.createElement('form').submit;
593
                    submitFn.apply(form);
594
                }
595
            }
596
            finally {
597
                // reset attrs and remove "extra" input elements
598
                form.setAttribute('action',a);
599
                form.setAttribute('enctype', et); // #380
600
                if(t) {
601
                    form.setAttribute('target', t);
602
                } else {
603
                    $form.removeAttr('target');
604
                }
605
                $(extraInputs).remove();
606
            }
607
        }
608

    
609
        if (s.forceSync) {
610
            doSubmit();
611
        }
612
        else {
613
            setTimeout(doSubmit, 10); // this lets dom updates render
614
        }
615

    
616
        var data, doc, domCheckCount = 50, callbackProcessed;
617

    
618
        function cb(e) {
619
            if (xhr.aborted || callbackProcessed) {
620
                return;
621
            }
622
            
623
            doc = getDoc(io);
624
            if(!doc) {
625
                log('cannot access response document');
626
                e = SERVER_ABORT;
627
            }
628
            if (e === CLIENT_TIMEOUT_ABORT && xhr) {
629
                xhr.abort('timeout');
630
                deferred.reject(xhr, 'timeout');
631
                return;
632
            }
633
            else if (e == SERVER_ABORT && xhr) {
634
                xhr.abort('server abort');
635
                deferred.reject(xhr, 'error', 'server abort');
636
                return;
637
            }
638

    
639
            if (!doc || doc.location.href == s.iframeSrc) {
640
                // response not received yet
641
                if (!timedOut) {
642
                    return;
643
                }
644
            }
645
            if (io.detachEvent) {
646
                io.detachEvent('onload', cb);
647
            }
648
            else {
649
                io.removeEventListener('load', cb, false);
650
            }
651

    
652
            var status = 'success', errMsg;
653
            try {
654
                if (timedOut) {
655
                    throw 'timeout';
656
                }
657

    
658
                var isXml = s.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc);
659
                log('isXml='+isXml);
660
                if (!isXml && window.opera && (doc.body === null || !doc.body.innerHTML)) {
661
                    if (--domCheckCount) {
662
                        // in some browsers (Opera) the iframe DOM is not always traversable when
663
                        // the onload callback fires, so we loop a bit to accommodate
664
                        log('requeing onLoad callback, DOM not available');
665
                        setTimeout(cb, 250);
666
                        return;
667
                    }
668
                    // let this fall through because server response could be an empty document
669
                    //log('Could not access iframe DOM after mutiple tries.');
670
                    //throw 'DOMException: not available';
671
                }
672

    
673
                //log('response detected');
674
                var docRoot = doc.body ? doc.body : doc.documentElement;
675
                xhr.responseText = docRoot ? docRoot.innerHTML : null;
676
                xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
677
                if (isXml) {
678
                    s.dataType = 'xml';
679
                }
680
                xhr.getResponseHeader = function(header){
681
                    var headers = {'content-type': s.dataType};
682
                    return headers[header.toLowerCase()];
683
                };
684
                // support for XHR 'status' & 'statusText' emulation :
685
                if (docRoot) {
686
                    xhr.status = Number( docRoot.getAttribute('status') ) || xhr.status;
687
                    xhr.statusText = docRoot.getAttribute('statusText') || xhr.statusText;
688
                }
689

    
690
                var dt = (s.dataType || '').toLowerCase();
691
                var scr = /(json|script|text)/.test(dt);
692
                if (scr || s.textarea) {
693
                    // see if user embedded response in textarea
694
                    var ta = doc.getElementsByTagName('textarea')[0];
695
                    if (ta) {
696
                        xhr.responseText = ta.value;
697
                        // support for XHR 'status' & 'statusText' emulation :
698
                        xhr.status = Number( ta.getAttribute('status') ) || xhr.status;
699
                        xhr.statusText = ta.getAttribute('statusText') || xhr.statusText;
700
                    }
701
                    else if (scr) {
702
                        // account for browsers injecting pre around json response
703
                        var pre = doc.getElementsByTagName('pre')[0];
704
                        var b = doc.getElementsByTagName('body')[0];
705
                        if (pre) {
706
                            xhr.responseText = pre.textContent ? pre.textContent : pre.innerText;
707
                        }
708
                        else if (b) {
709
                            xhr.responseText = b.textContent ? b.textContent : b.innerText;
710
                        }
711
                    }
712
                }
713
                else if (dt == 'xml' && !xhr.responseXML && xhr.responseText) {
714
                    xhr.responseXML = toXml(xhr.responseText);
715
                }
716

    
717
                try {
718
                    data = httpData(xhr, dt, s);
719
                }
720
                catch (err) {
721
                    status = 'parsererror';
722
                    xhr.error = errMsg = (err || status);
723
                }
724
            }
725
            catch (err) {
726
                log('error caught: ',err);
727
                status = 'error';
728
                xhr.error = errMsg = (err || status);
729
            }
730

    
731
            if (xhr.aborted) {
732
                log('upload aborted');
733
                status = null;
734
            }
735

    
736
            if (xhr.status) { // we've set xhr.status
737
                status = (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) ? 'success' : 'error';
738
            }
739

    
740
            // ordering of these callbacks/triggers is odd, but that's how $.ajax does it
741
            if (status === 'success') {
742
                if (s.success) {
743
                    s.success.call(s.context, data, 'success', xhr);
744
                }
745
                deferred.resolve(xhr.responseText, 'success', xhr);
746
                if (g) {
747
                    $.event.trigger("ajaxSuccess", [xhr, s]);
748
                }
749
            }
750
            else if (status) {
751
                if (errMsg === undefined) {
752
                    errMsg = xhr.statusText;
753
                }
754
                if (s.error) {
755
                    s.error.call(s.context, xhr, status, errMsg);
756
                }
757
                deferred.reject(xhr, 'error', errMsg);
758
                if (g) {
759
                    $.event.trigger("ajaxError", [xhr, s, errMsg]);
760
                }
761
            }
762

    
763
            if (g) {
764
                $.event.trigger("ajaxComplete", [xhr, s]);
765
            }
766

    
767
            if (g && ! --$.active) {
768
                $.event.trigger("ajaxStop");
769
            }
770

    
771
            if (s.complete) {
772
                s.complete.call(s.context, xhr, status);
773
            }
774

    
775
            callbackProcessed = true;
776
            if (s.timeout) {
777
                clearTimeout(timeoutHandle);
778
            }
779

    
780
            // clean up
781
            setTimeout(function() {
782
                if (!s.iframeTarget) {
783
                    $io.remove();
784
                }
785
                else { //adding else to clean up existing iframe response.
786
                    $io.attr('src', s.iframeSrc);
787
                }
788
                xhr.responseXML = null;
789
            }, 100);
790
        }
791

    
792
        var toXml = $.parseXML || function(s, doc) { // use parseXML if available (jQuery 1.5+)
793
            if (window.ActiveXObject) {
794
                doc = new ActiveXObject('Microsoft.XMLDOM');
795
                doc.async = 'false';
796
                doc.loadXML(s);
797
            }
798
            else {
799
                doc = (new DOMParser()).parseFromString(s, 'text/xml');
800
            }
801
            return (doc && doc.documentElement && doc.documentElement.nodeName != 'parsererror') ? doc : null;
802
        };
803
        var parseJSON = $.parseJSON || function(s) {
804
            /*jslint evil:true */
805
            return window['eval']('(' + s + ')');
806
        };
807

    
808
        var httpData = function( xhr, type, s ) { // mostly lifted from jq1.4.4
809

    
810
            var ct = xhr.getResponseHeader('content-type') || '',
811
                xml = type === 'xml' || !type && ct.indexOf('xml') >= 0,
812
                data = xml ? xhr.responseXML : xhr.responseText;
813

    
814
            if (xml && data.documentElement.nodeName === 'parsererror') {
815
                if ($.error) {
816
                    $.error('parsererror');
817
                }
818
            }
819
            if (s && s.dataFilter) {
820
                data = s.dataFilter(data, type);
821
            }
822
            if (typeof data === 'string') {
823
                if (type === 'json' || !type && ct.indexOf('json') >= 0) {
824
                    data = parseJSON(data);
825
                } else if (type === "script" || !type && ct.indexOf("javascript") >= 0) {
826
                    $.globalEval(data);
827
                }
828
            }
829
            return data;
830
        };
831

    
832
        return deferred;
833
    }
834
};
835

    
836
/**
837
 * ajaxForm() provides a mechanism for fully automating form submission.
838
 *
839
 * The advantages of using this method instead of ajaxSubmit() are:
840
 *
841
 * 1: This method will include coordinates for <input type="image" /> elements (if the element
842
 *    is used to submit the form).
843
 * 2. This method will include the submit element's name/value data (for the element that was
844
 *    used to submit the form).
845
 * 3. This method binds the submit() method to the form for you.
846
 *
847
 * The options argument for ajaxForm works exactly as it does for ajaxSubmit.  ajaxForm merely
848
 * passes the options argument along after properly binding events for submit elements and
849
 * the form itself.
850
 */
851
$.fn.ajaxForm = function(options) {
852
    options = options || {};
853
    options.delegation = options.delegation && $.isFunction($.fn.on);
854

    
855
    // in jQuery 1.3+ we can fix mistakes with the ready state
856
    if (!options.delegation && this.length === 0) {
857
        var o = { s: this.selector, c: this.context };
858
        if (!$.isReady && o.s) {
859
            log('DOM not ready, queuing ajaxForm');
860
            $(function() {
861
                $(o.s,o.c).ajaxForm(options);
862
            });
863
            return this;
864
        }
865
        // is your DOM ready?  http://docs.jquery.com/Tutorials:Introducing_$(document).ready()
866
        log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)'));
867
        return this;
868
    }
869

    
870
    if ( options.delegation ) {
871
        $(document)
872
            .off('submit.form-plugin', this.selector, doAjaxSubmit)
873
            .off('click.form-plugin', this.selector, captureSubmittingElement)
874
            .on('submit.form-plugin', this.selector, options, doAjaxSubmit)
875
            .on('click.form-plugin', this.selector, options, captureSubmittingElement);
876
        return this;
877
    }
878

    
879
    return this.ajaxFormUnbind()
880
        .bind('submit.form-plugin', options, doAjaxSubmit)
881
        .bind('click.form-plugin', options, captureSubmittingElement);
882
};
883

    
884
// private event handlers
885
function doAjaxSubmit(e) {
886
    /*jshint validthis:true */
887
    var options = e.data;
888
    if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed
889
        e.preventDefault();
890
        $(e.target).ajaxSubmit(options); // #365
891
    }
892
}
893

    
894
function captureSubmittingElement(e) {
895
    /*jshint validthis:true */
896
    var target = e.target;
897
    var $el = $(target);
898
    if (!($el.is("[type=submit],[type=image]"))) {
899
        // is this a child element of the submit el?  (ex: a span within a button)
900
        var t = $el.closest('[type=submit]');
901
        if (t.length === 0) {
902
            return;
903
        }
904
        target = t[0];
905
    }
906
    var form = this;
907
    form.clk = target;
908
    if (target.type == 'image') {
909
        if (e.offsetX !== undefined) {
910
            form.clk_x = e.offsetX;
911
            form.clk_y = e.offsetY;
912
        } else if (typeof $.fn.offset == 'function') {
913
            var offset = $el.offset();
914
            form.clk_x = e.pageX - offset.left;
915
            form.clk_y = e.pageY - offset.top;
916
        } else {
917
            form.clk_x = e.pageX - target.offsetLeft;
918
            form.clk_y = e.pageY - target.offsetTop;
919
        }
920
    }
921
    // clear form vars
922
    setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 100);
923
}
924

    
925

    
926
// ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
927
$.fn.ajaxFormUnbind = function() {
928
    return this.unbind('submit.form-plugin click.form-plugin');
929
};
930

    
931
/**
932
 * formToArray() gathers form element data into an array of objects that can
933
 * be passed to any of the following ajax functions: $.get, $.post, or load.
934
 * Each object in the array has both a 'name' and 'value' property.  An example of
935
 * an array for a simple login form might be:
936
 *
937
 * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ]
938
 *
939
 * It is this array that is passed to pre-submit callback functions provided to the
940
 * ajaxSubmit() and ajaxForm() methods.
941
 */
942
$.fn.formToArray = function(semantic, elements) {
943
    var a = [];
944
    if (this.length === 0) {
945
        return a;
946
    }
947

    
948
    var form = this[0];
949
    var formId = this.attr('id');
950
    var els = semantic ? form.getElementsByTagName('*') : form.elements;
951
    var els2;
952

    
953
    if (els && !/MSIE [678]/.test(navigator.userAgent)) { // #390
954
        els = $(els).get();  // convert to standard array
955
    }
956

    
957
    // #386; account for inputs outside the form which use the 'form' attribute
958
    if ( formId ) {
959
        els2 = $(':input[form=' + formId + ']').get();
960
        if ( els2.length ) {
961
            els = (els || []).concat(els2);
962
        }
963
    }
964

    
965
    if (!els || !els.length) {
966
        return a;
967
    }
968

    
969
    var i,j,n,v,el,max,jmax;
970
    for(i=0, max=els.length; i < max; i++) {
971
        el = els[i];
972
        n = el.name;
973
        if (!n || el.disabled) {
974
            continue;
975
        }
976

    
977
        if (semantic && form.clk && el.type == "image") {
978
            // handle image inputs on the fly when semantic == true
979
            if(form.clk == el) {
980
                a.push({name: n, value: $(el).val(), type: el.type });
981
                a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
982
            }
983
            continue;
984
        }
985

    
986
        v = $.fieldValue(el, true);
987
        if (v && v.constructor == Array) {
988
            if (elements) {
989
                elements.push(el);
990
            }
991
            for(j=0, jmax=v.length; j < jmax; j++) {
992
                a.push({name: n, value: v[j]});
993
            }
994
        }
995
        else if (feature.fileapi && el.type == 'file') {
996
            if (elements) {
997
                elements.push(el);
998
            }
999
            var files = el.files;
1000
            if (files.length) {
1001
                for (j=0; j < files.length; j++) {
1002
                    a.push({name: n, value: files[j], type: el.type});
1003
                }
1004
            }
1005
            else {
1006
                // #180
1007
                a.push({ name: n, value: '', type: el.type });
1008
            }
1009
        }
1010
        else if (v !== null && typeof v != 'undefined') {
1011
            if (elements) {
1012
                elements.push(el);
1013
            }
1014
            a.push({name: n, value: v, type: el.type, required: el.required});
1015
        }
1016
    }
1017

    
1018
    if (!semantic && form.clk) {
1019
        // input type=='image' are not found in elements array! handle it here
1020
        var $input = $(form.clk), input = $input[0];
1021
        n = input.name;
1022
        if (n && !input.disabled && input.type == 'image') {
1023
            a.push({name: n, value: $input.val()});
1024
            a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
1025
        }
1026
    }
1027
    return a;
1028
};
1029

    
1030
/**
1031
 * Serializes form data into a 'submittable' string. This method will return a string
1032
 * in the format: name1=value1&amp;name2=value2
1033
 */
1034
$.fn.formSerialize = function(semantic) {
1035
    //hand off to jQuery.param for proper encoding
1036
    return $.param(this.formToArray(semantic));
1037
};
1038

    
1039
/**
1040
 * Serializes all field elements in the jQuery object into a query string.
1041
 * This method will return a string in the format: name1=value1&amp;name2=value2
1042
 */
1043
$.fn.fieldSerialize = function(successful) {
1044
    var a = [];
1045
    this.each(function() {
1046
        var n = this.name;
1047
        if (!n) {
1048
            return;
1049
        }
1050
        var v = $.fieldValue(this, successful);
1051
        if (v && v.constructor == Array) {
1052
            for (var i=0,max=v.length; i < max; i++) {
1053
                a.push({name: n, value: v[i]});
1054
            }
1055
        }
1056
        else if (v !== null && typeof v != 'undefined') {
1057
            a.push({name: this.name, value: v});
1058
        }
1059
    });
1060
    //hand off to jQuery.param for proper encoding
1061
    return $.param(a);
1062
};
1063

    
1064
/**
1065
 * Returns the value(s) of the element in the matched set.  For example, consider the following form:
1066
 *
1067
 *  <form><fieldset>
1068
 *      <input name="A" type="text" />
1069
 *      <input name="A" type="text" />
1070
 *      <input name="B" type="checkbox" value="B1" />
1071
 *      <input name="B" type="checkbox" value="B2"/>
1072
 *      <input name="C" type="radio" value="C1" />
1073
 *      <input name="C" type="radio" value="C2" />
1074
 *  </fieldset></form>
1075
 *
1076
 *  var v = $('input[type=text]').fieldValue();
1077
 *  // if no values are entered into the text inputs
1078
 *  v == ['','']
1079
 *  // if values entered into the text inputs are 'foo' and 'bar'
1080
 *  v == ['foo','bar']
1081
 *
1082
 *  var v = $('input[type=checkbox]').fieldValue();
1083
 *  // if neither checkbox is checked
1084
 *  v === undefined
1085
 *  // if both checkboxes are checked
1086
 *  v == ['B1', 'B2']
1087
 *
1088
 *  var v = $('input[type=radio]').fieldValue();
1089
 *  // if neither radio is checked
1090
 *  v === undefined
1091
 *  // if first radio is checked
1092
 *  v == ['C1']
1093
 *
1094
 * The successful argument controls whether or not the field element must be 'successful'
1095
 * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls).
1096
 * The default value of the successful argument is true.  If this value is false the value(s)
1097
 * for each element is returned.
1098
 *
1099
 * Note: This method *always* returns an array.  If no valid value can be determined the
1100
 *    array will be empty, otherwise it will contain one or more values.
1101
 */
1102
$.fn.fieldValue = function(successful) {
1103
    for (var val=[], i=0, max=this.length; i < max; i++) {
1104
        var el = this[i];
1105
        var v = $.fieldValue(el, successful);
1106
        if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) {
1107
            continue;
1108
        }
1109
        if (v.constructor == Array) {
1110
            $.merge(val, v);
1111
        }
1112
        else {
1113
            val.push(v);
1114
        }
1115
    }
1116
    return val;
1117
};
1118

    
1119
/**
1120
 * Returns the value of the field element.
1121
 */
1122
$.fieldValue = function(el, successful) {
1123
    var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
1124
    if (successful === undefined) {
1125
        successful = true;
1126
    }
1127

    
1128
    if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
1129
        (t == 'checkbox' || t == 'radio') && !el.checked ||
1130
        (t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
1131
        tag == 'select' && el.selectedIndex == -1)) {
1132
            return null;
1133
    }
1134

    
1135
    if (tag == 'select') {
1136
        var index = el.selectedIndex;
1137
        if (index < 0) {
1138
            return null;
1139
        }
1140
        var a = [], ops = el.options;
1141
        var one = (t == 'select-one');
1142
        var max = (one ? index+1 : ops.length);
1143
        for(var i=(one ? index : 0); i < max; i++) {
1144
            var op = ops[i];
1145
            if (op.selected) {
1146
                var v = op.value;
1147
                if (!v) { // extra pain for IE...
1148
                    v = (op.attributes && op.attributes.value && !(op.attributes.value.specified)) ? op.text : op.value;
1149
                }
1150
                if (one) {
1151
                    return v;
1152
                }
1153
                a.push(v);
1154
            }
1155
        }
1156
        return a;
1157
    }
1158
    return $(el).val();
1159
};
1160

    
1161
/**
1162
 * Clears the form data.  Takes the following actions on the form's input fields:
1163
 *  - input text fields will have their 'value' property set to the empty string
1164
 *  - select elements will have their 'selectedIndex' property set to -1
1165
 *  - checkbox and radio inputs will have their 'checked' property set to false
1166
 *  - inputs of type submit, button, reset, and hidden will *not* be effected
1167
 *  - button elements will *not* be effected
1168
 */
1169
$.fn.clearForm = function(includeHidden) {
1170
    return this.each(function() {
1171
        $('input,select,textarea', this).clearFields(includeHidden);
1172
    });
1173
};
1174

    
1175
/**
1176
 * Clears the selected form elements.
1177
 */
1178
$.fn.clearFields = $.fn.clearInputs = function(includeHidden) {
1179
    var re = /^(?:color|date|datetime|email|month|number|password|range|search|tel|text|time|url|week)$/i; // 'hidden' is not in this list
1180
    return this.each(function() {
1181
        var t = this.type, tag = this.tagName.toLowerCase();
1182
        if (re.test(t) || tag == 'textarea') {
1183
            this.value = '';
1184
        }
1185
        else if (t == 'checkbox' || t == 'radio') {
1186
            this.checked = false;
1187
        }
1188
        else if (tag == 'select') {
1189
            this.selectedIndex = -1;
1190
        }
1191
    else if (t == "file") {
1192
      if (/MSIE/.test(navigator.userAgent)) {
1193
        $(this).replaceWith($(this).clone(true));
1194
      } else {
1195
        $(this).val('');
1196
      }
1197
    }
1198
        else if (includeHidden) {
1199
            // includeHidden can be the value true, or it can be a selector string
1200
            // indicating a special test; for example:
1201
            //  $('#myForm').clearForm('.special:hidden')
1202
            // the above would clean hidden inputs that have the class of 'special'
1203
            if ( (includeHidden === true && /hidden/.test(t)) ||
1204
                 (typeof includeHidden == 'string' && $(this).is(includeHidden)) ) {
1205
                this.value = '';
1206
            }
1207
        }
1208
    });
1209
};
1210

    
1211
/**
1212
 * Resets the form data.  Causes all form elements to be reset to their original value.
1213
 */
1214
$.fn.resetForm = function() {
1215
    return this.each(function() {
1216
        // guard against an input with the name of 'reset'
1217
        // note that IE reports the reset function as an 'object'
1218
        if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType)) {
1219
            this.reset();
1220
        }
1221
    });
1222
};
1223

    
1224
/**
1225
 * Enables or disables any matching elements.
1226
 */
1227
$.fn.enable = function(b) {
1228
    if (b === undefined) {
1229
        b = true;
1230
    }
1231
    return this.each(function() {
1232
        this.disabled = !b;
1233
    });
1234
};
1235

    
1236
/**
1237
 * Checks/unchecks any matching checkboxes or radio buttons and
1238
 * selects/deselects and matching option elements.
1239
 */
1240
$.fn.selected = function(select) {
1241
    if (select === undefined) {
1242
        select = true;
1243
    }
1244
    return this.each(function() {
1245
        var t = this.type;
1246
        if (t == 'checkbox' || t == 'radio') {
1247
            this.checked = select;
1248
        }
1249
        else if (this.tagName.toLowerCase() == 'option') {
1250
            var $sel = $(this).parent('select');
1251
            if (select && $sel[0] && $sel[0].type == 'select-one') {
1252
                // deselect all other options
1253
                $sel.find('option').selected(false);
1254
            }
1255
            this.selected = select;
1256
        }
1257
    });
1258
};
1259

    
1260
// expose debug var
1261
$.fn.ajaxSubmit.debug = false;
1262

    
1263
// helper fn for console logging
1264
function log() {
1265
    if (!$.fn.ajaxSubmit.debug) {
1266
        return;
1267
    }
1268
    var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,'');
1269
    if (window.console && window.console.log) {
1270
        window.console.log(msg);
1271
    }
1272
    else if (window.opera && window.opera.postError) {
1273
        window.opera.postError(msg);
1274
    }
1275
}
1276

    
1277
}));
1278

    
(18-18/22)