[SPIP][PLUGINS] v3.0-->v3.2
[lhc/web/www.git] / www / prive / javascript / jquery.form.js
1 /*!
2 * jQuery Form Plugin
3 * version: 4.2.2
4 * Requires jQuery v1.7.2 or later
5 * Project repository: https://github.com/jquery-form/form
6
7 * Copyright 2017 Kevin Morris
8 * Copyright 2006 M. Alsup
9
10 * Dual licensed under the LGPL-2.1+ or MIT licenses
11 * https://github.com/jquery-form/form#license
12
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
21 */
22 /* global ActiveXObject */
23
24 /* eslint-disable */
25 (function (factory) {
26 if (typeof define === 'function' && define.amd) {
27 // AMD. Register as an anonymous module.
28 define(['jquery'], factory);
29 } else if (typeof module === 'object' && module.exports) {
30 // Node/CommonJS
31 module.exports = function( root, jQuery ) {
32 if (typeof jQuery === 'undefined') {
33 // require('jQuery') returns a factory that requires window to build a jQuery instance, we normalize how we use modules
34 // that require this pattern but the window provided is a noop if it's defined (how jquery works)
35 if (typeof window !== 'undefined') {
36 jQuery = require('jquery');
37 }
38 else {
39 jQuery = require('jquery')(root);
40 }
41 }
42 factory(jQuery);
43 return jQuery;
44 };
45 } else {
46 // Browser globals
47 factory(jQuery);
48 }
49
50 }(function ($) {
51 /* eslint-enable */
52 'use strict';
53
54 /*
55 Usage Note:
56 -----------
57 Do not use both ajaxSubmit and ajaxForm on the same form. These
58 functions are mutually exclusive. Use ajaxSubmit if you want
59 to bind your own submit handler to the form. For example,
60
61 $(document).ready(function() {
62 $('#myForm').on('submit', function(e) {
63 e.preventDefault(); // <-- important
64 $(this).ajaxSubmit({
65 target: '#output'
66 });
67 });
68 });
69
70 Use ajaxForm when you want the plugin to manage all the event binding
71 for you. For example,
72
73 $(document).ready(function() {
74 $('#myForm').ajaxForm({
75 target: '#output'
76 });
77 });
78
79 You can also use ajaxForm with delegation (requires jQuery v1.7+), so the
80 form does not have to exist when you invoke ajaxForm:
81
82 $('#myForm').ajaxForm({
83 delegation: true,
84 target: '#output'
85 });
86
87 When using ajaxForm, the ajaxSubmit function will be invoked for you
88 at the appropriate time.
89 */
90
91 var rCRLF = /\r?\n/g;
92
93 /**
94 * Feature detection
95 */
96 var feature = {};
97
98 feature.fileapi = $('<input type="file">').get(0).files !== undefined;
99 feature.formdata = (typeof window.FormData !== 'undefined');
100
101 var hasProp = !!$.fn.prop;
102
103 // attr2 uses prop when it can but checks the return type for
104 // an expected string. This accounts for the case where a form
105 // contains inputs with names like "action" or "method"; in those
106 // cases "prop" returns the element
107 $.fn.attr2 = function() {
108 if (!hasProp) {
109 return this.attr.apply(this, arguments);
110 }
111
112 var val = this.prop.apply(this, arguments);
113
114 if ((val && val.jquery) || typeof val === 'string') {
115 return val;
116 }
117
118 return this.attr.apply(this, arguments);
119 };
120
121 /**
122 * ajaxSubmit() provides a mechanism for immediately submitting
123 * an HTML form using AJAX.
124 *
125 * @param {object|string} options jquery.form.js parameters or custom url for submission
126 * @param {object} data extraData
127 * @param {string} dataType ajax dataType
128 * @param {function} onSuccess ajax success callback function
129 */
130 $.fn.ajaxSubmit = function(options, data, dataType, onSuccess) {
131 // fast fail if nothing selected (http://dev.jquery.com/ticket/2752)
132 if (!this.length) {
133 log('ajaxSubmit: skipping submit process - no element selected');
134
135 return this;
136 }
137
138 /* eslint consistent-this: ["error", "$form"] */
139 var method, action, url, $form = this;
140
141 if (typeof options === 'function') {
142 options = {success: options};
143
144 } else if (typeof options === 'string' || (options === false && arguments.length > 0)) {
145 options = {
146 'url' : options,
147 'data' : data,
148 'dataType' : dataType
149 };
150
151 if (typeof onSuccess === 'function') {
152 options.success = onSuccess;
153 }
154
155 } else if (typeof options === 'undefined') {
156 options = {};
157 }
158
159 method = options.method || options.type || this.attr2('method');
160 action = options.url || this.attr2('action');
161
162 url = (typeof action === 'string') ? $.trim(action) : '';
163 url = url || window.location.href || '';
164 if (url) {
165 // clean url (don't include hash vaue)
166 url = (url.match(/^([^#]+)/) || [])[1];
167 }
168
169 options = $.extend(true, {
170 url : url,
171 success : $.ajaxSettings.success,
172 type : method || $.ajaxSettings.type,
173 iframeSrc : /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank' // eslint-disable-line no-script-url
174 }, options);
175
176 // hook for manipulating the form data before it is extracted;
177 // convenient for use with rich editors like tinyMCE or FCKEditor
178 var veto = {};
179
180 this.trigger('form-pre-serialize', [this, options, veto]);
181
182 if (veto.veto) {
183 log('ajaxSubmit: submit vetoed via form-pre-serialize trigger');
184
185 return this;
186 }
187
188 // provide opportunity to alter form data before it is serialized
189 if (options.beforeSerialize && options.beforeSerialize(this, options) === false) {
190 log('ajaxSubmit: submit aborted via beforeSerialize callback');
191
192 return this;
193 }
194
195 var traditional = options.traditional;
196
197 if (typeof traditional === 'undefined') {
198 traditional = $.ajaxSettings.traditional;
199 }
200
201 var elements = [];
202 var qx, a = this.formToArray(options.semantic, elements, options.filtering);
203
204 if (options.data) {
205 var optionsData = $.isFunction(options.data) ? options.data(a) : options.data;
206
207 options.extraData = optionsData;
208 qx = $.param(optionsData, traditional);
209 }
210
211 // give pre-submit callback an opportunity to abort the submit
212 if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) {
213 log('ajaxSubmit: submit aborted via beforeSubmit callback');
214
215 return this;
216 }
217
218 // fire vetoable 'validate' event
219 this.trigger('form-submit-validate', [a, this, options, veto]);
220 if (veto.veto) {
221 log('ajaxSubmit: submit vetoed via form-submit-validate trigger');
222
223 return this;
224 }
225
226 var q = $.param(a, traditional);
227
228 if (qx) {
229 q = (q ? (q + '&' + qx) : qx);
230 }
231
232 if (options.type.toUpperCase() === 'GET') {
233 options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
234 options.data = null; // data is null for 'get'
235 } else {
236 options.data = q; // data is the query string for 'post'
237 }
238
239 var callbacks = [];
240
241 if (options.resetForm) {
242 callbacks.push(function() {
243 $form.resetForm();
244 });
245 }
246
247 if (options.clearForm) {
248 callbacks.push(function() {
249 $form.clearForm(options.includeHidden);
250 });
251 }
252
253 // perform a load on the target only if dataType is not provided
254 if (!options.dataType && options.target) {
255 var oldSuccess = options.success || function(){};
256
257 callbacks.push(function(data, textStatus, jqXHR) {
258 var successArguments = arguments,
259 fn = options.replaceTarget ? 'replaceWith' : 'html';
260
261 $(options.target)[fn](data).each(function(){
262 oldSuccess.apply(this, successArguments);
263 });
264 });
265
266 } else if (options.success) {
267 if ($.isArray(options.success)) {
268 $.merge(callbacks, options.success);
269 } else {
270 callbacks.push(options.success);
271 }
272 }
273
274 options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg
275 var context = options.context || this; // jQuery 1.4+ supports scope context
276
277 for (var i = 0, max = callbacks.length; i < max; i++) {
278 callbacks[i].apply(context, [data, status, xhr || $form, $form]);
279 }
280 };
281
282 if (options.error) {
283 var oldError = options.error;
284
285 options.error = function(xhr, status, error) {
286 var context = options.context || this;
287
288 oldError.apply(context, [xhr, status, error, $form]);
289 };
290 }
291
292 if (options.complete) {
293 var oldComplete = options.complete;
294
295 options.complete = function(xhr, status) {
296 var context = options.context || this;
297
298 oldComplete.apply(context, [xhr, status, $form]);
299 };
300 }
301
302 // are there files to upload?
303
304 // [value] (issue #113), also see comment:
305 // https://github.com/malsup/form/commit/588306aedba1de01388032d5f42a60159eea9228#commitcomment-2180219
306 var fileInputs = $('input[type=file]:enabled', this).filter(function() {
307 return $(this).val() !== '';
308 });
309 var hasFileInputs = fileInputs.length > 0;
310 var mp = 'multipart/form-data';
311 var multipart = ($form.attr('enctype') === mp || $form.attr('encoding') === mp);
312 var fileAPI = feature.fileapi && feature.formdata;
313
314 log('fileAPI :' + fileAPI);
315
316 var shouldUseFrame = (hasFileInputs || multipart) && !fileAPI;
317 var jqxhr;
318
319 // options.iframe allows user to force iframe mode
320 // 06-NOV-09: now defaulting to iframe mode if file input is detected
321 if (options.iframe !== false && (options.iframe || shouldUseFrame)) {
322 // hack to fix Safari hang (thanks to Tim Molendijk for this)
323 // see: http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
324 if (options.closeKeepAlive) {
325 $.get(options.closeKeepAlive, function() {
326 jqxhr = fileUploadIframe(a);
327 });
328
329 } else {
330 jqxhr = fileUploadIframe(a);
331 }
332
333 } else if ((hasFileInputs || multipart) && fileAPI) {
334 jqxhr = fileUploadXhr(a);
335
336 } else {
337 jqxhr = $.ajax(options);
338 }
339
340 $form.removeData('jqxhr').data('jqxhr', jqxhr);
341
342 // clear element array
343 for (var k = 0; k < elements.length; k++) {
344 elements[k] = null;
345 }
346
347 // fire 'notify' event
348 this.trigger('form-submit-notify', [this, options]);
349
350 return this;
351
352 // utility fn for deep serialization
353 function deepSerialize(extraData) {
354 var serialized = $.param(extraData, options.traditional).split('&');
355 var len = serialized.length;
356 var result = [];
357 var i, part;
358
359 for (i = 0; i < len; i++) {
360 // #252; undo param space replacement
361 serialized[i] = serialized[i].replace(/\+/g, ' ');
362 part = serialized[i].split('=');
363 // #278; use array instead of object storage, favoring array serializations
364 result.push([decodeURIComponent(part[0]), decodeURIComponent(part[1])]);
365 }
366
367 return result;
368 }
369
370 // XMLHttpRequest Level 2 file uploads (big hat tip to francois2metz)
371 function fileUploadXhr(a) {
372 var formdata = new FormData();
373
374 for (var i = 0; i < a.length; i++) {
375 formdata.append(a[i].name, a[i].value);
376 }
377
378 if (options.extraData) {
379 var serializedData = deepSerialize(options.extraData);
380
381 for (i = 0; i < serializedData.length; i++) {
382 if (serializedData[i]) {
383 formdata.append(serializedData[i][0], serializedData[i][1]);
384 }
385 }
386 }
387
388 options.data = null;
389
390 var s = $.extend(true, {}, $.ajaxSettings, options, {
391 contentType : false,
392 processData : false,
393 cache : false,
394 type : method || 'POST'
395 });
396
397 if (options.uploadProgress) {
398 // workaround because jqXHR does not expose upload property
399 s.xhr = function() {
400 var xhr = $.ajaxSettings.xhr();
401
402 if (xhr.upload) {
403 xhr.upload.addEventListener('progress', function(event) {
404 var percent = 0;
405 var position = event.loaded || event.position; /* event.position is deprecated */
406 var total = event.total;
407
408 if (event.lengthComputable) {
409 percent = Math.ceil(position / total * 100);
410 }
411
412 options.uploadProgress(event, position, total, percent);
413 }, false);
414 }
415
416 return xhr;
417 };
418 }
419
420 s.data = null;
421
422 var beforeSend = s.beforeSend;
423
424 s.beforeSend = function(xhr, o) {
425 // Send FormData() provided by user
426 if (options.formData) {
427 o.data = options.formData;
428 } else {
429 o.data = formdata;
430 }
431
432 if (beforeSend) {
433 beforeSend.call(this, xhr, o);
434 }
435 };
436
437 return $.ajax(s);
438 }
439
440 // private function for handling file uploads (hat tip to YAHOO!)
441 function fileUploadIframe(a) {
442 var form = $form[0], el, i, s, g, id, $io, io, xhr, sub, n, timedOut, timeoutHandle;
443 var deferred = $.Deferred();
444
445 // #341
446 deferred.abort = function(status) {
447 xhr.abort(status);
448 };
449
450 if (a) {
451 // ensure that every serialized input is still enabled
452 for (i = 0; i < elements.length; i++) {
453 el = $(elements[i]);
454 if (hasProp) {
455 el.prop('disabled', false);
456 } else {
457 el.removeAttr('disabled');
458 }
459 }
460 }
461
462 s = $.extend(true, {}, $.ajaxSettings, options);
463 s.context = s.context || s;
464 id = 'jqFormIO' + new Date().getTime();
465 var ownerDocument = form.ownerDocument;
466 var $body = $form.closest('body');
467
468 if (s.iframeTarget) {
469 $io = $(s.iframeTarget, ownerDocument);
470 n = $io.attr2('name');
471 if (!n) {
472 $io.attr2('name', id);
473 } else {
474 id = n;
475 }
476
477 } else {
478 $io = $('<iframe name="' + id + '" src="' + s.iframeSrc + '" />', ownerDocument);
479 $io.css({position: 'absolute', top: '-1000px', left: '-1000px'});
480 }
481 io = $io[0];
482
483
484 xhr = { // mock object
485 aborted : 0,
486 responseText : null,
487 responseXML : null,
488 status : 0,
489 statusText : 'n/a',
490 getAllResponseHeaders : function() {},
491 getResponseHeader : function() {},
492 setRequestHeader : function() {},
493 abort : function(status) {
494 var e = (status === 'timeout' ? 'timeout' : 'aborted');
495
496 log('aborting upload... ' + e);
497 this.aborted = 1;
498
499 try { // #214, #257
500 if (io.contentWindow.document.execCommand) {
501 io.contentWindow.document.execCommand('Stop');
502 }
503 } catch (ignore) {}
504
505 $io.attr('src', s.iframeSrc); // abort op in progress
506 xhr.error = e;
507 if (s.error) {
508 s.error.call(s.context, xhr, e, status);
509 }
510
511 if (g) {
512 $.event.trigger('ajaxError', [xhr, s, e]);
513 }
514
515 if (s.complete) {
516 s.complete.call(s.context, xhr, e);
517 }
518 }
519 };
520
521 g = s.global;
522 // trigger ajax global events so that activity/block indicators work like normal
523 if (g && $.active++ === 0) {
524 $.event.trigger('ajaxStart');
525 }
526 if (g) {
527 $.event.trigger('ajaxSend', [xhr, s]);
528 }
529
530 if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) {
531 if (s.global) {
532 $.active--;
533 }
534 deferred.reject();
535
536 return deferred;
537 }
538
539 if (xhr.aborted) {
540 deferred.reject();
541
542 return deferred;
543 }
544
545 // add submitting element to data if we know it
546 sub = form.clk;
547 if (sub) {
548 n = sub.name;
549 if (n && !sub.disabled) {
550 s.extraData = s.extraData || {};
551 s.extraData[n] = sub.value;
552 if (sub.type === 'image') {
553 s.extraData[n + '.x'] = form.clk_x;
554 s.extraData[n + '.y'] = form.clk_y;
555 }
556 }
557 }
558
559 var CLIENT_TIMEOUT_ABORT = 1;
560 var SERVER_ABORT = 2;
561
562 function getDoc(frame) {
563 /* it looks like contentWindow or contentDocument do not
564 * carry the protocol property in ie8, when running under ssl
565 * frame.document is the only valid response document, since
566 * the protocol is know but not on the other two objects. strange?
567 * "Same origin policy" http://en.wikipedia.org/wiki/Same_origin_policy
568 */
569
570 var doc = null;
571
572 // IE8 cascading access check
573 try {
574 if (frame.contentWindow) {
575 doc = frame.contentWindow.document;
576 }
577 } catch (err) {
578 // IE8 access denied under ssl & missing protocol
579 log('cannot get iframe.contentWindow document: ' + err);
580 }
581
582 if (doc) { // successful getting content
583 return doc;
584 }
585
586 try { // simply checking may throw in ie8 under ssl or mismatched protocol
587 doc = frame.contentDocument ? frame.contentDocument : frame.document;
588 } catch (err) {
589 // last attempt
590 log('cannot get iframe.contentDocument: ' + err);
591 doc = frame.document;
592 }
593
594 return doc;
595 }
596
597 // Rails CSRF hack (thanks to Yvan Barthelemy)
598 var csrf_token = $('meta[name=csrf-token]').attr('content');
599 var csrf_param = $('meta[name=csrf-param]').attr('content');
600
601 if (csrf_param && csrf_token) {
602 s.extraData = s.extraData || {};
603 s.extraData[csrf_param] = csrf_token;
604 }
605
606 // take a breath so that pending repaints get some cpu time before the upload starts
607 function doSubmit() {
608 // make sure form attrs are set
609 var t = $form.attr2('target'),
610 a = $form.attr2('action'),
611 mp = 'multipart/form-data',
612 et = $form.attr('enctype') || $form.attr('encoding') || mp;
613
614 // update form attrs in IE friendly way
615 form.setAttribute('target', id);
616 if (!method || /post/i.test(method)) {
617 form.setAttribute('method', 'POST');
618 }
619 if (a !== s.url) {
620 form.setAttribute('action', s.url);
621 }
622
623 // ie borks in some cases when setting encoding
624 if (!s.skipEncodingOverride && (!method || /post/i.test(method))) {
625 $form.attr({
626 encoding : 'multipart/form-data',
627 enctype : 'multipart/form-data'
628 });
629 }
630
631 // support timout
632 if (s.timeout) {
633 timeoutHandle = setTimeout(function() {
634 timedOut = true; cb(CLIENT_TIMEOUT_ABORT);
635 }, s.timeout);
636 }
637
638 // look for server aborts
639 function checkState() {
640 try {
641 var state = getDoc(io).readyState;
642
643 log('state = ' + state);
644 if (state && state.toLowerCase() === 'uninitialized') {
645 setTimeout(checkState, 50);
646 }
647
648 } catch (e) {
649 log('Server abort: ', e, ' (', e.name, ')');
650 cb(SERVER_ABORT); // eslint-disable-line callback-return
651 if (timeoutHandle) {
652 clearTimeout(timeoutHandle);
653 }
654 timeoutHandle = undefined;
655 }
656 }
657
658 // add "extra" data to form if provided in options
659 var extraInputs = [];
660
661 try {
662 if (s.extraData) {
663 for (var n in s.extraData) {
664 if (s.extraData.hasOwnProperty(n)) {
665 // if using the $.param format that allows for multiple values with the same name
666 if ($.isPlainObject(s.extraData[n]) && s.extraData[n].hasOwnProperty('name') && s.extraData[n].hasOwnProperty('value')) {
667 extraInputs.push(
668 $('<input type="hidden" name="' + s.extraData[n].name + '">', ownerDocument).val(s.extraData[n].value)
669 .appendTo(form)[0]);
670 } else {
671 extraInputs.push(
672 $('<input type="hidden" name="' + n + '">', ownerDocument).val(s.extraData[n])
673 .appendTo(form)[0]);
674 }
675 }
676 }
677 }
678
679 if (!s.iframeTarget) {
680 // add iframe to doc and submit the form
681 $io.appendTo($body);
682 }
683
684 if (io.attachEvent) {
685 io.attachEvent('onload', cb);
686 } else {
687 io.addEventListener('load', cb, false);
688 }
689
690 setTimeout(checkState, 15);
691
692 try {
693 form.submit();
694
695 } catch (err) {
696 // just in case form has element with name/id of 'submit'
697 var submitFn = document.createElement('form').submit;
698
699 submitFn.apply(form);
700 }
701
702 } finally {
703 // reset attrs and remove "extra" input elements
704 form.setAttribute('action', a);
705 form.setAttribute('enctype', et); // #380
706 if (t) {
707 form.setAttribute('target', t);
708 } else {
709 $form.removeAttr('target');
710 }
711 $(extraInputs).remove();
712 }
713 }
714
715 if (s.forceSync) {
716 doSubmit();
717 } else {
718 setTimeout(doSubmit, 10); // this lets dom updates render
719 }
720
721 var data, doc, domCheckCount = 50, callbackProcessed;
722
723 function cb(e) {
724 if (xhr.aborted || callbackProcessed) {
725 return;
726 }
727
728 doc = getDoc(io);
729 if (!doc) {
730 log('cannot access response document');
731 e = SERVER_ABORT;
732 }
733 if (e === CLIENT_TIMEOUT_ABORT && xhr) {
734 xhr.abort('timeout');
735 deferred.reject(xhr, 'timeout');
736
737 return;
738
739 } else if (e === SERVER_ABORT && xhr) {
740 xhr.abort('server abort');
741 deferred.reject(xhr, 'error', 'server abort');
742
743 return;
744 }
745
746 if (!doc || doc.location.href === s.iframeSrc) {
747 // response not received yet
748 if (!timedOut) {
749 return;
750 }
751 }
752
753 if (io.detachEvent) {
754 io.detachEvent('onload', cb);
755 } else {
756 io.removeEventListener('load', cb, false);
757 }
758
759 var status = 'success', errMsg;
760
761 try {
762 if (timedOut) {
763 throw 'timeout';
764 }
765
766 var isXml = s.dataType === 'xml' || doc.XMLDocument || $.isXMLDoc(doc);
767
768 log('isXml=' + isXml);
769
770 if (!isXml && window.opera && (doc.body === null || !doc.body.innerHTML)) {
771 if (--domCheckCount) {
772 // in some browsers (Opera) the iframe DOM is not always traversable when
773 // the onload callback fires, so we loop a bit to accommodate
774 log('requeing onLoad callback, DOM not available');
775 setTimeout(cb, 250);
776
777 return;
778 }
779 // let this fall through because server response could be an empty document
780 // log('Could not access iframe DOM after mutiple tries.');
781 // throw 'DOMException: not available';
782 }
783
784 // log('response detected');
785 var docRoot = doc.body ? doc.body : doc.documentElement;
786
787 xhr.responseText = docRoot ? docRoot.innerHTML : null;
788 xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
789 if (isXml) {
790 s.dataType = 'xml';
791 }
792 xhr.getResponseHeader = function(header){
793 var headers = {'content-type': s.dataType};
794
795 return headers[header.toLowerCase()];
796 };
797 // support for XHR 'status' & 'statusText' emulation :
798 if (docRoot) {
799 xhr.status = Number(docRoot.getAttribute('status')) || xhr.status;
800 xhr.statusText = docRoot.getAttribute('statusText') || xhr.statusText;
801 }
802
803 var dt = (s.dataType || '').toLowerCase();
804 var scr = /(json|script|text)/.test(dt);
805
806 if (scr || s.textarea) {
807 // see if user embedded response in textarea
808 var ta = doc.getElementsByTagName('textarea')[0];
809
810 if (ta) {
811 xhr.responseText = ta.value;
812 // support for XHR 'status' & 'statusText' emulation :
813 xhr.status = Number(ta.getAttribute('status')) || xhr.status;
814 xhr.statusText = ta.getAttribute('statusText') || xhr.statusText;
815
816 } else if (scr) {
817 // account for browsers injecting pre around json response
818 var pre = doc.getElementsByTagName('pre')[0];
819 var b = doc.getElementsByTagName('body')[0];
820
821 if (pre) {
822 xhr.responseText = pre.textContent ? pre.textContent : pre.innerText;
823 } else if (b) {
824 xhr.responseText = b.textContent ? b.textContent : b.innerText;
825 }
826 }
827
828 } else if (dt === 'xml' && !xhr.responseXML && xhr.responseText) {
829 xhr.responseXML = toXml(xhr.responseText); // eslint-disable-line no-use-before-define
830 }
831
832 try {
833 data = httpData(xhr, dt, s); // eslint-disable-line no-use-before-define
834
835 } catch (err) {
836 status = 'parsererror';
837 xhr.error = errMsg = (err || status);
838 }
839
840 } catch (err) {
841 log('error caught: ', err);
842 status = 'error';
843 xhr.error = errMsg = (err || status);
844 }
845
846 if (xhr.aborted) {
847 log('upload aborted');
848 status = null;
849 }
850
851 if (xhr.status) { // we've set xhr.status
852 status = ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) ? 'success' : 'error';
853 }
854
855 // ordering of these callbacks/triggers is odd, but that's how $.ajax does it
856 if (status === 'success') {
857 if (s.success) {
858 s.success.call(s.context, data, 'success', xhr);
859 }
860
861 deferred.resolve(xhr.responseText, 'success', xhr);
862
863 if (g) {
864 $.event.trigger('ajaxSuccess', [xhr, s]);
865 }
866
867 } else if (status) {
868 if (typeof errMsg === 'undefined') {
869 errMsg = xhr.statusText;
870 }
871 if (s.error) {
872 s.error.call(s.context, xhr, status, errMsg);
873 }
874 deferred.reject(xhr, 'error', errMsg);
875 if (g) {
876 $.event.trigger('ajaxError', [xhr, s, errMsg]);
877 }
878 }
879
880 if (g) {
881 $.event.trigger('ajaxComplete', [xhr, s]);
882 }
883
884 if (g && !--$.active) {
885 $.event.trigger('ajaxStop');
886 }
887
888 if (s.complete) {
889 s.complete.call(s.context, xhr, status);
890 }
891
892 callbackProcessed = true;
893 if (s.timeout) {
894 clearTimeout(timeoutHandle);
895 }
896
897 // clean up
898 setTimeout(function() {
899 if (!s.iframeTarget) {
900 $io.remove();
901 } else { // adding else to clean up existing iframe response.
902 $io.attr('src', s.iframeSrc);
903 }
904 xhr.responseXML = null;
905 }, 100);
906 }
907
908 var toXml = $.parseXML || function(s, doc) { // use parseXML if available (jQuery 1.5+)
909 if (window.ActiveXObject) {
910 doc = new ActiveXObject('Microsoft.XMLDOM');
911 doc.async = 'false';
912 doc.loadXML(s);
913
914 } else {
915 doc = (new DOMParser()).parseFromString(s, 'text/xml');
916 }
917
918 return (doc && doc.documentElement && doc.documentElement.nodeName !== 'parsererror') ? doc : null;
919 };
920 var parseJSON = $.parseJSON || function(s) {
921 /* jslint evil:true */
922 return window['eval']('(' + s + ')'); // eslint-disable-line dot-notation
923 };
924
925 var httpData = function(xhr, type, s) { // mostly lifted from jq1.4.4
926
927 var ct = xhr.getResponseHeader('content-type') || '',
928 xml = ((type === 'xml' || !type) && ct.indexOf('xml') >= 0),
929 data = xml ? xhr.responseXML : xhr.responseText;
930
931 if (xml && data.documentElement.nodeName === 'parsererror') {
932 if ($.error) {
933 $.error('parsererror');
934 }
935 }
936 if (s && s.dataFilter) {
937 data = s.dataFilter(data, type);
938 }
939 if (typeof data === 'string') {
940 if ((type === 'json' || !type) && ct.indexOf('json') >= 0) {
941 data = parseJSON(data);
942 } else if ((type === 'script' || !type) && ct.indexOf('javascript') >= 0) {
943 $.globalEval(data);
944 }
945 }
946
947 return data;
948 };
949
950 return deferred;
951 }
952 };
953
954 /**
955 * ajaxForm() provides a mechanism for fully automating form submission.
956 *
957 * The advantages of using this method instead of ajaxSubmit() are:
958 *
959 * 1: This method will include coordinates for <input type="image"> elements (if the element
960 * is used to submit the form).
961 * 2. This method will include the submit element's name/value data (for the element that was
962 * used to submit the form).
963 * 3. This method binds the submit() method to the form for you.
964 *
965 * The options argument for ajaxForm works exactly as it does for ajaxSubmit. ajaxForm merely
966 * passes the options argument along after properly binding events for submit elements and
967 * the form itself.
968 */
969 $.fn.ajaxForm = function(options, data, dataType, onSuccess) {
970 if (typeof options === 'string' || (options === false && arguments.length > 0)) {
971 options = {
972 'url' : options,
973 'data' : data,
974 'dataType' : dataType
975 };
976
977 if (typeof onSuccess === 'function') {
978 options.success = onSuccess;
979 }
980 }
981
982 options = options || {};
983 options.delegation = options.delegation && $.isFunction($.fn.on);
984
985 // in jQuery 1.3+ we can fix mistakes with the ready state
986 if (!options.delegation && this.length === 0) {
987 var o = {s: this.selector, c: this.context};
988
989 if (!$.isReady && o.s) {
990 log('DOM not ready, queuing ajaxForm');
991 $(function() {
992 $(o.s, o.c).ajaxForm(options);
993 });
994
995 return this;
996 }
997
998 // is your DOM ready? http://docs.jquery.com/Tutorials:Introducing_$(document).ready()
999 log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)'));
1000
1001 return this;
1002 }
1003
1004 if (options.delegation) {
1005 $(document)
1006 .off('submit.form-plugin', this.selector, doAjaxSubmit)
1007 .off('click.form-plugin', this.selector, captureSubmittingElement)
1008 .on('submit.form-plugin', this.selector, options, doAjaxSubmit)
1009 .on('click.form-plugin', this.selector, options, captureSubmittingElement);
1010
1011 return this;
1012 }
1013
1014 return this.ajaxFormUnbind()
1015 .on('submit.form-plugin', options, doAjaxSubmit)
1016 .on('click.form-plugin', options, captureSubmittingElement);
1017 };
1018
1019 // private event handlers
1020 function doAjaxSubmit(e) {
1021 /* jshint validthis:true */
1022 var options = e.data;
1023
1024 if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed
1025 e.preventDefault();
1026 $(e.target).closest('form').ajaxSubmit(options); // #365
1027 }
1028 }
1029
1030 function captureSubmittingElement(e) {
1031 /* jshint validthis:true */
1032 var target = e.target;
1033 var $el = $(target);
1034
1035 if (!$el.is('[type=submit],[type=image]')) {
1036 // is this a child element of the submit el? (ex: a span within a button)
1037 var t = $el.closest('[type=submit]');
1038
1039 if (t.length === 0) {
1040 return;
1041 }
1042 target = t[0];
1043 }
1044
1045 var form = target.form;
1046
1047 form.clk = target;
1048
1049 if (target.type === 'image') {
1050 if (typeof e.offsetX !== 'undefined') {
1051 form.clk_x = e.offsetX;
1052 form.clk_y = e.offsetY;
1053
1054 } else if (typeof $.fn.offset === 'function') {
1055 var offset = $el.offset();
1056
1057 form.clk_x = e.pageX - offset.left;
1058 form.clk_y = e.pageY - offset.top;
1059
1060 } else {
1061 form.clk_x = e.pageX - target.offsetLeft;
1062 form.clk_y = e.pageY - target.offsetTop;
1063 }
1064 }
1065 // clear form vars
1066 setTimeout(function() {
1067 form.clk = form.clk_x = form.clk_y = null;
1068 }, 100);
1069 }
1070
1071
1072 // ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
1073 $.fn.ajaxFormUnbind = function() {
1074 return this.off('submit.form-plugin click.form-plugin');
1075 };
1076
1077 /**
1078 * formToArray() gathers form element data into an array of objects that can
1079 * be passed to any of the following ajax functions: $.get, $.post, or load.
1080 * Each object in the array has both a 'name' and 'value' property. An example of
1081 * an array for a simple login form might be:
1082 *
1083 * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ]
1084 *
1085 * It is this array that is passed to pre-submit callback functions provided to the
1086 * ajaxSubmit() and ajaxForm() methods.
1087 */
1088 $.fn.formToArray = function(semantic, elements, filtering) {
1089 var a = [];
1090
1091 if (this.length === 0) {
1092 return a;
1093 }
1094
1095 var form = this[0];
1096 var formId = this.attr('id');
1097 var els = (semantic || typeof form.elements === 'undefined') ? form.getElementsByTagName('*') : form.elements;
1098 var els2;
1099
1100 if (els) {
1101 els = $.makeArray(els); // convert to standard array
1102 }
1103
1104 // #386; account for inputs outside the form which use the 'form' attribute
1105 // FinesseRus: in non-IE browsers outside fields are already included in form.elements.
1106 if (formId && (semantic || /(Edge|Trident)\//.test(navigator.userAgent))) {
1107 els2 = $(':input[form="' + formId + '"]').get(); // hat tip @thet
1108 if (els2.length) {
1109 els = (els || []).concat(els2);
1110 }
1111 }
1112
1113 if (!els || !els.length) {
1114 return a;
1115 }
1116
1117 if ($.isFunction(filtering)) {
1118 els = $.map(els, filtering);
1119 }
1120
1121 var i, j, n, v, el, max, jmax;
1122
1123 for (i = 0, max = els.length; i < max; i++) {
1124 el = els[i];
1125 n = el.name;
1126 if (!n || el.disabled) {
1127 continue;
1128 }
1129
1130 if (semantic && form.clk && el.type === 'image') {
1131 // handle image inputs on the fly when semantic == true
1132 if (form.clk === el) {
1133 a.push({name: n, value: $(el).val(), type: el.type});
1134 a.push({name: n + '.x', value: form.clk_x}, {name: n + '.y', value: form.clk_y});
1135 }
1136 continue;
1137 }
1138
1139 v = $.fieldValue(el, true);
1140 if (v && v.constructor === Array) {
1141 if (elements) {
1142 elements.push(el);
1143 }
1144 for (j = 0, jmax = v.length; j < jmax; j++) {
1145 a.push({name: n, value: v[j]});
1146 }
1147
1148 } else if (feature.fileapi && el.type === 'file') {
1149 if (elements) {
1150 elements.push(el);
1151 }
1152
1153 var files = el.files;
1154
1155 if (files.length) {
1156 for (j = 0; j < files.length; j++) {
1157 a.push({name: n, value: files[j], type: el.type});
1158 }
1159 } else {
1160 // #180
1161 a.push({name: n, value: '', type: el.type});
1162 }
1163
1164 } else if (v !== null && typeof v !== 'undefined') {
1165 if (elements) {
1166 elements.push(el);
1167 }
1168 a.push({name: n, value: v, type: el.type, required: el.required});
1169 }
1170 }
1171
1172 if (!semantic && form.clk) {
1173 // input type=='image' are not found in elements array! handle it here
1174 var $input = $(form.clk), input = $input[0];
1175
1176 n = input.name;
1177
1178 if (n && !input.disabled && input.type === 'image') {
1179 a.push({name: n, value: $input.val()});
1180 a.push({name: n + '.x', value: form.clk_x}, {name: n + '.y', value: form.clk_y});
1181 }
1182 }
1183
1184 return a;
1185 };
1186
1187 /**
1188 * Serializes form data into a 'submittable' string. This method will return a string
1189 * in the format: name1=value1&amp;name2=value2
1190 */
1191 $.fn.formSerialize = function(semantic) {
1192 // hand off to jQuery.param for proper encoding
1193 return $.param(this.formToArray(semantic));
1194 };
1195
1196 /**
1197 * Serializes all field elements in the jQuery object into a query string.
1198 * This method will return a string in the format: name1=value1&amp;name2=value2
1199 */
1200 $.fn.fieldSerialize = function(successful) {
1201 var a = [];
1202
1203 this.each(function() {
1204 var n = this.name;
1205
1206 if (!n) {
1207 return;
1208 }
1209
1210 var v = $.fieldValue(this, successful);
1211
1212 if (v && v.constructor === Array) {
1213 for (var i = 0, max = v.length; i < max; i++) {
1214 a.push({name: n, value: v[i]});
1215 }
1216
1217 } else if (v !== null && typeof v !== 'undefined') {
1218 a.push({name: this.name, value: v});
1219 }
1220 });
1221
1222 // hand off to jQuery.param for proper encoding
1223 return $.param(a);
1224 };
1225
1226 /**
1227 * Returns the value(s) of the element in the matched set. For example, consider the following form:
1228 *
1229 * <form><fieldset>
1230 * <input name="A" type="text">
1231 * <input name="A" type="text">
1232 * <input name="B" type="checkbox" value="B1">
1233 * <input name="B" type="checkbox" value="B2">
1234 * <input name="C" type="radio" value="C1">
1235 * <input name="C" type="radio" value="C2">
1236 * </fieldset></form>
1237 *
1238 * var v = $('input[type=text]').fieldValue();
1239 * // if no values are entered into the text inputs
1240 * v === ['','']
1241 * // if values entered into the text inputs are 'foo' and 'bar'
1242 * v === ['foo','bar']
1243 *
1244 * var v = $('input[type=checkbox]').fieldValue();
1245 * // if neither checkbox is checked
1246 * v === undefined
1247 * // if both checkboxes are checked
1248 * v === ['B1', 'B2']
1249 *
1250 * var v = $('input[type=radio]').fieldValue();
1251 * // if neither radio is checked
1252 * v === undefined
1253 * // if first radio is checked
1254 * v === ['C1']
1255 *
1256 * The successful argument controls whether or not the field element must be 'successful'
1257 * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls).
1258 * The default value of the successful argument is true. If this value is false the value(s)
1259 * for each element is returned.
1260 *
1261 * Note: This method *always* returns an array. If no valid value can be determined the
1262 * array will be empty, otherwise it will contain one or more values.
1263 */
1264 $.fn.fieldValue = function(successful) {
1265 for (var val = [], i = 0, max = this.length; i < max; i++) {
1266 var el = this[i];
1267 var v = $.fieldValue(el, successful);
1268
1269 if (v === null || typeof v === 'undefined' || (v.constructor === Array && !v.length)) {
1270 continue;
1271 }
1272
1273 if (v.constructor === Array) {
1274 $.merge(val, v);
1275 } else {
1276 val.push(v);
1277 }
1278 }
1279
1280 return val;
1281 };
1282
1283 /**
1284 * Returns the value of the field element.
1285 */
1286 $.fieldValue = function(el, successful) {
1287 var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
1288
1289 if (typeof successful === 'undefined') {
1290 successful = true;
1291 }
1292
1293 /* eslint-disable no-mixed-operators */
1294 if (successful && (!n || el.disabled || t === 'reset' || t === 'button' ||
1295 (t === 'checkbox' || t === 'radio') && !el.checked ||
1296 (t === 'submit' || t === 'image') && el.form && el.form.clk !== el ||
1297 tag === 'select' && el.selectedIndex === -1)) {
1298 /* eslint-enable no-mixed-operators */
1299 return null;
1300 }
1301
1302 if (tag === 'select') {
1303 var index = el.selectedIndex;
1304
1305 if (index < 0) {
1306 return null;
1307 }
1308
1309 var a = [], ops = el.options;
1310 var one = (t === 'select-one');
1311 var max = (one ? index + 1 : ops.length);
1312
1313 for (var i = (one ? index : 0); i < max; i++) {
1314 var op = ops[i];
1315
1316 if (op.selected && !op.disabled) {
1317 var v = op.value;
1318
1319 if (!v) { // extra pain for IE...
1320 v = (op.attributes && op.attributes.value && !(op.attributes.value.specified)) ? op.text : op.value;
1321 }
1322
1323 if (one) {
1324 return v;
1325 }
1326
1327 a.push(v);
1328 }
1329 }
1330
1331 return a;
1332 }
1333
1334 return $(el).val().replace(rCRLF, '\r\n');
1335 };
1336
1337 /**
1338 * Clears the form data. Takes the following actions on the form's input fields:
1339 * - input text fields will have their 'value' property set to the empty string
1340 * - select elements will have their 'selectedIndex' property set to -1
1341 * - checkbox and radio inputs will have their 'checked' property set to false
1342 * - inputs of type submit, button, reset, and hidden will *not* be effected
1343 * - button elements will *not* be effected
1344 */
1345 $.fn.clearForm = function(includeHidden) {
1346 return this.each(function() {
1347 $('input,select,textarea', this).clearFields(includeHidden);
1348 });
1349 };
1350
1351 /**
1352 * Clears the selected form elements.
1353 */
1354 $.fn.clearFields = $.fn.clearInputs = function(includeHidden) {
1355 var re = /^(?:color|date|datetime|email|month|number|password|range|search|tel|text|time|url|week)$/i; // 'hidden' is not in this list
1356
1357 return this.each(function() {
1358 var t = this.type, tag = this.tagName.toLowerCase();
1359
1360 if (re.test(t) || tag === 'textarea') {
1361 this.value = '';
1362
1363 } else if (t === 'checkbox' || t === 'radio') {
1364 this.checked = false;
1365
1366 } else if (tag === 'select') {
1367 this.selectedIndex = -1;
1368
1369 } else if (t === 'file') {
1370 if (/MSIE/.test(navigator.userAgent)) {
1371 $(this).replaceWith($(this).clone(true));
1372 } else {
1373 $(this).val('');
1374 }
1375
1376 } else if (includeHidden) {
1377 // includeHidden can be the value true, or it can be a selector string
1378 // indicating a special test; for example:
1379 // $('#myForm').clearForm('.special:hidden')
1380 // the above would clean hidden inputs that have the class of 'special'
1381 if ((includeHidden === true && /hidden/.test(t)) ||
1382 (typeof includeHidden === 'string' && $(this).is(includeHidden))) {
1383 this.value = '';
1384 }
1385 }
1386 });
1387 };
1388
1389
1390 /**
1391 * Resets the form data or individual elements. Takes the following actions
1392 * on the selected tags:
1393 * - all fields within form elements will be reset to their original value
1394 * - input / textarea / select fields will be reset to their original value
1395 * - option / optgroup fields (for multi-selects) will defaulted individually
1396 * - non-multiple options will find the right select to default
1397 * - label elements will be searched against its 'for' attribute
1398 * - all others will be searched for appropriate children to default
1399 */
1400 $.fn.resetForm = function() {
1401 return this.each(function() {
1402 var el = $(this);
1403 var tag = this.tagName.toLowerCase();
1404
1405 switch (tag) {
1406 case 'input':
1407 this.checked = this.defaultChecked;
1408 // fall through
1409
1410 case 'textarea':
1411 this.value = this.defaultValue;
1412
1413 return true;
1414
1415 case 'option':
1416 case 'optgroup':
1417 var select = el.parents('select');
1418
1419 if (select.length && select[0].multiple) {
1420 if (tag === 'option') {
1421 this.selected = this.defaultSelected;
1422 } else {
1423 el.find('option').resetForm();
1424 }
1425 } else {
1426 select.resetForm();
1427 }
1428
1429 return true;
1430
1431 case 'select':
1432 el.find('option').each(function(i) { // eslint-disable-line consistent-return
1433 this.selected = this.defaultSelected;
1434 if (this.defaultSelected && !el[0].multiple) {
1435 el[0].selectedIndex = i;
1436
1437 return false;
1438 }
1439 });
1440
1441 return true;
1442
1443 case 'label':
1444 var forEl = $(el.attr('for'));
1445 var list = el.find('input,select,textarea');
1446
1447 if (forEl[0]) {
1448 list.unshift(forEl[0]);
1449 }
1450
1451 list.resetForm();
1452
1453 return true;
1454
1455 case 'form':
1456 // guard against an input with the name of 'reset'
1457 // note that IE reports the reset function as an 'object'
1458 if (typeof this.reset === 'function' || (typeof this.reset === 'object' && !this.reset.nodeType)) {
1459 this.reset();
1460 }
1461
1462 return true;
1463
1464 default:
1465 el.find('form,input,label,select,textarea').resetForm();
1466
1467 return true;
1468 }
1469 });
1470 };
1471
1472 /**
1473 * Enables or disables any matching elements.
1474 */
1475 $.fn.enable = function(b) {
1476 if (typeof b === 'undefined') {
1477 b = true;
1478 }
1479
1480 return this.each(function() {
1481 this.disabled = !b;
1482 });
1483 };
1484
1485 /**
1486 * Checks/unchecks any matching checkboxes or radio buttons and
1487 * selects/deselects and matching option elements.
1488 */
1489 $.fn.selected = function(select) {
1490 if (typeof select === 'undefined') {
1491 select = true;
1492 }
1493
1494 return this.each(function() {
1495 var t = this.type;
1496
1497 if (t === 'checkbox' || t === 'radio') {
1498 this.checked = select;
1499
1500 } else if (this.tagName.toLowerCase() === 'option') {
1501 var $sel = $(this).parent('select');
1502
1503 if (select && $sel[0] && $sel[0].type === 'select-one') {
1504 // deselect all other options
1505 $sel.find('option').selected(false);
1506 }
1507
1508 this.selected = select;
1509 }
1510 });
1511 };
1512
1513 // expose debug var
1514 $.fn.ajaxSubmit.debug = false;
1515
1516 // helper fn for console logging
1517 function log() {
1518 if (!$.fn.ajaxSubmit.debug) {
1519 return;
1520 }
1521
1522 var msg = '[jquery.form] ' + Array.prototype.join.call(arguments, '');
1523
1524 if (window.console && window.console.log) {
1525 window.console.log(msg);
1526
1527 } else if (window.opera && window.opera.postError) {
1528 window.opera.postError(msg);
1529 }
1530 }
1531 }));