discerner 1.1.18

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