bumble 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (125) hide show
  1. data/.gitignore +16 -0
  2. data/.rvmrc +1 -0
  3. data/Gemfile +4 -0
  4. data/Rakefile +1 -0
  5. data/Readme.mdown +8 -0
  6. data/app/controllers/bumble_controller.rb +45 -0
  7. data/app/controllers/comments_controller.rb +99 -0
  8. data/app/controllers/password_resets_controller.rb +34 -0
  9. data/app/controllers/posts_controller.rb +103 -0
  10. data/app/controllers/user_sessions_controller.rb +22 -0
  11. data/app/controllers/users_controller.rb +60 -0
  12. data/app/helpers/bumble_helper.rb +50 -0
  13. data/app/models/asset.rb +57 -0
  14. data/app/models/comment.rb +78 -0
  15. data/app/models/notifier.rb +35 -0
  16. data/app/models/post.rb +57 -0
  17. data/app/models/posts/blog.rb +3 -0
  18. data/app/models/posts/code.rb +3 -0
  19. data/app/models/posts/image.rb +3 -0
  20. data/app/models/posts/link.rb +7 -0
  21. data/app/models/posts/quote.rb +3 -0
  22. data/app/models/posts/video.rb +4 -0
  23. data/app/models/user.rb +35 -0
  24. data/app/models/user_session.rb +2 -0
  25. data/app/views/comments/_comment.html.haml +14 -0
  26. data/app/views/comments/_form.html.haml +9 -0
  27. data/app/views/comments/edit.html.haml +7 -0
  28. data/app/views/comments/index.atom.builder +15 -0
  29. data/app/views/comments/new.html.haml +7 -0
  30. data/app/views/layouts/_sidebar.html.haml +20 -0
  31. data/app/views/layouts/bumble.html.haml +35 -0
  32. data/app/views/notifier/activation_confirmation.erb +5 -0
  33. data/app/views/notifier/activation_instructions.erb +5 -0
  34. data/app/views/notifier/new_comment_alert.erb +8 -0
  35. data/app/views/notifier/password_reset_instructions.erb +8 -0
  36. data/app/views/password_resets/edit.html.haml +10 -0
  37. data/app/views/password_resets/new.html.haml +7 -0
  38. data/app/views/posts/_form.html.haml +10 -0
  39. data/app/views/posts/_post.html.haml +16 -0
  40. data/app/views/posts/_preview.html.haml +8 -0
  41. data/app/views/posts/edit.html.haml +10 -0
  42. data/app/views/posts/forms/_blog.html.haml +4 -0
  43. data/app/views/posts/forms/_code.html.haml +6 -0
  44. data/app/views/posts/forms/_image.html.haml +9 -0
  45. data/app/views/posts/forms/_link.html.haml +6 -0
  46. data/app/views/posts/forms/_quote.html.haml +4 -0
  47. data/app/views/posts/forms/_video.html.haml +6 -0
  48. data/app/views/posts/index.atom.builder +15 -0
  49. data/app/views/posts/index.html.haml +21 -0
  50. data/app/views/posts/new.html.haml +8 -0
  51. data/app/views/posts/search.html.haml +27 -0
  52. data/app/views/posts/show.html.haml +22 -0
  53. data/app/views/posts/types/_blog.html.haml +4 -0
  54. data/app/views/posts/types/_code.html.haml +7 -0
  55. data/app/views/posts/types/_image.html.haml +3 -0
  56. data/app/views/posts/types/_link.html.haml +3 -0
  57. data/app/views/posts/types/_quote.html.haml +2 -0
  58. data/app/views/posts/types/_video.html.haml +5 -0
  59. data/app/views/user_sessions/new.html.haml +10 -0
  60. data/app/views/users/_form.html.haml +12 -0
  61. data/app/views/users/_user.html.haml +3 -0
  62. data/app/views/users/delete.html.haml +5 -0
  63. data/app/views/users/edit.html.haml +8 -0
  64. data/app/views/users/index.html.haml +3 -0
  65. data/app/views/users/new.html.haml +7 -0
  66. data/app/views/users/show.html.haml +6 -0
  67. data/bumble.gemspec +31 -0
  68. data/config/initializers/bumble.rb +9 -0
  69. data/config/initializers/haml.rb +2 -0
  70. data/config/initializers/html5_forms.rb +163 -0
  71. data/config/initializers/inflections.rb +10 -0
  72. data/config/initializers/mime_types.rb +5 -0
  73. data/config/initializers/will_paginate.rb +3 -0
  74. data/config/routes.rb +29 -0
  75. data/lib/bumble.rb +18 -0
  76. data/lib/bumble/version.rb +3 -0
  77. data/lib/generators/bumble/assets_generator.rb +10 -0
  78. data/lib/generators/bumble/migrations_generator.rb +40 -0
  79. data/lib/generators/bumble/templates/assets/images/.gitignore +0 -0
  80. data/lib/generators/bumble/templates/assets/images/close.png +0 -0
  81. data/lib/generators/bumble/templates/assets/images/email.png +0 -0
  82. data/lib/generators/bumble/templates/assets/images/feed.png +0 -0
  83. data/lib/generators/bumble/templates/assets/images/image.png +0 -0
  84. data/lib/generators/bumble/templates/assets/images/loading.gif +0 -0
  85. data/lib/generators/bumble/templates/assets/images/preview.png +0 -0
  86. data/lib/generators/bumble/templates/assets/images/shortcut.png +0 -0
  87. data/lib/generators/bumble/templates/assets/images/youtube.png +0 -0
  88. data/lib/generators/bumble/templates/assets/javascripts/admin.js +95 -0
  89. data/lib/generators/bumble/templates/assets/javascripts/bumble.js +76 -0
  90. data/lib/generators/bumble/templates/assets/javascripts/iphone.js +7 -0
  91. data/lib/generators/bumble/templates/assets/javascripts/jquery.autogrow.js +37 -0
  92. data/lib/generators/bumble/templates/assets/javascripts/jquery.cookie.js +96 -0
  93. data/lib/generators/bumble/templates/assets/javascripts/jquery.form.js +665 -0
  94. data/lib/generators/bumble/templates/assets/javascripts/jquery.hint.js +44 -0
  95. data/lib/generators/bumble/templates/assets/javascripts/jquery.jgrow.js +85 -0
  96. data/lib/generators/bumble/templates/assets/javascripts/jquery.js +154 -0
  97. data/lib/generators/bumble/templates/assets/javascripts/jquery.ui.core.js +18 -0
  98. data/lib/generators/bumble/templates/assets/javascripts/jquery.ui.tabs.js +13 -0
  99. data/lib/generators/bumble/templates/assets/javascripts/jquery.ui.widget.js +18 -0
  100. data/lib/generators/bumble/templates/assets/javascripts/jquery.validate.js +16 -0
  101. data/lib/generators/bumble/templates/assets/javascripts/modernizr.js +13 -0
  102. data/lib/generators/bumble/templates/assets/javascripts/ui.core.js +289 -0
  103. data/lib/generators/bumble/templates/assets/javascripts/ui.tabs.js +593 -0
  104. data/lib/generators/bumble/templates/migrations/add_approved_to_comments.rb +15 -0
  105. data/lib/generators/bumble/templates/migrations/add_delta_to_posts.rb +9 -0
  106. data/lib/generators/bumble/templates/migrations/add_missing_indexes.rb +24 -0
  107. data/lib/generators/bumble/templates/migrations/add_url_to_users.rb +9 -0
  108. data/lib/generators/bumble/templates/migrations/create_assets.rb +18 -0
  109. data/lib/generators/bumble/templates/migrations/create_comments.rb +19 -0
  110. data/lib/generators/bumble/templates/migrations/create_posts.rb +25 -0
  111. data/lib/generators/bumble/templates/migrations/create_users.rb +28 -0
  112. data/lib/generators/bumble/templates/migrations/full_text_search.rb +13 -0
  113. data/public/stylesheets/sass/bumble.sass +423 -0
  114. data/public/stylesheets/sass/iphone.sass +79 -0
  115. data/public/stylesheets/sass/print.sass +2 -0
  116. data/public/stylesheets/sass/reset.sass +49 -0
  117. data/test/factories.rb +28 -0
  118. data/test/functional/comments_controller_test.rb +177 -0
  119. data/test/functional/password_resets_controller_test.rb +61 -0
  120. data/test/functional/posts_controller_test.rb +181 -0
  121. data/test/test_helper.rb +113 -0
  122. data/test/unit/comment_test.rb +18 -0
  123. data/test/unit/post_test.rb +18 -0
  124. data/test/unit/user_test.rb +37 -0
  125. metadata +403 -0
@@ -0,0 +1,665 @@
1
+ /*
2
+ * jQuery Form Plugin
3
+ * version: 2.40 (26-FEB-2010)
4
+ * @requires jQuery v1.3.2 or later
5
+ *
6
+ * Examples and documentation at: http://malsup.com/jquery/form/
7
+ * Dual licensed under the MIT and GPL licenses:
8
+ * http://www.opensource.org/licenses/mit-license.php
9
+ * http://www.gnu.org/licenses/gpl.html
10
+ */
11
+ ;(function($) {
12
+
13
+ /*
14
+ Usage Note:
15
+ -----------
16
+ Do not use both ajaxSubmit and ajaxForm on the same form. These
17
+ functions are intended to be exclusive. Use ajaxSubmit if you want
18
+ to bind your own submit handler to the form. For example,
19
+
20
+ $(document).ready(function() {
21
+ $('#myForm').bind('submit', function() {
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.
40
+ */
41
+
42
+ /**
43
+ * ajaxSubmit() provides a mechanism for immediately submitting
44
+ * an HTML form using AJAX.
45
+ */
46
+ $.fn.ajaxSubmit = function(options) {
47
+ // fast fail if nothing selected (http://dev.jquery.com/ticket/2752)
48
+ if (!this.length) {
49
+ log('ajaxSubmit: skipping submit process - no element selected');
50
+ return this;
51
+ }
52
+
53
+ 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
+ $(options.target).html(data).each(oldSuccess, arguments);
128
+ });
129
+ }
130
+ else if (options.success)
131
+ callbacks.push(options.success);
132
+
133
+ options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg
134
+ for (var i=0, max=callbacks.length; i < max; i++)
135
+ callbacks[i].apply(options, [data, status, xhr || $form, $form]);
136
+ };
137
+
138
+ // are there files to upload?
139
+ var files = $('input:file', this).fieldValue();
140
+ var found = false;
141
+ for (var j=0; j < files.length; j++)
142
+ if (files[j])
143
+ found = true;
144
+
145
+ var multipart = false;
146
+ // var mp = 'multipart/form-data';
147
+ // multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp);
148
+
149
+ // options.iframe allows user to force iframe mode
150
+ // 06-NOV-09: now defaulting to iframe mode if file input is detected
151
+ if ((files.length && options.iframe !== false) || options.iframe || found || multipart) {
152
+ // hack to fix Safari hang (thanks to Tim Molendijk for this)
153
+ // see: http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
154
+ if (options.closeKeepAlive)
155
+ $.get(options.closeKeepAlive, fileUpload);
156
+ else
157
+ fileUpload();
158
+ }
159
+ else
160
+ $.ajax(options);
161
+
162
+ // fire 'notify' event
163
+ this.trigger('form-submit-notify', [this, options]);
164
+ return this;
165
+
166
+
167
+ // private function for handling file uploads (hat tip to YAHOO!)
168
+ function fileUpload() {
169
+ var form = $form[0];
170
+
171
+ if ($(':input[name=submit]', form).length) {
172
+ alert('Error: Form elements must not be named "submit".');
173
+ return;
174
+ }
175
+
176
+ var opts = $.extend({}, $.ajaxSettings, options);
177
+ var s = $.extend(true, {}, $.extend(true, {}, $.ajaxSettings), opts);
178
+
179
+ var id = 'jqFormIO' + (new Date().getTime());
180
+ var $io = $('<iframe id="' + id + '" name="' + id + '" src="'+ opts.iframeSrc +'" onload="(jQuery(this).data(\'form-plugin-onload\'))()" />');
181
+ var io = $io[0];
182
+
183
+ $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });
184
+
185
+ var xhr = { // mock object
186
+ aborted: 0,
187
+ responseText: null,
188
+ responseXML: null,
189
+ status: 0,
190
+ statusText: 'n/a',
191
+ getAllResponseHeaders: function() {},
192
+ getResponseHeader: function() {},
193
+ setRequestHeader: function() {},
194
+ abort: function() {
195
+ this.aborted = 1;
196
+ $io.attr('src', opts.iframeSrc); // abort op in progress
197
+ }
198
+ };
199
+
200
+ var g = opts.global;
201
+ // trigger ajax global events so that activity/block indicators work like normal
202
+ if (g && ! $.active++) $.event.trigger("ajaxStart");
203
+ if (g) $.event.trigger("ajaxSend", [xhr, opts]);
204
+
205
+ if (s.beforeSend && s.beforeSend(xhr, s) === false) {
206
+ s.global && $.active--;
207
+ return;
208
+ }
209
+ if (xhr.aborted)
210
+ return;
211
+
212
+ var cbInvoked = false;
213
+ var timedOut = 0;
214
+
215
+ // add submitting element to data if we know it
216
+ var sub = form.clk;
217
+ if (sub) {
218
+ var n = sub.name;
219
+ if (n && !sub.disabled) {
220
+ opts.extraData = opts.extraData || {};
221
+ opts.extraData[n] = sub.value;
222
+ if (sub.type == "image") {
223
+ opts.extraData[name+'.x'] = form.clk_x;
224
+ opts.extraData[name+'.y'] = form.clk_y;
225
+ }
226
+ }
227
+ }
228
+
229
+ // take a breath so that pending repaints get some cpu time before the upload starts
230
+ function doSubmit() {
231
+ // make sure form attrs are set
232
+ var t = $form.attr('target'), a = $form.attr('action');
233
+
234
+ // update form attrs in IE friendly way
235
+ form.setAttribute('target',id);
236
+ if (form.getAttribute('method') != 'POST')
237
+ form.setAttribute('method', 'POST');
238
+ if (form.getAttribute('action') != opts.url)
239
+ form.setAttribute('action', opts.url);
240
+
241
+ // ie borks in some cases when setting encoding
242
+ if (! opts.skipEncodingOverride) {
243
+ $form.attr({
244
+ encoding: 'multipart/form-data',
245
+ enctype: 'multipart/form-data'
246
+ });
247
+ }
248
+
249
+ // support timout
250
+ if (opts.timeout)
251
+ setTimeout(function() { timedOut = true; cb(); }, opts.timeout);
252
+
253
+ // add "extra" data to form if provided in options
254
+ var extraInputs = [];
255
+ try {
256
+ if (opts.extraData)
257
+ for (var n in opts.extraData)
258
+ extraInputs.push(
259
+ $('<input type="hidden" name="'+n+'" value="'+opts.extraData[n]+'" />')
260
+ .appendTo(form)[0]);
261
+
262
+ // add iframe to doc and submit the form
263
+ $io.appendTo('body');
264
+ $io.data('form-plugin-onload', cb);
265
+ form.submit();
266
+ }
267
+ finally {
268
+ // reset attrs and remove "extra" input elements
269
+ form.setAttribute('action',a);
270
+ t ? form.setAttribute('target', t) : $form.removeAttr('target');
271
+ $(extraInputs).remove();
272
+ }
273
+ };
274
+
275
+ if (opts.forceSync)
276
+ doSubmit();
277
+ else
278
+ setTimeout(doSubmit, 10); // this lets dom updates render
279
+
280
+ var domCheckCount = 100;
281
+
282
+ function cb() {
283
+ if (cbInvoked)
284
+ return;
285
+
286
+ var ok = true;
287
+ try {
288
+ if (timedOut) throw 'timeout';
289
+ // extract the server response from the iframe
290
+ var data, doc;
291
+
292
+ doc = io.contentWindow ? io.contentWindow.document : io.contentDocument ? io.contentDocument : io.document;
293
+
294
+ var isXml = opts.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc);
295
+ log('isXml='+isXml);
296
+ if (!isXml && (doc.body == null || doc.body.innerHTML == '')) {
297
+ if (--domCheckCount) {
298
+ // in some browsers (Opera) the iframe DOM is not always traversable when
299
+ // the onload callback fires, so we loop a bit to accommodate
300
+ setTimeout(cb, 250);
301
+ return;
302
+ }
303
+ log('Could not access iframe DOM after 100 tries.');
304
+ return;
305
+ }
306
+
307
+ cbInvoked = true;
308
+ xhr.responseText = doc.body ? doc.body.innerHTML : null;
309
+ xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
310
+ xhr.getResponseHeader = function(header){
311
+ var headers = {'content-type': opts.dataType};
312
+ return headers[header];
313
+ };
314
+
315
+ if (opts.dataType == 'json' || opts.dataType == 'script') {
316
+ // see if user embedded response in textarea
317
+ var ta = doc.getElementsByTagName('textarea')[0];
318
+ if (ta)
319
+ xhr.responseText = ta.value;
320
+ else {
321
+ // account for browsers injecting pre around json response
322
+ var pre = doc.getElementsByTagName('pre')[0];
323
+ if (pre)
324
+ xhr.responseText = pre.innerHTML;
325
+ }
326
+ }
327
+ else if (opts.dataType == 'xml' && !xhr.responseXML && xhr.responseText != null) {
328
+ xhr.responseXML = toXml(xhr.responseText);
329
+ }
330
+ data = $.httpData(xhr, opts.dataType);
331
+ }
332
+ catch(e){
333
+ ok = false;
334
+ $.handleError(opts, xhr, 'error', e);
335
+ }
336
+
337
+ // ordering of these callbacks/triggers is odd, but that's how $.ajax does it
338
+ if (ok) {
339
+ opts.success(data, 'success');
340
+ if (g) $.event.trigger("ajaxSuccess", [xhr, opts]);
341
+ }
342
+ if (g) $.event.trigger("ajaxComplete", [xhr, opts]);
343
+ if (g && ! --$.active) $.event.trigger("ajaxStop");
344
+ if (opts.complete) opts.complete(xhr, ok ? 'success' : 'error');
345
+
346
+ // clean up
347
+ setTimeout(function() {
348
+ $io.removeData('form-plugin-onload');
349
+ $io.remove();
350
+ xhr.responseXML = null;
351
+ }, 100);
352
+ };
353
+
354
+ function toXml(s, doc) {
355
+ if (window.ActiveXObject) {
356
+ doc = new ActiveXObject('Microsoft.XMLDOM');
357
+ doc.async = 'false';
358
+ doc.loadXML(s);
359
+ }
360
+ else
361
+ doc = (new DOMParser()).parseFromString(s, 'text/xml');
362
+ return (doc && doc.documentElement && doc.documentElement.tagName != 'parsererror') ? doc : null;
363
+ };
364
+ };
365
+ };
366
+
367
+ /**
368
+ * ajaxForm() provides a mechanism for fully automating form submission.
369
+ *
370
+ * The advantages of using this method instead of ajaxSubmit() are:
371
+ *
372
+ * 1: This method will include coordinates for <input type="image" /> elements (if the element
373
+ * is used to submit the form).
374
+ * 2. This method will include the submit element's name/value data (for the element that was
375
+ * used to submit the form).
376
+ * 3. This method binds the submit() method to the form for you.
377
+ *
378
+ * The options argument for ajaxForm works exactly as it does for ajaxSubmit. ajaxForm merely
379
+ * passes the options argument along after properly binding events for submit elements and
380
+ * the form itself.
381
+ */
382
+ $.fn.ajaxForm = function(options) {
383
+ return this.ajaxFormUnbind().bind('submit.form-plugin', function(e) {
384
+ e.preventDefault();
385
+ $(this).ajaxSubmit(options);
386
+ }).bind('click.form-plugin', function(e) {
387
+ var target = e.target;
388
+ var $el = $(target);
389
+ if (!($el.is(":submit,input:image"))) {
390
+ // is this a child element of the submit el? (ex: a span within a button)
391
+ var t = $el.closest(':submit');
392
+ if (t.length == 0)
393
+ return;
394
+ target = t[0];
395
+ }
396
+ var form = this;
397
+ form.clk = target;
398
+ if (target.type == 'image') {
399
+ if (e.offsetX != undefined) {
400
+ form.clk_x = e.offsetX;
401
+ form.clk_y = e.offsetY;
402
+ } else if (typeof $.fn.offset == 'function') { // try to use dimensions plugin
403
+ var offset = $el.offset();
404
+ form.clk_x = e.pageX - offset.left;
405
+ form.clk_y = e.pageY - offset.top;
406
+ } else {
407
+ form.clk_x = e.pageX - target.offsetLeft;
408
+ form.clk_y = e.pageY - target.offsetTop;
409
+ }
410
+ }
411
+ // clear form vars
412
+ setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 100);
413
+ });
414
+ };
415
+
416
+ // ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
417
+ $.fn.ajaxFormUnbind = function() {
418
+ return this.unbind('submit.form-plugin click.form-plugin');
419
+ };
420
+
421
+ /**
422
+ * formToArray() gathers form element data into an array of objects that can
423
+ * be passed to any of the following ajax functions: $.get, $.post, or load.
424
+ * Each object in the array has both a 'name' and 'value' property. An example of
425
+ * an array for a simple login form might be:
426
+ *
427
+ * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ]
428
+ *
429
+ * It is this array that is passed to pre-submit callback functions provided to the
430
+ * ajaxSubmit() and ajaxForm() methods.
431
+ */
432
+ $.fn.formToArray = function(semantic) {
433
+ var a = [];
434
+ if (this.length == 0) return a;
435
+
436
+ var form = this[0];
437
+ var els = semantic ? form.getElementsByTagName('*') : form.elements;
438
+ if (!els) return a;
439
+ for(var i=0, max=els.length; i < max; i++) {
440
+ var el = els[i];
441
+ var n = el.name;
442
+ if (!n) continue;
443
+
444
+ if (semantic && form.clk && el.type == "image") {
445
+ // handle image inputs on the fly when semantic == true
446
+ if(!el.disabled && form.clk == el) {
447
+ a.push({name: n, value: $(el).val()});
448
+ a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
449
+ }
450
+ continue;
451
+ }
452
+
453
+ var v = $.fieldValue(el, true);
454
+ if (v && v.constructor == Array) {
455
+ for(var j=0, jmax=v.length; j < jmax; j++)
456
+ a.push({name: n, value: v[j]});
457
+ }
458
+ else if (v !== null && typeof v != 'undefined')
459
+ a.push({name: n, value: v});
460
+ }
461
+
462
+ if (!semantic && form.clk) {
463
+ // input type=='image' are not found in elements array! handle it here
464
+ var $input = $(form.clk), input = $input[0], n = input.name;
465
+ if (n && !input.disabled && input.type == 'image') {
466
+ a.push({name: n, value: $input.val()});
467
+ a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
468
+ }
469
+ }
470
+ return a;
471
+ };
472
+
473
+ /**
474
+ * Serializes form data into a 'submittable' string. This method will return a string
475
+ * in the format: name1=value1&amp;name2=value2
476
+ */
477
+ $.fn.formSerialize = function(semantic) {
478
+ //hand off to jQuery.param for proper encoding
479
+ return $.param(this.formToArray(semantic));
480
+ };
481
+
482
+ /**
483
+ * Serializes all field elements in the jQuery object into a query string.
484
+ * This method will return a string in the format: name1=value1&amp;name2=value2
485
+ */
486
+ $.fn.fieldSerialize = function(successful) {
487
+ var a = [];
488
+ this.each(function() {
489
+ var n = this.name;
490
+ if (!n) return;
491
+ var v = $.fieldValue(this, successful);
492
+ if (v && v.constructor == Array) {
493
+ for (var i=0,max=v.length; i < max; i++)
494
+ a.push({name: n, value: v[i]});
495
+ }
496
+ else if (v !== null && typeof v != 'undefined')
497
+ a.push({name: this.name, value: v});
498
+ });
499
+ //hand off to jQuery.param for proper encoding
500
+ return $.param(a);
501
+ };
502
+
503
+ /**
504
+ * Returns the value(s) of the element in the matched set. For example, consider the following form:
505
+ *
506
+ * <form><fieldset>
507
+ * <input name="A" type="text" />
508
+ * <input name="A" type="text" />
509
+ * <input name="B" type="checkbox" value="B1" />
510
+ * <input name="B" type="checkbox" value="B2"/>
511
+ * <input name="C" type="radio" value="C1" />
512
+ * <input name="C" type="radio" value="C2" />
513
+ * </fieldset></form>
514
+ *
515
+ * var v = $(':text').fieldValue();
516
+ * // if no values are entered into the text inputs
517
+ * v == ['','']
518
+ * // if values entered into the text inputs are 'foo' and 'bar'
519
+ * v == ['foo','bar']
520
+ *
521
+ * var v = $(':checkbox').fieldValue();
522
+ * // if neither checkbox is checked
523
+ * v === undefined
524
+ * // if both checkboxes are checked
525
+ * v == ['B1', 'B2']
526
+ *
527
+ * var v = $(':radio').fieldValue();
528
+ * // if neither radio is checked
529
+ * v === undefined
530
+ * // if first radio is checked
531
+ * v == ['C1']
532
+ *
533
+ * The successful argument controls whether or not the field element must be 'successful'
534
+ * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls).
535
+ * The default value of the successful argument is true. If this value is false the value(s)
536
+ * for each element is returned.
537
+ *
538
+ * Note: This method *always* returns an array. If no valid value can be determined the
539
+ * array will be empty, otherwise it will contain one or more values.
540
+ */
541
+ $.fn.fieldValue = function(successful) {
542
+ for (var val=[], i=0, max=this.length; i < max; i++) {
543
+ var el = this[i];
544
+ var v = $.fieldValue(el, successful);
545
+ if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length))
546
+ continue;
547
+ v.constructor == Array ? $.merge(val, v) : val.push(v);
548
+ }
549
+ return val;
550
+ };
551
+
552
+ /**
553
+ * Returns the value of the field element.
554
+ */
555
+ $.fieldValue = function(el, successful) {
556
+ var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
557
+ if (typeof successful == 'undefined') successful = true;
558
+
559
+ if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
560
+ (t == 'checkbox' || t == 'radio') && !el.checked ||
561
+ (t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
562
+ tag == 'select' && el.selectedIndex == -1))
563
+ return null;
564
+
565
+ if (tag == 'select') {
566
+ var index = el.selectedIndex;
567
+ if (index < 0) return null;
568
+ var a = [], ops = el.options;
569
+ var one = (t == 'select-one');
570
+ var max = (one ? index+1 : ops.length);
571
+ for(var i=(one ? index : 0); i < max; i++) {
572
+ var op = ops[i];
573
+ if (op.selected) {
574
+ var v = op.value;
575
+ if (!v) // extra pain for IE...
576
+ v = (op.attributes && op.attributes['value'] && !(op.attributes['value'].specified)) ? op.text : op.value;
577
+ if (one) return v;
578
+ a.push(v);
579
+ }
580
+ }
581
+ return a;
582
+ }
583
+ return el.value;
584
+ };
585
+
586
+ /**
587
+ * Clears the form data. Takes the following actions on the form's input fields:
588
+ * - input text fields will have their 'value' property set to the empty string
589
+ * - select elements will have their 'selectedIndex' property set to -1
590
+ * - checkbox and radio inputs will have their 'checked' property set to false
591
+ * - inputs of type submit, button, reset, and hidden will *not* be effected
592
+ * - button elements will *not* be effected
593
+ */
594
+ $.fn.clearForm = function() {
595
+ return this.each(function() {
596
+ $('input,select,textarea', this).clearFields();
597
+ });
598
+ };
599
+
600
+ /**
601
+ * Clears the selected form elements.
602
+ */
603
+ $.fn.clearFields = $.fn.clearInputs = function() {
604
+ return this.each(function() {
605
+ var t = this.type, tag = this.tagName.toLowerCase();
606
+ if (t == 'text' || t == 'password' || tag == 'textarea')
607
+ this.value = '';
608
+ else if (t == 'checkbox' || t == 'radio')
609
+ this.checked = false;
610
+ else if (tag == 'select')
611
+ this.selectedIndex = -1;
612
+ });
613
+ };
614
+
615
+ /**
616
+ * Resets the form data. Causes all form elements to be reset to their original value.
617
+ */
618
+ $.fn.resetForm = function() {
619
+ return this.each(function() {
620
+ // guard against an input with the name of 'reset'
621
+ // note that IE reports the reset function as an 'object'
622
+ if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType))
623
+ this.reset();
624
+ });
625
+ };
626
+
627
+ /**
628
+ * Enables or disables any matching elements.
629
+ */
630
+ $.fn.enable = function(b) {
631
+ if (b == undefined) b = true;
632
+ return this.each(function() {
633
+ this.disabled = !b;
634
+ });
635
+ };
636
+
637
+ /**
638
+ * Checks/unchecks any matching checkboxes or radio buttons and
639
+ * selects/deselects and matching option elements.
640
+ */
641
+ $.fn.selected = function(select) {
642
+ if (select == undefined) select = true;
643
+ return this.each(function() {
644
+ var t = this.type;
645
+ if (t == 'checkbox' || t == 'radio')
646
+ this.checked = select;
647
+ else if (this.tagName.toLowerCase() == 'option') {
648
+ var $sel = $(this).parent('select');
649
+ if (select && $sel[0] && $sel[0].type == 'select-one') {
650
+ // deselect all other options
651
+ $sel.find('option').selected(false);
652
+ }
653
+ this.selected = select;
654
+ }
655
+ });
656
+ };
657
+
658
+ // helper fn for console logging
659
+ // set $.fn.ajaxSubmit.debug to true to enable debug logging
660
+ function log() {
661
+ if ($.fn.ajaxSubmit.debug && window.console && window.console.log)
662
+ window.console.log('[jquery.form] ' + Array.prototype.join.call(arguments,''));
663
+ };
664
+
665
+ })(jQuery);