css header program
[libreplanet-static.git] / 2019 / assets / js / jquery.form-3.25.0.js
1 /*!
2 * jQuery Form Plugin
3 * version: 3.25.0-2013.01.18
4 * @requires jQuery v1.5 or later
5 *
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 * http://malsup.github.com/mit-license.txt
10 * http://malsup.github.com/gpl-license-v2.txt
11 */
12 /*global ActiveXObject alert */
13 ;(function($) {
14 "use strict";
15
16 /*
17 Usage Note:
18 -----------
19 Do not use both ajaxSubmit and ajaxForm on the same form. These
20 functions are mutually exclusive. Use ajaxSubmit if you want
21 to bind your own submit handler to the form. For example,
22
23 $(document).ready(function() {
24 $('#myForm').on('submit', function(e) {
25 e.preventDefault(); // <-- important
26 $(this).ajaxSubmit({
27 target: '#output'
28 });
29 });
30 });
31
32 Use ajaxForm when you want the plugin to manage all the event binding
33 for you. For example,
34
35 $(document).ready(function() {
36 $('#myForm').ajaxForm({
37 target: '#output'
38 });
39 });
40
41 You can also use ajaxForm with delegation (requires jQuery v1.7+), so the
42 form does not have to exist when you invoke ajaxForm:
43
44 $('#myForm').ajaxForm({
45 delegation: true,
46 target: '#output'
47 });
48
49 When using ajaxForm, the ajaxSubmit function will be invoked for you
50 at the appropriate time.
51 */
52
53 /**
54 * Feature detection
55 */
56 var feature = {};
57 feature.fileapi = $("<input type='file'/>").get(0).files !== undefined;
58 feature.formdata = window.FormData !== undefined;
59
60 /**
61 * ajaxSubmit() provides a mechanism for immediately submitting
62 * an HTML form using AJAX.
63 */
64 $.fn.ajaxSubmit = function(options) {
65 /*jshint scripturl:true */
66
67 // fast fail if nothing selected (http://dev.jquery.com/ticket/2752)
68 if (!this.length) {
69 log('ajaxSubmit: skipping submit process - no element selected');
70 return this;
71 }
72
73 var method, action, url, $form = this;
74
75 if (typeof options == 'function') {
76 options = { success: options };
77 }
78
79 method = this.attr('method');
80 action = this.attr('action');
81 url = (typeof action === 'string') ? $.trim(action) : '';
82 url = url || window.location.href || '';
83 if (url) {
84 // clean url (don't include hash vaue)
85 url = (url.match(/^([^#]+)/)||[])[1];
86 }
87
88 options = $.extend(true, {
89 url: url,
90 success: $.ajaxSettings.success,
91 type: method || 'GET',
92 iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank'
93 }, options);
94
95 // hook for manipulating the form data before it is extracted;
96 // convenient for use with rich editors like tinyMCE or FCKEditor
97 var veto = {};
98 this.trigger('form-pre-serialize', [this, options, veto]);
99 if (veto.veto) {
100 log('ajaxSubmit: submit vetoed via form-pre-serialize trigger');
101 return this;
102 }
103
104 // provide opportunity to alter form data before it is serialized
105 if (options.beforeSerialize && options.beforeSerialize(this, options) === false) {
106 log('ajaxSubmit: submit aborted via beforeSerialize callback');
107 return this;
108 }
109
110 var traditional = options.traditional;
111 if ( traditional === undefined ) {
112 traditional = $.ajaxSettings.traditional;
113 }
114
115 var elements = [];
116 var qx, a = this.formToArray(options.semantic, elements);
117 if (options.data) {
118 options.extraData = options.data;
119 qx = $.param(options.data, traditional);
120 }
121
122 // give pre-submit callback an opportunity to abort the submit
123 if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) {
124 log('ajaxSubmit: submit aborted via beforeSubmit callback');
125 return this;
126 }
127
128 // fire vetoable 'validate' event
129 this.trigger('form-submit-validate', [a, this, options, veto]);
130 if (veto.veto) {
131 log('ajaxSubmit: submit vetoed via form-submit-validate trigger');
132 return this;
133 }
134
135 var q = $.param(a, traditional);
136 if (qx) {
137 q = ( q ? (q + '&' + qx) : qx );
138 }
139 if (options.type.toUpperCase() == 'GET') {
140 options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
141 options.data = null; // data is null for 'get'
142 }
143 else {
144 options.data = q; // data is the query string for 'post'
145 }
146
147 var callbacks = [];
148 if (options.resetForm) {
149 callbacks.push(function() { $form.resetForm(); });
150 }
151 if (options.clearForm) {
152 callbacks.push(function() { $form.clearForm(options.includeHidden); });
153 }
154
155 // perform a load on the target only if dataType is not provided
156 if (!options.dataType && options.target) {
157 var oldSuccess = options.success || function(){};
158 callbacks.push(function(data) {
159 var fn = options.replaceTarget ? 'replaceWith' : 'html';
160 $(options.target)[fn](data).each(oldSuccess, arguments);
161 });
162 }
163 else if (options.success) {
164 callbacks.push(options.success);
165 }
166
167 options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg
168 var context = options.context || this ; // jQuery 1.4+ supports scope context
169 for (var i=0, max=callbacks.length; i < max; i++) {
170 callbacks[i].apply(context, [data, status, xhr || $form, $form]);
171 }
172 };
173
174 // are there files to upload?
175
176 // [value] (issue #113), also see comment:
177 // https://github.com/malsup/form/commit/588306aedba1de01388032d5f42a60159eea9228#commitcomment-2180219
178 var fileInputs = $('input[type=file]:enabled[value!=""]', this);
179
180 var hasFileInputs = fileInputs.length > 0;
181 var mp = 'multipart/form-data';
182 var multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp);
183
184 var fileAPI = feature.fileapi && feature.formdata;
185 log("fileAPI :" + fileAPI);
186 var shouldUseFrame = (hasFileInputs || multipart) && !fileAPI;
187
188 var jqxhr;
189
190 // options.iframe allows user to force iframe mode
191 // 06-NOV-09: now defaulting to iframe mode if file input is detected
192 if (options.iframe !== false && (options.iframe || shouldUseFrame)) {
193 // hack to fix Safari hang (thanks to Tim Molendijk for this)
194 // see: http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
195 if (options.closeKeepAlive) {
196 $.get(options.closeKeepAlive, function() {
197 jqxhr = fileUploadIframe(a);
198 });
199 }
200 else {
201 jqxhr = fileUploadIframe(a);
202 }
203 }
204 else if ((hasFileInputs || multipart) && fileAPI) {
205 jqxhr = fileUploadXhr(a);
206 }
207 else {
208 jqxhr = $.ajax(options);
209 }
210
211 $form.removeData('jqxhr').data('jqxhr', jqxhr);
212
213 // clear element array
214 for (var k=0; k < elements.length; k++)
215 elements[k] = null;
216
217 // fire 'notify' event
218 this.trigger('form-submit-notify', [this, options]);
219 return this;
220
221 // utility fn for deep serialization
222 function deepSerialize(extraData){
223 var serialized = $.param(extraData).split('&');
224 var len = serialized.length;
225 var result = {};
226 var i, part;
227 for (i=0; i < len; i++) {
228 // #252; undo param space replacement
229 serialized[i] = serialized[i].replace(/\+/g,' ');
230 part = serialized[i].split('=');
231 result[decodeURIComponent(part[0])] = decodeURIComponent(part[1]);
232 }
233 return result;
234 }
235
236 // XMLHttpRequest Level 2 file uploads (big hat tip to francois2metz)
237 function fileUploadXhr(a) {
238 var formdata = new FormData();
239
240 for (var i=0; i < a.length; i++) {
241 formdata.append(a[i].name, a[i].value);
242 }
243
244 if (options.extraData) {
245 var serializedData = deepSerialize(options.extraData);
246 for (var p in serializedData)
247 if (serializedData.hasOwnProperty(p))
248 formdata.append(p, serializedData[p]);
249 }
250
251 options.data = null;
252
253 var s = $.extend(true, {}, $.ajaxSettings, options, {
254 contentType: false,
255 processData: false,
256 cache: false,
257 type: method || 'POST'
258 });
259
260 if (options.uploadProgress) {
261 // workaround because jqXHR does not expose upload property
262 s.xhr = function() {
263 var xhr = jQuery.ajaxSettings.xhr();
264 if (xhr.upload) {
265 xhr.upload.onprogress = function(event) {
266 var percent = 0;
267 var position = event.loaded || event.position; /*event.position is deprecated*/
268 var total = event.total;
269 if (event.lengthComputable) {
270 percent = Math.ceil(position / total * 100);
271 }
272 options.uploadProgress(event, position, total, percent);
273 };
274 }
275 return xhr;
276 };
277 }
278
279 s.data = null;
280 var beforeSend = s.beforeSend;
281 s.beforeSend = function(xhr, o) {
282 o.data = formdata;
283 if(beforeSend)
284 beforeSend.call(this, xhr, o);
285 };
286 return $.ajax(s);
287 }
288
289 // private function for handling file uploads (hat tip to YAHOO!)
290 function fileUploadIframe(a) {
291 var form = $form[0], el, i, s, g, id, $io, io, xhr, sub, n, timedOut, timeoutHandle;
292 var useProp = !!$.fn.prop;
293 var deferred = $.Deferred();
294
295 if ($('[name=submit],[id=submit]', form).length) {
296 // if there is an input with a name or id of 'submit' then we won't be
297 // able to invoke the submit fn on the form (at least not x-browser)
298 alert('Error: Form elements must not have name or id of "submit".');
299 deferred.reject();
300 return deferred;
301 }
302
303 if (a) {
304 // ensure that every serialized input is still enabled
305 for (i=0; i < elements.length; i++) {
306 el = $(elements[i]);
307 if ( useProp )
308 el.prop('disabled', false);
309 else
310 el.removeAttr('disabled');
311 }
312 }
313
314 s = $.extend(true, {}, $.ajaxSettings, options);
315 s.context = s.context || s;
316 id = 'jqFormIO' + (new Date().getTime());
317 if (s.iframeTarget) {
318 $io = $(s.iframeTarget);
319 n = $io.attr('name');
320 if (!n)
321 $io.attr('name', id);
322 else
323 id = n;
324 }
325 else {
326 $io = $('<iframe name="' + id + '" src="'+ s.iframeSrc +'" />');
327 $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });
328 }
329 io = $io[0];
330
331
332 xhr = { // mock object
333 aborted: 0,
334 responseText: null,
335 responseXML: null,
336 status: 0,
337 statusText: 'n/a',
338 getAllResponseHeaders: function() {},
339 getResponseHeader: function() {},
340 setRequestHeader: function() {},
341 abort: function(status) {
342 var e = (status === 'timeout' ? 'timeout' : 'aborted');
343 log('aborting upload... ' + e);
344 this.aborted = 1;
345
346 try { // #214, #257
347 if (io.contentWindow.document.execCommand) {
348 io.contentWindow.document.execCommand('Stop');
349 }
350 }
351 catch(ignore) {}
352
353 $io.attr('src', s.iframeSrc); // abort op in progress
354 xhr.error = e;
355 if (s.error)
356 s.error.call(s.context, xhr, e, status);
357 if (g)
358 $.event.trigger("ajaxError", [xhr, s, e]);
359 if (s.complete)
360 s.complete.call(s.context, xhr, e);
361 }
362 };
363
364 g = s.global;
365 // trigger ajax global events so that activity/block indicators work like normal
366 if (g && 0 === $.active++) {
367 $.event.trigger("ajaxStart");
368 }
369 if (g) {
370 $.event.trigger("ajaxSend", [xhr, s]);
371 }
372
373 if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) {
374 if (s.global) {
375 $.active--;
376 }
377 deferred.reject();
378 return deferred;
379 }
380 if (xhr.aborted) {
381 deferred.reject();
382 return deferred;
383 }
384
385 // add submitting element to data if we know it
386 sub = form.clk;
387 if (sub) {
388 n = sub.name;
389 if (n && !sub.disabled) {
390 s.extraData = s.extraData || {};
391 s.extraData[n] = sub.value;
392 if (sub.type == "image") {
393 s.extraData[n+'.x'] = form.clk_x;
394 s.extraData[n+'.y'] = form.clk_y;
395 }
396 }
397 }
398
399 var CLIENT_TIMEOUT_ABORT = 1;
400 var SERVER_ABORT = 2;
401
402 function getDoc(frame) {
403 var doc = frame.contentWindow ? frame.contentWindow.document : frame.contentDocument ? frame.contentDocument : frame.document;
404 return doc;
405 }
406
407 // Rails CSRF hack (thanks to Yvan Barthelemy)
408 var csrf_token = $('meta[name=csrf-token]').attr('content');
409 var csrf_param = $('meta[name=csrf-param]').attr('content');
410 if (csrf_param && csrf_token) {
411 s.extraData = s.extraData || {};
412 s.extraData[csrf_param] = csrf_token;
413 }
414
415 // take a breath so that pending repaints get some cpu time before the upload starts
416 function doSubmit() {
417 // make sure form attrs are set
418 var t = $form.attr('target'), a = $form.attr('action');
419
420 // update form attrs in IE friendly way
421 form.setAttribute('target',id);
422 if (!method) {
423 form.setAttribute('method', 'POST');
424 }
425 if (a != s.url) {
426 form.setAttribute('action', s.url);
427 }
428
429 // ie borks in some cases when setting encoding
430 if (! s.skipEncodingOverride && (!method || /post/i.test(method))) {
431 $form.attr({
432 encoding: 'multipart/form-data',
433 enctype: 'multipart/form-data'
434 });
435 }
436
437 // support timout
438 if (s.timeout) {
439 timeoutHandle = setTimeout(function() { timedOut = true; cb(CLIENT_TIMEOUT_ABORT); }, s.timeout);
440 }
441
442 // look for server aborts
443 function checkState() {
444 try {
445 var state = getDoc(io).readyState;
446 log('state = ' + state);
447 if (state && state.toLowerCase() == 'uninitialized')
448 setTimeout(checkState,50);
449 }
450 catch(e) {
451 log('Server abort: ' , e, ' (', e.name, ')');
452 cb(SERVER_ABORT);
453 if (timeoutHandle)
454 clearTimeout(timeoutHandle);
455 timeoutHandle = undefined;
456 }
457 }
458
459 // add "extra" data to form if provided in options
460 var extraInputs = [];
461 try {
462 if (s.extraData) {
463 for (var n in s.extraData) {
464 if (s.extraData.hasOwnProperty(n)) {
465 // if using the $.param format that allows for multiple values with the same name
466 if($.isPlainObject(s.extraData[n]) && s.extraData[n].hasOwnProperty('name') && s.extraData[n].hasOwnProperty('value')) {
467 extraInputs.push(
468 $('<input type="hidden" name="'+s.extraData[n].name+'">').val(s.extraData[n].value)
469 .appendTo(form)[0]);
470 } else {
471 extraInputs.push(
472 $('<input type="hidden" name="'+n+'">').val(s.extraData[n])
473 .appendTo(form)[0]);
474 }
475 }
476 }
477 }
478
479 if (!s.iframeTarget) {
480 // add iframe to doc and submit the form
481 $io.appendTo('body');
482 if (io.attachEvent)
483 io.attachEvent('onload', cb);
484 else
485 io.addEventListener('load', cb, false);
486 }
487 setTimeout(checkState,15);
488 form.submit();
489 }
490 finally {
491 // reset attrs and remove "extra" input elements
492 form.setAttribute('action',a);
493 if(t) {
494 form.setAttribute('target', t);
495 } else {
496 $form.removeAttr('target');
497 }
498 $(extraInputs).remove();
499 }
500 }
501
502 if (s.forceSync) {
503 doSubmit();
504 }
505 else {
506 setTimeout(doSubmit, 10); // this lets dom updates render
507 }
508
509 var data, doc, domCheckCount = 50, callbackProcessed;
510
511 function cb(e) {
512 if (xhr.aborted || callbackProcessed) {
513 return;
514 }
515 try {
516 doc = getDoc(io);
517 }
518 catch(ex) {
519 log('cannot access response document: ', ex);
520 e = SERVER_ABORT;
521 }
522 if (e === CLIENT_TIMEOUT_ABORT && xhr) {
523 xhr.abort('timeout');
524 deferred.reject(xhr, 'timeout');
525 return;
526 }
527 else if (e == SERVER_ABORT && xhr) {
528 xhr.abort('server abort');
529 deferred.reject(xhr, 'error', 'server abort');
530 return;
531 }
532
533 if (!doc || doc.location.href == s.iframeSrc) {
534 // response not received yet
535 if (!timedOut)
536 return;
537 }
538 if (io.detachEvent)
539 io.detachEvent('onload', cb);
540 else
541 io.removeEventListener('load', cb, false);
542
543 var status = 'success', errMsg;
544 try {
545 if (timedOut) {
546 throw 'timeout';
547 }
548
549 var isXml = s.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc);
550 log('isXml='+isXml);
551 if (!isXml && window.opera && (doc.body === null || !doc.body.innerHTML)) {
552 if (--domCheckCount) {
553 // in some browsers (Opera) the iframe DOM is not always traversable when
554 // the onload callback fires, so we loop a bit to accommodate
555 log('requeing onLoad callback, DOM not available');
556 setTimeout(cb, 250);
557 return;
558 }
559 // let this fall through because server response could be an empty document
560 //log('Could not access iframe DOM after mutiple tries.');
561 //throw 'DOMException: not available';
562 }
563
564 //log('response detected');
565 var docRoot = doc.body ? doc.body : doc.documentElement;
566 xhr.responseText = docRoot ? docRoot.innerHTML : null;
567 xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
568 if (isXml)
569 s.dataType = 'xml';
570 xhr.getResponseHeader = function(header){
571 var headers = {'content-type': s.dataType};
572 return headers[header];
573 };
574 // support for XHR 'status' & 'statusText' emulation :
575 if (docRoot) {
576 xhr.status = Number( docRoot.getAttribute('status') ) || xhr.status;
577 xhr.statusText = docRoot.getAttribute('statusText') || xhr.statusText;
578 }
579
580 var dt = (s.dataType || '').toLowerCase();
581 var scr = /(json|script|text)/.test(dt);
582 if (scr || s.textarea) {
583 // see if user embedded response in textarea
584 var ta = doc.getElementsByTagName('textarea')[0];
585 if (ta) {
586 xhr.responseText = ta.value;
587 // support for XHR 'status' & 'statusText' emulation :
588 xhr.status = Number( ta.getAttribute('status') ) || xhr.status;
589 xhr.statusText = ta.getAttribute('statusText') || xhr.statusText;
590 }
591 else if (scr) {
592 // account for browsers injecting pre around json response
593 var pre = doc.getElementsByTagName('pre')[0];
594 var b = doc.getElementsByTagName('body')[0];
595 if (pre) {
596 xhr.responseText = pre.innerHTML ? pre.innerHTML : pre.textContent;
597 }
598 else if (b) {
599 xhr.responseText = b.innerHTML ? b.innerHTML : b.textContent;
600 }
601 }
602 }
603 else if (dt == 'xml' && !xhr.responseXML && xhr.responseText) {
604 xhr.responseXML = toXml(xhr.responseText);
605 }
606
607 try {
608 data = httpData(xhr, dt, s);
609 }
610 catch (e) {
611 status = 'parsererror';
612 xhr.error = errMsg = (e || status);
613 }
614 }
615 catch (e) {
616 log('error caught: ',e);
617 status = 'error';
618 xhr.error = errMsg = (e || status);
619 }
620
621 if (xhr.aborted) {
622 log('upload aborted');
623 status = null;
624 }
625
626 if (xhr.status) { // we've set xhr.status
627 status = (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) ? 'success' : 'error';
628 }
629
630 // ordering of these callbacks/triggers is odd, but that's how $.ajax does it
631 if (status === 'success') {
632 if (s.success)
633 s.success.call(s.context, data, 'success', xhr);
634 deferred.resolve(xhr.responseText, 'success', xhr);
635 if (g)
636 $.event.trigger("ajaxSuccess", [xhr, s]);
637 }
638 else if (status) {
639 if (errMsg === undefined)
640 errMsg = xhr.statusText;
641 if (s.error)
642 s.error.call(s.context, xhr, status, errMsg);
643 deferred.reject(xhr, 'error', errMsg);
644 if (g)
645 $.event.trigger("ajaxError", [xhr, s, errMsg]);
646 }
647
648 if (g)
649 $.event.trigger("ajaxComplete", [xhr, s]);
650
651 if (g && ! --$.active) {
652 $.event.trigger("ajaxStop");
653 }
654
655 if (s.complete)
656 s.complete.call(s.context, xhr, status);
657
658 callbackProcessed = true;
659 if (s.timeout)
660 clearTimeout(timeoutHandle);
661
662 // clean up
663 setTimeout(function() {
664 if (!s.iframeTarget)
665 $io.remove();
666 xhr.responseXML = null;
667 }, 100);
668 }
669
670 var toXml = $.parseXML;
671
672 var parseJSON = $.parseJSON;
673
674 var httpData = function( xhr, type, s ) { // mostly lifted from jq1.4.4
675
676 var ct = xhr.getResponseHeader('content-type') || '',
677 xml = type === 'xml' || !type && ct.indexOf('xml') >= 0,
678 data = xml ? xhr.responseXML : xhr.responseText;
679
680 if (xml && data.documentElement.nodeName === 'parsererror') {
681 if ($.error)
682 $.error('parsererror');
683 }
684 if (s && s.dataFilter) {
685 data = s.dataFilter(data, type);
686 }
687 if (typeof data === 'string') {
688 if (type === 'json' || !type && ct.indexOf('json') >= 0) {
689 data = parseJSON(data);
690 } else if (type === "script" || !type && ct.indexOf("javascript") >= 0) {
691 $.globalEval(data);
692 }
693 }
694 return data;
695 };
696
697 return deferred;
698 }
699 };
700
701 /**
702 * ajaxForm() provides a mechanism for fully automating form submission.
703 *
704 * The advantages of using this method instead of ajaxSubmit() are:
705 *
706 * 1: This method will include coordinates for <input type="image" /> elements (if the element
707 * is used to submit the form).
708 * 2. This method will include the submit element's name/value data (for the element that was
709 * used to submit the form).
710 * 3. This method binds the submit() method to the form for you.
711 *
712 * The options argument for ajaxForm works exactly as it does for ajaxSubmit. ajaxForm merely
713 * passes the options argument along after properly binding events for submit elements and
714 * the form itself.
715 */
716 $.fn.ajaxForm = function(options) {
717 options = options || {};
718 options.delegation = options.delegation && $.isFunction($.fn.on);
719
720 // in jQuery 1.3+ we can fix mistakes with the ready state
721 if (!options.delegation && this.length === 0) {
722 var o = { s: this.selector, c: this.context };
723 if (!$.isReady && o.s) {
724 log('DOM not ready, queuing ajaxForm');
725 $(function() {
726 $(o.s,o.c).ajaxForm(options);
727 });
728 return this;
729 }
730 // is your DOM ready? http://docs.jquery.com/Tutorials:Introducing_$(document).ready()
731 log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)'));
732 return this;
733 }
734
735 if ( options.delegation ) {
736 $(document)
737 .off('submit.form-plugin', this.selector, doAjaxSubmit)
738 .off('click.form-plugin', this.selector, captureSubmittingElement)
739 .on('submit.form-plugin', this.selector, options, doAjaxSubmit)
740 .on('click.form-plugin', this.selector, options, captureSubmittingElement);
741 return this;
742 }
743
744 return this.ajaxFormUnbind()
745 .bind('submit.form-plugin', options, doAjaxSubmit)
746 .bind('click.form-plugin', options, captureSubmittingElement);
747 };
748
749 // private event handlers
750 function doAjaxSubmit(e) {
751 /*jshint validthis:true */
752 var options = e.data;
753 if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed
754 e.preventDefault();
755 $(this).ajaxSubmit(options);
756 }
757 }
758
759 function captureSubmittingElement(e) {
760 /*jshint validthis:true */
761 var target = e.target;
762 var $el = $(target);
763 if (!($el.is("[type=submit],[type=image]"))) {
764 // is this a child element of the submit el? (ex: a span within a button)
765 var t = $el.closest('[type=submit]');
766 if (t.length === 0) {
767 return;
768 }
769 target = t[0];
770 }
771 var form = this;
772 form.clk = target;
773 if (target.type == 'image') {
774 if (e.offsetX !== undefined) {
775 form.clk_x = e.offsetX;
776 form.clk_y = e.offsetY;
777 } else if (typeof $.fn.offset == 'function') {
778 var offset = $el.offset();
779 form.clk_x = e.pageX - offset.left;
780 form.clk_y = e.pageY - offset.top;
781 } else {
782 form.clk_x = e.pageX - target.offsetLeft;
783 form.clk_y = e.pageY - target.offsetTop;
784 }
785 }
786 // clear form vars
787 setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 100);
788 }
789
790
791 // ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
792 $.fn.ajaxFormUnbind = function() {
793 return this.unbind('submit.form-plugin click.form-plugin');
794 };
795
796 /**
797 * formToArray() gathers form element data into an array of objects that can
798 * be passed to any of the following ajax functions: $.get, $.post, or load.
799 * Each object in the array has both a 'name' and 'value' property. An example of
800 * an array for a simple login form might be:
801 *
802 * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ]
803 *
804 * It is this array that is passed to pre-submit callback functions provided to the
805 * ajaxSubmit() and ajaxForm() methods.
806 */
807 $.fn.formToArray = function(semantic, elements) {
808 var a = [];
809 if (this.length === 0) {
810 return a;
811 }
812
813 var form = this[0];
814 var els = semantic ? form.getElementsByTagName('*') : form.elements;
815 if (!els) {
816 return a;
817 }
818
819 var i,j,n,v,el,max,jmax;
820 for(i=0, max=els.length; i < max; i++) {
821 el = els[i];
822 n = el.name;
823 if (!n) {
824 continue;
825 }
826
827 if (semantic && form.clk && el.type == "image") {
828 // handle image inputs on the fly when semantic == true
829 if(!el.disabled && form.clk == el) {
830 a.push({name: n, value: $(el).val(), type: el.type });
831 a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
832 }
833 continue;
834 }
835
836 v = $.fieldValue(el, true);
837 if (v && v.constructor == Array) {
838 if (elements)
839 elements.push(el);
840 for(j=0, jmax=v.length; j < jmax; j++) {
841 a.push({name: n, value: v[j]});
842 }
843 }
844 else if (feature.fileapi && el.type == 'file' && !el.disabled) {
845 if (elements)
846 elements.push(el);
847 var files = el.files;
848 if (files.length) {
849 for (j=0; j < files.length; j++) {
850 a.push({name: n, value: files[j], type: el.type});
851 }
852 }
853 else {
854 // #180
855 a.push({ name: n, value: '', type: el.type });
856 }
857 }
858 else if (v !== null && typeof v != 'undefined') {
859 if (elements)
860 elements.push(el);
861 a.push({name: n, value: v, type: el.type, required: el.required});
862 }
863 }
864
865 if (!semantic && form.clk) {
866 // input type=='image' are not found in elements array! handle it here
867 var $input = $(form.clk), input = $input[0];
868 n = input.name;
869 if (n && !input.disabled && input.type == 'image') {
870 a.push({name: n, value: $input.val()});
871 a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
872 }
873 }
874 return a;
875 };
876
877 /**
878 * Serializes form data into a 'submittable' string. This method will return a string
879 * in the format: name1=value1&amp;name2=value2
880 */
881 $.fn.formSerialize = function(semantic) {
882 //hand off to jQuery.param for proper encoding
883 return $.param(this.formToArray(semantic));
884 };
885
886 /**
887 * Serializes all field elements in the jQuery object into a query string.
888 * This method will return a string in the format: name1=value1&amp;name2=value2
889 */
890 $.fn.fieldSerialize = function(successful) {
891 var a = [];
892 this.each(function() {
893 var n = this.name;
894 if (!n) {
895 return;
896 }
897 var v = $.fieldValue(this, successful);
898 if (v && v.constructor == Array) {
899 for (var i=0,max=v.length; i < max; i++) {
900 a.push({name: n, value: v[i]});
901 }
902 }
903 else if (v !== null && typeof v != 'undefined') {
904 a.push({name: this.name, value: v});
905 }
906 });
907 //hand off to jQuery.param for proper encoding
908 return $.param(a);
909 };
910
911 /**
912 * Returns the value(s) of the element in the matched set. For example, consider the following form:
913 *
914 * <form><fieldset>
915 * <input name="A" type="text" />
916 * <input name="A" type="text" />
917 * <input name="B" type="checkbox" value="B1" />
918 * <input name="B" type="checkbox" value="B2"/>
919 * <input name="C" type="radio" value="C1" />
920 * <input name="C" type="radio" value="C2" />
921 * </fieldset></form>
922 *
923 * var v = $('input[type=text]').fieldValue();
924 * // if no values are entered into the text inputs
925 * v == ['','']
926 * // if values entered into the text inputs are 'foo' and 'bar'
927 * v == ['foo','bar']
928 *
929 * var v = $('input[type=checkbox]').fieldValue();
930 * // if neither checkbox is checked
931 * v === undefined
932 * // if both checkboxes are checked
933 * v == ['B1', 'B2']
934 *
935 * var v = $('input[type=radio]').fieldValue();
936 * // if neither radio is checked
937 * v === undefined
938 * // if first radio is checked
939 * v == ['C1']
940 *
941 * The successful argument controls whether or not the field element must be 'successful'
942 * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls).
943 * The default value of the successful argument is true. If this value is false the value(s)
944 * for each element is returned.
945 *
946 * Note: This method *always* returns an array. If no valid value can be determined the
947 * array will be empty, otherwise it will contain one or more values.
948 */
949 $.fn.fieldValue = function(successful) {
950 for (var val=[], i=0, max=this.length; i < max; i++) {
951 var el = this[i];
952 var v = $.fieldValue(el, successful);
953 if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) {
954 continue;
955 }
956 if (v.constructor == Array)
957 $.merge(val, v);
958 else
959 val.push(v);
960 }
961 return val;
962 };
963
964 /**
965 * Returns the value of the field element.
966 */
967 $.fieldValue = function(el, successful) {
968 var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
969 if (successful === undefined) {
970 successful = true;
971 }
972
973 if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
974 (t == 'checkbox' || t == 'radio') && !el.checked ||
975 (t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
976 tag == 'select' && el.selectedIndex == -1)) {
977 return null;
978 }
979
980 if (tag == 'select') {
981 var index = el.selectedIndex;
982 if (index < 0) {
983 return null;
984 }
985 var a = [], ops = el.options;
986 var one = (t == 'select-one');
987 var max = (one ? index+1 : ops.length);
988 for(var i=(one ? index : 0); i < max; i++) {
989 var op = ops[i];
990 if (op.selected) {
991 var v = op.value;
992 if (!v) { // extra pain for IE...
993 v = (op.attributes && op.attributes['value'] && !(op.attributes['value'].specified)) ? op.text : op.value;
994 }
995 if (one) {
996 return v;
997 }
998 a.push(v);
999 }
1000 }
1001 return a;
1002 }
1003 return $(el).val();
1004 };
1005
1006 /**
1007 * Clears the form data. Takes the following actions on the form's input fields:
1008 * - input text fields will have their 'value' property set to the empty string
1009 * - select elements will have their 'selectedIndex' property set to -1
1010 * - checkbox and radio inputs will have their 'checked' property set to false
1011 * - inputs of type submit, button, reset, and hidden will *not* be effected
1012 * - button elements will *not* be effected
1013 */
1014 $.fn.clearForm = function(includeHidden) {
1015 return this.each(function() {
1016 $('input,select,textarea', this).clearFields(includeHidden);
1017 });
1018 };
1019
1020 /**
1021 * Clears the selected form elements.
1022 */
1023 $.fn.clearFields = $.fn.clearInputs = function(includeHidden) {
1024 var re = /^(?:color|date|datetime|email|month|number|password|range|search|tel|text|time|url|week)$/i; // 'hidden' is not in this list
1025 return this.each(function() {
1026 var t = this.type, tag = this.tagName.toLowerCase();
1027 if (re.test(t) || tag == 'textarea') {
1028 this.value = '';
1029 }
1030 else if (t == 'checkbox' || t == 'radio') {
1031 this.checked = false;
1032 }
1033 else if (tag == 'select') {
1034 this.selectedIndex = -1;
1035 }
1036 else if (t == "file") {
1037 if (/MSIE/.test(navigator.userAgent)) {
1038 $(this).replaceWith($(this).clone());
1039 } else {
1040 $(this).val('');
1041 }
1042 }
1043 else if (includeHidden) {
1044 // includeHidden can be the value true, or it can be a selector string
1045 // indicating a special test; for example:
1046 // $('#myForm').clearForm('.special:hidden')
1047 // the above would clean hidden inputs that have the class of 'special'
1048 if ( (includeHidden === true && /hidden/.test(t)) ||
1049 (typeof includeHidden == 'string' && $(this).is(includeHidden)) )
1050 this.value = '';
1051 }
1052 });
1053 };
1054
1055 /**
1056 * Resets the form data. Causes all form elements to be reset to their original value.
1057 */
1058 $.fn.resetForm = function() {
1059 return this.each(function() {
1060 // guard against an input with the name of 'reset'
1061 // note that IE reports the reset function as an 'object'
1062 if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType)) {
1063 this.reset();
1064 }
1065 });
1066 };
1067
1068 /**
1069 * Enables or disables any matching elements.
1070 */
1071 $.fn.enable = function(b) {
1072 if (b === undefined) {
1073 b = true;
1074 }
1075 return this.each(function() {
1076 this.disabled = !b;
1077 });
1078 };
1079
1080 /**
1081 * Checks/unchecks any matching checkboxes or radio buttons and
1082 * selects/deselects and matching option elements.
1083 */
1084 $.fn.selected = function(select) {
1085 if (select === undefined) {
1086 select = true;
1087 }
1088 return this.each(function() {
1089 var t = this.type;
1090 if (t == 'checkbox' || t == 'radio') {
1091 this.checked = select;
1092 }
1093 else if (this.tagName.toLowerCase() == 'option') {
1094 var $sel = $(this).parent('select');
1095 if (select && $sel[0] && $sel[0].type == 'select-one') {
1096 // deselect all other options
1097 $sel.find('option').selected(false);
1098 }
1099 this.selected = select;
1100 }
1101 });
1102 };
1103
1104 // expose debug var
1105 $.fn.ajaxSubmit.debug = false;
1106
1107 // helper fn for console logging
1108 function log() {
1109 if (!$.fn.ajaxSubmit.debug)
1110 return;
1111 var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,'');
1112 if (window.console && window.console.log) {
1113 window.console.log(msg);
1114 }
1115 else if (window.opera && window.opera.postError) {
1116 window.opera.postError(msg);
1117 }
1118 }
1119
1120 })(jQuery);