diff -r 07239de796bb -r e756a8c72c3d cms/drupal/misc/vertical-tabs.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cms/drupal/misc/vertical-tabs.js Fri Sep 08 12:04:06 2017 +0200 @@ -0,0 +1,201 @@ + +(function ($) { + +/** + * This script transforms a set of fieldsets into a stack of vertical + * tabs. Another tab pane can be selected by clicking on the respective + * tab. + * + * Each tab may have a summary which can be updated by another + * script. For that to work, each fieldset has an associated + * 'verticalTabCallback' (with jQuery.data() attached to the fieldset), + * which is called every time the user performs an update to a form + * element inside the tab pane. + */ +Drupal.behaviors.verticalTabs = { + attach: function (context) { + $('.vertical-tabs-panes', context).once('vertical-tabs', function () { + var focusID = $(':hidden.vertical-tabs-active-tab', this).val(); + var tab_focus; + + // Check if there are some fieldsets that can be converted to vertical-tabs + var $fieldsets = $('> fieldset', this); + if ($fieldsets.length == 0) { + return; + } + + // Create the tab column. + var tab_list = $(''); + $(this).wrap('
').before(tab_list); + + // Transform each fieldset into a tab. + $fieldsets.each(function () { + var vertical_tab = new Drupal.verticalTab({ + title: $('> legend', this).text(), + fieldset: $(this) + }); + tab_list.append(vertical_tab.item); + $(this) + .removeClass('collapsible collapsed') + .addClass('vertical-tabs-pane') + .data('verticalTab', vertical_tab); + if (this.id == focusID) { + tab_focus = $(this); + } + }); + + $('> li:first', tab_list).addClass('first'); + $('> li:last', tab_list).addClass('last'); + + if (!tab_focus) { + // If the current URL has a fragment and one of the tabs contains an + // element that matches the URL fragment, activate that tab. + if (window.location.hash && $(this).find(window.location.hash).length) { + tab_focus = $(this).find(window.location.hash).closest('.vertical-tabs-pane'); + } + else { + tab_focus = $('> .vertical-tabs-pane:first', this); + } + } + if (tab_focus.length) { + tab_focus.data('verticalTab').focus(); + } + }); + } +}; + +/** + * The vertical tab object represents a single tab within a tab group. + * + * @param settings + * An object with the following keys: + * - title: The name of the tab. + * - fieldset: The jQuery object of the fieldset that is the tab pane. + */ +Drupal.verticalTab = function (settings) { + var self = this; + $.extend(this, settings, Drupal.theme('verticalTab', settings)); + + this.link.click(function () { + self.focus(); + return false; + }); + + // Keyboard events added: + // Pressing the Enter key will open the tab pane. + this.link.keydown(function(event) { + if (event.keyCode == 13) { + self.focus(); + // Set focus on the first input field of the visible fieldset/tab pane. + $("fieldset.vertical-tabs-pane :input:visible:enabled:first").focus(); + return false; + } + }); + + this.fieldset + .bind('summaryUpdated', function () { + self.updateSummary(); + }) + .trigger('summaryUpdated'); +}; + +Drupal.verticalTab.prototype = { + /** + * Displays the tab's content pane. + */ + focus: function () { + this.fieldset + .siblings('fieldset.vertical-tabs-pane') + .each(function () { + var tab = $(this).data('verticalTab'); + tab.fieldset.hide(); + tab.item.removeClass('selected'); + }) + .end() + .show() + .siblings(':hidden.vertical-tabs-active-tab') + .val(this.fieldset.attr('id')); + this.item.addClass('selected'); + // Mark the active tab for screen readers. + $('#active-vertical-tab').remove(); + this.link.append('' + Drupal.t('(active tab)') + ''); + }, + + /** + * Updates the tab's summary. + */ + updateSummary: function () { + this.summary.html(this.fieldset.drupalGetSummary()); + }, + + /** + * Shows a vertical tab pane. + */ + tabShow: function () { + // Display the tab. + this.item.show(); + // Show the vertical tabs. + this.item.closest('.vertical-tabs').show(); + // Update .first marker for items. We need recurse from parent to retain the + // actual DOM element order as jQuery implements sortOrder, but not as public + // method. + this.item.parent().children('.vertical-tab-button').removeClass('first') + .filter(':visible:first').addClass('first'); + // Display the fieldset. + this.fieldset.removeClass('vertical-tab-hidden').show(); + // Focus this tab. + this.focus(); + return this; + }, + + /** + * Hides a vertical tab pane. + */ + tabHide: function () { + // Hide this tab. + this.item.hide(); + // Update .first marker for items. We need recurse from parent to retain the + // actual DOM element order as jQuery implements sortOrder, but not as public + // method. + this.item.parent().children('.vertical-tab-button').removeClass('first') + .filter(':visible:first').addClass('first'); + // Hide the fieldset. + this.fieldset.addClass('vertical-tab-hidden').hide(); + // Focus the first visible tab (if there is one). + var $firstTab = this.fieldset.siblings('.vertical-tabs-pane:not(.vertical-tab-hidden):first'); + if ($firstTab.length) { + $firstTab.data('verticalTab').focus(); + } + // Hide the vertical tabs (if no tabs remain). + else { + this.item.closest('.vertical-tabs').hide(); + } + return this; + } +}; + +/** + * Theme function for a vertical tab. + * + * @param settings + * An object with the following keys: + * - title: The name of the tab. + * @return + * This function has to return an object with at least these keys: + * - item: The root tab jQuery element + * - link: The anchor tag that acts as the clickable area of the tab + * (jQuery version) + * - summary: The jQuery element that contains the tab summary + */ +Drupal.theme.prototype.verticalTab = function (settings) { + var tab = {}; + tab.item = $('
  • ') + .append(tab.link = $('') + .append(tab.title = $('').text(settings.title)) + .append(tab.summary = $('') + ) + ); + return tab; +}; + +})(jQuery);