Security update for Core, with self-updated composer
[yaffs-website] / web / core / modules / toolbar / js / toolbar.menu.es6.js
1 /**
2  * @file
3  * Builds a nested accordion widget.
4  *
5  * Invoke on an HTML list element with the jQuery plugin pattern.
6  *
7  * @example
8  * $('.toolbar-menu').drupalToolbarMenu();
9  */
10
11 (function ($, Drupal, drupalSettings) {
12   /**
13    * Store the open menu tray.
14    */
15   let activeItem = Drupal.url(drupalSettings.path.currentPath);
16
17   $.fn.drupalToolbarMenu = function () {
18     const ui = {
19       handleOpen: Drupal.t('Extend'),
20       handleClose: Drupal.t('Collapse'),
21     };
22
23     /**
24      * Handle clicks from the disclosure button on an item with sub-items.
25      *
26      * @param {Object} event
27      *   A jQuery Event object.
28      */
29     function toggleClickHandler(event) {
30       const $toggle = $(event.target);
31       const $item = $toggle.closest('li');
32       // Toggle the list item.
33       toggleList($item);
34       // Close open sibling menus.
35       const $openItems = $item.siblings().filter('.open');
36       toggleList($openItems, false);
37     }
38
39     /**
40      * Handle clicks from a menu item link.
41      *
42      * @param {Object} event
43      *   A jQuery Event object.
44      */
45     function linkClickHandler(event) {
46       // If the toolbar is positioned fixed (and therefore hiding content
47       // underneath), then users expect clicks in the administration menu tray
48       // to take them to that destination but for the menu tray to be closed
49       // after clicking: otherwise the toolbar itself is obstructing the view
50       // of the destination they chose.
51       if (!Drupal.toolbar.models.toolbarModel.get('isFixed')) {
52         Drupal.toolbar.models.toolbarModel.set('activeTab', null);
53       }
54       // Stopping propagation to make sure that once a toolbar-box is clicked
55       // (the whitespace part), the page is not redirected anymore.
56       event.stopPropagation();
57     }
58
59     /**
60      * Toggle the open/close state of a list is a menu.
61      *
62      * @param {jQuery} $item
63      *   The li item to be toggled.
64      *
65      * @param {Boolean} switcher
66      *   A flag that forces toggleClass to add or a remove a class, rather than
67      *   simply toggling its presence.
68      */
69     function toggleList($item, switcher) {
70       const $toggle = $item.children('.toolbar-box').children('.toolbar-handle');
71       switcher = (typeof switcher !== 'undefined') ? switcher : !$item.hasClass('open');
72       // Toggle the item open state.
73       $item.toggleClass('open', switcher);
74       // Twist the toggle.
75       $toggle.toggleClass('open', switcher);
76       // Adjust the toggle text.
77       $toggle
78         .find('.action')
79         // Expand Structure, Collapse Structure.
80         .text((switcher) ? ui.handleClose : ui.handleOpen);
81     }
82
83     /**
84      * Add markup to the menu elements.
85      *
86      * Items with sub-elements have a list toggle attached to them. Menu item
87      * links and the corresponding list toggle are wrapped with in a div
88      * classed with .toolbar-box. The .toolbar-box div provides a positioning
89      * context for the item list toggle.
90      *
91      * @param {jQuery} $menu
92      *   The root of the menu to be initialized.
93      */
94     function initItems($menu) {
95       const options = {
96         class: 'toolbar-icon toolbar-handle',
97         action: ui.handleOpen,
98         text: '',
99       };
100       // Initialize items and their links.
101       $menu.find('li > a').wrap('<div class="toolbar-box">');
102       // Add a handle to each list item if it has a menu.
103       $menu.find('li').each((index, element) => {
104         const $item = $(element);
105         if ($item.children('ul.toolbar-menu').length) {
106           const $box = $item.children('.toolbar-box');
107           options.text = Drupal.t('@label', { '@label': $box.find('a').text() });
108           $item.children('.toolbar-box')
109             .append(Drupal.theme('toolbarMenuItemToggle', options));
110         }
111       });
112     }
113
114     /**
115      * Adds a level class to each list based on its depth in the menu.
116      *
117      * This function is called recursively on each sub level of lists elements
118      * until the depth of the menu is exhausted.
119      *
120      * @param {jQuery} $lists
121      *   A jQuery object of ul elements.
122      *
123      * @param {number} level
124      *   The current level number to be assigned to the list elements.
125      */
126     function markListLevels($lists, level) {
127       level = (!level) ? 1 : level;
128       const $lis = $lists.children('li').addClass(`level-${level}`);
129       $lists = $lis.children('ul');
130       if ($lists.length) {
131         markListLevels($lists, level + 1);
132       }
133     }
134
135     /**
136      * On page load, open the active menu item.
137      *
138      * Marks the trail of the active link in the menu back to the root of the
139      * menu with .menu-item--active-trail.
140      *
141      * @param {jQuery} $menu
142      *   The root of the menu.
143      */
144     function openActiveItem($menu) {
145       const pathItem = $menu.find(`a[href="${location.pathname}"]`);
146       if (pathItem.length && !activeItem) {
147         activeItem = location.pathname;
148       }
149       if (activeItem) {
150         const $activeItem = $menu.find(`a[href="${activeItem}"]`).addClass('menu-item--active');
151         const $activeTrail = $activeItem.parentsUntil('.root', 'li').addClass('menu-item--active-trail');
152         toggleList($activeTrail, true);
153       }
154     }
155
156     // Return the jQuery object.
157     return this.each(function (selector) {
158       const $menu = $(this).once('toolbar-menu');
159       if ($menu.length) {
160         // Bind event handlers.
161         $menu
162           .on('click.toolbar', '.toolbar-box', toggleClickHandler)
163           .on('click.toolbar', '.toolbar-box a', linkClickHandler);
164
165         $menu.addClass('root');
166         initItems($menu);
167         markListLevels($menu);
168         // Restore previous and active states.
169         openActiveItem($menu);
170       }
171     });
172   };
173
174   /**
175    * A toggle is an interactive element often bound to a click handler.
176    *
177    * @param {object} options
178    *   Options for the button.
179    * @param {string} options.class
180    *   Class to set on the button.
181    * @param {string} options.action
182    *   Action for the button.
183    * @param {string} options.text
184    *   Used as label for the button.
185    *
186    * @return {string}
187    *   A string representing a DOM fragment.
188    */
189   Drupal.theme.toolbarMenuItemToggle = function (options) {
190     return `<button class="${options.class}"><span class="action">${options.action}</span><span class="label">${options.text}</span></button>`;
191   };
192 }(jQuery, Drupal, drupalSettings));