summaryrefslogtreecommitdiff
path: root/css/yaml4/add-ons/accessible-tabs/.svn/text-base/jquery.tabs.js.svn-base
blob: b028f0655a55b0a3e7815a3e3b162f91450e73a9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
/**
 * Accessible Tabs - jQuery plugin for accessible, unobtrusive tabs
 * Build to seemlessly work with the CCS-Framework YAML (yaml.de) not depending on YAML though
 * @requires jQuery - tested with 1.4.2 but might as well work with older versions
 *
 * english article: http://blog.ginader.de/archives/2009/02/07/jQuery-Accessible-Tabs-How-to-make-tabs-REALLY-accessible.php
 * german article: http://blog.ginader.de/archives/2009/02/07/jQuery-Accessible-Tabs-Wie-man-Tabs-WIRKLICH-zugaenglich-macht.php
 *
 * code: http://github.com/ginader/Accessible-Tabs
 * please report issues at: http://github.com/ginader/Accessible-Tabs/issues
 *
 * Copyright (c) 2007 Dirk Ginader (ginader.de)
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
 *
 * Version: 1.9.2
 *
 * History:
 * * 1.0 initial release
 * * 1.1 added a lot of Accessibility enhancements
 * * * rewrite to use "fn.extend" structure
 * * * added check for existing ids on the content containers to use to proper anchors in the tabs
 * * 1.1.1 changed the headline markup. thanks to Mike Davies for the hint.
 * * 1.5 thanks to Dirk Jesse, Ansgar Hein, David Maciejewski and Mike West for commiting patches to this release
 * * * new option syncheights that syncs the heights of the tab contents when the SyncHeight plugin
 * *   is available http://blog.ginader.de/dev/jquery/syncheight/index.php
 * * * fixed the hardcoded current class
 * * * new option tabsListClass to be applied to the generated list of tabs above the content so lists
 * *   inside the tabscontent can be styled differently
 * * * added clearfix and tabcounter that adds a class in the schema "tabamount{number amount of tabs}"
 * *   to the ul containg the tabs so one can style the tabs to fit 100% into the width
 * * * new option "syncHeightMethodName" fixed issue: http://github.com/ginader/Accessible-Tabs/issues/2/find
 * * * new Method showAccessibleTab({index number of the tab to show starting with 0})	fixed issue: http://github.com/ginader/Accessible-Tabs/issues/3/find
 * * * added support for the Cursor Keys to come closer to the WAI ARIA Tab Panel Best Practices http://github.com/ginader/Accessible-Tabs/issues/1/find
 * * 1.6
 * * * new option "saveState" to allow tabs remember their selected state using cookies requires the cookie plugin: http://plugins.jquery.com/project/Cookie
 * * * changed supported jquery version to 1.4.2 to make sure it's future compatible
 * * * new option "autoAnchor" which allows to add ID's to headlines in the tabs markup that allow direct linking into a tab i.e.: file.html#headlineID
 * * 1.7
 * * * new option "pagination" that adds links to show the next/previous tab. This adds the following markup to each tab for you to style:
 * * *  <ul class="pagination">
 * * *    <li class="previous"><a href="#{the-id-of-the-previous-tab}"><span>{the headline of the previous tab}</span></a></li>
 * * *    <li class="next"><a href="#{the-id-of-the-next-tab}"><span>{the headline of the previous tab}</span></a></li>
 * * *  </ul>
 * * 1.8
 * * * new option "position" can be 'top' or 'bottom'. Defines where the tabs list is inserted.
 * * 1.8.1
 * * * Bugfix for broken pagination in ie6 and 7: Selector and object access modified by Daniel Knts (www.MilkmanMedia.de). Thanks to Carolin Moll for the report.
 * * 1.8.2
 * * * Bugfix for issue described by Sunshine here: http://blog.ginader.de/archives/2009/02/07/jQuery-Accessible-Tabs-How-to-make-tabs-REALLY-accessible.php#c916
 * * 1.8.3
 * * * Bugfix by Michael Schulze: Only change current class in tab navigation and not in all unordered lists inside the tabs.
 * * 1.9
 * * * new method showAccessibleTabSelector({valid jQuery selector of the tab to show}) that allows the opening of tabs \
 * * * by jQuery Selector instead of the index in showAccessibleTab() fixing issue https://github.com/ginader/Accessible-Tabs/issues/15
 * * 1.9.1 by Michael Schulze:
 * * * firstNavItemClass and lastNavItemClass to define a custom classname on the first and last tab
 * * * wrapInnerNavLinks: inner wrap for a-tags in tab navigation.
 * * 1.9.2
 * * * Bugfix by Dirk Jesse: fixing an issue that happened when passing multiple selectors to the init call instead of one
 * * * Bugfix that fixes a reset of the tabs counter when accessibleTabs() was called more than once on a page
 */


(function($) {
	var debugMode = true;
	$.fn.extend({
		// We assume there could be multiple sets of tabs on a page, so,
		// the unique id for each invididual tab's heading is identified with params q and r (e.g., id="accessibletabscontent0-2")
		getUniqueId: function(p, q, r){
			if (r===undefined) {r='';} else {r='-'+r;}
			return p + q + r;
		},
		accessibleTabs: function(config) {
			var defaults = {
				wrapperClass: 'content', // Classname to apply to the div that is wrapped around the original Markup
				currentClass: 'current', // Classname to apply to the LI of the selected Tab
				tabhead: 'h4', // Tag or valid Query Selector of the Elements to Transform the Tabs-Navigation from (originals are removed)
				tabheadClass: 'tabhead', // Classname to apply to the target heading element for each tab div
				tabbody: '.tabbody', // Tag or valid Query Selector of the Elements to be treated as the Tab Body
				fx:'show', // can be "fadeIn", "slideDown", "show"
				fxspeed: 'normal', // speed (String|Number): "slow", "normal", or "fast") or the number of milliseconds to run the animation
				currentInfoText: 'current tab: ', // text to indicate for screenreaders which tab is the current one
				currentInfoPosition: 'prepend', // Definition where to insert the Info Text. Can be either "prepend" or "append"
				currentInfoClass: 'current-info', // Class to apply to the span wrapping the CurrentInfoText
				tabsListClass:'tabs-list', // Class to apply to the generated list of tabs above the content
				syncheights:false, // syncs the heights of the tab contents when the SyncHeight plugin is available http://blog.ginader.de/dev/jquery/syncheight/index.php
				syncHeightMethodName:'syncHeight', // set the Method name of the plugin you want to use to sync the tab contents. Defaults to the SyncHeight plugin: http://github.com/ginader/syncHeight
				cssClassAvailable:false, // Enable individual css classes for tabs. Gets the appropriate class name of a tabhead element and apply it to the tab list element. Boolean value
				saveState:false, // save the selected tab into a cookie so it stays selected after a reload. This requires that the wrapping div needs to have an ID (so we know which tab we're saving)
				autoAnchor:false, // will move over any existing id of a headline in tabs markup so it can be linked to it
				pagination:false, // adds buttons to each tab to switch to the next/previous tab
				position:'top', // can be 'top' or 'bottom'. Defines where the tabs list is inserted.
				wrapInnerNavLinks: '', // inner wrap for a-tags in tab navigation. See http://api.jquery.com/wrapInner/ for further informations
				firstNavItemClass: 'first', // Classname of the first list item in the tab navigation
				lastNavItemClass: 'last' // Classname of the last list item in the tab navigation
			};
			var keyCodes = {
				37 : -1, //LEFT
				38 : -1, //UP
				39 : +1, //RIGHT
				40 : +1 //DOWN
			};
			var positions = {
				top : 'prepend',
				bottom : 'append'
			};
			this.options = $.extend(defaults, config);

			var tabsCount = 0;
			if($("body").data('accessibleTabsCount') !== undefined){
				tabsCount = $("body").data('accessibleTabsCount');
			}
			$("body").data('accessibleTabsCount',this.size()+tabsCount);

			var o = this;
			return this.each(function(t) {
				var el = $(this);
				var list = '';
				var tabCount = 0;
				var ids = [];

				$(el).wrapInner('<div class="'+o.options.wrapperClass+'"></div>');

				$(el).find(o.options.tabhead).each(function(i){
					var id = '';
					elId = $(this).attr('id');
					if(elId){
						// Skip this item if it already exists.
						if(elId.indexOf('accessibletabscontent') === 0) {
							return;
						}
						id =' id="'+elId+'"';
					}
					var tabId = o.getUniqueId('accessibletabscontent', tabsCount+t, i);//get a unique id to assign to this tab's heading
					var navItemId = o.getUniqueId('accessibletabsnavigation', tabsCount+t, i);//get a unique id for this navigation item
					ids.push(tabId);
					if(o.options.cssClassAvailable === true) {
						var cssClass = '';
						if($(this).attr('class')) {
							cssClass = $(this).attr('class');
							cssClass = ' class="'+cssClass+'"';
							list += '<li id="'+navItemId+'"><a'+id+''+cssClass+' href="#'+tabId+'">'+$(this).html()+'</a></li>';
						}
					} else {
						list += '<li id="'+navItemId+'"><a'+id+' href="#'+tabId+'">'+$(this).html()+'</a></li>';
					}
					$(this).attr({"id": tabId, "class": o.options.tabheadClass, "tabindex": "-1"});//assign the unique id and the tabheadClass class name to this tab's heading
					tabCount++;
				});

				if (o.options.syncheights && $.fn[o.options.syncHeightMethodName]) {
					$(el).find(o.options.tabbody)[o.options.syncHeightMethodName]();
					$(window).resize(function(){
						$(el).find(o.options.tabbody)[o.options.syncHeightMethodName]();
					});
				}

				// Ensure that the call to setup tabs is re-runnable
				var tabs_selector = '.' + o.options.tabsListClass;
				if(!$(el).find(tabs_selector).length) {
					$(el)[positions[o.options.position]]('<ul class="clearfix '+o.options.tabsListClass+' tabamount'+tabCount+'"></ul>');
				}

				$(el).find(tabs_selector).append(list);

				// initial show first content block and hide the others
				var content = $(el).find(o.options.tabbody);
				if (content.length > 0) {
					$(content).hide();
					$(content[0]).show();
				}
				$(el).find("ul."+o.options.tabsListClass+">li:first").addClass(o.options.currentClass).addClass(o.options.firstNavItemClass)
					.find('a')[o.options.currentInfoPosition]('<span class="'+o.options.currentInfoClass+'">'+o.options.currentInfoText+'</span>')
					.parents("ul."+o.options.tabsListClass).children('li:last').addClass(o.options.lastNavItemClass);

				if (o.options.wrapInnerNavLinks) {
					$(el).find('ul.'+o.options.tabsListClass+'>li>a').wrapInner(o.options.wrapInnerNavLinks);
				}

				$(el).find('ul.'+o.options.tabsListClass+'>li>a').each(function(i){
					$(this).click(function(event){
						event.preventDefault();
						el.trigger("showTab.accessibleTabs", [$(event.target)]);
						if(o.options.saveState && $.cookie){
							$.cookie('accessibletab_'+el.attr('id')+'_active',i);
						}
						$(el).find('ul.'+o.options.tabsListClass+'>li.'+o.options.currentClass).removeClass(o.options.currentClass)
						.find("span."+o.options.currentInfoClass).remove();
						$(this).blur();
						$(el).find(o.options.tabbody+':visible').hide();
						$(el).find(o.options.tabbody).eq(i)[o.options.fx](o.options.fxspeed);
						$(this)[o.options.currentInfoPosition]('<span class="'+o.options.currentInfoClass+'">'+o.options.currentInfoText+'</span>')
						.parent().addClass(o.options.currentClass);
						//now, only after writing the currentInfoText span to the tab list link, set focus to the tab's heading
						$($(this).attr("href")).focus().keyup(function(event){
							if(keyCodes[event.keyCode]){
								o.showAccessibleTab(i+keyCodes[event.keyCode]);
								$(this).unbind( "keyup" );
							}
						});

						// $(el).find('.accessibletabsanchor').keyup(function(event){
						//	   if(keyCodes[event.keyCode]){
						//		   o.showAccessibleTab(i+keyCodes[event.keyCode]);
						//	   }
						// });


					});

					$(this).focus(function(event){
						$(document).keyup(function(event){
							if(keyCodes[event.keyCode]){
								o.showAccessibleTab(i+keyCodes[event.keyCode]);
							}
						});
					});
					$(this).blur(function(event){
						$(document).unbind( "keyup" );
					});

				});

				if(o.options.saveState && $.cookie){
					var savedState = $.cookie('accessibletab_'+el.attr('id')+'_active');
					debug($.cookie('accessibletab_'+el.attr('id')+'_active'));
					if(savedState !== null){
						o.showAccessibleTab(savedState,el.attr('id'));
					}
				}

				if(o.options.autoAnchor && window.location.hash){
					var anchorTab = $('.'+o.options.tabsListClass).find(window.location.hash);
					if(anchorTab.size()){
						anchorTab.click();
					}
				}

				if(o.options.pagination){
					var m = '<ul class="pagination">';
					m +='	 <li class="previous"><a href="#{previousAnchor}"><span>{previousHeadline}</span></a></li>';
					m +='	 <li class="next"><a href="#{nextAnchor}"><span>{nextHeadline}</span></a></li>';
					m +='</ul>';
					var tabs = $(el).find('.tabbody');
					var tabcount = tabs.size();
					tabs.each(function(idx){
						$(this).append(m);
						var next = idx+1;
						if(next>=tabcount){next = 0;}
						var previous = idx-1;
						if(previous<0){previous = tabcount-1;}
						var p = $(this).find('.pagination');
						var previousEl = p.find('.previous');
						previousEl.find('span').text($('#'+ids[previous]).text());
						previousEl.find('a').attr('href','#'+ids[previous])
						.click(function(event){
							event.preventDefault();
							$(el).find('.tabs-list a').eq(previous).click();
						});
						var nextEl = p.find('.next');
						nextEl.find('span').text($('#'+ids[next]).text());
						nextEl.find('a').attr('href','#'+ids[next])
						.click(function(event){
							event.preventDefault();
							$(el).find('.tabs-list a').eq(next).click();
						});
					});
				}
			});
		},
		showAccessibleTab: function(index,id){
			debug('showAccessibleTab');
			var o = this;
			if(id) {
				var el = $('#'+id);
				var links = el.find('ul.'+o.options.tabsListClass+'>li>a');
				el.trigger("showTab.accessibleTabs", [links.eq(index)]);
				links.eq(index).click();
			} else {
				return this.each(function() {
					var el = $(this);
					el.trigger("showTab.accessibleTabs");
					var links = el.find('ul.'+o.options.tabsListClass+'>li>a');
					el.trigger("showTab.accessibleTabs", [links.eq(index)]);
					links.eq(index).click();
				});
			}
		},
		showAccessibleTabSelector: function(selector){
			debug('showAccessibleTabSelector');
			var o = this;
			var el = $(selector);
			if(el){
				if(el.get(0).nodeName.toLowerCase() == 'a'){
					el.click();
				}else{
					debug('the selector of a showAccessibleTabSelector() call needs to point to a tabs headline!');
				}
			}
		}
	});
	// private Methods
	function debug(msg,info){
		if(debugMode && window.console && window.console.log){
			if(info){
				window.console.log(info+': ',msg);
			}else{
				window.console.log(msg);
			}
		}
	}
})(jQuery);