joosy 0.1.0.alpha → 1.0.0.RC1

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