82dcd2c62ed91343ef8ceee1e29424e9bfc4f8f6
[weblabels.fsf.org.git] / defectivebydesign.org / 20130319 / files / misc / vertical-tabs.js
1
2 (function ($) {
3
4 /**
5 * This script transforms a set of fieldsets into a stack of vertical
6 * tabs. Another tab pane can be selected by clicking on the respective
7 * tab.
8 *
9 * Each tab may have a summary which can be updated by another
10 * script. For that to work, each fieldset has an associated
11 * 'verticalTabCallback' (with jQuery.data() attached to the fieldset),
12 * which is called every time the user performs an update to a form
13 * element inside the tab pane.
14 */
15 Drupal.behaviors.verticalTabs = {
16 attach: function (context) {
17 $('.vertical-tabs-panes', context).once('vertical-tabs', function () {
18 var focusID = $(':hidden.vertical-tabs-active-tab', this).val();
19 var tab_focus;
20
21 // Check if there are some fieldsets that can be converted to vertical-tabs
22 var $fieldsets = $('> fieldset', this);
23 if ($fieldsets.length == 0) {
24 return;
25 }
26
27 // Create the tab column.
28 var tab_list = $('<ul class="vertical-tabs-list"></ul>');
29 $(this).wrap('<div class="vertical-tabs clearfix"></div>').before(tab_list);
30
31 // Transform each fieldset into a tab.
32 $fieldsets.each(function () {
33 var vertical_tab = new Drupal.verticalTab({
34 title: $('> legend', this).text(),
35 fieldset: $(this)
36 });
37 tab_list.append(vertical_tab.item);
38 $(this)
39 .removeClass('collapsible collapsed')
40 .addClass('vertical-tabs-pane')
41 .data('verticalTab', vertical_tab);
42 if (this.id == focusID) {
43 tab_focus = $(this);
44 }
45 });
46
47 $('> li:first', tab_list).addClass('first');
48 $('> li:last', tab_list).addClass('last');
49
50 if (!tab_focus) {
51 // If the current URL has a fragment and one of the tabs contains an
52 // element that matches the URL fragment, activate that tab.
53 if (window.location.hash && $(window.location.hash, this).length) {
54 tab_focus = $(window.location.hash, this).closest('.vertical-tabs-pane');
55 }
56 else {
57 tab_focus = $('> .vertical-tabs-pane:first', this);
58 }
59 }
60 if (tab_focus.length) {
61 tab_focus.data('verticalTab').focus();
62 }
63 });
64 }
65 };
66
67 /**
68 * The vertical tab object represents a single tab within a tab group.
69 *
70 * @param settings
71 * An object with the following keys:
72 * - title: The name of the tab.
73 * - fieldset: The jQuery object of the fieldset that is the tab pane.
74 */
75 Drupal.verticalTab = function (settings) {
76 var self = this;
77 $.extend(this, settings, Drupal.theme('verticalTab', settings));
78
79 this.link.click(function () {
80 self.focus();
81 return false;
82 });
83
84 // Keyboard events added:
85 // Pressing the Enter key will open the tab pane.
86 this.link.keydown(function(event) {
87 if (event.keyCode == 13) {
88 self.focus();
89 // Set focus on the first input field of the visible fieldset/tab pane.
90 $("fieldset.vertical-tabs-pane :input:visible:enabled:first").focus();
91 return false;
92 }
93 });
94
95 // Pressing the Enter key lets you leave the tab again.
96 this.fieldset.keydown(function(event) {
97 // Enter key should not trigger inside <textarea> to allow for multi-line entries.
98 if (event.keyCode == 13 && event.target.nodeName != "TEXTAREA") {
99 // Set focus on the selected tab button again.
100 $(".vertical-tab-button.selected a").focus();
101 return false;
102 }
103 });
104
105 this.fieldset
106 .bind('summaryUpdated', function () {
107 self.updateSummary();
108 })
109 .trigger('summaryUpdated');
110 };
111
112 Drupal.verticalTab.prototype = {
113 /**
114 * Displays the tab's content pane.
115 */
116 focus: function () {
117 this.fieldset
118 .siblings('fieldset.vertical-tabs-pane')
119 .each(function () {
120 var tab = $(this).data('verticalTab');
121 tab.fieldset.hide();
122 tab.item.removeClass('selected');
123 })
124 .end()
125 .show()
126 .siblings(':hidden.vertical-tabs-active-tab')
127 .val(this.fieldset.attr('id'));
128 this.item.addClass('selected');
129 // Mark the active tab for screen readers.
130 $('#active-vertical-tab').remove();
131 this.link.append('<span id="active-vertical-tab" class="element-invisible">' + Drupal.t('(active tab)') + '</span>');
132 },
133
134 /**
135 * Updates the tab's summary.
136 */
137 updateSummary: function () {
138 this.summary.html(this.fieldset.drupalGetSummary());
139 },
140
141 /**
142 * Shows a vertical tab pane.
143 */
144 tabShow: function () {
145 // Display the tab.
146 this.item.show();
147 // Update .first marker for items. We need recurse from parent to retain the
148 // actual DOM element order as jQuery implements sortOrder, but not as public
149 // method.
150 this.item.parent().children('.vertical-tab-button').removeClass('first')
151 .filter(':visible:first').addClass('first');
152 // Display the fieldset.
153 this.fieldset.removeClass('vertical-tab-hidden').show();
154 // Focus this tab.
155 this.focus();
156 return this;
157 },
158
159 /**
160 * Hides a vertical tab pane.
161 */
162 tabHide: function () {
163 // Hide this tab.
164 this.item.hide();
165 // Update .first marker for items. We need recurse from parent to retain the
166 // actual DOM element order as jQuery implements sortOrder, but not as public
167 // method.
168 this.item.parent().children('.vertical-tab-button').removeClass('first')
169 .filter(':visible:first').addClass('first');
170 // Hide the fieldset.
171 this.fieldset.addClass('vertical-tab-hidden').hide();
172 // Focus the first visible tab (if there is one).
173 var $firstTab = this.fieldset.siblings('.vertical-tabs-pane:not(.vertical-tab-hidden):first');
174 if ($firstTab.length) {
175 $firstTab.data('verticalTab').focus();
176 }
177 return this;
178 }
179 };
180
181 /**
182 * Theme function for a vertical tab.
183 *
184 * @param settings
185 * An object with the following keys:
186 * - title: The name of the tab.
187 * @return
188 * This function has to return an object with at least these keys:
189 * - item: The root tab jQuery element
190 * - link: The anchor tag that acts as the clickable area of the tab
191 * (jQuery version)
192 * - summary: The jQuery element that contains the tab summary
193 */
194 Drupal.theme.prototype.verticalTab = function (settings) {
195 var tab = {};
196 tab.item = $('<li class="vertical-tab-button" tabindex="-1"></li>')
197 .append(tab.link = $('<a href="#"></a>')
198 .append(tab.title = $('<strong></strong>').text(settings.title))
199 .append(tab.summary = $('<span class="summary"></span>')
200 )
201 );
202 return tab;
203 };
204
205 })(jQuery);