logical_tabs 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|