PageSourceSearch

https://www.blackanddecker.es/sites/g/files/xnuzat3376/files/cohes…/scripts/drupal-menu/drupal-menu.js?tl1qlh

js blackanddecker.es collected 2026-09-26 14:09:06 UTC 22,554 bytes, 520 lines download raw bytes

1(function ($, Drupal, once) {
2    'use strict';
3
4    Drupal.behaviors.DX8Menus = {
5        // Attachment trigger.
6        attach: function (context, settings) {
7            // Cache
8            var menuItemLinks = [];
9
10            // Counters
11            var menuItemLinkCounter;
12
13            // Libs
14            var cmm = new Drupal.CohesionResponsiveBreakpoints();
15
16            // States
17            var over = false;
18            var $lastAnimatedSubmenu = $();
19            var hasEntered = false;
20
21            // Constants
22            var onceMenuItem = 'js-coh-menu-item-init';
23            var onceMenuItemLink = 'js-coh-menu-item-link-init';
24            var nameSpace = '.coh-menu-item-link';
25
26            // Constants - classes
27            var cls = {
28                isCollapsed: 'is-collapsed',
29                isExpanded: 'is-expanded',
30                both: 'is-collapsed is-expanded',
31                hasChildren: 'has-children',
32                menuListContainer: 'coh-menu-list-container',
33                menuListItem: 'coh-menu-list-item',
34                menuListLink: 'js-coh-menu-item-link'
35            };
36
37            // Constants - aria attributes
38            var aria = {
39                expanded: 'aria-expanded',
40                popup: 'aria-haspopup'
41            };
42
43            // Events
44            var onEnter = ['pointerenter'];
45            var onLeave = ['mouseleave'];
46            var onClick = ['click'];
47            var onFocus = ['focus'];
48            var onFocusOut = ['focusout'];
49            var onKeyDown = ['keydown'];
50
51            /**
52             * Call back for initializing the Drupal menu item link functionality
53             * @param {Object} settings
54             * @returns {undefined}
55             */
56            function initDrupalMenuItemLink(settings) {
57                var bOnClick = true;
58
59                settings = settings.cohesion;
60                var $a = settings.settings.$a; // Can be either `<a>`, `<button>` or <span>
61                var $li = settings.settings.$li;
62                var isParent = settings.settings.isParent;
63                var thisNameSpace = nameSpace + $a[0].nodeName;
64
65                // Ensure all previous bound events are removed (by nameSpace)
66                $li.off(thisNameSpace);
67                $a.off(thisNameSpace);
68
69                // Grab the current breakpoint setting
70                var setting = settings.settings.breakpoints[settings.key];
71
72                // If animation target exists, store for later use
73                if (typeof setting.animationTarget !== 'undefined') {
74                    $a.data('currentAnimationTarget', $(setting.animationTarget, $li));
75                }
76
77                // If animation target is stored, remove inline display property
78                if ($a.data('currentAnimationTarget')) {
79                    $($a.data('currentAnimationTarget')).css('display','');
80                }
81
82                var toggleSiblings = setting.link_interaction === 'toggle-on-click-hide-siblings' || setting.button_interaction === 'toggle-on-click-hide-siblings';
83
84                if ((setting.link_interaction === 'toggle-on-hover' || toggleSiblings || setting.link_interaction === 'toggle-on-click' || setting.button_interaction === 'toggle-on-click' || settings.button_interaction === 'toggle-parent-on-click') && $li.hasClass(cls.hasChildren) ) {
85
86                    $a.attr(aria.popup, true);
87
88                } else {
89                    $a.removeAttr(aria.popup);
90                    $a.removeAttr(aria.expanded);
91                }
92
93                // Hover events
94                if (setting.link_interaction === 'toggle-on-hover') {
95                    over = false;
96
97                    if ($li.hasClass(cls.hasChildren)) {
98                        $li.on(onLeave.join(thisNameSpace + ' ') + thisNameSpace, function (event) {
99                            // hotfix/COH-4793 - prevent leave event being able to fire before enter event to stop menu show/hide getting inverted on page load.
100                            if (!hasEntered) {
101                                return;
102                            }
103                            window.setTimeout(function () {
104                                over = false;
105                            }, 200);
106
107                            // Disable click events for links and buttons.
108                            event.preventDefault();
109
110                            // If the menu is closed when leaving ensure it doesnt get reopened
111                            if($li.hasClass(cls.isCollapsed))   {
112                                return;
113                            }
114
115                            toggleSubMenu($li, $a, setting, true, event);
116                        });
117
118                        $li.on(onEnter.join(thisNameSpace + ' ') + thisNameSpace, function (event) {
119                            // Once the menu is open, make the link clickable - delay so it doesn't happen immediately on touch device.
120                            window.setTimeout(function () {
121                                over = true;
122                            }, 200);
123
124                            // Disable click events for links and buttons.
125                            event.preventDefault();
126                            // Ensure that the mouseEnter event always fires after a mouseLeave event when mousing between menu elements.
127                            setTimeout(function () {
128                                toggleSubMenu($li, $a, setting);
129                                hasEntered = true;
130                            }, 1);
131
132                        });
133
134                        $a.on(onClick.join(thisNameSpace + ' ') + thisNameSpace, function (event) {
135                            if (!over) {
136                                event.preventDefault();
137                            }
138                        });
139
140                    }
141                    bOnClick = false;
142                }
143
144                // Click events
145                if (bOnClick) {
146                    $a.on(onClick.join(thisNameSpace + ' ') + thisNameSpace, function (e) {
147
148                        // Click through to link || return to parent
149                        if (setting.link_interaction === 'click-through-to-link') {
150                            return;
151                        }
152
153                        // No interaction
154                        if (setting.link_interaction === 'no-interaction') {
155                            e.preventDefault();
156                            return;
157                        }
158
159                        // If has children - do something with them
160                        if ($li.hasClass(cls.hasChildren)) {
161
162                            e.preventDefault();
163
164                            // On click, toggle sub-menu visibility and hide sibling items. If no sub-menu, go to link
165                            toggleSubMenu($li, $(this), setting, toggleSiblings);
166
167                            if(setting.link_interaction === 'toggle-parent-on-click') {
168                                return;
169                            }
170                        }
171                    });
172                }
173
174                // Focus events
175                $a.on(onFocus.join(thisNameSpace + ' ') + thisNameSpace, function (e) {
176                    over = true;
177
178                    // Ensure the pointer is always up to date on focus - probably a little overkill, but safer than sorry
179                    for (var i = 0; i < menuItemLinks.length; i++) {
180                        if ($(this).is(menuItemLinks[i]['$a'])) {
181                            menuItemLinkCounter = i;
182                            break;
183                        }
184                    }
185
186                    $a.on(onKeyDown.join(thisNameSpace + ' ') + thisNameSpace, function (e) {
187
188                        // Handle accessibility keys
189                        switch (e.key) {
190
191                            case ' ':
192                            case 'Spacebar': // IE
193                                e.preventDefault();
194                                e.stopPropagation();
195
196                                // If menu list item does not have child menu, space follows link
197                                if (!$li.hasClass(cls.hasChildren)) {
198                                    window.location = $a.attr('href');
199                                }
200
201                                // If menu list item has child menu, space toggles child menu
202                                if ($li.hasClass(cls.hasChildren)) {
203                                    toggleSubMenu($li, $a, setting, toggleSiblings);
204                                }
205
206                                break;
207
208                            case 'ArrowDown':
209                            case 'ArrowRight':
210                            case 'Down': // IE
211                            case 'Right': // IE
212                                e.preventDefault();
213                                e.stopPropagation();
214
215                                // Handle parent menu items (when collapsed you should be able to navigate left / right)
216                                if (isParent && (e.key === 'ArrowRight' || e.key === 'Right') && $li.next('.' + cls.menuListItem) && !$li.hasClass(cls.isExpanded)) {
217                                    $('.' + cls.menuListLink, $li.next('.' + cls.menuListItem)).eq(0).focus();
218                                    break;
219                                }
220
221                                // If expanded then move into child items
222                                if ($li.hasClass(cls.isExpanded) && $li.hasClass(cls.hasChildren)) {
223                                    focusNextMenuItem($li);
224                                }
225
226                                // If !expanded then do it
227                                if (!$li.hasClass(cls.isExpanded) && $li.hasClass(cls.hasChildren)) {
228                                    toggleSubMenu($li, $a, setting, toggleSiblings);
229                                }
230
231                                // If !children move onto the next sibling
232                                if (!$li.hasClass(cls.hasChildren)) {
233                                    focusNextMenuItem($li);
234                                }
235
236                                break;
237
238                            case 'Escape':
239                            case 'Esc': // IE
240                                e.preventDefault();
241                                e.stopPropagation();
242
243                                if ($li.parent().closest('.' + cls.menuListItem)) {
244
245                                    toggleSubMenu($li.parent().closest('.' + cls.menuListItem), $li.parent().closest('.' + cls.menuListItem).children('.' + cls.menuListLink), setting, toggleSiblings);
246
247                                    $li.parent().closest('.' + cls.menuListItem).children('.' + cls.menuListLink).focus();
248                                }
249
250                                break;
251
252                            case 'ArrowUp':
253                            case 'ArrowLeft':
254                            case 'Up': // IE
255                            case 'Left': // IE
256                                e.preventDefault();
257                                e.stopPropagation();
258
259                                // Handle parent menu items (when collapsed you should be able to navigate left / right)
260                                if (isParent && (e.key === 'ArrowLeft' || e.key === 'Left') && $li.prev('.' + cls.menuListItem) && !$li.hasClass(cls.isExpanded)) {
261                                    $('.' + cls.menuListLink, $li.prev('.' + cls.menuListItem)).eq(0).focus();
262                                    break;
263                                }
264
265                                // If !children || isCollapsed then move back to the previous link
266                                if (!$li.hasClass(cls.hasChildren) || $li.hasClass(cls.isCollapsed)) {
267                                    focusPreviousMenuItem();
268                                }
269
270                                // If !collapsed then do it, toggle siblings and move back to the previous link
271                                if (!$li.hasClass(cls.isCollapsed) && $li.hasClass(cls.hasChildren)) {
272                                    toggleSubMenu($li, $a, setting, toggleSiblings);
273                                }
274
275                                break;
276
277                            case 'Enter':
278                                e.preventDefault();
279                                e.stopPropagation();
280                                // Enter should always follow the link, expanding the menu can be handled with down or space.
281                                window.location = $a.attr('href');
282
283                                break;
284
285                            default:
286                                return;
287                        }
288
289
290                    });
291                });
292
293                $a.on(onFocusOut.join(thisNameSpace + ' ') + thisNameSpace, function (e) {
294                    // Remove the previous keyboard events
295                    $a.off(onKeyDown.join(thisNameSpace + ' ') + thisNameSpace);
296                    over = false;
297                });
298            }
299
300            /**
301             *
302             * @param {type} settings
303             * @returns {undefined}
304             */
305            function initDrupalMenuItem(settings) {
306                settings = settings.cohesion;
307                var $a = settings.settings.$a; // Can be either `<a>`, `<button>` or <span>
308                var $li = settings.settings.$li;
309                var bInteracted = false;
310                var $interactees = $a.add($a.siblings('a, button, span')); // used to ensure sibling A and BUTTON elements get same aria values.
311
312                // Grab the current breakpoint setting
313                var setting = settings.settings.breakpoints[settings.key];
314
315                if (typeof $li.data('interacted') !== 'undefined' && $li.data('interacted') === true) {
316                    bInteracted = true;
317                }
318
319                if (!bInteracted) {
320                    $li.toggleClass(cls.isCollapsed, setting === 'hidden' || (setting === 'trail' && !$li.hasClass('in-active-trail')));
321                    $li.toggleClass(cls.isExpanded, setting === 'visible' || (setting === 'trail' && $li.hasClass('in-active-trail')));
322                    if ($li.hasClass(cls.hasChildren)) {
323
324                        $interactees.each(function () {
325                            var $interactee = $(this);
326
327                            if ($interactee.attr(aria.popup) === "true") {
328                                if ($li.hasClass(cls.isCollapsed)) {
329                                    $interactee.attr(aria.expanded, false);
330                                }
331                                if ($li.hasClass(cls.isExpanded)) {
332                                    $interactee.attr(aria.expanded, true);
333                                }
334                            }
335                        });
336                    }
337                }
338            }
339
340            function toggleSiblingsFn($li, $a, setting) {
341                var $siblings = $li.siblings('li.has-children');
342                $siblings.children('a, button, span').each(function() {
343                    var $this = $(this);
344                    if($this.attr(aria.expanded) === "true") {
345                        toggleSubMenu($this.parent('li'), $this, setting, false);
346                    }
347                });
348            }
349
350            function toggleSubMenu($li, $a, setting, toggleSiblings) {
351                var $interactees = $a.add($a.siblings('a, button, span'));
352                var $submenu;
353
354                if (setting.animationTarget && setting.animationType) {
355                    // hardcoded general sibling selector for animation target, as sub menu will always be a sibling of parent menu's link
356                    $submenu = $('~' + setting.animationTarget, $a);
357
358                    if(setting.button_interaction === 'toggle-parent-on-click')  {
359                        $submenu = $('> ' + setting.animationTarget, $li);
360                        $interactees = $interactees.add($('> a, > button, > span', $li));
361                    }
362
363                    var animationOriginArray;
364
365                    if (setting.animationOrigin) {
366                        // convert animation origin string to array
367                        animationOriginArray = setting.animationOrigin.split(',');
368                    }
369
370                    if ((setting.link_interaction !== 'toggle-on-click-hide-siblings' || setting.button_interaction !== 'toggle-on-click-hide-siblings') && $lastAnimatedSubmenu.length && !$lastAnimatedSubmenu.is($submenu)) {
371                        $lastAnimatedSubmenu.stop(true, true);
372                    }
373
374                    $submenu.stop(true, true).toggle({
375                        effect: setting.animationType,
376                        direction: setting.animationDirection,
377                        distance: setting.animationDistance,
378                        percent: setting.animationScale,
379                        origin: animationOriginArray,
380                        size: setting.animationFoldHeight,
381                        horizFirst: setting.animationHorizontalFirst,
382                        times: setting.animationIterations,
383                        easing: setting.animationEasing,
384                        duration: setting.animationDuration
385                    });
386                    $lastAnimatedSubmenu = $submenu;
387                }
388                $li.toggleClass(cls.both);
389
390                $interactees.each(function () {
391                    var $interactee = $(this);
392                    if ($interactee.attr(aria.popup) === "true") {
393                        if ($li.hasClass(cls.isCollapsed)) {
394                            $interactee.attr(aria.expanded, false);
395                        }
396                        if ($li.hasClass(cls.isExpanded)) {
397                            $interactee.attr(aria.expanded, true);
398                        }
399                    }
400                });
401
402                $li.data('interacted', true);
403
404                // Call the call back if defined
405                if (toggleSiblings) {
406                    toggleSiblingsFn($li, $a, setting);
407                }
408            }
409
410            function focusPreviousMenuItem() {
411                if (menuItemLinkCounter > 0) {
412                    menuItemLinkCounter--;
413                    menuItemLinks[menuItemLinkCounter]['$a'].focus();
414                }
415            }
416
417            function focusNextMenuItem() {
418                if (menuItemLinkCounter + 1 < menuItemLinks.length) {
419                    menuItemLinkCounter++;
420                    menuItemLinks[menuItemLinkCounter]['$a'].focus();
421                }
422            }
423
424            function focusMenuItem() {
425                menuItemLinks[menuItemLinkCounter]['$a'].focus();
426            }
427
428            // Each menu item link
429            var menuItems = $(once(onceMenuItemLink, '.js-coh-menu-item-link, .js-coh-menu-item-button'));
430            menuItems.each(function (i, e) {
431                var $this = $(this),
432                    $li = $(this).closest('.coh-menu-list-item'),
433                    responsiveSettings = $this.data('cohSettings'),
434                    key,
435                    settings = {
436                        $a: $this,
437                        $li: $li,
438                        isParent: !$li.parent().closest('.' + cls.menuListItem).length,
439                        breakpoints: {}
440                    };
441
442                // Keep a list of the menu links
443                menuItemLinks.push({
444                    $a: $this,
445                    $li: $li,
446                    tabindex: $this.attr('tabindex') || i
447                });
448
449                // Ensure they are in the correct order
450                if (i + 1 === menuItems.length) {
451
452                    menuItemLinks.sort(function (a, b) {
453                        return a.tabindex - b.tabindex;
454                    });
455                }
456
457                for (var i = 0; i < cmm.breakpoints.length; i++) {
458
459                    key = cmm.breakpoints[i].key;
460
461                    // Populate all breakpoints regardless of whether the settings are set or not to simulate inheritance
462                    settings.breakpoints[key] = {};
463                    if (responsiveSettings && typeof responsiveSettings[key] !== 'undefined') {
464
465                        settings.breakpoints[key] = responsiveSettings[key];
466
467                        var previous = responsiveSettings[key];
468
469                    } else {
470
471                        if (typeof cmm.breakpoints[i - 1] !== 'undefined' && typeof previous !== 'undefined') {
472                            settings.breakpoints[key] = previous;
473                        }
474                    }
475                }
476
477                cmm.addListeners(settings, initDrupalMenuItemLink);
478            });
479
480            // Each menu item
481            $(once(onceMenuItem, '.js-coh-menu-item')).each(function () {
482
483                var $this = $(this),
484                    responsiveSettings = $this.data('cohSettings'),
485                    key,
486                    settings = {
487                        $li: $this,
488                        $a: $('> a, > button, > span', $this),
489                        breakpoints: {}
490                    };
491
492                // No children or settings then just return
493                if ($this.hasClass(cls.hasChildren)) {
494                    for (var i = 0; i < cmm.breakpoints.length; i++) {
495
496                        key = cmm.breakpoints[i].key;
497
498                        // Populate all breakpoints regardless of whether the settings are set or not to simulate inheritance
499                        settings.breakpoints[key] = {};
500                        if (typeof responsiveSettings[key] !== 'undefined') {
501
502                            settings.breakpoints[key] = responsiveSettings[key];
503
504                            var previous = responsiveSettings[key];
505
506                        } else {
507
508                            if (typeof cmm.breakpoints[i - 1] !== 'undefined' && typeof previous !== 'undefined') {
509                                settings.breakpoints[key] = previous;
510                            }
511                        }
512                    }
513
514                    cmm.addListeners(settings, initDrupalMenuItem);
515                }
516            });
517        }
518    };
519
520})(jQuery, Drupal, once);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.