Backup of db before drupal security update
[yaffs-website] / web / core / misc / vertical-tabs.js
1 /**
2  * @file
3  * Define vertical tabs functionality.
4  */
5
6 /**
7  * Triggers when form values inside a vertical tab changes.
8  *
9  * This is used to update the summary in vertical tabs in order to know what
10  * are the important fields' values.
11  *
12  * @event summaryUpdated
13  */
14
15 (function ($, Drupal, drupalSettings) {
16
17   'use strict';
18
19   /**
20    * This script transforms a set of details into a stack of vertical tabs.
21    *
22    * Each tab may have a summary which can be updated by another
23    * script. For that to work, each details element has an associated
24    * 'verticalTabCallback' (with jQuery.data() attached to the details),
25    * which is called every time the user performs an update to a form
26    * element inside the tab pane.
27    *
28    * @type {Drupal~behavior}
29    *
30    * @prop {Drupal~behaviorAttach} attach
31    *   Attaches behaviors for vertical tabs.
32    */
33   Drupal.behaviors.verticalTabs = {
34     attach: function (context) {
35       var width = drupalSettings.widthBreakpoint || 640;
36       var mq = '(max-width: ' + width + 'px)';
37
38       if (window.matchMedia(mq).matches) {
39         return;
40       }
41
42       $(context).find('[data-vertical-tabs-panes]').once('vertical-tabs').each(function () {
43         var $this = $(this).addClass('vertical-tabs__panes');
44         var focusID = $this.find(':hidden.vertical-tabs__active-tab').val();
45         var tab_focus;
46
47         // Check if there are some details that can be converted to
48         // vertical-tabs.
49         var $details = $this.find('> details');
50         if ($details.length === 0) {
51           return;
52         }
53
54         // Create the tab column.
55         var tab_list = $('<ul class="vertical-tabs__menu"></ul>');
56         $this.wrap('<div class="vertical-tabs clearfix"></div>').before(tab_list);
57
58         // Transform each details into a tab.
59         $details.each(function () {
60           var $that = $(this);
61           var vertical_tab = new Drupal.verticalTab({
62             title: $that.find('> summary').text(),
63             details: $that
64           });
65           tab_list.append(vertical_tab.item);
66           $that
67             .removeClass('collapsed')
68             // prop() can't be used on browsers not supporting details element,
69             // the style won't apply to them if prop() is used.
70             .attr('open', true)
71             .addClass('vertical-tabs__pane')
72             .data('verticalTab', vertical_tab);
73           if (this.id === focusID) {
74             tab_focus = $that;
75           }
76         });
77
78         $(tab_list).find('> li').eq(0).addClass('first');
79         $(tab_list).find('> li').eq(-1).addClass('last');
80
81         if (!tab_focus) {
82           // If the current URL has a fragment and one of the tabs contains an
83           // element that matches the URL fragment, activate that tab.
84           var $locationHash = $this.find(window.location.hash);
85           if (window.location.hash && $locationHash.length) {
86             tab_focus = $locationHash.closest('.vertical-tabs__pane');
87           }
88           else {
89             tab_focus = $this.find('> .vertical-tabs__pane').eq(0);
90           }
91         }
92         if (tab_focus.length) {
93           tab_focus.data('verticalTab').focus();
94         }
95       });
96     }
97   };
98
99   /**
100    * The vertical tab object represents a single tab within a tab group.
101    *
102    * @constructor
103    *
104    * @param {object} settings
105    *   Settings object.
106    * @param {string} settings.title
107    *   The name of the tab.
108    * @param {jQuery} settings.details
109    *   The jQuery object of the details element that is the tab pane.
110    *
111    * @fires event:summaryUpdated
112    *
113    * @listens event:summaryUpdated
114    */
115   Drupal.verticalTab = function (settings) {
116     var self = this;
117     $.extend(this, settings, Drupal.theme('verticalTab', settings));
118
119     this.link.attr('href', '#' + settings.details.attr('id'));
120
121     this.link.on('click', function (e) {
122       e.preventDefault();
123       self.focus();
124     });
125
126     // Keyboard events added:
127     // Pressing the Enter key will open the tab pane.
128     this.link.on('keydown', function (event) {
129       if (event.keyCode === 13) {
130         event.preventDefault();
131         self.focus();
132         // Set focus on the first input field of the visible details/tab pane.
133         $('.vertical-tabs__pane :input:visible:enabled').eq(0).trigger('focus');
134       }
135     });
136
137     this.details
138       .on('summaryUpdated', function () {
139         self.updateSummary();
140       })
141       .trigger('summaryUpdated');
142   };
143
144   Drupal.verticalTab.prototype = {
145
146     /**
147      * Displays the tab's content pane.
148      */
149     focus: function () {
150       this.details
151         .siblings('.vertical-tabs__pane')
152         .each(function () {
153           var tab = $(this).data('verticalTab');
154           tab.details.hide();
155           tab.item.removeClass('is-selected');
156         })
157         .end()
158         .show()
159         .siblings(':hidden.vertical-tabs__active-tab')
160         .val(this.details.attr('id'));
161       this.item.addClass('is-selected');
162       // Mark the active tab for screen readers.
163       $('#active-vertical-tab').remove();
164       this.link.append('<span id="active-vertical-tab" class="visually-hidden">' + Drupal.t('(active tab)') + '</span>');
165     },
166
167     /**
168      * Updates the tab's summary.
169      */
170     updateSummary: function () {
171       this.summary.html(this.details.drupalGetSummary());
172     },
173
174     /**
175      * Shows a vertical tab pane.
176      *
177      * @return {Drupal.verticalTab}
178      *   The verticalTab instance.
179      */
180     tabShow: function () {
181       // Display the tab.
182       this.item.show();
183       // Show the vertical tabs.
184       this.item.closest('.js-form-type-vertical-tabs').show();
185       // Update .first marker for items. We need recurse from parent to retain
186       // the actual DOM element order as jQuery implements sortOrder, but not
187       // as public method.
188       this.item.parent().children('.vertical-tabs__menu-item').removeClass('first')
189         .filter(':visible').eq(0).addClass('first');
190       // Display the details element.
191       this.details.removeClass('vertical-tab--hidden').show();
192       // Focus this tab.
193       this.focus();
194       return this;
195     },
196
197     /**
198      * Hides a vertical tab pane.
199      *
200      * @return {Drupal.verticalTab}
201      *   The verticalTab instance.
202      */
203     tabHide: function () {
204       // Hide this tab.
205       this.item.hide();
206       // Update .first marker for items. We need recurse from parent to retain
207       // the actual DOM element order as jQuery implements sortOrder, but not
208       // as public method.
209       this.item.parent().children('.vertical-tabs__menu-item').removeClass('first')
210         .filter(':visible').eq(0).addClass('first');
211       // Hide the details element.
212       this.details.addClass('vertical-tab--hidden').hide();
213       // Focus the first visible tab (if there is one).
214       var $firstTab = this.details.siblings('.vertical-tabs__pane:not(.vertical-tab--hidden)').eq(0);
215       if ($firstTab.length) {
216         $firstTab.data('verticalTab').focus();
217       }
218       // Hide the vertical tabs (if no tabs remain).
219       else {
220         this.item.closest('.js-form-type-vertical-tabs').hide();
221       }
222       return this;
223     }
224   };
225
226   /**
227    * Theme function for a vertical tab.
228    *
229    * @param {object} settings
230    *   An object with the following keys:
231    * @param {string} settings.title
232    *   The name of the tab.
233    *
234    * @return {object}
235    *   This function has to return an object with at least these keys:
236    *   - item: The root tab jQuery element
237    *   - link: The anchor tag that acts as the clickable area of the tab
238    *       (jQuery version)
239    *   - summary: The jQuery element that contains the tab summary
240    */
241   Drupal.theme.verticalTab = function (settings) {
242     var tab = {};
243     tab.item = $('<li class="vertical-tabs__menu-item" tabindex="-1"></li>')
244       .append(tab.link = $('<a href="#"></a>')
245         .append(tab.title = $('<strong class="vertical-tabs__menu-item-title"></strong>').text(settings.title))
246         .append(tab.summary = $('<span class="vertical-tabs__menu-item-summary"></span>')
247         )
248       );
249     return tab;
250   };
251
252 })(jQuery, Drupal, drupalSettings);