hydra-head 4.0.0 → 4.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. data/Gemfile +5 -1
  2. data/Gemfile.lock +17 -3
  3. data/README.textile +32 -15
  4. data/app/assets/javascripts/jquery.form.js +932 -531
  5. data/app/helpers/hydra/facets_helper_behavior.rb +1 -1
  6. data/app/helpers/hydra/hydra_helper_behavior.rb +1 -1
  7. data/app/helpers/hydra/javascript_includes_helper_behavior.rb +0 -4
  8. data/app/models/ability.rb +1 -0
  9. data/app/models/common_metadata_asset.rb +7 -1
  10. data/app/models/generic_content.rb +5 -1
  11. data/app/models/generic_image.rb +7 -3
  12. data/app/models/hydra/ability.rb +2 -0
  13. data/app/models/hydra/datastream/common_mods_index_methods.rb +2 -0
  14. data/app/models/hydra/datastream/mods_article.rb +2 -0
  15. data/app/models/hydra/datastream/mods_dataset.rb +7 -0
  16. data/app/models/hydra/datastream/mods_generic_content.rb +2 -0
  17. data/app/models/hydra/datastream/mods_image.rb +1 -0
  18. data/app/models/hydra/datastream/properties.rb +6 -1
  19. data/app/models/hydra/datastream/rights_metadata.rb +1 -0
  20. data/app/models/hydra/role_mapper_behavior.rb +1 -0
  21. data/app/models/mods_asset.rb +1 -1
  22. data/app/models/role_mapper.rb +3 -0
  23. data/app/models/superuser.rb +6 -0
  24. data/app/models/user_attribute.rb +6 -0
  25. data/app/models/uses_default_partials.rb +8 -0
  26. data/app/views/catalog/_delete_partials/_default.html.erb +2 -9
  27. data/app/views/catalog/_edit_partials/_default.html.erb +0 -1
  28. data/app/views/catalog/show.html.erb +1 -1
  29. data/app/views/generic_content_objects/_edit_description.html.erb +1 -1
  30. data/app/views/generic_content_objects/_show_description.html.erb +0 -2
  31. data/app/views/generic_contents/_edit.html.erb +10 -1
  32. data/app/views/generic_contents/_show.html.erb +0 -2
  33. data/app/views/generic_images/_edit.html.erb +1 -3
  34. data/app/views/generic_images/_show.html.erb +0 -2
  35. data/app/views/hydra/file_assets/_result.html.erb +1 -2
  36. data/config/fedora.yml +1 -1
  37. data/fedora_conf/conf/development/fedora.fcfg +1 -1
  38. data/fedora_conf/conf/test/fedora.fcfg +1 -1
  39. data/hydra-head.gemspec +2 -1
  40. data/lib/application_helper.rb +1 -0
  41. data/lib/generators/hydra/templates/config/fedora.yml +1 -1
  42. data/lib/generators/hydra/templates/config/initializers/hydra_config.rb +1 -12
  43. data/lib/hydra-head/routes.rb +1 -1
  44. data/lib/hydra-head/version.rb +1 -1
  45. data/lib/hydra/access_controls_enforcement.rb +10 -3
  46. data/lib/hydra/access_controls_evaluation.rb +2 -1
  47. data/lib/hydra/assets.rb +3 -0
  48. data/lib/hydra/assets_controller_helper.rb +4 -0
  49. data/lib/hydra/catalog.rb +1 -0
  50. data/lib/hydra/common_mods_index_methods.rb +1 -1
  51. data/lib/hydra/controller.rb +1 -0
  52. data/lib/hydra/file_assets.rb +7 -5
  53. data/lib/hydra/file_assets_helper.rb +4 -32
  54. data/lib/hydra/generic_content.rb +1 -0
  55. data/lib/hydra/generic_image.rb +2 -0
  56. data/lib/hydra/generic_user_attributes.rb +5 -0
  57. data/lib/hydra/model_methods.rb +4 -3
  58. data/lib/hydra/model_mixins.rb +1 -0
  59. data/lib/hydra/model_mixins/common_metadata.rb +1 -0
  60. data/lib/hydra/model_mixins/mods_object.rb +1 -0
  61. data/lib/hydra/models/file_asset.rb +4 -0
  62. data/lib/hydra/mods_article.rb +1 -0
  63. data/lib/hydra/mods_dataset.rb +1 -0
  64. data/lib/hydra/mods_generic_content.rb +1 -0
  65. data/lib/hydra/mods_image.rb +1 -0
  66. data/lib/hydra/repository_controller.rb +2 -0
  67. data/lib/hydra/rights_metadata.rb +1 -0
  68. data/lib/hydra/submission_workflow.rb +1 -0
  69. data/lib/hydra/superuser_attributes.rb +6 -0
  70. data/lib/hydra/user.rb +1 -0
  71. data/lib/mediashelf/active_fedora_helper.rb +2 -0
  72. data/lib/prev_next_links.rb +6 -0
  73. data/lib/user_attributes_loader.rb +2 -0
  74. data/lib/uva/mods_index_methods.rb +1 -0
  75. data/tasks/hydra-head.rake +9 -1
  76. data/tasks/hyhead_rspec.rake +2 -2
  77. data/test_support/etc/Gemfile +2 -1
  78. data/test_support/features/button_delete_asset.feature +13 -0
  79. data/test_support/features/file_upload.feature +7 -0
  80. data/test_support/features/step_definitions/file_list_steps.rb +8 -0
  81. data/test_support/features/step_definitions/html_validity_steps.rb +42 -45
  82. data/test_support/spec/controllers/file_assets_controller_spec.rb +12 -17
  83. data/test_support/spec/helpers/access_controls_enforcement_spec.rb +27 -1
  84. data/test_support/spec/helpers/assets_controller_helper_spec.rb +3 -3
  85. data/test_support/spec/integration/file_asset_spec.rb +0 -44
  86. data/test_support/spec/models/hydra_rights_metadata_spec.rb +3 -1
  87. data/test_support/spec/spec_helper.rb +16 -0
  88. data/vendor/cache/factory_girl-2.6.4.gem +0 -0
  89. data/vendor/cache/jquery-rails-2.0.2.gem +0 -0
  90. metadata +26 -24
  91. data/BLACKLIGHT_CONFIG.textile +0 -76
  92. data/HOW_DO_I.textile +0 -107
  93. data/HOW_TO_GET_STARTED.textile +0 -588
  94. data/app/models/audio_asset.rb +0 -8
  95. data/app/models/image_asset.rb +0 -8
  96. data/app/models/video_asset.rb +0 -8
  97. data/fedora/conf/fedora.fcfg +0 -1021
  98. data/lib/application_controller.rb +0 -24
  99. data/lib/block_mapper.rb +0 -52
  100. data/lib/ead_mapper.rb +0 -7
  101. data/lib/field_maps.rb +0 -507
  102. data/lib/marc_mapper.rb +0 -131
  103. data/lib/marc_record_ext.rb +0 -97
  104. data/test_support/spec/models/audio_asset_spec.rb +0 -23
  105. data/test_support/spec/models/image_asset_spec.rb +0 -23
  106. data/test_support/spec/models/video_asset_spec.rb +0 -23
  107. data/vendor/cache/factory_girl-3.1.1.gem +0 -0
data/Gemfile CHANGED
@@ -6,5 +6,9 @@ gemspec
6
6
  # For rvm users:
7
7
  # bundle config build.ruby-debug-base19 --with-ruby-include=$rvm_path/src/ruby-1.9.3-p0
8
8
  group :test do
9
- gem 'cucumber-rails', '>=1.2.0', :require=>false
9
+ gem 'cucumber-rails', '>=1.2.0', :require=>false
10
+ gem 'rcov', :platform => :mri_18
11
+ gem 'simplecov', :platform => :mri_19
12
+ gem 'simplecov-rcov', :platform => :mri_19
10
13
  end
14
+
data/Gemfile.lock CHANGED
@@ -8,6 +8,7 @@ PATH
8
8
  block_helpers
9
9
  cancan
10
10
  devise
11
+ jquery-rails
11
12
  rails (= 3.2.3)
12
13
  sanitize
13
14
 
@@ -115,8 +116,8 @@ GEM
115
116
  equivalent-xml (0.2.9)
116
117
  nokogiri (>= 1.4.3)
117
118
  erubis (2.7.0)
118
- factory_girl (3.1.1)
119
- activesupport (>= 3.0.0)
119
+ factory_girl (2.6.4)
120
+ activesupport (>= 2.3.9)
120
121
  fastercsv (1.5.4)
121
122
  ffi (1.0.11)
122
123
  fssm (0.2.9)
@@ -135,6 +136,9 @@ GEM
135
136
  logger
136
137
  mediashelf-loggable
137
138
  journey (1.0.3)
139
+ jquery-rails (2.0.2)
140
+ railties (>= 3.2.0, < 5.0)
141
+ thor (~> 0.14)
138
142
  json (1.6.6)
139
143
  kaminari (0.13.0)
140
144
  actionpack (>= 3.0.0)
@@ -185,6 +189,7 @@ GEM
185
189
  rdoc (~> 3.4)
186
190
  thor (~> 0.14.6)
187
191
  rake (0.9.2.2)
192
+ rcov (1.0.0)
188
193
  rdf (0.3.5.2)
189
194
  addressable (>= 2.2.6)
190
195
  rdf-rdfxml (0.3.5)
@@ -241,6 +246,12 @@ GEM
241
246
  libwebsocket (~> 0.1.3)
242
247
  multi_json (~> 1.0)
243
248
  rubyzip
249
+ simplecov (0.6.4)
250
+ multi_json (~> 1.0)
251
+ simplecov-html (~> 0.5.3)
252
+ simplecov-html (0.5.3)
253
+ simplecov-rcov (0.2.3)
254
+ simplecov (>= 0.4.1)
244
255
  solrizer (1.2.0)
245
256
  daemons
246
257
  mediashelf-loggable (~> 0.4.7)
@@ -278,11 +289,14 @@ PLATFORMS
278
289
 
279
290
  DEPENDENCIES
280
291
  cucumber-rails (>= 1.2.0)
281
- factory_girl
292
+ factory_girl (~> 2.6.0)
282
293
  hydra-head!
283
294
  jettywrapper (>= 1.0.4)
284
295
  mocha
296
+ rcov
285
297
  rspec-rails
298
+ simplecov
299
+ simplecov-rcov
286
300
  solrizer-fedora (>= 2.0.0)
287
301
  sqlite3
288
302
  yard
data/README.textile CHANGED
@@ -17,14 +17,14 @@ See "http://github.com/projecthydra/hydra-head/wiki/Installation-Prerequisites":
17
17
 
18
18
  Ruby 1.9.3 is required by Hydra-Head release 4.0; RVM is strongly suggested.
19
19
 
20
- h3. Install Rails, Bundler and Devise
20
+ h3. Install Rails
21
21
 
22
22
  Currently hydra-head is compatible with Rails 3.2
23
23
 
24
24
  <pre>
25
25
  gem install 'rails' --version '~>3.2.2'
26
26
  </pre>
27
-
27
+
28
28
  h3. Generate a new rails application:
29
29
 
30
30
  <pre>
@@ -34,29 +34,46 @@ cd my_hydra_head
34
34
 
35
35
  h3. Install Dependencies
36
36
 
37
- First, add them to the "Gemfile":http://gembundler.com/gemfile.html of your application:
37
+ First, add them to the "Gemfile":http://gembundler.com/gemfile.html of your application. You may already have a Gemfile in the new rails application you just generated; in that case, be sure to add blacklight, hydra-head, devise and the development/test group per below:
38
38
 
39
39
  <pre>
40
40
  source 'http://rubygems.org'
41
41
 
42
- gem 'rails', '~>3.2.2'
42
+ gem 'rails', '~>3.2.3'
43
43
 
44
- gem 'blacklight', '~> 3.3.1'
44
+ gem 'blacklight', '~> 3.3.2'
45
45
  gem 'hydra-head', '~> 4.0.0'
46
46
 
47
- # We will assume that you're using sqlite3 for testing/demo,
47
+ # We will assume that you're using sqlite3 for testing/demo,
48
48
  # but in a production setup you probably want to use a real sql database like mysql or postgres
49
49
  gem 'sqlite3'
50
50
 
51
- # We will assume you're using devise in tutorials/documentation.
51
+ # We will assume you're using devise in tutorials/documentation.
52
52
  # You are free to implement your own User/Authentication solution in its place.
53
53
  gem 'devise'
54
54
 
55
- # For testing. You will probably want to use these to run the tests you write for your hydra head
56
- group :development, :test do
57
- gem 'rspec-rails', '>=2.9.0'
58
- gem "jettywrapper"
59
- end # (leave this comment here to catch a stray line inserted by blacklight!)
55
+ # Rails uses asset pipeline. You will need these gems for used your assets in development.
56
+ # However, you won't need them in production because they will be precompiled.
57
+ group :assets do
58
+ gem 'sass-rails', '~> 3.2.0'
59
+ gem 'compass-rails', '~> 1.0.0'
60
+ gem 'compass-susy-plugin', '~> 0.9.0'
61
+ gem 'jquery-rails'
62
+ end
63
+
64
+ # You will probably want to use these to run the tests you write for your hydra head
65
+ # For testing with Cucumber
66
+ group :cucumber do
67
+ gem 'cucumber'
68
+ gem 'cucumber-rails'
69
+ end
70
+
71
+ # For testing with rspec
72
+ group :development, :test do
73
+ gem 'rspec-rails', '>=2.9.0'
74
+ gem 'jettywrapper'
75
+ gem 'database_cleaner'
76
+ end
60
77
  </pre>
61
78
 
62
79
  To install all of the dependencies, run:
@@ -95,11 +112,11 @@ rake db:test:prepare
95
112
 
96
113
  h3. You're done.
97
114
 
98
- Congratulations. You've set up the code for your Hydra Head.
115
+ Congratulations. You've set up the code for your Hydra Head.
99
116
 
100
117
  Read "Tools for Developing and Testing your Application":http://github.com/projecthydra/hydra-head/wiki/Tools-for-Developing-and-Testing-your-Application, then read "How to Get Started":http://github.com/projecthydra/hydra-head/wiki/How-to-Get-Started to get a sense of what you can do with your Hydra Head.
101
118
 
102
- h2. Modifying and Testing the hydra-head Gem
119
+ h2. Modifying and Testing the hydra-head Gem
103
120
 
104
121
  For those developers who want to or need to work on the hydra-head gem itself, see the "Instructions for Contributors":http://github.com/projecthydra/hydra-head/wiki/For-Contributors
105
122
 
@@ -107,6 +124,6 @@ h2. Acknowledgements
107
124
 
108
125
  h3. Design & Strategic Contributions
109
126
 
110
- The Hydra Framework would not exist without the extensive design effort undertaken by representatives of repository initiatives from Stanford University, University of Virginia, University of Hull and MediaShelf LLC. Contributors to that effort include Tom Cramer, Lynn McRae, Martha Sites, Richard Green, Chris Awre, and Matt Zumwalt.
127
+ The Hydra Framework would not exist without the extensive design effort undertaken by representatives of repository initiatives from Stanford University, University of Virginia, University of Hull and MediaShelf LLC. Contributors to that effort include Tom Cramer, Lynn McRae, Martha Sites, Richard Green, Chris Awre, and Matt Zumwalt.
111
128
 
112
129
  Thorny Staples from Fedora Commons & DuraSpace deserves special thanks for putting all of these people in the same room together.
@@ -1,372 +1,664 @@
1
1
  /*!
2
2
  * jQuery Form Plugin
3
- * version: 2.43 (12-MAR-2010)
3
+ * version: 3.09 (16-APR-2012)
4
4
  * @requires jQuery v1.3.2 or later
5
5
  *
6
6
  * Examples and documentation at: http://malsup.com/jquery/form/
7
+ * Project repository: https://github.com/malsup/form
7
8
  * 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
9
+ * http://malsup.github.com/mit-license.txt
10
+ * http://malsup.github.com/gpl-license-v2.txt
10
11
  */
12
+ /*global ActiveXObject alert */
11
13
  ;(function($) {
14
+ "use strict";
12
15
 
13
16
  /*
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() {
22
- $(this).ajaxSubmit({
23
- target: '#output'
24
- });
25
- return false; // <-- important!
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.
17
+ Usage Note:
18
+ -----------
19
+ Do not use both ajaxSubmit and ajaxForm on the same form. These
20
+ functions are mutually exclusive. Use ajaxSubmit if you want
21
+ to bind your own submit handler to the form. For example,
22
+
23
+ $(document).ready(function() {
24
+ $('#myForm').on('submit', function(e) {
25
+ e.preventDefault(); // <-- important
26
+ $(this).ajaxSubmit({
27
+ target: '#output'
28
+ });
29
+ });
30
+ });
31
+
32
+ Use ajaxForm when you want the plugin to manage all the event binding
33
+ for you. For example,
34
+
35
+ $(document).ready(function() {
36
+ $('#myForm').ajaxForm({
37
+ target: '#output'
38
+ });
39
+ });
40
+
41
+ You can also use ajaxForm with delegation (requires jQuery v1.7+), so the
42
+ form does not have to exist when you invoke ajaxForm:
43
+
44
+ $('#myForm').ajaxForm({
45
+ delegation: true,
46
+ target: '#output'
47
+ });
48
+
49
+ When using ajaxForm, the ajaxSubmit function will be invoked for you
50
+ at the appropriate time.
40
51
  */
41
52
 
53
+ /**
54
+ * Feature detection
55
+ */
56
+ var feature = {};
57
+ feature.fileapi = $("<input type='file'/>").get(0).files !== undefined;
58
+ feature.formdata = window.FormData !== undefined;
59
+
42
60
  /**
43
61
  * ajaxSubmit() provides a mechanism for immediately submitting
44
62
  * an HTML form using AJAX.
45
63
  */
46
64
  $.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
- if (typeof options == 'function')
54
- options = { success: options };
55
-
56
- var url = $.trim(this.attr('action'));
57
- if (url) {
58
- // clean url (don't include hash vaue)
59
- url = (url.match(/^([^#]+)/)||[])[1];
60
- }
61
- url = url || window.location.href || '';
62
-
63
- options = $.extend({
64
- url: url,
65
- type: this.attr('method') || 'GET',
66
- iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank'
67
- }, options || {});
68
-
69
- // hook for manipulating the form data before it is extracted;
70
- // convenient for use with rich editors like tinyMCE or FCKEditor
71
- var veto = {};
72
- this.trigger('form-pre-serialize', [this, options, veto]);
73
- if (veto.veto) {
74
- log('ajaxSubmit: submit vetoed via form-pre-serialize trigger');
75
- return this;
76
- }
77
-
78
- // provide opportunity to alter form data before it is serialized
79
- if (options.beforeSerialize && options.beforeSerialize(this, options) === false) {
80
- log('ajaxSubmit: submit aborted via beforeSerialize callback');
81
- return this;
82
- }
83
-
84
- var a = this.formToArray(options.semantic);
85
- if (options.data) {
86
- options.extraData = options.data;
87
- for (var n in options.data) {
88
- if(options.data[n] instanceof Array) {
89
- for (var k in options.data[n])
90
- a.push( { name: n, value: options.data[n][k] } );
91
- }
92
- else
93
- a.push( { name: n, value: options.data[n] } );
94
- }
95
- }
96
-
97
- // give pre-submit callback an opportunity to abort the submit
98
- if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) {
99
- log('ajaxSubmit: submit aborted via beforeSubmit callback');
100
- return this;
101
- }
102
-
103
- // fire vetoable 'validate' event
104
- this.trigger('form-submit-validate', [a, this, options, veto]);
105
- if (veto.veto) {
106
- log('ajaxSubmit: submit vetoed via form-submit-validate trigger');
107
- return this;
108
- }
109
-
110
- var q = $.param(a);
111
-
112
- if (options.type.toUpperCase() == 'GET') {
113
- options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
114
- options.data = null; // data is null for 'get'
115
- }
116
- else
117
- options.data = q; // data is the query string for 'post'
118
-
119
- var $form = this, callbacks = [];
120
- if (options.resetForm) callbacks.push(function() { $form.resetForm(); });
121
- if (options.clearForm) callbacks.push(function() { $form.clearForm(); });
122
-
123
- // perform a load on the target only if dataType is not provided
124
- if (!options.dataType && options.target) {
125
- var oldSuccess = options.success || function(){};
126
- callbacks.push(function(data) {
127
- var fn = options.replaceTarget ? 'replaceWith' : 'html';
128
- $(options.target)[fn](data).each(oldSuccess, arguments);
129
- });
130
- }
131
- else if (options.success)
132
- callbacks.push(options.success);
133
-
134
- options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg
135
- for (var i=0, max=callbacks.length; i < max; i++)
136
- callbacks[i].apply(options, [data, status, xhr || $form, $form]);
137
- };
138
-
139
- // are there files to upload?
140
- var files = $('input:file', this).fieldValue();
141
- var found = false;
142
- for (var j=0; j < files.length; j++)
143
- if (files[j])
144
- found = true;
145
-
146
- var multipart = false;
147
- // var mp = 'multipart/form-data';
148
- // multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp);
149
-
150
- // options.iframe allows user to force iframe mode
151
- // 06-NOV-09: now defaulting to iframe mode if file input is detected
152
- if ((files.length && options.iframe !== false) || options.iframe || found || multipart) {
153
- // hack to fix Safari hang (thanks to Tim Molendijk for this)
154
- // see: http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
155
- if (options.closeKeepAlive)
156
- $.get(options.closeKeepAlive, fileUpload);
157
- else
158
- fileUpload();
159
- }
160
- else
161
- $.ajax(options);
162
-
163
- // fire 'notify' event
164
- this.trigger('form-submit-notify', [this, options]);
165
- return this;
166
-
167
-
168
- // private function for handling file uploads (hat tip to YAHOO!)
169
- function fileUpload() {
170
- var form = $form[0];
171
-
172
- if ($(':input[name=submit]', form).length) {
173
- alert('Error: Form elements must not be named "submit".');
174
- return;
175
- }
176
-
177
- var opts = $.extend({}, $.ajaxSettings, options);
178
- var s = $.extend(true, {}, $.extend(true, {}, $.ajaxSettings), opts);
179
-
180
- var id = 'jqFormIO' + (new Date().getTime());
181
- var $io = $('<iframe id="' + id + '" name="' + id + '" src="'+ opts.iframeSrc +'" onload="(jQuery(this).data(\'form-plugin-onload\'))()" />');
182
- var io = $io[0];
183
-
184
- $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });
185
-
186
- var xhr = { // mock object
187
- aborted: 0,
188
- responseText: null,
189
- responseXML: null,
190
- status: 0,
191
- statusText: 'n/a',
192
- getAllResponseHeaders: function() {},
193
- getResponseHeader: function() {},
194
- setRequestHeader: function() {},
195
- abort: function() {
196
- this.aborted = 1;
197
- $io.attr('src', opts.iframeSrc); // abort op in progress
198
- }
199
- };
200
-
201
- var g = opts.global;
202
- // trigger ajax global events so that activity/block indicators work like normal
203
- if (g && ! $.active++) $.event.trigger("ajaxStart");
204
- if (g) $.event.trigger("ajaxSend", [xhr, opts]);
205
-
206
- if (s.beforeSend && s.beforeSend(xhr, s) === false) {
207
- s.global && $.active--;
208
- return;
209
- }
210
- if (xhr.aborted)
211
- return;
212
-
213
- var cbInvoked = false;
214
- var timedOut = 0;
215
-
216
- // add submitting element to data if we know it
217
- var sub = form.clk;
218
- if (sub) {
219
- var n = sub.name;
220
- if (n && !sub.disabled) {
221
- opts.extraData = opts.extraData || {};
222
- opts.extraData[n] = sub.value;
223
- if (sub.type == "image") {
224
- opts.extraData[n+'.x'] = form.clk_x;
225
- opts.extraData[n+'.y'] = form.clk_y;
226
- }
227
- }
228
- }
229
-
230
- // take a breath so that pending repaints get some cpu time before the upload starts
231
- function doSubmit() {
232
- // make sure form attrs are set
233
- var t = $form.attr('target'), a = $form.attr('action');
234
-
235
- // update form attrs in IE friendly way
236
- form.setAttribute('target',id);
237
- if (form.getAttribute('method') != 'POST')
238
- form.setAttribute('method', 'POST');
239
- if (form.getAttribute('action') != opts.url)
240
- form.setAttribute('action', opts.url);
241
-
242
- // ie borks in some cases when setting encoding
243
- if (! opts.skipEncodingOverride) {
244
- $form.attr({
245
- encoding: 'multipart/form-data',
246
- enctype: 'multipart/form-data'
247
- });
248
- }
249
-
250
- // support timout
251
- if (opts.timeout)
252
- setTimeout(function() { timedOut = true; cb(); }, opts.timeout);
253
-
254
- // add "extra" data to form if provided in options
255
- var extraInputs = [];
256
- try {
257
- if (opts.extraData)
258
- for (var n in opts.extraData)
259
- extraInputs.push(
260
- $('<input type="hidden" name="'+n+'" value="'+opts.extraData[n]+'" />')
261
- .appendTo(form)[0]);
262
-
263
- // add iframe to doc and submit the form
264
- $io.appendTo('body');
265
- $io.data('form-plugin-onload', cb);
266
- form.submit();
267
- }
268
- finally {
269
- // reset attrs and remove "extra" input elements
270
- form.setAttribute('action',a);
271
- t ? form.setAttribute('target', t) : $form.removeAttr('target');
272
- $(extraInputs).remove();
273
- }
274
- };
275
-
276
- if (opts.forceSync)
277
- doSubmit();
278
- else
279
- setTimeout(doSubmit, 10); // this lets dom updates render
280
-
281
- var domCheckCount = 100;
282
-
283
- function cb() {
284
- if (cbInvoked)
285
- return;
286
-
287
- var ok = true;
288
- try {
289
- if (timedOut) throw 'timeout';
290
- // extract the server response from the iframe
291
- var data, doc;
292
-
293
- doc = io.contentWindow ? io.contentWindow.document : io.contentDocument ? io.contentDocument : io.document;
294
-
295
- var isXml = opts.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc);
296
- log('isXml='+isXml);
297
- if (!isXml && (doc.body == null || doc.body.innerHTML == '')) {
298
- if (--domCheckCount) {
299
- // in some browsers (Opera) the iframe DOM is not always traversable when
300
- // the onload callback fires, so we loop a bit to accommodate
301
- log('requeing onLoad callback, DOM not available');
302
- setTimeout(cb, 250);
303
- return;
304
- }
305
- log('Could not access iframe DOM after 100 tries.');
306
- return;
307
- }
308
-
309
- log('response detected');
310
- cbInvoked = true;
311
- xhr.responseText = doc.body ? doc.body.innerHTML : null;
312
- xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
313
- xhr.getResponseHeader = function(header){
314
- var headers = {'content-type': opts.dataType};
315
- return headers[header];
316
- };
317
-
318
- if (opts.dataType == 'json' || opts.dataType == 'script') {
319
- // see if user embedded response in textarea
320
- var ta = doc.getElementsByTagName('textarea')[0];
321
- if (ta)
322
- xhr.responseText = ta.value;
323
- else {
324
- // account for browsers injecting pre around json response
325
- var pre = doc.getElementsByTagName('pre')[0];
326
- if (pre)
327
- xhr.responseText = pre.innerHTML;
328
- }
329
- }
330
- else if (opts.dataType == 'xml' && !xhr.responseXML && xhr.responseText != null) {
331
- xhr.responseXML = toXml(xhr.responseText);
332
- }
333
- data = $.httpData(xhr, opts.dataType);
334
- }
335
- catch(e){
336
- log('error caught:',e);
337
- ok = false;
338
- xhr.error = e;
339
- $.handleError(opts, xhr, 'error', e);
340
- }
341
-
342
- // ordering of these callbacks/triggers is odd, but that's how $.ajax does it
343
- if (ok) {
344
- opts.success(data, 'success');
345
- if (g) $.event.trigger("ajaxSuccess", [xhr, opts]);
346
- }
347
- if (g) $.event.trigger("ajaxComplete", [xhr, opts]);
348
- if (g && ! --$.active) $.event.trigger("ajaxStop");
349
- if (opts.complete) opts.complete(xhr, ok ? 'success' : 'error');
350
-
351
- // clean up
352
- setTimeout(function() {
353
- $io.removeData('form-plugin-onload');
354
- $io.remove();
355
- xhr.responseXML = null;
356
- }, 100);
357
- };
358
-
359
- function toXml(s, doc) {
360
- if (window.ActiveXObject) {
361
- doc = new ActiveXObject('Microsoft.XMLDOM');
362
- doc.async = 'false';
363
- doc.loadXML(s);
364
- }
365
- else
366
- doc = (new DOMParser()).parseFromString(s, 'text/xml');
367
- return (doc && doc.documentElement && doc.documentElement.tagName != 'parsererror') ? doc : null;
368
- };
369
- };
65
+ /*jshint scripturl:true */
66
+
67
+ // fast fail if nothing selected (http://dev.jquery.com/ticket/2752)
68
+ if (!this.length) {
69
+ log('ajaxSubmit: skipping submit process - no element selected');
70
+ return this;
71
+ }
72
+
73
+ var method, action, url, $form = this;
74
+
75
+ if (typeof options == 'function') {
76
+ options = { success: options };
77
+ }
78
+
79
+ method = this.attr('method');
80
+ action = this.attr('action');
81
+ url = (typeof action === 'string') ? $.trim(action) : '';
82
+ url = url || window.location.href || '';
83
+ if (url) {
84
+ // clean url (don't include hash vaue)
85
+ url = (url.match(/^([^#]+)/)||[])[1];
86
+ }
87
+
88
+ options = $.extend(true, {
89
+ url: url,
90
+ success: $.ajaxSettings.success,
91
+ type: method || 'GET',
92
+ iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank'
93
+ }, options);
94
+
95
+ // hook for manipulating the form data before it is extracted;
96
+ // convenient for use with rich editors like tinyMCE or FCKEditor
97
+ var veto = {};
98
+ this.trigger('form-pre-serialize', [this, options, veto]);
99
+ if (veto.veto) {
100
+ log('ajaxSubmit: submit vetoed via form-pre-serialize trigger');
101
+ return this;
102
+ }
103
+
104
+ // provide opportunity to alter form data before it is serialized
105
+ if (options.beforeSerialize && options.beforeSerialize(this, options) === false) {
106
+ log('ajaxSubmit: submit aborted via beforeSerialize callback');
107
+ return this;
108
+ }
109
+
110
+ var traditional = options.traditional;
111
+ if ( traditional === undefined ) {
112
+ traditional = $.ajaxSettings.traditional;
113
+ }
114
+
115
+ var elements = [];
116
+ var qx, a = this.formToArray(options.semantic, elements);
117
+ if (options.data) {
118
+ options.extraData = options.data;
119
+ qx = $.param(options.data, traditional);
120
+ }
121
+
122
+ // give pre-submit callback an opportunity to abort the submit
123
+ if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) {
124
+ log('ajaxSubmit: submit aborted via beforeSubmit callback');
125
+ return this;
126
+ }
127
+
128
+ // fire vetoable 'validate' event
129
+ this.trigger('form-submit-validate', [a, this, options, veto]);
130
+ if (veto.veto) {
131
+ log('ajaxSubmit: submit vetoed via form-submit-validate trigger');
132
+ return this;
133
+ }
134
+
135
+ var q = $.param(a, traditional);
136
+ if (qx) {
137
+ q = ( q ? (q + '&' + qx) : qx );
138
+ }
139
+ if (options.type.toUpperCase() == 'GET') {
140
+ options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
141
+ options.data = null; // data is null for 'get'
142
+ }
143
+ else {
144
+ options.data = q; // data is the query string for 'post'
145
+ }
146
+
147
+ var callbacks = [];
148
+ if (options.resetForm) {
149
+ callbacks.push(function() { $form.resetForm(); });
150
+ }
151
+ if (options.clearForm) {
152
+ callbacks.push(function() { $form.clearForm(options.includeHidden); });
153
+ }
154
+
155
+ // perform a load on the target only if dataType is not provided
156
+ if (!options.dataType && options.target) {
157
+ var oldSuccess = options.success || function(){};
158
+ callbacks.push(function(data) {
159
+ var fn = options.replaceTarget ? 'replaceWith' : 'html';
160
+ $(options.target)[fn](data).each(oldSuccess, arguments);
161
+ });
162
+ }
163
+ else if (options.success) {
164
+ callbacks.push(options.success);
165
+ }
166
+
167
+ options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg
168
+ var context = options.context || options; // jQuery 1.4+ supports scope context
169
+ for (var i=0, max=callbacks.length; i < max; i++) {
170
+ callbacks[i].apply(context, [data, status, xhr || $form, $form]);
171
+ }
172
+ };
173
+
174
+ // are there files to upload?
175
+ var fileInputs = $('input:file:enabled[value]', this); // [value] (issue #113)
176
+ var hasFileInputs = fileInputs.length > 0;
177
+ var mp = 'multipart/form-data';
178
+ var multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp);
179
+
180
+ var fileAPI = feature.fileapi && feature.formdata;
181
+ log("fileAPI :" + fileAPI);
182
+ var shouldUseFrame = (hasFileInputs || multipart) && !fileAPI;
183
+
184
+ // options.iframe allows user to force iframe mode
185
+ // 06-NOV-09: now defaulting to iframe mode if file input is detected
186
+ if (options.iframe !== false && (options.iframe || shouldUseFrame)) {
187
+ // hack to fix Safari hang (thanks to Tim Molendijk for this)
188
+ // see: http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
189
+ if (options.closeKeepAlive) {
190
+ $.get(options.closeKeepAlive, function() {
191
+ fileUploadIframe(a);
192
+ });
193
+ }
194
+ else {
195
+ fileUploadIframe(a);
196
+ }
197
+ }
198
+ else if ((hasFileInputs || multipart) && fileAPI) {
199
+ fileUploadXhr(a);
200
+ }
201
+ else {
202
+ $.ajax(options);
203
+ }
204
+
205
+ // clear element array
206
+ for (var k=0; k < elements.length; k++)
207
+ elements[k] = null;
208
+
209
+ // fire 'notify' event
210
+ this.trigger('form-submit-notify', [this, options]);
211
+ return this;
212
+
213
+ // XMLHttpRequest Level 2 file uploads (big hat tip to francois2metz)
214
+ function fileUploadXhr(a) {
215
+ var formdata = new FormData();
216
+
217
+ for (var i=0; i < a.length; i++) {
218
+ formdata.append(a[i].name, a[i].value);
219
+ }
220
+
221
+ if (options.extraData) {
222
+ for (var p in options.extraData)
223
+ if (options.extraData.hasOwnProperty(p))
224
+ formdata.append(p, options.extraData[p]);
225
+ }
226
+
227
+ options.data = null;
228
+
229
+ var s = $.extend(true, {}, $.ajaxSettings, options, {
230
+ contentType: false,
231
+ processData: false,
232
+ cache: false,
233
+ type: 'POST'
234
+ });
235
+
236
+ if (options.uploadProgress) {
237
+ // workaround because jqXHR does not expose upload property
238
+ s.xhr = function() {
239
+ var xhr = jQuery.ajaxSettings.xhr();
240
+ if (xhr.upload) {
241
+ xhr.upload.onprogress = function(event) {
242
+ var percent = 0;
243
+ var position = event.loaded || event.position; /*event.position is deprecated*/
244
+ var total = event.total;
245
+ if (event.lengthComputable) {
246
+ percent = Math.ceil(position / total * 100);
247
+ }
248
+ options.uploadProgress(event, position, total, percent);
249
+ };
250
+ }
251
+ return xhr;
252
+ };
253
+ }
254
+
255
+ s.data = null;
256
+ var beforeSend = s.beforeSend;
257
+ s.beforeSend = function(xhr, o) {
258
+ o.data = formdata;
259
+ if(beforeSend)
260
+ beforeSend.call(o, xhr, options);
261
+ };
262
+ $.ajax(s);
263
+ }
264
+
265
+ // private function for handling file uploads (hat tip to YAHOO!)
266
+ function fileUploadIframe(a) {
267
+ var form = $form[0], el, i, s, g, id, $io, io, xhr, sub, n, timedOut, timeoutHandle;
268
+ var useProp = !!$.fn.prop;
269
+
270
+ if ($(':input[name=submit],:input[id=submit]', form).length) {
271
+ // if there is an input with a name or id of 'submit' then we won't be
272
+ // able to invoke the submit fn on the form (at least not x-browser)
273
+ alert('Error: Form elements must not have name or id of "submit".');
274
+ return;
275
+ }
276
+
277
+ if (a) {
278
+ // ensure that every serialized input is still enabled
279
+ for (i=0; i < elements.length; i++) {
280
+ el = $(elements[i]);
281
+ if ( useProp )
282
+ el.prop('disabled', false);
283
+ else
284
+ el.removeAttr('disabled');
285
+ }
286
+ }
287
+
288
+ s = $.extend(true, {}, $.ajaxSettings, options);
289
+ s.context = s.context || s;
290
+ id = 'jqFormIO' + (new Date().getTime());
291
+ if (s.iframeTarget) {
292
+ $io = $(s.iframeTarget);
293
+ n = $io.attr('name');
294
+ if (!n)
295
+ $io.attr('name', id);
296
+ else
297
+ id = n;
298
+ }
299
+ else {
300
+ $io = $('<iframe name="' + id + '" src="'+ s.iframeSrc +'" />');
301
+ $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });
302
+ }
303
+ io = $io[0];
304
+
305
+
306
+ xhr = { // mock object
307
+ aborted: 0,
308
+ responseText: null,
309
+ responseXML: null,
310
+ status: 0,
311
+ statusText: 'n/a',
312
+ getAllResponseHeaders: function() {},
313
+ getResponseHeader: function() {},
314
+ setRequestHeader: function() {},
315
+ abort: function(status) {
316
+ var e = (status === 'timeout' ? 'timeout' : 'aborted');
317
+ log('aborting upload... ' + e);
318
+ this.aborted = 1;
319
+ $io.attr('src', s.iframeSrc); // abort op in progress
320
+ xhr.error = e;
321
+ if (s.error)
322
+ s.error.call(s.context, xhr, e, status);
323
+ if (g)
324
+ $.event.trigger("ajaxError", [xhr, s, e]);
325
+ if (s.complete)
326
+ s.complete.call(s.context, xhr, e);
327
+ }
328
+ };
329
+
330
+ g = s.global;
331
+ // trigger ajax global events so that activity/block indicators work like normal
332
+ if (g && 0 === $.active++) {
333
+ $.event.trigger("ajaxStart");
334
+ }
335
+ if (g) {
336
+ $.event.trigger("ajaxSend", [xhr, s]);
337
+ }
338
+
339
+ if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) {
340
+ if (s.global) {
341
+ $.active--;
342
+ }
343
+ return;
344
+ }
345
+ if (xhr.aborted) {
346
+ return;
347
+ }
348
+
349
+ // add submitting element to data if we know it
350
+ sub = form.clk;
351
+ if (sub) {
352
+ n = sub.name;
353
+ if (n && !sub.disabled) {
354
+ s.extraData = s.extraData || {};
355
+ s.extraData[n] = sub.value;
356
+ if (sub.type == "image") {
357
+ s.extraData[n+'.x'] = form.clk_x;
358
+ s.extraData[n+'.y'] = form.clk_y;
359
+ }
360
+ }
361
+ }
362
+
363
+ var CLIENT_TIMEOUT_ABORT = 1;
364
+ var SERVER_ABORT = 2;
365
+
366
+ function getDoc(frame) {
367
+ var doc = frame.contentWindow ? frame.contentWindow.document : frame.contentDocument ? frame.contentDocument : frame.document;
368
+ return doc;
369
+ }
370
+
371
+ // Rails CSRF hack (thanks to Yvan Barthelemy)
372
+ var csrf_token = $('meta[name=csrf-token]').attr('content');
373
+ var csrf_param = $('meta[name=csrf-param]').attr('content');
374
+ if (csrf_param && csrf_token) {
375
+ s.extraData = s.extraData || {};
376
+ s.extraData[csrf_param] = csrf_token;
377
+ }
378
+
379
+ // take a breath so that pending repaints get some cpu time before the upload starts
380
+ function doSubmit() {
381
+ // make sure form attrs are set
382
+ var t = $form.attr('target'), a = $form.attr('action');
383
+
384
+ // update form attrs in IE friendly way
385
+ form.setAttribute('target',id);
386
+ if (!method) {
387
+ form.setAttribute('method', 'POST');
388
+ }
389
+ if (a != s.url) {
390
+ form.setAttribute('action', s.url);
391
+ }
392
+
393
+ // ie borks in some cases when setting encoding
394
+ if (! s.skipEncodingOverride && (!method || /post/i.test(method))) {
395
+ $form.attr({
396
+ encoding: 'multipart/form-data',
397
+ enctype: 'multipart/form-data'
398
+ });
399
+ }
400
+
401
+ // support timout
402
+ if (s.timeout) {
403
+ timeoutHandle = setTimeout(function() { timedOut = true; cb(CLIENT_TIMEOUT_ABORT); }, s.timeout);
404
+ }
405
+
406
+ // look for server aborts
407
+ function checkState() {
408
+ try {
409
+ var state = getDoc(io).readyState;
410
+ log('state = ' + state);
411
+ if (state && state.toLowerCase() == 'uninitialized')
412
+ setTimeout(checkState,50);
413
+ }
414
+ catch(e) {
415
+ log('Server abort: ' , e, ' (', e.name, ')');
416
+ cb(SERVER_ABORT);
417
+ if (timeoutHandle)
418
+ clearTimeout(timeoutHandle);
419
+ timeoutHandle = undefined;
420
+ }
421
+ }
422
+
423
+ // add "extra" data to form if provided in options
424
+ var extraInputs = [];
425
+ try {
426
+ if (s.extraData) {
427
+ for (var n in s.extraData) {
428
+ if (s.extraData.hasOwnProperty(n)) {
429
+ extraInputs.push(
430
+ $('<input type="hidden" name="'+n+'">').attr('value',s.extraData[n])
431
+ .appendTo(form)[0]);
432
+ }
433
+ }
434
+ }
435
+
436
+ if (!s.iframeTarget) {
437
+ // add iframe to doc and submit the form
438
+ $io.appendTo('body');
439
+ if (io.attachEvent)
440
+ io.attachEvent('onload', cb);
441
+ else
442
+ io.addEventListener('load', cb, false);
443
+ }
444
+ setTimeout(checkState,15);
445
+ form.submit();
446
+ }
447
+ finally {
448
+ // reset attrs and remove "extra" input elements
449
+ form.setAttribute('action',a);
450
+ if(t) {
451
+ form.setAttribute('target', t);
452
+ } else {
453
+ $form.removeAttr('target');
454
+ }
455
+ $(extraInputs).remove();
456
+ }
457
+ }
458
+
459
+ if (s.forceSync) {
460
+ doSubmit();
461
+ }
462
+ else {
463
+ setTimeout(doSubmit, 10); // this lets dom updates render
464
+ }
465
+
466
+ var data, doc, domCheckCount = 50, callbackProcessed;
467
+
468
+ function cb(e) {
469
+ if (xhr.aborted || callbackProcessed) {
470
+ return;
471
+ }
472
+ try {
473
+ doc = getDoc(io);
474
+ }
475
+ catch(ex) {
476
+ log('cannot access response document: ', ex);
477
+ e = SERVER_ABORT;
478
+ }
479
+ if (e === CLIENT_TIMEOUT_ABORT && xhr) {
480
+ xhr.abort('timeout');
481
+ return;
482
+ }
483
+ else if (e == SERVER_ABORT && xhr) {
484
+ xhr.abort('server abort');
485
+ return;
486
+ }
487
+
488
+ if (!doc || doc.location.href == s.iframeSrc) {
489
+ // response not received yet
490
+ if (!timedOut)
491
+ return;
492
+ }
493
+ if (io.detachEvent)
494
+ io.detachEvent('onload', cb);
495
+ else
496
+ io.removeEventListener('load', cb, false);
497
+
498
+ var status = 'success', errMsg;
499
+ try {
500
+ if (timedOut) {
501
+ throw 'timeout';
502
+ }
503
+
504
+ var isXml = s.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc);
505
+ log('isXml='+isXml);
506
+ if (!isXml && window.opera && (doc.body === null || !doc.body.innerHTML)) {
507
+ if (--domCheckCount) {
508
+ // in some browsers (Opera) the iframe DOM is not always traversable when
509
+ // the onload callback fires, so we loop a bit to accommodate
510
+ log('requeing onLoad callback, DOM not available');
511
+ setTimeout(cb, 250);
512
+ return;
513
+ }
514
+ // let this fall through because server response could be an empty document
515
+ //log('Could not access iframe DOM after mutiple tries.');
516
+ //throw 'DOMException: not available';
517
+ }
518
+
519
+ //log('response detected');
520
+ var docRoot = doc.body ? doc.body : doc.documentElement;
521
+ xhr.responseText = docRoot ? docRoot.innerHTML : null;
522
+ xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
523
+ if (isXml)
524
+ s.dataType = 'xml';
525
+ xhr.getResponseHeader = function(header){
526
+ var headers = {'content-type': s.dataType};
527
+ return headers[header];
528
+ };
529
+ // support for XHR 'status' & 'statusText' emulation :
530
+ if (docRoot) {
531
+ xhr.status = Number( docRoot.getAttribute('status') ) || xhr.status;
532
+ xhr.statusText = docRoot.getAttribute('statusText') || xhr.statusText;
533
+ }
534
+
535
+ var dt = (s.dataType || '').toLowerCase();
536
+ var scr = /(json|script|text)/.test(dt);
537
+ if (scr || s.textarea) {
538
+ // see if user embedded response in textarea
539
+ var ta = doc.getElementsByTagName('textarea')[0];
540
+ if (ta) {
541
+ xhr.responseText = ta.value;
542
+ // support for XHR 'status' & 'statusText' emulation :
543
+ xhr.status = Number( ta.getAttribute('status') ) || xhr.status;
544
+ xhr.statusText = ta.getAttribute('statusText') || xhr.statusText;
545
+ }
546
+ else if (scr) {
547
+ // account for browsers injecting pre around json response
548
+ var pre = doc.getElementsByTagName('pre')[0];
549
+ var b = doc.getElementsByTagName('body')[0];
550
+ if (pre) {
551
+ xhr.responseText = pre.textContent ? pre.textContent : pre.innerText;
552
+ }
553
+ else if (b) {
554
+ xhr.responseText = b.textContent ? b.textContent : b.innerText;
555
+ }
556
+ }
557
+ }
558
+ else if (dt == 'xml' && !xhr.responseXML && xhr.responseText) {
559
+ xhr.responseXML = toXml(xhr.responseText);
560
+ }
561
+
562
+ try {
563
+ data = httpData(xhr, dt, s);
564
+ }
565
+ catch (e) {
566
+ status = 'parsererror';
567
+ xhr.error = errMsg = (e || status);
568
+ }
569
+ }
570
+ catch (e) {
571
+ log('error caught: ',e);
572
+ status = 'error';
573
+ xhr.error = errMsg = (e || status);
574
+ }
575
+
576
+ if (xhr.aborted) {
577
+ log('upload aborted');
578
+ status = null;
579
+ }
580
+
581
+ if (xhr.status) { // we've set xhr.status
582
+ status = (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) ? 'success' : 'error';
583
+ }
584
+
585
+ // ordering of these callbacks/triggers is odd, but that's how $.ajax does it
586
+ if (status === 'success') {
587
+ if (s.success)
588
+ s.success.call(s.context, data, 'success', xhr);
589
+ if (g)
590
+ $.event.trigger("ajaxSuccess", [xhr, s]);
591
+ }
592
+ else if (status) {
593
+ if (errMsg === undefined)
594
+ errMsg = xhr.statusText;
595
+ if (s.error)
596
+ s.error.call(s.context, xhr, status, errMsg);
597
+ if (g)
598
+ $.event.trigger("ajaxError", [xhr, s, errMsg]);
599
+ }
600
+
601
+ if (g)
602
+ $.event.trigger("ajaxComplete", [xhr, s]);
603
+
604
+ if (g && ! --$.active) {
605
+ $.event.trigger("ajaxStop");
606
+ }
607
+
608
+ if (s.complete)
609
+ s.complete.call(s.context, xhr, status);
610
+
611
+ callbackProcessed = true;
612
+ if (s.timeout)
613
+ clearTimeout(timeoutHandle);
614
+
615
+ // clean up
616
+ setTimeout(function() {
617
+ if (!s.iframeTarget)
618
+ $io.remove();
619
+ xhr.responseXML = null;
620
+ }, 100);
621
+ }
622
+
623
+ var toXml = $.parseXML || function(s, doc) { // use parseXML if available (jQuery 1.5+)
624
+ if (window.ActiveXObject) {
625
+ doc = new ActiveXObject('Microsoft.XMLDOM');
626
+ doc.async = 'false';
627
+ doc.loadXML(s);
628
+ }
629
+ else {
630
+ doc = (new DOMParser()).parseFromString(s, 'text/xml');
631
+ }
632
+ return (doc && doc.documentElement && doc.documentElement.nodeName != 'parsererror') ? doc : null;
633
+ };
634
+ var parseJSON = $.parseJSON || function(s) {
635
+ /*jslint evil:true */
636
+ return window['eval']('(' + s + ')');
637
+ };
638
+
639
+ var httpData = function( xhr, type, s ) { // mostly lifted from jq1.4.4
640
+
641
+ var ct = xhr.getResponseHeader('content-type') || '',
642
+ xml = type === 'xml' || !type && ct.indexOf('xml') >= 0,
643
+ data = xml ? xhr.responseXML : xhr.responseText;
644
+
645
+ if (xml && data.documentElement.nodeName === 'parsererror') {
646
+ if ($.error)
647
+ $.error('parsererror');
648
+ }
649
+ if (s && s.dataFilter) {
650
+ data = s.dataFilter(data, type);
651
+ }
652
+ if (typeof data === 'string') {
653
+ if (type === 'json' || !type && ct.indexOf('json') >= 0) {
654
+ data = parseJSON(data);
655
+ } else if (type === "script" || !type && ct.indexOf("javascript") >= 0) {
656
+ $.globalEval(data);
657
+ }
658
+ }
659
+ return data;
660
+ };
661
+ }
370
662
  };
371
663
 
372
664
  /**
@@ -375,9 +667,9 @@ $.fn.ajaxSubmit = function(options) {
375
667
  * The advantages of using this method instead of ajaxSubmit() are:
376
668
  *
377
669
  * 1: This method will include coordinates for <input type="image" /> elements (if the element
378
- * is used to submit the form).
670
+ * is used to submit the form).
379
671
  * 2. This method will include the submit element's name/value data (for the element that was
380
- * used to submit the form).
672
+ * used to submit the form).
381
673
  * 3. This method binds the submit() method to the form for you.
382
674
  *
383
675
  * The options argument for ajaxForm works exactly as it does for ajaxSubmit. ajaxForm merely
@@ -385,42 +677,83 @@ $.fn.ajaxSubmit = function(options) {
385
677
  * the form itself.
386
678
  */
387
679
  $.fn.ajaxForm = function(options) {
388
- return this.ajaxFormUnbind().bind('submit.form-plugin', function(e) {
389
- e.preventDefault();
390
- $(this).ajaxSubmit(options);
391
- }).bind('click.form-plugin', function(e) {
392
- var target = e.target;
393
- var $el = $(target);
394
- if (!($el.is(":submit,input:image"))) {
395
- // is this a child element of the submit el? (ex: a span within a button)
396
- var t = $el.closest(':submit');
397
- if (t.length == 0)
398
- return;
399
- target = t[0];
400
- }
401
- var form = this;
402
- form.clk = target;
403
- if (target.type == 'image') {
404
- if (e.offsetX != undefined) {
405
- form.clk_x = e.offsetX;
406
- form.clk_y = e.offsetY;
407
- } else if (typeof $.fn.offset == 'function') { // try to use dimensions plugin
408
- var offset = $el.offset();
409
- form.clk_x = e.pageX - offset.left;
410
- form.clk_y = e.pageY - offset.top;
411
- } else {
412
- form.clk_x = e.pageX - target.offsetLeft;
413
- form.clk_y = e.pageY - target.offsetTop;
414
- }
415
- }
416
- // clear form vars
417
- setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 100);
418
- });
680
+ options = options || {};
681
+ options.delegation = options.delegation && $.isFunction($.fn.on);
682
+
683
+ // in jQuery 1.3+ we can fix mistakes with the ready state
684
+ if (!options.delegation && this.length === 0) {
685
+ var o = { s: this.selector, c: this.context };
686
+ if (!$.isReady && o.s) {
687
+ log('DOM not ready, queuing ajaxForm');
688
+ $(function() {
689
+ $(o.s,o.c).ajaxForm(options);
690
+ });
691
+ return this;
692
+ }
693
+ // is your DOM ready? http://docs.jquery.com/Tutorials:Introducing_$(document).ready()
694
+ log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)'));
695
+ return this;
696
+ }
697
+
698
+ if ( options.delegation ) {
699
+ $(document)
700
+ .off('submit.form-plugin', this.selector, doAjaxSubmit)
701
+ .off('click.form-plugin', this.selector, captureSubmittingElement)
702
+ .on('submit.form-plugin', this.selector, options, doAjaxSubmit)
703
+ .on('click.form-plugin', this.selector, options, captureSubmittingElement);
704
+ return this;
705
+ }
706
+
707
+ return this.ajaxFormUnbind()
708
+ .bind('submit.form-plugin', options, doAjaxSubmit)
709
+ .bind('click.form-plugin', options, captureSubmittingElement);
419
710
  };
420
711
 
712
+ // private event handlers
713
+ function doAjaxSubmit(e) {
714
+ /*jshint validthis:true */
715
+ var options = e.data;
716
+ if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed
717
+ e.preventDefault();
718
+ $(this).ajaxSubmit(options);
719
+ }
720
+ }
721
+
722
+ function captureSubmittingElement(e) {
723
+ /*jshint validthis:true */
724
+ var target = e.target;
725
+ var $el = $(target);
726
+ if (!($el.is(":submit,input:image"))) {
727
+ // is this a child element of the submit el? (ex: a span within a button)
728
+ var t = $el.closest(':submit');
729
+ if (t.length === 0) {
730
+ return;
731
+ }
732
+ target = t[0];
733
+ }
734
+ var form = this;
735
+ form.clk = target;
736
+ if (target.type == 'image') {
737
+ if (e.offsetX !== undefined) {
738
+ form.clk_x = e.offsetX;
739
+ form.clk_y = e.offsetY;
740
+ } else if (typeof $.fn.offset == 'function') {
741
+ var offset = $el.offset();
742
+ form.clk_x = e.pageX - offset.left;
743
+ form.clk_y = e.pageY - offset.top;
744
+ } else {
745
+ form.clk_x = e.pageX - target.offsetLeft;
746
+ form.clk_y = e.pageY - target.offsetTop;
747
+ }
748
+ }
749
+ // clear form vars
750
+ setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 100);
751
+ }
752
+
753
+
421
754
  // ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
422
755
  $.fn.ajaxFormUnbind = function() {
423
- return this.unbind('submit.form-plugin click.form-plugin');
756
+ return this.unbind('submit.form-plugin click.form-plugin');
424
757
  };
425
758
 
426
759
  /**
@@ -434,45 +767,74 @@ $.fn.ajaxFormUnbind = function() {
434
767
  * It is this array that is passed to pre-submit callback functions provided to the
435
768
  * ajaxSubmit() and ajaxForm() methods.
436
769
  */
437
- $.fn.formToArray = function(semantic) {
438
- var a = [];
439
- if (this.length == 0) return a;
440
-
441
- var form = this[0];
442
- var els = semantic ? form.getElementsByTagName('*') : form.elements;
443
- if (!els) return a;
444
- for(var i=0, max=els.length; i < max; i++) {
445
- var el = els[i];
446
- var n = el.name;
447
- if (!n) continue;
448
-
449
- if (semantic && form.clk && el.type == "image") {
450
- // handle image inputs on the fly when semantic == true
451
- if(!el.disabled && form.clk == el) {
452
- a.push({name: n, value: $(el).val()});
453
- a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
454
- }
455
- continue;
456
- }
457
-
458
- var v = $.fieldValue(el, true);
459
- if (v && v.constructor == Array) {
460
- for(var j=0, jmax=v.length; j < jmax; j++)
461
- a.push({name: n, value: v[j]});
462
- }
463
- else if (v !== null && typeof v != 'undefined')
464
- a.push({name: n, value: v});
465
- }
466
-
467
- if (!semantic && form.clk) {
468
- // input type=='image' are not found in elements array! handle it here
469
- var $input = $(form.clk), input = $input[0], n = input.name;
470
- if (n && !input.disabled && input.type == 'image') {
471
- a.push({name: n, value: $input.val()});
472
- a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
473
- }
474
- }
475
- return a;
770
+ $.fn.formToArray = function(semantic, elements) {
771
+ var a = [];
772
+ if (this.length === 0) {
773
+ return a;
774
+ }
775
+
776
+ var form = this[0];
777
+ var els = semantic ? form.getElementsByTagName('*') : form.elements;
778
+ if (!els) {
779
+ return a;
780
+ }
781
+
782
+ var i,j,n,v,el,max,jmax;
783
+ for(i=0, max=els.length; i < max; i++) {
784
+ el = els[i];
785
+ n = el.name;
786
+ if (!n) {
787
+ continue;
788
+ }
789
+
790
+ if (semantic && form.clk && el.type == "image") {
791
+ // handle image inputs on the fly when semantic == true
792
+ if(!el.disabled && form.clk == el) {
793
+ a.push({name: n, value: $(el).val(), type: el.type });
794
+ a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
795
+ }
796
+ continue;
797
+ }
798
+
799
+ v = $.fieldValue(el, true);
800
+ if (v && v.constructor == Array) {
801
+ if (elements)
802
+ elements.push(el);
803
+ for(j=0, jmax=v.length; j < jmax; j++) {
804
+ a.push({name: n, value: v[j]});
805
+ }
806
+ }
807
+ else if (feature.fileapi && el.type == 'file' && !el.disabled) {
808
+ if (elements)
809
+ elements.push(el);
810
+ var files = el.files;
811
+ if (files.length) {
812
+ for (j=0; j < files.length; j++) {
813
+ a.push({name: n, value: files[j], type: el.type});
814
+ }
815
+ }
816
+ else {
817
+ // #180
818
+ a.push({ name: n, value: '', type: el.type });
819
+ }
820
+ }
821
+ else if (v !== null && typeof v != 'undefined') {
822
+ if (elements)
823
+ elements.push(el);
824
+ a.push({name: n, value: v, type: el.type, required: el.required});
825
+ }
826
+ }
827
+
828
+ if (!semantic && form.clk) {
829
+ // input type=='image' are not found in elements array! handle it here
830
+ var $input = $(form.clk), input = $input[0];
831
+ n = input.name;
832
+ if (n && !input.disabled && input.type == 'image') {
833
+ a.push({name: n, value: $input.val()});
834
+ a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
835
+ }
836
+ }
837
+ return a;
476
838
  };
477
839
 
478
840
  /**
@@ -480,8 +842,8 @@ $.fn.formToArray = function(semantic) {
480
842
  * in the format: name1=value1&amp;name2=value2
481
843
  */
482
844
  $.fn.formSerialize = function(semantic) {
483
- //hand off to jQuery.param for proper encoding
484
- return $.param(this.formToArray(semantic));
845
+ //hand off to jQuery.param for proper encoding
846
+ return $.param(this.formToArray(semantic));
485
847
  };
486
848
 
487
849
  /**
@@ -489,32 +851,36 @@ $.fn.formSerialize = function(semantic) {
489
851
  * This method will return a string in the format: name1=value1&amp;name2=value2
490
852
  */
491
853
  $.fn.fieldSerialize = function(successful) {
492
- var a = [];
493
- this.each(function() {
494
- var n = this.name;
495
- if (!n) return;
496
- var v = $.fieldValue(this, successful);
497
- if (v && v.constructor == Array) {
498
- for (var i=0,max=v.length; i < max; i++)
499
- a.push({name: n, value: v[i]});
500
- }
501
- else if (v !== null && typeof v != 'undefined')
502
- a.push({name: this.name, value: v});
503
- });
504
- //hand off to jQuery.param for proper encoding
505
- return $.param(a);
854
+ var a = [];
855
+ this.each(function() {
856
+ var n = this.name;
857
+ if (!n) {
858
+ return;
859
+ }
860
+ var v = $.fieldValue(this, successful);
861
+ if (v && v.constructor == Array) {
862
+ for (var i=0,max=v.length; i < max; i++) {
863
+ a.push({name: n, value: v[i]});
864
+ }
865
+ }
866
+ else if (v !== null && typeof v != 'undefined') {
867
+ a.push({name: this.name, value: v});
868
+ }
869
+ });
870
+ //hand off to jQuery.param for proper encoding
871
+ return $.param(a);
506
872
  };
507
873
 
508
874
  /**
509
875
  * Returns the value(s) of the element in the matched set. For example, consider the following form:
510
876
  *
511
877
  * <form><fieldset>
512
- * <input name="A" type="text" />
513
- * <input name="A" type="text" />
514
- * <input name="B" type="checkbox" value="B1" />
515
- * <input name="B" type="checkbox" value="B2"/>
516
- * <input name="C" type="radio" value="C1" />
517
- * <input name="C" type="radio" value="C2" />
878
+ * <input name="A" type="text" />
879
+ * <input name="A" type="text" />
880
+ * <input name="B" type="checkbox" value="B1" />
881
+ * <input name="B" type="checkbox" value="B2"/>
882
+ * <input name="C" type="radio" value="C1" />
883
+ * <input name="C" type="radio" value="C2" />
518
884
  * </fieldset></form>
519
885
  *
520
886
  * var v = $(':text').fieldValue();
@@ -541,51 +907,63 @@ $.fn.fieldSerialize = function(successful) {
541
907
  * for each element is returned.
542
908
  *
543
909
  * Note: This method *always* returns an array. If no valid value can be determined the
544
- * array will be empty, otherwise it will contain one or more values.
910
+ * array will be empty, otherwise it will contain one or more values.
545
911
  */
546
912
  $.fn.fieldValue = function(successful) {
547
- for (var val=[], i=0, max=this.length; i < max; i++) {
548
- var el = this[i];
549
- var v = $.fieldValue(el, successful);
550
- if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length))
551
- continue;
552
- v.constructor == Array ? $.merge(val, v) : val.push(v);
553
- }
554
- return val;
913
+ for (var val=[], i=0, max=this.length; i < max; i++) {
914
+ var el = this[i];
915
+ var v = $.fieldValue(el, successful);
916
+ if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) {
917
+ continue;
918
+ }
919
+ if (v.constructor == Array)
920
+ $.merge(val, v);
921
+ else
922
+ val.push(v);
923
+ }
924
+ return val;
555
925
  };
556
926
 
557
927
  /**
558
928
  * Returns the value of the field element.
559
929
  */
560
930
  $.fieldValue = function(el, successful) {
561
- var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
562
- if (typeof successful == 'undefined') successful = true;
563
-
564
- if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
565
- (t == 'checkbox' || t == 'radio') && !el.checked ||
566
- (t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
567
- tag == 'select' && el.selectedIndex == -1))
568
- return null;
569
-
570
- if (tag == 'select') {
571
- var index = el.selectedIndex;
572
- if (index < 0) return null;
573
- var a = [], ops = el.options;
574
- var one = (t == 'select-one');
575
- var max = (one ? index+1 : ops.length);
576
- for(var i=(one ? index : 0); i < max; i++) {
577
- var op = ops[i];
578
- if (op.selected) {
579
- var v = op.value;
580
- if (!v) // extra pain for IE...
581
- v = (op.attributes && op.attributes['value'] && !(op.attributes['value'].specified)) ? op.text : op.value;
582
- if (one) return v;
583
- a.push(v);
584
- }
585
- }
586
- return a;
587
- }
588
- return el.value;
931
+ var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
932
+ if (successful === undefined) {
933
+ successful = true;
934
+ }
935
+
936
+ if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
937
+ (t == 'checkbox' || t == 'radio') && !el.checked ||
938
+ (t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
939
+ tag == 'select' && el.selectedIndex == -1)) {
940
+ return null;
941
+ }
942
+
943
+ if (tag == 'select') {
944
+ var index = el.selectedIndex;
945
+ if (index < 0) {
946
+ return null;
947
+ }
948
+ var a = [], ops = el.options;
949
+ var one = (t == 'select-one');
950
+ var max = (one ? index+1 : ops.length);
951
+ for(var i=(one ? index : 0); i < max; i++) {
952
+ var op = ops[i];
953
+ if (op.selected) {
954
+ var v = op.value;
955
+ if (!v) { // extra pain for IE...
956
+ v = (op.attributes && op.attributes['value'] && !(op.attributes['value'].specified)) ? op.text : op.value;
957
+ }
958
+ if (one) {
959
+ return v;
960
+ }
961
+ a.push(v);
962
+ }
963
+ }
964
+ return a;
965
+ }
966
+ return $(el).val();
589
967
  };
590
968
 
591
969
  /**
@@ -596,47 +974,63 @@ $.fieldValue = function(el, successful) {
596
974
  * - inputs of type submit, button, reset, and hidden will *not* be effected
597
975
  * - button elements will *not* be effected
598
976
  */
599
- $.fn.clearForm = function() {
600
- return this.each(function() {
601
- $('input,select,textarea', this).clearFields();
602
- });
977
+ $.fn.clearForm = function(includeHidden) {
978
+ return this.each(function() {
979
+ $('input,select,textarea', this).clearFields(includeHidden);
980
+ });
603
981
  };
604
982
 
605
983
  /**
606
984
  * Clears the selected form elements.
607
985
  */
608
- $.fn.clearFields = $.fn.clearInputs = function() {
609
- return this.each(function() {
610
- var t = this.type, tag = this.tagName.toLowerCase();
611
- if (t == 'text' || t == 'password' || tag == 'textarea')
612
- this.value = '';
613
- else if (t == 'checkbox' || t == 'radio')
614
- this.checked = false;
615
- else if (tag == 'select')
616
- this.selectedIndex = -1;
617
- });
986
+ $.fn.clearFields = $.fn.clearInputs = function(includeHidden) {
987
+ var re = /^(?:color|date|datetime|email|month|number|password|range|search|tel|text|time|url|week)$/i; // 'hidden' is not in this list
988
+ return this.each(function() {
989
+ var t = this.type, tag = this.tagName.toLowerCase();
990
+ if (re.test(t) || tag == 'textarea') {
991
+ this.value = '';
992
+ }
993
+ else if (t == 'checkbox' || t == 'radio') {
994
+ this.checked = false;
995
+ }
996
+ else if (tag == 'select') {
997
+ this.selectedIndex = -1;
998
+ }
999
+ else if (includeHidden) {
1000
+ // includeHidden can be the valud true, or it can be a selector string
1001
+ // indicating a special test; for example:
1002
+ // $('#myForm').clearForm('.special:hidden')
1003
+ // the above would clean hidden inputs that have the class of 'special'
1004
+ if ( (includeHidden === true && /hidden/.test(t)) ||
1005
+ (typeof includeHidden == 'string' && $(this).is(includeHidden)) )
1006
+ this.value = '';
1007
+ }
1008
+ });
618
1009
  };
619
1010
 
620
1011
  /**
621
1012
  * Resets the form data. Causes all form elements to be reset to their original value.
622
1013
  */
623
1014
  $.fn.resetForm = function() {
624
- return this.each(function() {
625
- // guard against an input with the name of 'reset'
626
- // note that IE reports the reset function as an 'object'
627
- if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType))
628
- this.reset();
629
- });
1015
+ return this.each(function() {
1016
+ // guard against an input with the name of 'reset'
1017
+ // note that IE reports the reset function as an 'object'
1018
+ if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType)) {
1019
+ this.reset();
1020
+ }
1021
+ });
630
1022
  };
631
1023
 
632
1024
  /**
633
1025
  * Enables or disables any matching elements.
634
1026
  */
635
1027
  $.fn.enable = function(b) {
636
- if (b == undefined) b = true;
637
- return this.each(function() {
638
- this.disabled = !b;
639
- });
1028
+ if (b === undefined) {
1029
+ b = true;
1030
+ }
1031
+ return this.each(function() {
1032
+ this.disabled = !b;
1033
+ });
640
1034
  };
641
1035
 
642
1036
  /**
@@ -644,32 +1038,39 @@ $.fn.enable = function(b) {
644
1038
  * selects/deselects and matching option elements.
645
1039
  */
646
1040
  $.fn.selected = function(select) {
647
- if (select == undefined) select = true;
648
- return this.each(function() {
649
- var t = this.type;
650
- if (t == 'checkbox' || t == 'radio')
651
- this.checked = select;
652
- else if (this.tagName.toLowerCase() == 'option') {
653
- var $sel = $(this).parent('select');
654
- if (select && $sel[0] && $sel[0].type == 'select-one') {
655
- // deselect all other options
656
- $sel.find('option').selected(false);
657
- }
658
- this.selected = select;
659
- }
660
- });
1041
+ if (select === undefined) {
1042
+ select = true;
1043
+ }
1044
+ return this.each(function() {
1045
+ var t = this.type;
1046
+ if (t == 'checkbox' || t == 'radio') {
1047
+ this.checked = select;
1048
+ }
1049
+ else if (this.tagName.toLowerCase() == 'option') {
1050
+ var $sel = $(this).parent('select');
1051
+ if (select && $sel[0] && $sel[0].type == 'select-one') {
1052
+ // deselect all other options
1053
+ $sel.find('option').selected(false);
1054
+ }
1055
+ this.selected = select;
1056
+ }
1057
+ });
661
1058
  };
662
1059
 
1060
+ // expose debug var
1061
+ $.fn.ajaxSubmit.debug = false;
1062
+
663
1063
  // helper fn for console logging
664
- // set $.fn.ajaxSubmit.debug to true to enable debug logging
665
1064
  function log() {
666
- if ($.fn.ajaxSubmit.debug) {
667
- var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,'');
668
- if (window.console && window.console.log)
669
- window.console.log(msg);
670
- else if (window.opera && window.opera.postError)
671
- window.opera.postError(msg);
672
- }
673
- };
1065
+ if (!$.fn.ajaxSubmit.debug)
1066
+ return;
1067
+ var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,'');
1068
+ if (window.console && window.console.log) {
1069
+ window.console.log(msg);
1070
+ }
1071
+ else if (window.opera && window.opera.postError) {
1072
+ window.opera.postError(msg);
1073
+ }
1074
+ }
674
1075
 
675
1076
  })(jQuery);