hat-trick 0.0.1

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