_vertical-tabs.js 6.5 KB

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