1(function ($, Drupal) { 2 3 Drupal.behaviors.districtMenuMainMenu = { 4 5 /** 6 * Navigation help text. Used for screen reader announcements and dialog. 7 */ 8 helpText: function () { 9 var text = 'Use the keyboard arrow keys to easily traverse the main navigation.'; 10 text += ' Press the escape key at any time to jump to the top level parent link.'; 11 return text; 12 }, 13 14 /** 15 * Attach all functionality here. 16 */ 17 attach: function (context, settings) { 18 var behavior = this; 19 20 $(once('district-menu-main-menu', '.js-menu-extras-main', context)).each(function () { 21 behavior.initNavigationHelp(this); 22 behavior.initNavigationKeys(this) 23 }); 24 }, 25 26 /** 27 * Map key press to navigation behaviors. 28 */ 29 initNavigationKeys: function (context) { 30 var behavior = this; 31 $('.menu-level-0 > .menu-item', context).keydown(function (e) { 32 var $current = $(this); 33 var $target = $(e.target); 34 35 switch (e.keyCode) { 36 case 37: // left 37 e.preventDefault(); 38 behavior.navigateLeft($current, $target); 39 break; 40 41 case 38: // up 42 e.preventDefault(); 43 behavior.navigateUp($current, $target); 44 break; 45 46 case 39: // right 47 e.preventDefault(); 48 behavior.navigateRight($current, $target); 49 break; 50 51 case 40: // down 52 e.preventDefault(); 53 behavior.navigateDown($current, $target); 54 break; 55 56 case 27: // escape 57 e.preventDefault(); 58 behavior.navigateEscape($current, $target); 59 break; 60 } 61 }); 62 }, 63 64 /** 65 * Generate navigation help elements and functionality. 66 */ 67 initNavigationHelp: function (context) { 68 var behavior = this; 69 // Create hidden dialog elements. 70 var $hidden = $('<div class="hidden"></div>').appendTo('body'); 71 var $helpDialogEl = $('<div id="nav-help-dialog"</div>') 72 .text(this.helpText()).appendTo($hidden); 73 // Create help menu item. Only accessible via keyboard. 74 var $help = $('<li class="menu-item menu-item--nav-help"><a href="#" role="button" tabindex="0" aria-label="Keyboard navigation help" class="nav-help sr-only">?</a></li>'); 75 $help.prependTo($('.menu-level-0', context)); 76 $help = $('.menu-level-0', context).find('.nav-help'); 77 // Show help link when focused. 78 $help.focus(function () { 79 $(this).removeClass('sr-only'); 80 }); 81 // Hide help link when not focused. 82 $help.blur(function () { 83 $(this).addClass('sr-only'); 84 }); 85 // Create dialog object. 86 var helpDialog = Drupal.dialog($helpDialogEl, { 87 title: 'Main navigation help', 88 modal: true, 89 close: function ( event, ui ) { 90 $help.focus() 91 } 92 }) 93 // Trigger dialog when link is clicked/keyboard enter button. 94 $help.click(function () { 95 if (!helpDialog.open) { 96 Drupal.announce(behavior.helpText()) 97 helpDialog.showModal(); 98 } 99 else { 100 helpDialog.close(); 101 } 102 }) 103 // Hide dialog when navigating away from help link. 104 $help.keydown('keydown', function (e) { 105 if (e.keyCode == 13) { 106 return; 107 } 108 if (helpDialog.open) { 109 helpDialog.close(); 110 } 111 }); 112 }, 113 114 /** 115 * Check if target link is a top level menu item. 116 */ 117 isTopLevelLink: function ($current, $target) { 118 return $target.is($current.find('> a')); 119 }, 120 121 /** 122 * Check if target link is a second level menu item. 123 */ 124 isSecondLevelLink: function ($current, $target) { 125 return $target.is($current.find('.menu-level-1 > .menu-item > a')); 126 }, 127 128 /** 129 * Check if target link is a third level menu item. 130 */ 131 isThirdLevelLink: function ($current, $target) { 132 return $target.is($current.find('.menu-level-2 > .menu-item > a')); 133 }, 134 135 /** 136 * Keyboard arrow left behavior. 137 */ 138 navigateLeft: function ($current, $target) { 139 if (this.isTopLevelLink($current, $target)) { 140 $current.prev('.menu-item').find(' > a').focus(); 141 } 142 else if (this.isSecondLevelLink($current, $target)) { 143 $target.parent('.menu-item').prev('.menu-item').find(' > a').focus(); 144 } 145 else if (this.isThirdLevelLink($current, $target)) { 146 $target.parents('.menu-level-1 > .menu-item') 147 .prev('.menu-item').find(' > a').focus(); 148 } 149 }, 150 151 /** 152 * Keyboard arrow right behavior. 153 */ 154 navigateRight: function ($current, $target) { 155 if (this.isTopLevelLink($current, $target)) { 156 // If last top level menu item. try to find the next focusable element. 157 if ($current.is(':last-of-type')) { 158 $current.parents('.menu--main').next().find('a, button, input').first().focus() 159 } 160 else { 161 $current.next('.menu-item').find(' > a').focus(); 162 } 163 } 164 else if (this.isSecondLevelLink($current, $target)) { 165 $target.parent('.menu-item').next('.menu-item').find(' > a').focus(); 166 } 167 else if (this.isThirdLevelLink($current, $target)) { 168 $target.parents('.menu-level-1 > .menu-item') 169 .next('.menu-item').find(' > a').focus(); 170 } 171 }, 172 173 /** 174 * Keyboard arrow up behavior. 175 */ 176 navigateUp: function ($current, $target) { 177 if (this.isSecondLevelLink($current, $target)) { 178 $current.find('> a').focus(); 179 } 180 else if (this.isThirdLevelLink($current, $target)) { 181 $target.parents('.menu-level-1 > .menu-item') 182 .find(' > a').focus(); 183 } 184 }, 185 186 /** 187 * Keyboard arrow down behavior. 188 */ 189 navigateDown: function ($current, $target) { 190 if (this.isTopLevelLink($current, $target)) { 191 $current.find('.menu-level-1 a').first().focus(); 192 } 193 else if (this.isSecondLevelLink($current, $target)) { 194 $target.next().find('.menu-level-2 a').first().focus(); 195 } 196 else { 197 $target.parent('.menu-item').next('.menu-item').find(' > a').focus(); 198 } 199 }, 200 201 /** 202 * Keyboard escape key behavior. 203 */ 204 navigateEscape: function ($current, $target) { 205 $current.find('> a').focus(); 206 } 207 }; 208 209}(jQuery, Drupal));
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.