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,593 @@
1
+ /*
2
+ * jQuery UI Tabs
3
+ *
4
+ * Copyright (c) 2007, 2008 Klaus Hartl (stilbuero.de)
5
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
6
+ * and GPL (GPL-LICENSE.txt) licenses.
7
+ *
8
+ * http://docs.jquery.com/UI/Tabs
9
+ *
10
+ * Depends:
11
+ * ui.core.js
12
+ *
13
+ */
14
+ (function($) {
15
+
16
+ $.widget("ui.tabs", {
17
+ init: function() {
18
+ this.options.event += '.tabs'; // namespace event
19
+
20
+ // create tabs
21
+ this.tabify(true);
22
+ },
23
+ setData: function(key, value) {
24
+ if ((/^selected/).test(key))
25
+ this.select(value);
26
+ else {
27
+ this.options[key] = value;
28
+ this.tabify();
29
+ }
30
+ },
31
+ length: function() {
32
+ return this.$tabs.length;
33
+ },
34
+ tabId: function(a) {
35
+ return a.title && a.title.replace(/\s/g, '_').replace(/[^A-Za-z0-9\-_:\.]/g, '')
36
+ || this.options.idPrefix + $.data(a);
37
+ },
38
+ ui: function(tab, panel) {
39
+ return {
40
+ options: this.options,
41
+ tab: tab,
42
+ panel: panel
43
+ };
44
+ },
45
+ tabify: function(init) {
46
+
47
+ this.$lis = $('li:has(a[href])', this.element);
48
+ this.$tabs = this.$lis.map(function() { return $('a', this)[0]; });
49
+ this.$panels = $([]);
50
+
51
+ var self = this, o = this.options;
52
+
53
+ this.$tabs.each(function(i, a) {
54
+ // inline tab
55
+ if (a.hash && a.hash.replace('#', '')) // Safari 2 reports '#' for an empty hash
56
+ self.$panels = self.$panels.add(a.hash);
57
+ // remote tab
58
+ else if ($(a).attr('href') != '#') { // prevent loading the page itself if href is just "#"
59
+ $.data(a, 'href.tabs', a.href); // required for restore on destroy
60
+ $.data(a, 'load.tabs', a.href); // mutable
61
+ var id = self.tabId(a);
62
+ a.href = '#' + id;
63
+ var $panel = $('#' + id);
64
+ if (!$panel.length) {
65
+ $panel = $(o.panelTemplate).attr('id', id).addClass(o.panelClass)
66
+ .insertAfter( self.$panels[i - 1] || self.element );
67
+ $panel.data('destroy.tabs', true);
68
+ }
69
+ self.$panels = self.$panels.add( $panel );
70
+ }
71
+ // invalid tab href
72
+ else
73
+ o.disabled.push(i + 1);
74
+ });
75
+
76
+ if (init) {
77
+
78
+ // attach necessary classes for styling if not present
79
+ this.element.addClass(o.navClass);
80
+ this.$panels.each(function() {
81
+ var $this = $(this);
82
+ $this.addClass(o.panelClass);
83
+ });
84
+
85
+ // Selected tab
86
+ // use "selected" option or try to retrieve:
87
+ // 1. from fragment identifier in url
88
+ // 2. from cookie
89
+ // 3. from selected class attribute on <li>
90
+ if (o.selected === undefined) {
91
+ if (location.hash) {
92
+ this.$tabs.each(function(i, a) {
93
+ if (a.hash == location.hash) {
94
+ o.selected = i;
95
+ // prevent page scroll to fragment
96
+ if ($.browser.msie || $.browser.opera) { // && !o.remote
97
+ var $toShow = $(location.hash), toShowId = $toShow.attr('id');
98
+ $toShow.attr('id', '');
99
+ setTimeout(function() {
100
+ $toShow.attr('id', toShowId); // restore id
101
+ }, 500);
102
+ }
103
+ scrollTo(0, 0);
104
+ return false; // break
105
+ }
106
+ });
107
+ }
108
+ else if (o.cookie) {
109
+ var index = parseInt($.cookie('ui-tabs' + $.data(self.element)),10);
110
+ if (index && self.$tabs[index])
111
+ o.selected = index;
112
+ }
113
+ else if (self.$lis.filter('.' + o.selectedClass).length)
114
+ o.selected = self.$lis.index( self.$lis.filter('.' + o.selectedClass)[0] );
115
+ }
116
+ o.selected = o.selected === null || o.selected !== undefined ? o.selected : 0; // first tab selected by default
117
+
118
+ // Take disabling tabs via class attribute from HTML
119
+ // into account and update option properly.
120
+ // A selected tab cannot become disabled.
121
+ o.disabled = $.unique(o.disabled.concat(
122
+ $.map(this.$lis.filter('.' + o.disabledClass),
123
+ function(n, i) { return self.$lis.index(n); } )
124
+ )).sort();
125
+ if ($.inArray(o.selected, o.disabled) != -1)
126
+ o.disabled.splice($.inArray(o.selected, o.disabled), 1);
127
+
128
+ // highlight selected tab
129
+ this.$panels.addClass(o.hideClass);
130
+ this.$lis.removeClass(o.selectedClass);
131
+ if (o.selected !== null) {
132
+ this.$panels.eq(o.selected).show().removeClass(o.hideClass); // use show and remove class to show in any case no matter how it has been hidden before
133
+ this.$lis.eq(o.selected).addClass(o.selectedClass);
134
+
135
+ // seems to be expected behavior that the show callback is fired
136
+ var onShow = function() {
137
+ $(self.element).triggerHandler('tabsshow',
138
+ [self.ui(self.$tabs[o.selected], self.$panels[o.selected])], o.show);
139
+ };
140
+
141
+ // load if remote tab
142
+ if ($.data(this.$tabs[o.selected], 'load.tabs'))
143
+ this.load(o.selected, onShow);
144
+ // just trigger show event
145
+ else
146
+ onShow();
147
+
148
+ }
149
+
150
+ // clean up to avoid memory leaks in certain versions of IE 6
151
+ $(window).bind('unload', function() {
152
+ self.$tabs.unbind('.tabs');
153
+ self.$lis = self.$tabs = self.$panels = null;
154
+ });
155
+
156
+ }
157
+
158
+ // disable tabs
159
+ for (var i = 0, li; li = this.$lis[i]; i++)
160
+ $(li)[$.inArray(i, o.disabled) != -1 && !$(li).hasClass(o.selectedClass) ? 'addClass' : 'removeClass'](o.disabledClass);
161
+
162
+ // reset cache if switching from cached to not cached
163
+ if (o.cache === false)
164
+ this.$tabs.removeData('cache.tabs');
165
+
166
+ // set up animations
167
+ var hideFx, showFx, baseFx = { 'min-width': 0, duration: 1 }, baseDuration = 'normal';
168
+ if (o.fx && o.fx.constructor == Array)
169
+ hideFx = o.fx[0] || baseFx, showFx = o.fx[1] || baseFx;
170
+ else
171
+ hideFx = showFx = o.fx || baseFx;
172
+
173
+ // reset some styles to maintain print style sheets etc.
174
+ var resetCSS = { display: '', overflow: '', height: '' };
175
+ if (!$.browser.msie) // not in IE to prevent ClearType font issue
176
+ resetCSS.opacity = '';
177
+
178
+ // Hide a tab, animation prevents browser scrolling to fragment,
179
+ // $show is optional.
180
+ function hideTab(clicked, $hide, $show) {
181
+ $hide.animate(hideFx, hideFx.duration || baseDuration, function() { //
182
+ $hide.addClass(o.hideClass).css(resetCSS); // maintain flexible height and accessibility in print etc.
183
+ if ($.browser.msie && hideFx.opacity)
184
+ $hide[0].style.filter = '';
185
+ if ($show)
186
+ showTab(clicked, $show, $hide);
187
+ });
188
+ }
189
+
190
+ // Show a tab, animation prevents browser scrolling to fragment,
191
+ // $hide is optional.
192
+ function showTab(clicked, $show, $hide) {
193
+ if (showFx === baseFx)
194
+ $show.css('display', 'block'); // prevent occasionally occuring flicker in Firefox cause by gap between showing and hiding the tab panels
195
+ $show.animate(showFx, showFx.duration || baseDuration, function() {
196
+ $show.removeClass(o.hideClass).css(resetCSS); // maintain flexible height and accessibility in print etc.
197
+ if ($.browser.msie && showFx.opacity)
198
+ $show[0].style.filter = '';
199
+
200
+ // callback
201
+ $(self.element).triggerHandler('tabsshow',
202
+ [self.ui(clicked, $show[0])], o.show);
203
+
204
+ });
205
+ }
206
+
207
+ // switch a tab
208
+ function switchTab(clicked, $li, $hide, $show) {
209
+ /*if (o.bookmarkable && trueClick) { // add to history only if true click occured, not a triggered click
210
+ $.ajaxHistory.update(clicked.hash);
211
+ }*/
212
+ $li.addClass(o.selectedClass)
213
+ .siblings().removeClass(o.selectedClass);
214
+ hideTab(clicked, $hide, $show);
215
+ }
216
+
217
+ // attach tab event handler, unbind to avoid duplicates from former tabifying...
218
+ this.$tabs.unbind('.tabs').bind(o.event, function() {
219
+
220
+ //var trueClick = e.clientX; // add to history only if true click occured, not a triggered click
221
+ var $li = $(this).parents('li:eq(0)'),
222
+ $hide = self.$panels.filter(':visible'),
223
+ $show = $(this.hash);
224
+
225
+ // If tab is already selected and not unselectable or tab disabled or
226
+ // or is already loading or click callback returns false stop here.
227
+ // Check if click handler returns false last so that it is not executed
228
+ // for a disabled or loading tab!
229
+ if (($li.hasClass(o.selectedClass) && !o.unselect)
230
+ || $li.hasClass(o.disabledClass)
231
+ || $(this).hasClass(o.loadingClass)
232
+ || $(self.element).triggerHandler('tabsselect', [self.ui(this, $show[0])], o.select) === false
233
+ ) {
234
+ this.blur();
235
+ return false;
236
+ }
237
+
238
+ self.options.selected = self.$tabs.index(this);
239
+
240
+ // if tab may be closed
241
+ if (o.unselect) {
242
+ if ($li.hasClass(o.selectedClass)) {
243
+ self.options.selected = null;
244
+ $li.removeClass(o.selectedClass);
245
+ self.$panels.stop();
246
+ hideTab(this, $hide);
247
+ this.blur();
248
+ return false;
249
+ } else if (!$hide.length) {
250
+ self.$panels.stop();
251
+ var a = this;
252
+ self.load(self.$tabs.index(this), function() {
253
+ $li.addClass(o.selectedClass).addClass(o.unselectClass);
254
+ showTab(a, $show);
255
+ });
256
+ this.blur();
257
+ return false;
258
+ }
259
+ }
260
+
261
+ if (o.cookie)
262
+ $.cookie('ui-tabs' + $.data(self.element), self.options.selected, o.cookie);
263
+
264
+ // stop possibly running animations
265
+ self.$panels.stop();
266
+
267
+ // show new tab
268
+ if ($show.length) {
269
+
270
+ // prevent scrollbar scrolling to 0 and than back in IE7, happens only if bookmarking/history is enabled
271
+ /*if ($.browser.msie && o.bookmarkable) {
272
+ var showId = this.hash.replace('#', '');
273
+ $show.attr('id', '');
274
+ setTimeout(function() {
275
+ $show.attr('id', showId); // restore id
276
+ }, 0);
277
+ }*/
278
+
279
+ var a = this;
280
+ self.load(self.$tabs.index(this), $hide.length ?
281
+ function() {
282
+ switchTab(a, $li, $hide, $show);
283
+ } :
284
+ function() {
285
+ $li.addClass(o.selectedClass);
286
+ showTab(a, $show);
287
+ }
288
+ );
289
+
290
+ // Set scrollbar to saved position - need to use timeout with 0 to prevent browser scroll to target of hash
291
+ /*var scrollX = window.pageXOffset || document.documentElement && document.documentElement.scrollLeft || document.body.scrollLeft || 0;
292
+ var scrollY = window.pageYOffset || document.documentElement && document.documentElement.scrollTop || document.body.scrollTop || 0;
293
+ setTimeout(function() {
294
+ scrollTo(scrollX, scrollY);
295
+ }, 0);*/
296
+
297
+ } else
298
+ throw 'jQuery UI Tabs: Mismatching fragment identifier.';
299
+
300
+ // Prevent IE from keeping other link focussed when using the back button
301
+ // and remove dotted border from clicked link. This is controlled in modern
302
+ // browsers via CSS, also blur removes focus from address bar in Firefox
303
+ // which can become a usability and annoying problem with tabsRotate.
304
+ if ($.browser.msie)
305
+ this.blur();
306
+
307
+ //return o.bookmarkable && !!trueClick; // convert trueClick == undefined to Boolean required in IE
308
+ return false;
309
+
310
+ });
311
+
312
+ // disable click if event is configured to something else
313
+ if (!(/^click/).test(o.event))
314
+ this.$tabs.bind('click.tabs', function() { return false; });
315
+
316
+ },
317
+ add: function(url, label, index) {
318
+ if (index == undefined)
319
+ index = this.$tabs.length; // append by default
320
+
321
+ var o = this.options;
322
+ var $li = $(o.tabTemplate.replace(/#\{href\}/g, url).replace(/#\{label\}/g, label));
323
+ $li.data('destroy.tabs', true);
324
+
325
+ var id = url.indexOf('#') == 0 ? url.replace('#', '') : this.tabId( $('a:first-child', $li)[0] );
326
+
327
+ // try to find an existing element before creating a new one
328
+ var $panel = $('#' + id);
329
+ if (!$panel.length) {
330
+ $panel = $(o.panelTemplate).attr('id', id)
331
+ .addClass(o.hideClass)
332
+ .data('destroy.tabs', true);
333
+ }
334
+ $panel.addClass(o.panelClass);
335
+ if (index >= this.$lis.length) {
336
+ $li.appendTo(this.element);
337
+ $panel.appendTo(this.element[0].parentNode);
338
+ } else {
339
+ $li.insertBefore(this.$lis[index]);
340
+ $panel.insertBefore(this.$panels[index]);
341
+ }
342
+
343
+ o.disabled = $.map(o.disabled,
344
+ function(n, i) { return n >= index ? ++n : n });
345
+
346
+ this.tabify();
347
+
348
+ if (this.$tabs.length == 1) {
349
+ $li.addClass(o.selectedClass);
350
+ $panel.removeClass(o.hideClass);
351
+ var href = $.data(this.$tabs[0], 'load.tabs');
352
+ if (href)
353
+ this.load(index, href);
354
+ }
355
+
356
+ // callback
357
+ this.element.triggerHandler('tabsadd',
358
+ [this.ui(this.$tabs[index], this.$panels[index])], o.add
359
+ );
360
+ },
361
+ remove: function(index) {
362
+ var o = this.options, $li = this.$lis.eq(index).remove(),
363
+ $panel = this.$panels.eq(index).remove();
364
+
365
+ // If selected tab was removed focus tab to the right or
366
+ // in case the last tab was removed the tab to the left.
367
+ if ($li.hasClass(o.selectedClass) && this.$tabs.length > 1)
368
+ this.select(index + (index + 1 < this.$tabs.length ? 1 : -1));
369
+
370
+ o.disabled = $.map($.grep(o.disabled, function(n, i) { return n != index; }),
371
+ function(n, i) { return n >= index ? --n : n });
372
+
373
+ this.tabify();
374
+
375
+ // callback
376
+ this.element.triggerHandler('tabsremove',
377
+ [this.ui($li.find('a')[0], $panel[0])], o.remove
378
+ );
379
+ },
380
+ enable: function(index) {
381
+ var o = this.options;
382
+ if ($.inArray(index, o.disabled) == -1)
383
+ return;
384
+
385
+ var $li = this.$lis.eq(index).removeClass(o.disabledClass);
386
+ if ($.browser.safari) { // fix disappearing tab (that used opacity indicating disabling) after enabling in Safari 2...
387
+ $li.css('display', 'inline-block');
388
+ setTimeout(function() {
389
+ $li.css('display', 'block');
390
+ }, 0);
391
+ }
392
+
393
+ o.disabled = $.grep(o.disabled, function(n, i) { return n != index; });
394
+
395
+ // callback
396
+ this.element.triggerHandler('tabsenable',
397
+ [this.ui(this.$tabs[index], this.$panels[index])], o.enable
398
+ );
399
+
400
+ },
401
+ disable: function(index) {
402
+ var self = this, o = this.options;
403
+ if (index != o.selected) { // cannot disable already selected tab
404
+ this.$lis.eq(index).addClass(o.disabledClass);
405
+
406
+ o.disabled.push(index);
407
+ o.disabled.sort();
408
+
409
+ // callback
410
+ this.element.triggerHandler('tabsdisable',
411
+ [this.ui(this.$tabs[index], this.$panels[index])], o.disable
412
+ );
413
+ }
414
+ },
415
+ select: function(index) {
416
+ if (typeof index == 'string')
417
+ index = this.$tabs.index( this.$tabs.filter('[href$=' + index + ']')[0] );
418
+ this.$tabs.eq(index).trigger(this.options.event);
419
+ },
420
+ load: function(index, callback) { // callback is for internal usage only
421
+
422
+ var self = this, o = this.options, $a = this.$tabs.eq(index), a = $a[0],
423
+ bypassCache = callback == undefined || callback === false, url = $a.data('load.tabs');
424
+
425
+ callback = callback || function() {};
426
+
427
+ // no remote or from cache - just finish with callback
428
+ if (!url || !bypassCache && $.data(a, 'cache.tabs')) {
429
+ callback();
430
+ return;
431
+ }
432
+
433
+ // load remote from here on
434
+
435
+ var inner = function(parent) {
436
+ var $parent = $(parent), $inner = $parent.find('*:last');
437
+ return $inner.length && $inner || $parent;
438
+ };
439
+ var cleanup = function() {
440
+ self.$tabs.filter('.' + o.loadingClass).removeClass(o.loadingClass)
441
+ .each(function() {
442
+ if (o.spinner)
443
+ inner(this).parent().html(inner(this).data('label.tabs'));
444
+ });
445
+ self.xhr = null;
446
+ };
447
+
448
+ if (o.spinner) {
449
+ var label = inner(a).html();
450
+ inner(a).wrapInner('<em></em>')
451
+ .find('em').data('label.tabs', label).html(o.spinner);
452
+ }
453
+
454
+ var ajaxOptions = $.extend({}, o.ajaxOptions, {
455
+ url: url,
456
+ success: function(r, s) {
457
+ $(a.hash).html(r);
458
+ cleanup();
459
+
460
+ if (o.cache)
461
+ $.data(a, 'cache.tabs', true); // if loaded once do not load them again
462
+
463
+ // callbacks
464
+ $(self.element).triggerHandler('tabsload',
465
+ [self.ui(self.$tabs[index], self.$panels[index])], o.load
466
+ );
467
+ o.ajaxOptions.success && o.ajaxOptions.success(r, s);
468
+
469
+ // This callback is required because the switch has to take
470
+ // place after loading has completed. Call last in order to
471
+ // fire load before show callback...
472
+ callback();
473
+ }
474
+ });
475
+ if (this.xhr) {
476
+ // terminate pending requests from other tabs and restore tab label
477
+ this.xhr.abort();
478
+ cleanup();
479
+ }
480
+ $a.addClass(o.loadingClass);
481
+ setTimeout(function() { // timeout is again required in IE, "wait" for id being restored
482
+ self.xhr = $.ajax(ajaxOptions);
483
+ }, 0);
484
+
485
+ },
486
+ url: function(index, url) {
487
+ this.$tabs.eq(index).removeData('cache.tabs').data('load.tabs', url);
488
+ },
489
+ destroy: function() {
490
+ var o = this.options;
491
+ this.element.unbind('.tabs')
492
+ .removeClass(o.navClass).removeData('tabs');
493
+ this.$tabs.each(function() {
494
+ var href = $.data(this, 'href.tabs');
495
+ if (href)
496
+ this.href = href;
497
+ var $this = $(this).unbind('.tabs');
498
+ $.each(['href', 'load', 'cache'], function(i, prefix) {
499
+ $this.removeData(prefix + '.tabs');
500
+ });
501
+ });
502
+ this.$lis.add(this.$panels).each(function() {
503
+ if ($.data(this, 'destroy.tabs'))
504
+ $(this).remove();
505
+ else
506
+ $(this).removeClass([o.selectedClass, o.unselectClass,
507
+ o.disabledClass, o.panelClass, o.hideClass].join(' '));
508
+ });
509
+ }
510
+ });
511
+
512
+ $.ui.tabs.defaults = {
513
+ // basic setup
514
+ unselect: false,
515
+ event: 'click',
516
+ disabled: [],
517
+ cookie: null, // e.g. { expires: 7, path: '/', domain: 'jquery.com', secure: true }
518
+ // TODO history: false,
519
+
520
+ // Ajax
521
+ spinner: 'Loading&#8230;',
522
+ cache: false,
523
+ idPrefix: 'ui-tabs-',
524
+ ajaxOptions: {},
525
+
526
+ // animations
527
+ fx: null, // e.g. { height: 'toggle', opacity: 'toggle', duration: 200 }
528
+
529
+ // templates
530
+ tabTemplate: '<li><a href="#{href}"><span>#{label}</span></a></li>',
531
+ panelTemplate: '<div></div>',
532
+
533
+ // CSS classes
534
+ navClass: 'ui-tabs-nav',
535
+ selectedClass: 'ui-tabs-selected',
536
+ unselectClass: 'ui-tabs-unselect',
537
+ disabledClass: 'ui-tabs-disabled',
538
+ panelClass: 'ui-tabs-panel',
539
+ hideClass: 'ui-tabs-hide',
540
+ loadingClass: 'ui-tabs-loading'
541
+ };
542
+
543
+ $.ui.tabs.getter = "length";
544
+
545
+ /*
546
+ * Tabs Extensions
547
+ */
548
+
549
+ /*
550
+ * Rotate
551
+ */
552
+ $.extend($.ui.tabs.prototype, {
553
+ rotation: null,
554
+ rotate: function(ms, continuing) {
555
+
556
+ continuing = continuing || false;
557
+
558
+ var self = this, t = this.options.selected;
559
+
560
+ function start() {
561
+ self.rotation = setInterval(function() {
562
+ t = ++t < self.$tabs.length ? t : 0;
563
+ self.select(t);
564
+ }, ms);
565
+ }
566
+
567
+ function stop(e) {
568
+ if (!e || e.clientX) { // only in case of a true click
569
+ clearInterval(self.rotation);
570
+ }
571
+ }
572
+
573
+ // start interval
574
+ if (ms) {
575
+ start();
576
+ if (!continuing)
577
+ this.$tabs.bind(this.options.event, stop);
578
+ else
579
+ this.$tabs.bind(this.options.event, function() {
580
+ stop();
581
+ t = self.options.selected;
582
+ start();
583
+ });
584
+ }
585
+ // stop interval
586
+ else {
587
+ stop();
588
+ this.$tabs.unbind(this.options.event, stop);
589
+ }
590
+ }
591
+ });
592
+
593
+ })(jQuery);