1/* 2* This content is licensed according to the W3C Software License at 3* https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document 4* 5* File: MenuItem.js 6* 7* Desc: Popup Menu Menuitem widget that implements ARIA Authoring Practices 8* 9* Author: Jon Gunderson, Ku Ja Eun and Nicholas Hoyt 10*/ 11 12/* 13* @constructor MenuItem 14* 15* @desc 16* Wrapper object for a simple menu item in a popup menu 17* 18* @param domNode 19* The DOM element node that serves as the menu item container. 20* The menuObj PopupMenu is responsible for checking that it has 21* requisite metadata, e.g. role="menuitem". 22* 23* @param menuObj 24* The object that is a wrapper for the PopupMenu DOM element that 25* contains the menu item DOM element. See PopupMenu.js 26*/ 27var MenuItem = function (domNode, menuObj) { 28 29 if (typeof popupObj !== 'object') { 30 popupObj = false; 31 } 32 33 this.domNode = domNode; 34 this.menu = menuObj; 35 this.popupMenu = false; 36 this.isMenubarItem = false; 37 38 this.keyCode = Object.freeze({ 39 'TAB': 9, 40 'RETURN': 13, 41 'ESC': 27, 42 'SPACE': 32, 43 'PAGEUP': 33, 44 'PAGEDOWN': 34, 45 'END': 35, 46 'HOME': 36, 47 'LEFT': 37, 48 'UP': 38, 49 'RIGHT': 39, 50 'DOWN': 40 51 }); 52}; 53 54MenuItem.prototype.init = function () { 55 this.domNode.tabIndex = -1; 56 57 if (!this.domNode.getAttribute('role')) { 58 this.domNode.setAttribute('role', 'menuitem'); 59 } 60 61 if (this.domNode.parentNode.tagName === 'LI') { 62 this.domNode.parentNode.setAttribute('role', 'none'); 63 } 64 65 this.domNode.addEventListener('keydown', this.handleKeydown.bind(this)); 66// this.domNode.addEventListener('click', this.handleClick.bind(this)); 67 this.domNode.addEventListener('focus', this.handleFocus.bind(this)); 68 this.domNode.addEventListener('blur', this.handleBlur.bind(this)); 69 this.domNode.addEventListener('mouseover', this.handleMouseover.bind(this)); 70 this.domNode.addEventListener('mouseout', this.handleMouseout.bind(this)); 71 72 // Initialize flyout menu 73 74 var nextElement = this.domNode.nextElementSibling; 75 76 if (nextElement && nextElement.tagName === 'UL') { 77 console.log('popup sub-menu'); 78 this.popupMenu = new PopupMenu(nextElement, this.menu, this); 79 this.popupMenu.init(); 80 } 81 82}; 83 84MenuItem.prototype.isExpanded = function () { 85 return this.domNode.getAttribute('aria-expanded') === 'true'; 86}; 87 88/* EVENT HANDLERS */ 89 90MenuItem.prototype.handleKeydown = function (event) { 91 var tgt = event.currentTarget, 92 char = event.key, 93 flag = false, 94 clickEvent; 95 96 function isPrintableCharacter (str) { 97 return str.length === 1 && str.match(/\S/); 98 } 99 100 switch (event.keyCode) { 101 case this.keyCode.RETURN: 102 if (this.domNode.getAttribute('href') != '#') { 103 return 104 } 105 case this.keyCode.SPACE: 106 if (this.popupMenu) { 107 this.popupMenu.open(); 108 this.popupMenu.setFocusToFirstItem(); 109 } 110 else { 111 112 // Create simulated mouse event to mimic the behavior of ATs 113 // and let the event handler handleClick do the housekeeping. 114 try { 115 clickEvent = new MouseEvent('click', { 116 'view': window, 117 'bubbles': true, 118 'cancelable': true 119 }); 120 } 121 catch (err) { 122 if (document.createEvent) { 123 // DOM Level 3 for IE 9+ 124 clickEvent = document.createEvent('MouseEvents'); 125 clickEvent.initEvent('click', true, true); 126 } 127 } 128 tgt.dispatchEvent(clickEvent); 129 } 130 131 flag = true; 132 break; 133 134 case this.keyCode.ESC: 135 this.menu.setFocusToController(); 136 this.menu.close(true); 137 flag = true; 138 break; 139 140 case this.keyCode.UP: 141 this.menu.setFocusToPreviousItem(this); 142 flag = true; 143 break; 144 145 case this.keyCode.DOWN: 146 this.menu.setFocusToNextItem(this); 147 flag = true; 148 break; 149 150 case this.keyCode.LEFT: 151 this.menu.setFocusToController('previous', true); 152 this.menu.close(true); 153 flag = true; 154 break; 155 156 case this.keyCode.RIGHT: 157 if (this.popupMenu) { 158 this.popupMenu.open(); 159 this.popupMenu.setFocusToFirstItem(); 160 } 161 else { 162 this.menu.setFocusToController('next', true); 163 this.menu.close(true); 164 } 165 flag = true; 166 break; 167 168 case this.keyCode.HOME: 169 case this.keyCode.PAGEUP: 170 this.menu.setFocusToFirstItem(); 171 flag = true; 172 break; 173 174 case this.keyCode.END: 175 case this.keyCode.PAGEDOWN: 176 this.menu.setFocusToLastItem(); 177 flag = true; 178 break; 179 180 case this.keyCode.TAB: 181 this.menu.setFocusToController(); 182 this.menu.close(true); 183 break; 184 185 default: 186 if (isPrintableCharacter(char)) { 187 this.menu.setFocusByFirstCharacter(this, char); 188 flag = true; 189 } 190 break; 191 } 192 193 if (flag) { 194 event.stopPropagation(); 195 event.preventDefault(); 196 } 197}; 198 199MenuItem.prototype.handleClick = function (event) { 200 this.menu.setFocusToController(); 201 this.menu.close(true); 202}; 203 204MenuItem.prototype.handleFocus = function (event) { 205 this.menu.hasFocus = true; 206 if (!this.menu.controller.isMenubarItem) { 207 this.menu.controller.hasFocus = true; 208 } 209}; 210 211MenuItem.prototype.handleBlur = function (event) { 212 this.menu.hasFocus = false;
213 if (!this.menu.controller.isMenubarItem) { 214 this.menu.controller.hasFocus = false; 215 } 216 setTimeout(this.menu.close.bind(this.menu, false), 300); 217}; 218 219MenuItem.prototype.handleMouseover = function (event) { 220 this.menu.hasHover = true; 221 this.menu.open(); 222 if (this.popupMenu) { 223 this.popupMenu.hasHover = true; 224 this.popupMenu.open(); 225 } 226}; 227 228MenuItem.prototype.handleMouseout = function (event) { 229 if (this.popupMenu) { 230 this.popupMenu.hasHover = false; 231 this.popupMenu.close(true); 232 } 233 234 this.menu.hasHover = false; 235 setTimeout(this.menu.close.bind(this.menu, false), 300); 236};
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.