vertical-tabs.js 6.0 KB

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