logical_tabs 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +11 -0
- data/Gemfile.lock +105 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +115 -0
- data/Rakefile +23 -0
- data/VERSION +1 -0
- data/doc/README +2 -0
- data/lib/generators/logical_tabs/install/install_generator.rb +75 -0
- data/lib/generators/logical_tabs/install/templates/javascripts/logical_tabs_jquery.js +688 -0
- data/lib/generators/logical_tabs/install/templates/javascripts/logical_tabs_prototype.js +256 -0
- data/lib/generators/logical_tabs/install/templates/stylesheets/logical_tabs.css +69 -0
- data/lib/generators/logical_tabs/install/templates/stylesheets/sass/logical_tabs.sass +69 -0
- data/lib/logical_tabs.rb +8 -0
- data/lib/logical_tabs/helper.rb +24 -0
- data/lib/logical_tabs/tab_pane.rb +74 -0
- data/lib/logical_tabs/tabbed_panel.rb +98 -0
- metadata +127 -0
@@ -0,0 +1,256 @@
|
|
1
|
+
/**
|
2
|
+
* Javascript code to support switching, memorizing, and preselecting
|
3
|
+
* tabs in a tabbed_panel structure for the plugin logical_tabs.
|
4
|
+
*
|
5
|
+
* NOTE: Includes a full copy of CookieJar by Lalit Patel, see below.
|
6
|
+
*
|
7
|
+
* Author : Evan Dorn
|
8
|
+
* Website: http://lrdesign.com/tools
|
9
|
+
* License: MIT License, see included LICENSE file.
|
10
|
+
* Version: 1.0
|
11
|
+
* Updated: April 22, 2010
|
12
|
+
*
|
13
|
+
*/
|
14
|
+
|
15
|
+
// Set up the observer on the tabs, and
|
16
|
+
window.onload = function() {
|
17
|
+
$$('.tabbed_panel li.tab a.tab_link').invoke('observe', 'click', handle_tab_click);
|
18
|
+
$$('.tabbed_panel').each(function(item, index){ pre_select_tab(item)});
|
19
|
+
}
|
20
|
+
|
21
|
+
// Given a tabbed_panel div element, checks to see if there's a pre-stored
|
22
|
+
// tab association. If there is, select that tab.
|
23
|
+
function pre_select_tab(tabbed_panel) {
|
24
|
+
tab_memory = get_tab_memory();
|
25
|
+
tab_id = tab_memory[memory_key_from_tabbed_panel(tabbed_panel.identify())]
|
26
|
+
if(tab_id != null) {
|
27
|
+
select_tab(tab_id);
|
28
|
+
}
|
29
|
+
}
|
30
|
+
|
31
|
+
// Select a tab that's been clicked on and store that preference in a cookie.
|
32
|
+
function handle_tab_click(event) {
|
33
|
+
tab_id = Event.element(event).up().identify();
|
34
|
+
select_tab(tab_id);
|
35
|
+
store_tab_preference(tab_id);
|
36
|
+
}
|
37
|
+
|
38
|
+
// Handle the display changes necessary for selecting a new tab.
|
39
|
+
// iterate the tabs of the parent tabbed_panel and set them and their panes
|
40
|
+
// selected or not based on whether they match the clicked-on
|
41
|
+
// tab.
|
42
|
+
function select_tab(tab_id) {
|
43
|
+
$(get_tabbed_panel_id(tab_id)).select('li.tab').each(function(tab, index){
|
44
|
+
pane_id = tab.identify().replace(/_tab$/,"_pane");
|
45
|
+
pane = $(pane_id);
|
46
|
+
if(tab.identify() == tab_id) {
|
47
|
+
tab.removeClassName('tab_unselected');
|
48
|
+
tab.addClassName('tab_selected');
|
49
|
+
pane.removeClassName('pane_unselected');
|
50
|
+
pane.addClassName('pane_selected');
|
51
|
+
} else {
|
52
|
+
tab.removeClassName('tab_selected');
|
53
|
+
tab.addClassName('tab_unselected');
|
54
|
+
pane.removeClassName('pane_selected');
|
55
|
+
pane.addClassName('pane_unselected');
|
56
|
+
}
|
57
|
+
});
|
58
|
+
}
|
59
|
+
|
60
|
+
// extract the containing tabbed panel's ID from the tab's ID
|
61
|
+
function get_tabbed_panel_id(tab_id) {
|
62
|
+
return tab_id.substring(0, tab_id.indexOf("_tp_"));
|
63
|
+
}
|
64
|
+
|
65
|
+
// Generate the key associated with this page's path and this tabbed_panel,
|
66
|
+
// starting from the id of an individual tab.
|
67
|
+
function memory_key_from_tab(tab_id) {
|
68
|
+
return (location.pathname + "--" + get_tabbed_panel_id(tab_id)).replace(/^\//,'')
|
69
|
+
}
|
70
|
+
|
71
|
+
// Generate the key associated with this page's path and this tabbed_panel,
|
72
|
+
// starting from the id of the tabbed panel.
|
73
|
+
function memory_key_from_tabbed_panel(tabbed_panel_id) {
|
74
|
+
return (location.pathname + "--" + tabbed_panel_id).replace(/^\//,'')
|
75
|
+
}
|
76
|
+
|
77
|
+
// Store which tab was selected for this URL and tabbed panel combination
|
78
|
+
function store_tab_preference(tab_id) {
|
79
|
+
tab_memory = get_tab_memory();
|
80
|
+
if (tab_memory == null) {
|
81
|
+
tab_memory = new Object();
|
82
|
+
}
|
83
|
+
tab_memory[memory_key_from_tab(tab_id)] = tab_id;
|
84
|
+
tab_memory_cookie.put('tab_memory', tab_memory);
|
85
|
+
}
|
86
|
+
|
87
|
+
// Retrieve the array of memorized tabs from the cookie.
|
88
|
+
function get_tab_memory() {
|
89
|
+
return tab_memory_cookie.get('tab_memory');
|
90
|
+
}
|
91
|
+
|
92
|
+
|
93
|
+
/**
|
94
|
+
* Javascript code to store data as JSON strings in cookies.
|
95
|
+
* It uses prototype.js 1.5.1 (http://www.prototypejs.org)
|
96
|
+
*
|
97
|
+
* Author : Lalit Patel
|
98
|
+
* Website: http://www.lalit.org/lab/jsoncookies
|
99
|
+
* License: Apache Software License 2
|
100
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
101
|
+
* Version: 0.5
|
102
|
+
* Updated: Jan 26, 2009
|
103
|
+
*
|
104
|
+
* Chnage Log:
|
105
|
+
* v 0.5
|
106
|
+
* - Changed License from CC to Apache 2
|
107
|
+
* v 0.4
|
108
|
+
* - Removed a extra comma in options (was breaking in IE and Opera). (Thanks Jason)
|
109
|
+
* - Removed the parameter name from the initialize function
|
110
|
+
* - Changed the way expires date was being calculated. (Thanks David)
|
111
|
+
* v 0.3
|
112
|
+
* - Removed dependancy on json.js (http://www.json.org/json.js)
|
113
|
+
* - empty() function only deletes the cookies set by CookieJar
|
114
|
+
*/
|
115
|
+
|
116
|
+
var CookieJar = Class.create();
|
117
|
+
|
118
|
+
CookieJar.prototype = {
|
119
|
+
|
120
|
+
/**
|
121
|
+
* Append before all cookie names to differntiate them.
|
122
|
+
*/
|
123
|
+
appendString: "__CJ_",
|
124
|
+
|
125
|
+
/**
|
126
|
+
* Initializes the cookie jar with the options.
|
127
|
+
*/
|
128
|
+
initialize: function(options) {
|
129
|
+
this.options = {
|
130
|
+
expires: 3600, // seconds (1 hr)
|
131
|
+
path: '', // cookie path
|
132
|
+
domain: '', // cookie domain
|
133
|
+
secure: '' // secure ?
|
134
|
+
};
|
135
|
+
Object.extend(this.options, options || {});
|
136
|
+
|
137
|
+
if (this.options.expires != '') {
|
138
|
+
var date = new Date();
|
139
|
+
date = new Date(date.getTime() + (this.options.expires * 1000));
|
140
|
+
this.options.expires = '; expires=' + date.toGMTString();
|
141
|
+
}
|
142
|
+
if (this.options.path != '') {
|
143
|
+
this.options.path = '; path=' + escape(this.options.path);
|
144
|
+
}
|
145
|
+
if (this.options.domain != '') {
|
146
|
+
this.options.domain = '; domain=' + escape(this.options.domain);
|
147
|
+
}
|
148
|
+
if (this.options.secure == 'secure') {
|
149
|
+
this.options.secure = '; secure';
|
150
|
+
} else {
|
151
|
+
this.options.secure = '';
|
152
|
+
}
|
153
|
+
},
|
154
|
+
|
155
|
+
/**
|
156
|
+
* Adds a name values pair.
|
157
|
+
*/
|
158
|
+
put: function(name, value) {
|
159
|
+
name = this.appendString + name;
|
160
|
+
cookie = this.options;
|
161
|
+
var type = typeof value;
|
162
|
+
switch(type) {
|
163
|
+
case 'undefined':
|
164
|
+
case 'function' :
|
165
|
+
case 'unknown' : return false;
|
166
|
+
case 'boolean' :
|
167
|
+
case 'string' :
|
168
|
+
case 'number' : value = String(value.toString());
|
169
|
+
}
|
170
|
+
var cookie_str = name + "=" + escape(Object.toJSON(value));
|
171
|
+
try {
|
172
|
+
document.cookie = cookie_str + cookie.expires + cookie.path + cookie.domain + cookie.secure;
|
173
|
+
} catch (e) {
|
174
|
+
return false;
|
175
|
+
}
|
176
|
+
return true;
|
177
|
+
},
|
178
|
+
|
179
|
+
/**
|
180
|
+
* Removes a particular cookie (name value pair) form the Cookie Jar.
|
181
|
+
*/
|
182
|
+
remove: function(name) {
|
183
|
+
name = this.appendString + name;
|
184
|
+
cookie = this.options;
|
185
|
+
try {
|
186
|
+
var date = new Date();
|
187
|
+
date.setTime(date.getTime() - (3600 * 1000));
|
188
|
+
var expires = '; expires=' + date.toGMTString();
|
189
|
+
document.cookie = name + "=" + expires + cookie.path + cookie.domain + cookie.secure;
|
190
|
+
} catch (e) {
|
191
|
+
return false;
|
192
|
+
}
|
193
|
+
return true;
|
194
|
+
},
|
195
|
+
|
196
|
+
/**
|
197
|
+
* Return a particular cookie by name;
|
198
|
+
*/
|
199
|
+
get: function(name) {
|
200
|
+
name = this.appendString + name;
|
201
|
+
var cookies = document.cookie.match(name + '=(.*?)(;|$)');
|
202
|
+
if (cookies) {
|
203
|
+
return (unescape(cookies[1])).evalJSON();
|
204
|
+
} else {
|
205
|
+
return null;
|
206
|
+
}
|
207
|
+
},
|
208
|
+
|
209
|
+
/**
|
210
|
+
* Empties the Cookie Jar. Deletes all the cookies.
|
211
|
+
*/
|
212
|
+
empty: function() {
|
213
|
+
keys = this.getKeys();
|
214
|
+
size = keys.size();
|
215
|
+
for(i=0; i<size; i++) {
|
216
|
+
this.remove(keys[i]);
|
217
|
+
}
|
218
|
+
},
|
219
|
+
|
220
|
+
/**
|
221
|
+
* Returns all cookies as a single object
|
222
|
+
*/
|
223
|
+
getPack: function() {
|
224
|
+
pack = {};
|
225
|
+
keys = this.getKeys();
|
226
|
+
|
227
|
+
size = keys.size();
|
228
|
+
for(i=0; i<size; i++) {
|
229
|
+
pack[keys[i]] = this.get(keys[i]);
|
230
|
+
}
|
231
|
+
return pack;
|
232
|
+
},
|
233
|
+
|
234
|
+
/**
|
235
|
+
* Returns all keys.
|
236
|
+
*/
|
237
|
+
getKeys: function() {
|
238
|
+
keys = $A();
|
239
|
+
keyRe= /[^=; ]+(?=\=)/g;
|
240
|
+
str = document.cookie;
|
241
|
+
CJRe = new RegExp("^" + this.appendString);
|
242
|
+
while((match = keyRe.exec(str)) != undefined) {
|
243
|
+
if (CJRe.test(match[0].strip())) {
|
244
|
+
keys.push(match[0].strip().gsub("^" + this.appendString,""));
|
245
|
+
}
|
246
|
+
}
|
247
|
+
return keys;
|
248
|
+
}
|
249
|
+
};
|
250
|
+
|
251
|
+
// set up tab memory cookie
|
252
|
+
tab_memory_cookie = new CookieJar({
|
253
|
+
expires: 3600 * 24 * 365, // one year
|
254
|
+
path: '/',
|
255
|
+
name: 'tab_memory'
|
256
|
+
})
|
@@ -0,0 +1,69 @@
|
|
1
|
+
.tabbed_panel {
|
2
|
+
padding: 0px;
|
3
|
+
clear: both;
|
4
|
+
width: 100%;
|
5
|
+
margin-top: 1em; }
|
6
|
+
|
7
|
+
.panes {
|
8
|
+
clear: both;
|
9
|
+
border-left: solid 1px #cccccc;
|
10
|
+
border-bottom: solid 1px #cccccc;
|
11
|
+
border-top: solid 1px #999999;
|
12
|
+
border-right: solid 1px #999999;
|
13
|
+
background-color: white;
|
14
|
+
padding: 10px;
|
15
|
+
margin: 0; }
|
16
|
+
|
17
|
+
@media screen, projection {
|
18
|
+
.pane_print_header {
|
19
|
+
display: none; }
|
20
|
+
|
21
|
+
.pane_selected {
|
22
|
+
display: block; }
|
23
|
+
|
24
|
+
.pane_unselected {
|
25
|
+
display: none; }
|
26
|
+
|
27
|
+
li.tab {
|
28
|
+
position: relative;
|
29
|
+
top: 1px;
|
30
|
+
float: left;
|
31
|
+
padding: 0;
|
32
|
+
margin: 0px 3px 0px 0px;
|
33
|
+
font: bold 1.2em sans-serif;
|
34
|
+
list-style: none;
|
35
|
+
border-left: solid 1px #cccccc;
|
36
|
+
border-bottom: solid 1px #999999;
|
37
|
+
border-top: solid 1px #999999;
|
38
|
+
border-right: solid 1px #999999;
|
39
|
+
-moz-user-select: none;
|
40
|
+
-khtml-user-select: none;
|
41
|
+
cursor: pointer;
|
42
|
+
font-size: 11px; }
|
43
|
+
|
44
|
+
li.tab_selected {
|
45
|
+
background-color: white;
|
46
|
+
border-bottom: 1px solid white; }
|
47
|
+
|
48
|
+
li.tab_unselected {
|
49
|
+
background-color: #aaaacc; }
|
50
|
+
|
51
|
+
li.tab_unselected:hover {
|
52
|
+
background-color: #aa7777; }
|
53
|
+
|
54
|
+
li.tab a {
|
55
|
+
display: block;
|
56
|
+
float: left;
|
57
|
+
padding: 4px 10px;
|
58
|
+
color: black;
|
59
|
+
text-decoration: none; }
|
60
|
+
|
61
|
+
li.tab a:hover {
|
62
|
+
color: #000099; } }
|
63
|
+
|
64
|
+
@media print, handheld {
|
65
|
+
.tabbed_panel ul.tabs, .tabbed_panel ul.tabs li {
|
66
|
+
display: none;
|
67
|
+
visibility: hidden; }
|
68
|
+
.tabbed_panel .pane_unselected {
|
69
|
+
display: block; } }
|
@@ -0,0 +1,69 @@
|
|
1
|
+
.tabbed_panel
|
2
|
+
:padding 0px
|
3
|
+
:clear both
|
4
|
+
:width 100%
|
5
|
+
:margin-top 1em
|
6
|
+
|
7
|
+
.panes
|
8
|
+
:clear both
|
9
|
+
:border-left solid 1px #CCC
|
10
|
+
:border-bottom solid 1px #CCC
|
11
|
+
:border-top solid 1px #999
|
12
|
+
:border-right solid 1px #999
|
13
|
+
:background-color white
|
14
|
+
:padding 10px
|
15
|
+
:margin 0
|
16
|
+
|
17
|
+
@media screen, projection
|
18
|
+
.pane_print_header
|
19
|
+
:display none
|
20
|
+
.pane_selected
|
21
|
+
:display block
|
22
|
+
|
23
|
+
.pane_unselected
|
24
|
+
:display none
|
25
|
+
|
26
|
+
li.tab
|
27
|
+
:position relative
|
28
|
+
:top 1px
|
29
|
+
:float left
|
30
|
+
:padding 0
|
31
|
+
:margin 0px 3px 0px 0px
|
32
|
+
:font bold 1.2em sans-serif
|
33
|
+
:list-style none
|
34
|
+
:border-left solid 1px #CCC
|
35
|
+
:border-bottom solid 1px #999
|
36
|
+
:border-top solid 1px #999
|
37
|
+
:border-right solid 1px #999
|
38
|
+
:-moz-user-select none
|
39
|
+
:-khtml-user-select none
|
40
|
+
:cursor pointer
|
41
|
+
:font-size 11px
|
42
|
+
|
43
|
+
li.tab_selected
|
44
|
+
:background-color white
|
45
|
+
:border-bottom 1px solid white
|
46
|
+
|
47
|
+
li.tab_unselected
|
48
|
+
:background-color #aac
|
49
|
+
|
50
|
+
li.tab_unselected:hover
|
51
|
+
:background-color #A77
|
52
|
+
|
53
|
+
li.tab a
|
54
|
+
:display block
|
55
|
+
:float left
|
56
|
+
:padding 4px 10px
|
57
|
+
:color black
|
58
|
+
:text-decoration none
|
59
|
+
|
60
|
+
li.tab a:hover
|
61
|
+
:color #009
|
62
|
+
|
63
|
+
@media print, handheld
|
64
|
+
.tabbed_panel
|
65
|
+
ul.tabs, ul.tabs li
|
66
|
+
:display none
|
67
|
+
:visibility hidden
|
68
|
+
.pane_unselected
|
69
|
+
:display block
|
data/lib/logical_tabs.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
module LogicalTabs
|
2
|
+
require 'logical_tabs/tabbed_panel'
|
3
|
+
|
4
|
+
module Helper
|
5
|
+
def tabbed_panel( options = {}, &block)
|
6
|
+
@panel_count = defined?(@panel_count) ? @panel_count + 1 : 0
|
7
|
+
options.merge!({:seq => @panel_count}) unless options[:base_id]
|
8
|
+
tabbed_panel = TabbedPanel.new(self, options)
|
9
|
+
|
10
|
+
if block_given?
|
11
|
+
yield(tabbed_panel)
|
12
|
+
output = tabbed_panel.render
|
13
|
+
output
|
14
|
+
end
|
15
|
+
return output
|
16
|
+
end
|
17
|
+
|
18
|
+
alias_method :create_tabbed_panel, :tabbed_panel
|
19
|
+
|
20
|
+
def wrap_in_div(id, &block)
|
21
|
+
content_tag(:div, capture(&block), :class => 'wrapper', :id => id)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module LogicalTabs
|
2
|
+
require 'logical_tabs/tabbed_panel'
|
3
|
+
|
4
|
+
# This contains the info for a single tab and pane
|
5
|
+
class TabPane
|
6
|
+
attr_accessor :tabbed_panel, :name, :base_id, :content, :tab_text, :pane_content
|
7
|
+
|
8
|
+
# tabbed_panel must be an instance of TabbedPanel
|
9
|
+
#
|
10
|
+
# Name is required, is used as the primary reference to this tab
|
11
|
+
#
|
12
|
+
# Other options:
|
13
|
+
# :base_id The string used as the beginning of the ID for CSS
|
14
|
+
# IDs for the tab and pane. :base_id => 'foo' will
|
15
|
+
# generate a tab with ID 'foo_tab' and a panel with ID
|
16
|
+
# 'foo_panel'. Defaults to name.downcase
|
17
|
+
#
|
18
|
+
# :tab_text The text to display in the tab. Defaults to name.
|
19
|
+
#
|
20
|
+
# :content The content for the panel itself, in HTML
|
21
|
+
#
|
22
|
+
def initialize(tabbed_panel, name, options = {})
|
23
|
+
@tabbed_panel = tabbed_panel
|
24
|
+
@name = name
|
25
|
+
@base_id = options[:base_id] || name.underscore.gsub(/\s+/,'_')
|
26
|
+
@tab_text = options[:tab_text] || name
|
27
|
+
@content = options[:content] || ''
|
28
|
+
end
|
29
|
+
|
30
|
+
# Generates output for the tab.
|
31
|
+
# Pass "true" to set this as the selected tab.
|
32
|
+
def render_tab(selected = false)
|
33
|
+
v.content_tag(:li,
|
34
|
+
tab_link,
|
35
|
+
:id => (composite_id + "_tab").html_safe,
|
36
|
+
:class => ("tab " + (selected ? "tab_selected" : "tab_unselected")).html_safe
|
37
|
+
).html_safe
|
38
|
+
end
|
39
|
+
|
40
|
+
def render_header(selected = false)
|
41
|
+
v.content_tag(:h3,
|
42
|
+
@tab_text,
|
43
|
+
:class => ("pane_print_header ").html_safe
|
44
|
+
).html_safe
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
def tab_link
|
49
|
+
v.content_tag(:a, @tab_text, :href => '#', :class => 'tab_link' ).html_safe
|
50
|
+
end
|
51
|
+
|
52
|
+
# generates an ID for this tab_pane that includes the base_id of the
|
53
|
+
# containing tabbed_panel and the base_id of this tab_pane.
|
54
|
+
def composite_id
|
55
|
+
(@tabbed_panel.base_id + "_tp_" + @base_id).html_safe
|
56
|
+
end
|
57
|
+
|
58
|
+
# Generates HTML output for the panel
|
59
|
+
# Pass "true" to set this as the selected/visible panel
|
60
|
+
def render_pane(selected = false)
|
61
|
+
v.content_tag(:div,
|
62
|
+
render_header + @content,
|
63
|
+
:id => (composite_id + "_pane").html_safe,
|
64
|
+
:class => "pane " + (selected ? "pane_selected" : "pane_unselected")
|
65
|
+
).html_safe
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
private
|
70
|
+
# Shortcut to the view
|
71
|
+
def v; @tabbed_panel.v; end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|