bettertabs 1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. data/.gitignore +5 -0
  2. data/Gemfile +4 -0
  3. data/Gemfile.lock +14 -0
  4. data/README.md +288 -0
  5. data/Rakefile +2 -0
  6. data/bettertabs.gemspec +19 -0
  7. data/lib/bettertabs.rb +7 -0
  8. data/lib/bettertabs/bettertabs_builder.rb +162 -0
  9. data/lib/bettertabs/bettertabs_helper.rb +16 -0
  10. data/lib/bettertabs/javascripts/README.md +38 -0
  11. data/lib/bettertabs/javascripts/jquery.bettertabs.coffee +77 -0
  12. data/lib/bettertabs/javascripts/jquery.bettertabs.js +81 -0
  13. data/lib/bettertabs/javascripts/jquery.bettertabs.min.js +4 -0
  14. data/lib/bettertabs/version.rb +3 -0
  15. data/test/README_for_TEST.txt +5 -0
  16. data/test/ruby_1_9/rails_3_0/.rspec +1 -0
  17. data/test/ruby_1_9/rails_3_0/.rvmc +1 -0
  18. data/test/ruby_1_9/rails_3_0/Gemfile +11 -0
  19. data/test/ruby_1_9/rails_3_0/Gemfile.lock +105 -0
  20. data/test/ruby_1_9/rails_3_0/README +3 -0
  21. data/test/ruby_1_9/rails_3_0/Rakefile +7 -0
  22. data/test/ruby_1_9/rails_3_0/app/controllers/application_controller.rb +3 -0
  23. data/test/ruby_1_9/rails_3_0/app/controllers/bettertabs_controller.rb +24 -0
  24. data/test/ruby_1_9/rails_3_0/app/helpers/application_helper.rb +2 -0
  25. data/test/ruby_1_9/rails_3_0/app/views/bettertabs/_ajax.html.haml +6 -0
  26. data/test/ruby_1_9/rails_3_0/app/views/bettertabs/_link.html.haml +4 -0
  27. data/test/ruby_1_9/rails_3_0/app/views/bettertabs/_mixed.html.haml +6 -0
  28. data/test/ruby_1_9/rails_3_0/app/views/bettertabs/_tab_content.html.haml +1 -0
  29. data/test/ruby_1_9/rails_3_0/app/views/bettertabs/ajax.html.haml +2 -0
  30. data/test/ruby_1_9/rails_3_0/app/views/bettertabs/link_tab_1.html.haml +2 -0
  31. data/test/ruby_1_9/rails_3_0/app/views/bettertabs/link_tab_2.html.haml +2 -0
  32. data/test/ruby_1_9/rails_3_0/app/views/bettertabs/mixed.html.haml +2 -0
  33. data/test/ruby_1_9/rails_3_0/app/views/bettertabs/mixed_with_erb.html.erb +8 -0
  34. data/test/ruby_1_9/rails_3_0/app/views/bettertabs/static.html.haml +7 -0
  35. data/test/ruby_1_9/rails_3_0/app/views/layouts/application.html.erb +23 -0
  36. data/test/ruby_1_9/rails_3_0/config.ru +4 -0
  37. data/test/ruby_1_9/rails_3_0/config/application.rb +47 -0
  38. data/test/ruby_1_9/rails_3_0/config/boot.rb +6 -0
  39. data/test/ruby_1_9/rails_3_0/config/environment.rb +5 -0
  40. data/test/ruby_1_9/rails_3_0/config/environments/development.rb +26 -0
  41. data/test/ruby_1_9/rails_3_0/config/environments/test.rb +35 -0
  42. data/test/ruby_1_9/rails_3_0/config/initializers/mime_types.rb +5 -0
  43. data/test/ruby_1_9/rails_3_0/config/initializers/secret_token.rb +7 -0
  44. data/test/ruby_1_9/rails_3_0/config/initializers/session_store.rb +8 -0
  45. data/test/ruby_1_9/rails_3_0/config/routes.rb +6 -0
  46. data/test/ruby_1_9/rails_3_0/public/404.html +26 -0
  47. data/test/ruby_1_9/rails_3_0/public/422.html +26 -0
  48. data/test/ruby_1_9/rails_3_0/public/500.html +26 -0
  49. data/test/ruby_1_9/rails_3_0/public/favicon.ico +0 -0
  50. data/test/ruby_1_9/rails_3_0/public/images/rails.png +0 -0
  51. data/test/ruby_1_9/rails_3_0/public/javascripts/jquery-1.5.2.min.js +16 -0
  52. data/test/ruby_1_9/rails_3_0/public/javascripts/jquery.bettertabs.min.js +4 -0
  53. data/test/ruby_1_9/rails_3_0/public/javascripts/rails.js +278 -0
  54. data/test/ruby_1_9/rails_3_0/public/robots.txt +5 -0
  55. data/test/ruby_1_9/rails_3_0/public/stylesheets/bettertabs.css +23 -0
  56. data/test/ruby_1_9/rails_3_0/public/stylesheets/layout.css +8 -0
  57. data/test/ruby_1_9/rails_3_0/script/rails +6 -0
  58. data/test/ruby_1_9/rails_3_0/spec/requests/bettertabs_spec.rb +54 -0
  59. data/test/ruby_1_9/rails_3_0/spec/spec_helper.rb +19 -0
  60. metadata +122 -0
@@ -0,0 +1,5 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
4
+ log
5
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in bettertabs.gemspec
4
+ gemspec
@@ -0,0 +1,14 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ bettertabs (0.0.1)
5
+
6
+ GEM
7
+ remote: http://rubygems.org/
8
+ specs:
9
+
10
+ PLATFORMS
11
+ ruby
12
+
13
+ DEPENDENCIES
14
+ bettertabs!
@@ -0,0 +1,288 @@
1
+ Bettertabs
2
+ ==========
3
+
4
+ We know that splitting content into several tabs is easy, but doing well, clean, DRY, accessible, usable, fast and testable is not so simple after all.
5
+
6
+ Bettertabs is a helper for Rails that renders the markup for a tabbed area in a easy and declarative way, forcing you to keep things simple and ensuring accessibility and usability, no matter if the content is loaded statically or via ajax.
7
+
8
+
9
+ ## Features and Key Points ##
10
+
11
+ * Generate markup with a rails helper and add JavaScript behavior with a jQuery plugin
12
+ * Simplicity: Easy to install and easy to use
13
+ * Forces you to DRY-up your views for your tabbed content
14
+ * Forces you to make it awesome accessible and usable:
15
+ * Designed to work with and without JavaScript
16
+ * When click on a tab, the address bar url is changed (feature supported only in HTML5 browsers), so:
17
+ * The browser's back and reload buttons will still work as expected
18
+ * All tabbed sections can be permalinked, keeping the selected tab
19
+ * Flexible and customizable
20
+ * Makes testing views simple. Because it works without javascript, you can assert view components with the rails built-in functional and integration tests.
21
+ * The CSS styles are up to you.
22
+
23
+
24
+ ## Requirements: ##
25
+ * Ruby 1.9.2
26
+ * Rails 3
27
+ * The [Bettertabs jQuery plugin](https://github.com/agoragames/bettertabs/raw/master/lib/bettertabs/javascripts/jquery.bettertabs.min.js) (that requires [jQuery](http://jquery.com/) 1.3, 1.4 or 1.5)
28
+
29
+ Although you can use bettertabs without javascript, and also should be possible to use another javascript for this, since the bettertabs helper only generates the appropiate markup.
30
+
31
+
32
+ ## Install ##
33
+
34
+ Gem dependency. Add bettertabs to your gem file and run `bundle install`.
35
+
36
+ gem 'bettertabs'
37
+
38
+
39
+ Download the [bettertabs.jquery.min plugin](https://github.com/agoragames/bettertabs/raw/master/lib/bettertabs/javascripts/jquery.bettertabs.min.js) (or the [uncompressed version](https://github.com/agoragames/bettertabs/raw/master/lib/bettertabs/javascripts/jquery.bettertabs.js)), put it in your `public/javascripts` folder and include it in your layout after the jQuery library, for example:
40
+
41
+ <%= javascript_include_tag 'jquery', 'rails', 'jquery.bettertabs.min' %>
42
+
43
+ Now you can use the `bettertabs` helper as in the examples below.
44
+
45
+
46
+ ## Usage and examples ##
47
+
48
+ Bettertabs supports three kinds of tabs:
49
+
50
+ * **Link Tabs**: Loads only the active tab contents; when click on another tab, go to the specified URL. No JavaScript needed.
51
+ * **Static Tabs**: Loads all content of all static tabs, but only show the active content; when click on another tab, activate its related content. When JavaScript disabled, it behaves like *link tabs*.
52
+ * **Ajax Tabs**: Loads only the active tab contents; when click on another tab, loads its content via ajax and show. When JavaScript disabled, it behaves like *link tabs*.
53
+
54
+
55
+ ### Link Tabs Example ###
56
+
57
+ A simple tabbed content can be created using linked tabs:
58
+
59
+ In view `app/views/home/simple.html.erb`:
60
+
61
+ <%= bettertabs :simpletabs do |tab| %>
62
+ <%= tab.link :tab1, 'Tab One' do %>
63
+ Hello world.
64
+ <% end %>
65
+ <%= tab.link :tab2, 'Tab Two' do %>
66
+ This is the <b>dark side</b> of the moon.
67
+ <% end %>
68
+ <% end %>
69
+
70
+
71
+ This will define a linked two-tabs widget. The second tab ("Tab Two") is a link to the same url, but with the extra param `?simpletabs_selected_tab=tab2`, that will make the bettertabs helper activate the :tab2 tab.
72
+
73
+ In this case no JavaScript is required, and a new request will be performed when click the tabs links.
74
+ To change this to a javascript static tabs, you only need to change `tab.link` declarations for `tab.static`.
75
+ To change this to ajax tabs, in this case, is only needed to change the `tab.link` declarations for `tab.ajax`.
76
+
77
+
78
+ ### Static Tabs Example ###
79
+
80
+ This example will show three tabs: "Home", "Who am I?" and "Contact Me".
81
+ The partials and files organization used in this examples are not mandatory but recommended, it will make easier to change to ajax tabs if needed.
82
+
83
+ In view `app/views/home/index.html.erb`:
84
+
85
+ <h1>Static Tabs Example</h1>
86
+ <%= render 'mytabs' %>
87
+
88
+ In partial `app/views/home/_mytabs.html.erb`:
89
+
90
+ <%= bettertabs :mytabs do |tab| %>
91
+ <%= tab.static :home do %>
92
+ <h2>Home</h2>
93
+ <%= raw @content_for_home %>
94
+ <% end %>
95
+ <%= tab.static :about, 'Who am I?', :partial => '/shared/about' %>
96
+ <%= tab.static :contact_me %>
97
+ <% end %>
98
+
99
+
100
+ The :about and :contact_me tabs will get the content from the referenced partials. Put any content there, for example:
101
+
102
+ In partial `app/views/shared/_about.html.erb`:
103
+
104
+ Hello, my name is <%= @myname %>.
105
+
106
+ In partial `app/views/home/_contact_me.html.erb`:
107
+
108
+ <h2>How to contact me:<h2/>
109
+ <p><%= @contact_info.inspect %></p>
110
+
111
+ In controller `app/controllers/home_controller.rb`:
112
+
113
+ class HomeController < ApplicationController
114
+
115
+ def index
116
+ # Declare instance variables as usual
117
+ @content_for_home = very_slow_function()
118
+ @myname = 'Lucifer'
119
+ @contact_info = { :address => 'The Hell', :telephone => '666'}
120
+ end
121
+
122
+ end
123
+
124
+ This will work with and without JavaScript, anyway, all vars and content should be loaded in the request because all static tabs need to render its content.
125
+
126
+
127
+ ### Improve with AJAX ###
128
+
129
+ Ajax tabs call the same URL as link tabs but loading the content asynchronously.
130
+
131
+ In the previous example, `@content_for_home = very_slow_function()` has to be executed even if the user see another tab content. Using ajax or link tabs, the content is only loaded when needed.
132
+
133
+ Lets create some tabs with ajax:
134
+
135
+ In view `app/views/home/index.html.erb`:
136
+
137
+ <h1>Ajax Tabs Example</h1>
138
+ <%= render 'mytabs' %>
139
+
140
+ In partial `app/views/home/_mytabs.html.erb`:
141
+
142
+ <%= bettertabs :mytabs do |tab| %>
143
+ <%= tab.ajax :home do %>
144
+ <h2>Home</h2>
145
+ <%= raw @content_for_home %>
146
+ <% end %>
147
+ <%= tab.ajax :about, 'Who am I?', :partial => '/shared/about' %>
148
+ <%= tab.ajax :contact_me %>
149
+ <% end %>
150
+
151
+ **Note** that the only difference between this example and the *static tabs example* is to use `tab.ajax` declaration instead of `tab.static`.
152
+
153
+ Partials `app/views/shared/_about.html.erb` and `app/views/home/_contact_us.html.erb` (same as the static tabs example).
154
+
155
+ In controller `app/controllers/home_controller.rb`, you can load only the needed data for each tab:
156
+
157
+ class HomeController < ApplicationController
158
+
159
+ def index
160
+ # Execute only the selected tab needed code (optimization).
161
+ case params[:mytabs_selected_tab]
162
+ when 'home' then
163
+ @content_for_home = very_slow_function()
164
+ when 'about' then
165
+ @myname = 'Lucifer'
166
+ when 'contact_us' then
167
+ @contact_info = { :address => 'The Hell', :telephone => '666'}
168
+ end
169
+
170
+ # When ajax, load only the selected tab content (handled by bettertabs helper)
171
+ respond_to do |format|
172
+ format.js { render :partial => 'mytabs' } # when ajax call, bettertabs only renders the active content.
173
+ end
174
+ end
175
+
176
+ end
177
+
178
+ The only needed code in the controller to make it work with ajax is the `format.js { render :partial => 'mytabs' }` line.
179
+
180
+ Since the *_mytabs* partial only contains the bettertabs helper, and bettertabs helper only renders the selected content when ajax call, this line is enough to return the selected content to the ajax call.
181
+
182
+ #### Advantages if you follow this file structure ####
183
+
184
+ Having the bettertabs helper in a separated partial gives you the following advantages:
185
+
186
+ * It works without JavaScript out-of-the-box
187
+ * Since the URL is changed in the browser when a tab is clicked and it works without JavaScript, a permalink is defined for each tab, so it allows:
188
+ * to bookmark the page on any selected tab
189
+ * to reload the page keeping the selected tab
190
+ * Easily change the behavior of a tab to be `ajax`, `static` or `link`. It always work.
191
+ * Keep your views DRY, clean and readable
192
+
193
+
194
+ ### Mixed Example ###
195
+
196
+ Is easy to mix all types of tabs, and customize them using the provided options. For example (Using ruby 1.9.2 with HAML):
197
+
198
+ = bettertabs :bettertabs_example, :selected_tab => :chooseme do |tab|
199
+ = tab.static :simplest_tab, class: 'awesome-tab' do
200
+ Click this tab to see this content.
201
+
202
+ = tab.static :chooseme, 'Please, Click me!' # as default, renders partial: 'chooseme'
203
+
204
+ = tab.static :render_another_partial, partial: 'another_partial'
205
+
206
+ = tab.link :link_to_another_place, url: go_to_other_place_url # will make a new request
207
+
208
+ = tab.ajax :simple_ajax, title: 'Content is loaded when needed and only once.'
209
+
210
+ = tab.ajax :album, url: show_remote_album_path, partial: 'shared/album'
211
+
212
+
213
+ #### From the Controller: ####
214
+
215
+ The default tab :url option value is the current path but with `param[:"{bettertabs_id}_selected_tab"]` to the current tab id.
216
+ This means that, by default, you can handle all needed information in the same controller action for all tab contents. Of course you can change this using a different value for the :url option.
217
+
218
+ If the tab is ajax, the ajax call will be performed to the same url provided in the :url option.
219
+ You can know if a call is ajax checking if `controller.request.xhr?` is true.
220
+
221
+ So, if `request.xhr?` is *true* then you should render a partial to be used as content.
222
+
223
+ If `request.xhr?` is *false* then you can just render the action as usual, bettertabs helper will auto select the appropriate tab based on `params[:"{bettertabs_id}_selected_tab"]` value.
224
+
225
+ When a call is AJAX, the bettertabs helper only render the active content (no tabs, no wrappers, no other content), so you can just render the partial where the bettertabs helper is used and it will work for any selected tab.
226
+
227
+ You can write your controller code as follows, assuming that bettertabs_id is :example, and it's placed alone in the partial 'bettertabs_example':
228
+
229
+ def show_bettertabs_example
230
+ @common_for_all_tabs = code_here
231
+
232
+ # A simple way of execute code only for the selected tab.
233
+ # Note that static tabs are always rendered, even if they are not selected.
234
+ case params[:example_selected_tab]
235
+ when 'chooseme' then
236
+ code_only_for_this_tab
237
+ else
238
+ code_for_all_other_tabs
239
+ end
240
+
241
+ # Ready for ajax ...
242
+ # You can check request.xhr? or use the common method respond_to
243
+ respond_to do |format|
244
+ format.js { render :partial => 'bettertabs_example' } # render only the selected content
245
+ end
246
+ end
247
+
248
+
249
+
250
+ ## TODO ##
251
+
252
+ * Improve the Rails testing application.
253
+ * it should use rspec and capybabra to test even the javascript (http://media.railscasts.com/videos/257_request_specs_and_capybara.mov)
254
+ * Try to make it work with ruby 1.8.x
255
+ * Create a basic CSS stylesheet that may serve as base (and add the links here in the documentation)
256
+
257
+
258
+ ## Current Spec sheet (will become automatic tests) ##
259
+ * Should always work with javascript disabled (using the urls of the tabs links)
260
+ * The bettertabs hepler should accept:
261
+ * args: (bettertabs_id, options)
262
+ * options can be:
263
+ * :selected_tab => tab_id to select by default
264
+ * :selected_tab should be overridden with params[:"#{bettertabs_id}_selected_tab"] if present
265
+ * any other option is used as wrapper html_options (wrapper is the top-level widget dom element).
266
+ * The bettertabs helper should render clear markup:
267
+ * A wrapper with class 'bettertabs'
268
+ * Tabs markup
269
+ * ul.tabs > li > a
270
+ * selected tab is ul.tabs > li.active > a
271
+ * each a element has element attributes:
272
+ * data-tab-type (for javascript: change click behavior depending on type "static", "link" or "ajax")
273
+ * data-show-content-id (for javascript: element id to show when select this tab)
274
+ * sections for each tab content.
275
+ * use a unique html id (based on bettertabs_id and tab_id) for each Tab and Content
276
+ * The bettertabs builder ".from" method should accept:
277
+ * args: (tab_id, tab_name, options, &block)
278
+ * args: (tab_id, options, &block)
279
+ * If block_given? the block is used as content related to this tab
280
+ * options can be:
281
+ * :partial => to use as content. Defaults to tab_id
282
+ * if block_given? this option can not be used (if used, raise an error)
283
+ * :url => for the tab link, that should go to this selected tab when javascript is disabled. Defaults to { :"#{bettertabs_id}_selected_tab" => tab_id }
284
+ * :tab_type => used in the markup as the link data-tab-type value. Can be :static, :link or :ajax (or the corresponding strings). Raise error otherwise. Defaults to :static.
285
+ * The bettertabs builder ".static", ".link" and ".ajax" methods are only a convenient way to use ".for" method with :tab_type set to :static, :link or :ajax respectively.
286
+ * Content is rendered only for active tab, except when tab_type is :static, where content is always rendered (ready to show when select that tab using javascript).
287
+ * When ajax call (format.js), the bettertabs helper should return ONLY the content of the selected tab (to simplify the controller render partial calls.).
288
+
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,19 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "bettertabs/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "bettertabs"
7
+ s.version = Bettertabs::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Mario Izquierdo"]
10
+ s.email = ["tothemario@gmail.com"]
11
+ s.homepage = ""
12
+ s.summary = %q{The better (simple, accessible, usable, flexible and fast) way to split content in tabs.}
13
+ s.description = %q{The bettertabs helper defines the markup for a tabbed area in a easy and declarative way, using the appropiate JavaScript but ensuring accessibility and usability, no matter if the content is loaded statically, via ajax or the tabs are links. In the other hand, the CSS styles are up to you. }
14
+
15
+ s.rubyforge_project = "bettertabs"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.require_paths = ["lib"]
19
+ end
@@ -0,0 +1,7 @@
1
+ require 'bettertabs/bettertabs_builder'
2
+ require 'bettertabs/bettertabs_helper'
3
+
4
+ # Include bettertabs in the Application
5
+ module ApplicationHelper
6
+ include BettertabsHelper
7
+ end
@@ -0,0 +1,162 @@
1
+ class BettertabsBuilder
2
+
3
+ TAB_TYPE_STATIC = :static
4
+ TAB_TYPE_LINK = :link
5
+ TAB_TYPE_AJAX = :ajax
6
+ TAB_TYPES = [TAB_TYPE_STATIC, TAB_TYPE_LINK, TAB_TYPE_AJAX]
7
+
8
+ def initialize(bettertabs_id, template, selected_tab_id = nil, options = {})
9
+ @bettertabs_id = bettertabs_id
10
+ @template = template
11
+ @selected_tab_id = selected_tab_id
12
+ @render_only_active_content = options.delete(:render_only_active_content) # used in ajax calls
13
+ @wrapper_html_options = options
14
+
15
+ @tabs = []
16
+ @contents = []
17
+ end
18
+
19
+ # Static tabs generator
20
+ def static(tab_id, *args, &block)
21
+ get_options(args)[:tab_type] = TAB_TYPE_STATIC
22
+ self.for(tab_id, *args, &block)
23
+ end
24
+
25
+ # Linked tabs generator
26
+ def link(tab_id, *args, &block)
27
+ get_options(args)[:tab_type] = TAB_TYPE_LINK
28
+ self.for(tab_id, *args, &block)
29
+ end
30
+
31
+ # Ajax tabs generator
32
+ def ajax(tab_id, *args, &block)
33
+ get_options(args)[:tab_type] = TAB_TYPE_AJAX
34
+ self.for(tab_id, *args, &block)
35
+ end
36
+
37
+ # Generic tab and content generator
38
+ def for(tab_id, *args, &block)
39
+ # Initialize vars and options
40
+ options = get_options(args)
41
+ tab_id = tab_id.to_s
42
+ tab_text = get_tab_text(tab_id, args)
43
+ raise "Bettertabs: #{tab_html_id_for(tab_id)} error. Used :partial option and a block of content at the same time." if block_given? and options[:partial]
44
+ partial = options.delete(:partial) || tab_id.to_s unless block_given?
45
+ url = options.delete(:url) || { :"#{@bettertabs_id}_selected_tab" => tab_id }
46
+ tab_type = (options.delete(:tab_type) || TAB_TYPE_STATIC).to_sym
47
+ raise "Bettertabs: #{tab_type.inspect} tab type not supported. Use one of #{TAB_TYPES.inspect} instead." unless TAB_TYPES.include?(tab_type)
48
+ @selected_tab_id ||= tab_id # defaults to first tab
49
+
50
+ if @render_only_active_content
51
+ if active?(tab_id)
52
+ @only_active_content = block_given? ? @template.capture(&block) : @template.render(:partial => partial)
53
+ end
54
+ else
55
+ # Tabs
56
+ tab_html_options = options # any other option will be used as tab html_option
57
+ @tabs << { tab_id: tab_id, text: tab_text, url: url, html_options: tab_html_options, tab_type: tab_type, active: active?(tab_id) }
58
+
59
+ # Content
60
+ content_html_options = { id: content_html_id_for(tab_id), class: "content #{active?(tab_id) ? 'active' : 'hidden'}" }
61
+ if active?(tab_id) or tab_type == TAB_TYPE_STATIC # Only render content for selected tab (static content is always rendered).
62
+ content = block_given? ? @template.capture(&block) : @template.render(:partial => partial)
63
+ else
64
+ content = ''
65
+ end
66
+ @contents << { tab_id: tab_id, tab_text: tab_text, content: content, html_options: content_html_options, tab_type: tab_type, active: active?(tab_id) }
67
+ end
68
+ nil
69
+ end
70
+
71
+ # Renders the bettertabs markup.
72
+ # The following instance variables are available:
73
+ # * @tabs: list of tabs. Each one is a hash with the followin keys:
74
+ # * :tab_id => symbol identifier of the tab
75
+ # * :text => text to show in the rendered tab
76
+ # * :url => string with the tab url
77
+ # * :html_options => for use in the tab markup (included 'data-tab-type').
78
+ # * :tab_type => One of TAB_TYPES
79
+ # * :active => true if this is the selected tab
80
+ # * @contents: list of contents asociated with each tab. Each one is a hash with the following keys:
81
+ # * :tab_id => symbol identifier of the tab
82
+ # * :tab_text => text that is used in the corresponding tab
83
+ # * :content => string with the content inside
84
+ # * :html_options => for use in the container markup
85
+ # * :tab_type => One of TAB_TYPES
86
+ # * :active => true if this is the selected content
87
+ # * @selected_tab_id: String with the selected tab id (to match against each tab[:tab_id])
88
+ # * @wrapper_html_options: hash with the html options used in the bettertabs container
89
+ # * @bettertabs_id: id of the bettertabs widget
90
+ def render
91
+ if @render_only_active_content
92
+ @only_active_content.html_safe
93
+ else
94
+
95
+ # Wrapper
96
+ @wrapper_html_options ||= {}
97
+ @wrapper_html_options[:"data-initial-active-tab-id"] = tab_html_id_for @selected_tab_id
98
+ tag(:div, @wrapper_html_options) do
99
+
100
+ # Tabs list
101
+ tag(:ul, class: 'tabs') do
102
+ @tabs.map do |tab|
103
+ tag(:li, class: ('active' if tab[:active]), id: tab_html_id_for(tab[:tab_id])) do
104
+ tab[:html_options][:"data-tab-type"] ||= tab[:tab_type] # for javascript: change click behavior depending on type :static, :link or :ajax
105
+ tab[:html_options][:"data-show-content-id"] ||= content_html_id_for(tab[:tab_id]) # for javascript: element id to show when select this tab
106
+ @template.link_to(tab[:text], tab[:url], tab[:html_options])
107
+ end
108
+ end.join.html_safe
109
+ end +
110
+
111
+ # Content sections
112
+ @contents.map do |content|
113
+ tag(:div, content[:html_options]) do
114
+ content[:content] # this should be blank unless content[:active] or content[:tab_type] == :static
115
+ end
116
+ end.join.html_safe
117
+ end.html_safe +
118
+ jquery_tag("$('##{@bettertabs_id}').bettertabs();")
119
+
120
+ end
121
+ end
122
+
123
+
124
+ private
125
+
126
+ # Alias of @template.content_tag
127
+ def tag(name, content_or_options_with_block = nil, options = nil, escape = true, &block)
128
+ @template.content_tag(name, content_or_options_with_block, options, escape, &block)
129
+ end
130
+
131
+ # Wraps javascript code inside a javascript_tag using the jQuery(function($){ ... }); on init call.
132
+ def jquery_tag(jquery_code)
133
+ @template.javascript_tag("jQuery(function($){ #{ jquery_code } });")
134
+ end
135
+
136
+ # Get the options hash from an array of args. If options are not present, create them and initialize to {}
137
+ def get_options(args)
138
+ args << {} unless args.last.is_a?(Hash)
139
+ args.last
140
+ end
141
+
142
+ # Get the default name of the tab, from the args list.
143
+ def get_tab_text(tab_id, args)
144
+ String === args.first ? args.first : tab_id.to_s.titleize
145
+ end
146
+
147
+ # Check if this tab_id is the same as @selected_tab_id
148
+ def active?(tab_id)
149
+ @selected_tab_id.to_s == tab_id.to_s
150
+ end
151
+
152
+ # Tab html id
153
+ def tab_html_id_for(tab_id)
154
+ "#{tab_id}_#{@bettertabs_id}_tab"
155
+ end
156
+
157
+ # Content html id
158
+ def content_html_id_for(tab_id)
159
+ "#{tab_id}_#{@bettertabs_id}_content"
160
+ end
161
+
162
+ end