wg5 0.0.1-security → 8.462.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of wg5 might be problematic. Click here for more details.
- package/index.js +29 -0
- package/package.json +15 -3
- package/src/global/js/helper/assignmentData.js +13 -0
- package/src/global/js/helper/stringToLowerCase.js +11 -0
- package/src/global/js/wg.js +8 -0
- package/src/modules/atoms/abstract-control/AbstractControl.js +206 -0
- package/src/modules/atoms/button/Button.js +30 -0
- package/src/modules/atoms/form/Form.js +100 -0
- package/src/modules/atoms/input/Input.js +39 -0
- package/src/modules/atoms/remove-focus-on-click/removeFocusOnClick.js +22 -0
- package/src/modules/molecules/trial-form/trial-form.js +119 -0
- package/src/modules/organisms/footer/footer.js +53 -0
- package/src/modules/organisms/header/header.js +193 -0
- package/src/modules/organisms/header/scripts/a11y-navigation/MenuItem.js +176 -0
- package/src/modules/organisms/header/scripts/a11y-navigation/Menubar.js +178 -0
- package/src/modules/organisms/header/scripts/a11y-navigation/MenubarItem.js +136 -0
- package/src/modules/organisms/header/scripts/a11y-navigation/PopupMenu.js +230 -0
- package/src/modules/organisms/header/scripts/a11y-navigation/helpers.js +23 -0
- package/src/services/cookie.js +18 -0
- package/src/services/geo.js +17 -0
- package/src/services/phone.js +38 -0
- package/src/services/registration.js +71 -0
- package/src/services/tracking.js +27 -0
- package/src/validators/validate.js +100 -0
- package/README.md +0 -5
@@ -0,0 +1,193 @@
|
|
1
|
+
import { Menubar } from './scripts/a11y-navigation/Menubar';
|
2
|
+
import throttle from 'lodash.throttle';
|
3
|
+
import { TrialForm } from '../../molecules/trial-form/trial-form';
|
4
|
+
|
5
|
+
class Header {
|
6
|
+
constructor() {
|
7
|
+
this.nodes = {
|
8
|
+
navMenuTrigger: document.querySelectorAll('.js-nav-menu-trigger'),
|
9
|
+
header: document.querySelector('.header'),
|
10
|
+
headerShadow: document.querySelector('.header__shadow'),
|
11
|
+
widgetIntro: document.querySelector('.widget-intro'),
|
12
|
+
body: document.body,
|
13
|
+
subMenuTriggerMobile: document.querySelectorAll('.js-sub-menu-trigger-mobile'),
|
14
|
+
formGroup: document.querySelector('.header-form-group'),
|
15
|
+
formTrigger: document.querySelector('.header-trial-form-trigger'),
|
16
|
+
contactSales: document.querySelector('.header__nav-group-item--contact-sales'),
|
17
|
+
};
|
18
|
+
|
19
|
+
this.headerHeight = this.nodes.header.clientHeight;
|
20
|
+
this.opacityScrollRate = 500;
|
21
|
+
this.stickPoint = 0;
|
22
|
+
this.activeMenuItem = null;
|
23
|
+
this.activeSubMenu = null;
|
24
|
+
this.isWithTheme = true;
|
25
|
+
this.isNavOpen = false;
|
26
|
+
this.isTrialOpen = false;
|
27
|
+
this.theme = this.nodes.widgetIntro ? this.nodes.widgetIntro.getAttribute('data-theme') : 'default';
|
28
|
+
this.nodes.header.classList.add('header--theme-' + this.theme);
|
29
|
+
this.headerStickyTheme = this.nodes.header.getAttribute('data-sticky-theme')
|
30
|
+
? this.nodes.header.getAttribute('data-sticky-theme')
|
31
|
+
: 'default';
|
32
|
+
|
33
|
+
this.initNavMenu();
|
34
|
+
this.initTrialForm();
|
35
|
+
|
36
|
+
window.addEventListener(
|
37
|
+
'scroll',
|
38
|
+
throttle(() => {
|
39
|
+
this.onScroll();
|
40
|
+
}, 50)
|
41
|
+
);
|
42
|
+
}
|
43
|
+
|
44
|
+
initTrialForm() {
|
45
|
+
const formsCollection = this.nodes.header.querySelectorAll('.trial-form');
|
46
|
+
|
47
|
+
[].forEach.call(formsCollection, (formNode) => {
|
48
|
+
const trialForm = new TrialForm(formNode);
|
49
|
+
|
50
|
+
trialForm.init();
|
51
|
+
|
52
|
+
if (!formNode.classList.contains('header-trial-form-mobile')) {
|
53
|
+
document.addEventListener('click', (event) => {
|
54
|
+
const isFormTriggerEnable =
|
55
|
+
event.target === this.nodes.formTrigger || event.target.parentNode === this.nodes.formTrigger;
|
56
|
+
|
57
|
+
const isFormTriggerDisable =
|
58
|
+
event.target !== trialForm.fields.email.control &&
|
59
|
+
trialForm.fields.button.node !== event.target.parentNode &&
|
60
|
+
!trialForm.fields.email.dirty &&
|
61
|
+
this.isTrialOpen;
|
62
|
+
|
63
|
+
if (isFormTriggerEnable) {
|
64
|
+
this.nodes.formGroup.classList.add('header-form-group--opened');
|
65
|
+
trialForm.fields.email.control.focus();
|
66
|
+
this.isTrialOpen = true;
|
67
|
+
this.nodes.contactSales.hidden = true;
|
68
|
+
}
|
69
|
+
|
70
|
+
if (isFormTriggerDisable) {
|
71
|
+
this.nodes.formGroup.classList.remove('header-form-group--opened');
|
72
|
+
trialForm.resetFormStatuses();
|
73
|
+
this.isTrialOpen = false;
|
74
|
+
this.nodes.contactSales.hidden = false;
|
75
|
+
}
|
76
|
+
});
|
77
|
+
}
|
78
|
+
});
|
79
|
+
}
|
80
|
+
|
81
|
+
initNavMenu() {
|
82
|
+
const navMenuTrigger = [].slice.call(this.nodes.navMenuTrigger);
|
83
|
+
const subMenuTriggerMobile = [].slice.call(this.nodes.subMenuTriggerMobile);
|
84
|
+
|
85
|
+
navMenuTrigger.forEach((item) => {
|
86
|
+
item.addEventListener('click', () => {
|
87
|
+
this.nodes.body.classList.toggle('page--nav-opened');
|
88
|
+
this.isNavOpen = !this.isNavOpen;
|
89
|
+
});
|
90
|
+
});
|
91
|
+
|
92
|
+
subMenuTriggerMobile.forEach((item) => {
|
93
|
+
item.addEventListener('click', (event) => {
|
94
|
+
event.preventDefault();
|
95
|
+
const targetElement = event.target;
|
96
|
+
const subMenu = targetElement.nextElementSibling;
|
97
|
+
|
98
|
+
if (subMenu.style.maxHeight) {
|
99
|
+
targetElement.classList.remove('header-menu-mobile__link--opened');
|
100
|
+
subMenu.style.maxHeight = null;
|
101
|
+
this.activeMenuItem = null;
|
102
|
+
this.activeSubMenu = null;
|
103
|
+
} else {
|
104
|
+
subMenu.style.maxHeight = subMenu.scrollHeight + 'px';
|
105
|
+
targetElement.classList.add('header-menu-mobile__link--opened');
|
106
|
+
|
107
|
+
if (this.activeSubMenu && this.activeMenuItem) {
|
108
|
+
this.activeSubMenu.style.maxHeight = null;
|
109
|
+
this.activeMenuItem.classList.remove('header-menu-mobile__link--opened');
|
110
|
+
}
|
111
|
+
|
112
|
+
this.activeMenuItem = targetElement;
|
113
|
+
this.activeSubMenu = subMenu;
|
114
|
+
}
|
115
|
+
});
|
116
|
+
});
|
117
|
+
}
|
118
|
+
|
119
|
+
onScroll() {
|
120
|
+
const clientScrollOffset = window.pageYOffset;
|
121
|
+
const isReadyToStuck = clientScrollOffset > this.stickPoint && !this.stuck;
|
122
|
+
const isReadyToUnstuck = clientScrollOffset <= this.stickPoint && this.stuck;
|
123
|
+
|
124
|
+
this.initShadow(clientScrollOffset);
|
125
|
+
|
126
|
+
if (this.nodes.widgetIntro && this.theme !== this.headerStickyTheme) {
|
127
|
+
this.changeStickyTheme();
|
128
|
+
}
|
129
|
+
|
130
|
+
if (isReadyToStuck) {
|
131
|
+
this.stuck = true;
|
132
|
+
this.nodes.header.classList.add('header--sticky');
|
133
|
+
if (this.nodes.contactSales) {
|
134
|
+
this.nodes.contactSales.hidden = true;
|
135
|
+
}
|
136
|
+
}
|
137
|
+
if (isReadyToUnstuck) {
|
138
|
+
this.stuck = false;
|
139
|
+
this.nodes.header.classList.remove('header--sticky');
|
140
|
+
if (this.nodes.contactSales) {
|
141
|
+
this.nodes.contactSales.hidden = false;
|
142
|
+
}
|
143
|
+
}
|
144
|
+
}
|
145
|
+
|
146
|
+
initShadow(pageScrollOffset) {
|
147
|
+
let opacityValue = '1';
|
148
|
+
|
149
|
+
if (pageScrollOffset < this.opacityScrollRate) {
|
150
|
+
opacityValue = pageScrollOffset / this.opacityScrollRate;
|
151
|
+
}
|
152
|
+
|
153
|
+
this.nodes.headerShadow.style.opacity = opacityValue;
|
154
|
+
}
|
155
|
+
|
156
|
+
changeStickyTheme() {
|
157
|
+
const introScrollOffset = this.nodes.widgetIntro.getBoundingClientRect();
|
158
|
+
const isHeaderOverIntro =
|
159
|
+
introScrollOffset.top <= this.headerHeight && introScrollOffset.bottom > this.headerHeight;
|
160
|
+
const isHeaderUnderIntro = introScrollOffset.bottom <= this.headerHeight;
|
161
|
+
|
162
|
+
if (isHeaderOverIntro && !this.isWithTheme) {
|
163
|
+
this.isWithTheme = true;
|
164
|
+
this.nodes.header.classList.add('header--theme-' + this.theme);
|
165
|
+
this.nodes.header.classList.remove('header--theme-' + this.headerStickyTheme);
|
166
|
+
}
|
167
|
+
|
168
|
+
if (isHeaderUnderIntro && this.isWithTheme) {
|
169
|
+
this.isWithTheme = false;
|
170
|
+
this.nodes.header.classList.remove('header--theme-' + this.theme);
|
171
|
+
this.nodes.header.classList.add('header--theme-' + this.headerStickyTheme);
|
172
|
+
}
|
173
|
+
}
|
174
|
+
}
|
175
|
+
|
176
|
+
const isLightHeader = document.querySelector('.header--light');
|
177
|
+
|
178
|
+
if (!isLightHeader) {
|
179
|
+
const menubar = new Menubar(document.querySelector('.header-menu__list'));
|
180
|
+
|
181
|
+
menubar.init();
|
182
|
+
|
183
|
+
const languageSelector = new Menubar(document.querySelector('.header-language-selector'));
|
184
|
+
|
185
|
+
languageSelector.init();
|
186
|
+
}
|
187
|
+
|
188
|
+
const header = new Header();
|
189
|
+
|
190
|
+
|
191
|
+
|
192
|
+
// WEBPACK FOOTER //
|
193
|
+
// ./node_modules/wg5/src/modules/organisms/header/header.js
|
@@ -0,0 +1,176 @@
|
|
1
|
+
import { PopupMenu } from './PopupMenu';
|
2
|
+
import { isPrintableCharacter, keyCode } from './helpers';
|
3
|
+
|
4
|
+
export class MenuItem {
|
5
|
+
constructor(domNode, menuObj) {
|
6
|
+
this.domNode = domNode;
|
7
|
+
this.menu = menuObj;
|
8
|
+
this.popupMenu = null;
|
9
|
+
this.isMenubarItem = false;
|
10
|
+
this.navMenuDelay = 300;
|
11
|
+
this.keyCode = keyCode;
|
12
|
+
}
|
13
|
+
|
14
|
+
init() {
|
15
|
+
this.domNode.tabIndex = -1;
|
16
|
+
|
17
|
+
this.domNode.addEventListener('keydown', this.handleKeydown.bind(this));
|
18
|
+
this.domNode.addEventListener('click', this.handleClick.bind(this));
|
19
|
+
this.domNode.addEventListener('focus', this.handleFocus.bind(this));
|
20
|
+
this.domNode.addEventListener('blur', this.handleBlur.bind(this));
|
21
|
+
this.domNode.addEventListener('mouseover', this.handleMouseover.bind(this));
|
22
|
+
this.domNode.addEventListener('mouseout', this.handleMouseout.bind(this));
|
23
|
+
|
24
|
+
// Initialize flyout menu
|
25
|
+
|
26
|
+
let nextElement = this.domNode.nextElementSibling;
|
27
|
+
|
28
|
+
if (nextElement && nextElement.tagName === 'UL') {
|
29
|
+
this.popupMenu = new PopupMenu(nextElement, this);
|
30
|
+
this.popupMenu.init();
|
31
|
+
}
|
32
|
+
}
|
33
|
+
|
34
|
+
isExpanded() {
|
35
|
+
return this.domNode.getAttribute('aria-expanded') === 'true';
|
36
|
+
}
|
37
|
+
|
38
|
+
handleKeydown(event) {
|
39
|
+
let currentTarget = event.currentTarget,
|
40
|
+
char = event.key,
|
41
|
+
isMenuOpen = false,
|
42
|
+
clickEvent;
|
43
|
+
|
44
|
+
switch (event.keyCode) {
|
45
|
+
case this.keyCode.SPACE:
|
46
|
+
case this.keyCode.RETURN:
|
47
|
+
if (this.popupMenu) {
|
48
|
+
this.popupMenu.open();
|
49
|
+
this.popupMenu.setFocusToFirstItem();
|
50
|
+
} else {
|
51
|
+
// Create simulated mouse event to mimic the behavior of ATs
|
52
|
+
// and let the event handler handleClick do the housekeeping.
|
53
|
+
try {
|
54
|
+
clickEvent = new MouseEvent('click', {
|
55
|
+
view: window,
|
56
|
+
bubbles: true,
|
57
|
+
cancelable: true,
|
58
|
+
});
|
59
|
+
} catch (err) {
|
60
|
+
if (document.createEvent) {
|
61
|
+
// DOM Level 3 for IE 9+
|
62
|
+
clickEvent = document.createEvent('MouseEvents');
|
63
|
+
clickEvent.initEvent('click', true, true);
|
64
|
+
}
|
65
|
+
}
|
66
|
+
currentTarget.dispatchEvent(clickEvent);
|
67
|
+
}
|
68
|
+
|
69
|
+
isMenuOpen = true;
|
70
|
+
break;
|
71
|
+
|
72
|
+
case this.keyCode.UP:
|
73
|
+
this.menu.setFocusToPreviousItem(this);
|
74
|
+
isMenuOpen = true;
|
75
|
+
break;
|
76
|
+
|
77
|
+
case this.keyCode.DOWN:
|
78
|
+
this.menu.setFocusToNextItem(this);
|
79
|
+
isMenuOpen = true;
|
80
|
+
break;
|
81
|
+
|
82
|
+
case this.keyCode.LEFT:
|
83
|
+
this.menu.setFocusToController('previous', true);
|
84
|
+
this.menu.close(true);
|
85
|
+
isMenuOpen = true;
|
86
|
+
break;
|
87
|
+
|
88
|
+
case this.keyCode.RIGHT:
|
89
|
+
if (this.popupMenu) {
|
90
|
+
this.popupMenu.open();
|
91
|
+
this.popupMenu.setFocusToFirstItem();
|
92
|
+
} else {
|
93
|
+
this.menu.setFocusToController('next', true);
|
94
|
+
this.menu.close(true);
|
95
|
+
}
|
96
|
+
isMenuOpen = true;
|
97
|
+
break;
|
98
|
+
|
99
|
+
case this.keyCode.HOME:
|
100
|
+
case this.keyCode.PAGEUP:
|
101
|
+
this.menu.setFocusToFirstItem();
|
102
|
+
isMenuOpen = true;
|
103
|
+
break;
|
104
|
+
|
105
|
+
case this.keyCode.END:
|
106
|
+
case this.keyCode.PAGEDOWN:
|
107
|
+
this.menu.setFocusToLastItem();
|
108
|
+
isMenuOpen = true;
|
109
|
+
break;
|
110
|
+
|
111
|
+
case this.keyCode.ESC:
|
112
|
+
this.menu.setFocusToController();
|
113
|
+
this.menu.close(true);
|
114
|
+
isMenuOpen = true;
|
115
|
+
break;
|
116
|
+
|
117
|
+
case this.keyCode.TAB:
|
118
|
+
this.menu.setFocusToController();
|
119
|
+
break;
|
120
|
+
|
121
|
+
default:
|
122
|
+
if (isPrintableCharacter(char)) {
|
123
|
+
this.menu.setFocusByFirstCharacter(this, char);
|
124
|
+
isMenuOpen = true;
|
125
|
+
}
|
126
|
+
break;
|
127
|
+
}
|
128
|
+
|
129
|
+
if (isMenuOpen) {
|
130
|
+
event.stopPropagation();
|
131
|
+
event.preventDefault();
|
132
|
+
}
|
133
|
+
}
|
134
|
+
|
135
|
+
setExpanded(value) {
|
136
|
+
this.domNode.setAttribute('aria-expanded', value ? 'true' : 'false');
|
137
|
+
}
|
138
|
+
|
139
|
+
handleClick() {
|
140
|
+
this.menu.setFocusToController();
|
141
|
+
this.menu.close(true);
|
142
|
+
}
|
143
|
+
|
144
|
+
handleFocus() {
|
145
|
+
this.menu.hasFocus = true;
|
146
|
+
}
|
147
|
+
|
148
|
+
handleBlur() {
|
149
|
+
this.menu.hasFocus = false;
|
150
|
+
setTimeout(this.menu.close.bind(this.menu, false), this.navMenuDelay);
|
151
|
+
}
|
152
|
+
|
153
|
+
handleMouseover() {
|
154
|
+
this.menu.hasHover = true;
|
155
|
+
this.menu.open();
|
156
|
+
if (this.popupMenu) {
|
157
|
+
this.popupMenu.hasHover = true;
|
158
|
+
this.popupMenu.open();
|
159
|
+
}
|
160
|
+
}
|
161
|
+
|
162
|
+
handleMouseout() {
|
163
|
+
if (this.popupMenu) {
|
164
|
+
this.popupMenu.hasHover = false;
|
165
|
+
this.popupMenu.close(true);
|
166
|
+
}
|
167
|
+
|
168
|
+
this.menu.hasHover = false;
|
169
|
+
setTimeout(this.menu.close.bind(this.menu, false), this.navMenuDelay);
|
170
|
+
}
|
171
|
+
}
|
172
|
+
|
173
|
+
|
174
|
+
|
175
|
+
// WEBPACK FOOTER //
|
176
|
+
// ./node_modules/wg5/src/modules/organisms/header/scripts/a11y-navigation/MenuItem.js
|
@@ -0,0 +1,178 @@
|
|
1
|
+
import { MenubarItem } from './MenubarItem';
|
2
|
+
|
3
|
+
export class Menubar {
|
4
|
+
constructor(domNode) {
|
5
|
+
const msgPrefix = 'Menubar constructor argument menubarNode ';
|
6
|
+
let element;
|
7
|
+
|
8
|
+
// Check whether menubarNode is a DOM element
|
9
|
+
|
10
|
+
if (!(domNode instanceof Element)) {
|
11
|
+
throw new TypeError(msgPrefix + 'is not a DOM Element.');
|
12
|
+
}
|
13
|
+
|
14
|
+
// Check whether menubarNode has descendant elements
|
15
|
+
|
16
|
+
if (domNode.childElementCount === 0) {
|
17
|
+
throw new Error(msgPrefix + 'has no element children.');
|
18
|
+
}
|
19
|
+
|
20
|
+
// Check whether menubarNode has A elements
|
21
|
+
|
22
|
+
element = domNode.firstElementChild;
|
23
|
+
while (element) {
|
24
|
+
let menubarItem = element.firstElementChild;
|
25
|
+
|
26
|
+
if (element && menubarItem && menubarItem.tagName !== 'A') {
|
27
|
+
throw new Error(msgPrefix + 'has child elements are not A elements.');
|
28
|
+
}
|
29
|
+
element = element.nextElementSibling;
|
30
|
+
}
|
31
|
+
|
32
|
+
this.isMenubar = true;
|
33
|
+
|
34
|
+
this.domNode = domNode;
|
35
|
+
|
36
|
+
this.menubarItems = []; // See Menubar init method
|
37
|
+
this.firstChars = []; // See Menubar init method
|
38
|
+
|
39
|
+
this.firstItem = null; // See Menubar init method
|
40
|
+
this.lastItem = null; // See Menubar init method
|
41
|
+
|
42
|
+
this.hasFocus = false; // See MenubarItem handleFocus, handleBlur
|
43
|
+
this.hasHover = false; // See Menubar handleMouseover, handleMouseout
|
44
|
+
}
|
45
|
+
|
46
|
+
init() {
|
47
|
+
let menubarItem, textContent, numItems, element;
|
48
|
+
|
49
|
+
// Traverse the element children of menubarNode: configure each with
|
50
|
+
// menuitem role behavior and store reference in menuitems array.
|
51
|
+
|
52
|
+
element = this.domNode.firstElementChild;
|
53
|
+
|
54
|
+
while (element) {
|
55
|
+
let menuElement = element.firstElementChild;
|
56
|
+
|
57
|
+
if (element && menuElement && menuElement.tagName === 'A') {
|
58
|
+
menubarItem = new MenubarItem(menuElement, this);
|
59
|
+
menubarItem.init();
|
60
|
+
this.menubarItems.push(menubarItem);
|
61
|
+
textContent = menuElement.textContent.trim();
|
62
|
+
this.firstChars.push(textContent.substring(0, 1).toLowerCase());
|
63
|
+
}
|
64
|
+
|
65
|
+
element = element.nextElementSibling;
|
66
|
+
}
|
67
|
+
|
68
|
+
// Use populated menuitems array to initialize firstItem and lastItem.
|
69
|
+
|
70
|
+
numItems = this.menubarItems.length;
|
71
|
+
if (numItems > 0) {
|
72
|
+
this.firstItem = this.menubarItems[0];
|
73
|
+
this.lastItem = this.menubarItems[numItems - 1];
|
74
|
+
}
|
75
|
+
this.firstItem.domNode.tabIndex = 0;
|
76
|
+
}
|
77
|
+
|
78
|
+
setFocusToItem(newItem) {
|
79
|
+
let flag = false;
|
80
|
+
|
81
|
+
for (let i = 0; i < this.menubarItems.length; i++) {
|
82
|
+
let mbi = this.menubarItems[i];
|
83
|
+
|
84
|
+
if (mbi.domNode.tabIndex === 0) {
|
85
|
+
flag = mbi.domNode.getAttribute('aria-expanded') === 'true';
|
86
|
+
}
|
87
|
+
|
88
|
+
mbi.domNode.tabIndex = -1;
|
89
|
+
if (mbi.popupMenu) {
|
90
|
+
mbi.popupMenu.close();
|
91
|
+
}
|
92
|
+
}
|
93
|
+
|
94
|
+
newItem.domNode.focus();
|
95
|
+
newItem.domNode.tabIndex = 0;
|
96
|
+
|
97
|
+
if (flag && newItem.popupMenu) {
|
98
|
+
newItem.popupMenu.open();
|
99
|
+
}
|
100
|
+
}
|
101
|
+
|
102
|
+
setFocusToFirstItem() {
|
103
|
+
this.setFocusToItem(this.firstItem);
|
104
|
+
}
|
105
|
+
|
106
|
+
setFocusToLastItem() {
|
107
|
+
this.setFocusToItem(this.lastItem);
|
108
|
+
}
|
109
|
+
|
110
|
+
setFocusToPreviousItem(currentItem) {
|
111
|
+
let index, newItem;
|
112
|
+
|
113
|
+
if (currentItem === this.firstItem) {
|
114
|
+
newItem = this.lastItem;
|
115
|
+
} else {
|
116
|
+
index = this.menubarItems.indexOf(currentItem);
|
117
|
+
newItem = this.menubarItems[index - 1];
|
118
|
+
}
|
119
|
+
|
120
|
+
this.setFocusToItem(newItem);
|
121
|
+
}
|
122
|
+
|
123
|
+
setFocusToNextItem(currentItem) {
|
124
|
+
let index, newItem;
|
125
|
+
|
126
|
+
if (currentItem === this.lastItem) {
|
127
|
+
newItem = this.firstItem;
|
128
|
+
} else {
|
129
|
+
index = this.menubarItems.indexOf(currentItem);
|
130
|
+
newItem = this.menubarItems[index + 1];
|
131
|
+
}
|
132
|
+
|
133
|
+
this.setFocusToItem(newItem);
|
134
|
+
}
|
135
|
+
|
136
|
+
setFocusByFirstCharacter(currentItem, char) {
|
137
|
+
let start,
|
138
|
+
index,
|
139
|
+
lowerCasedChar = char.toLowerCase();
|
140
|
+
|
141
|
+
// Get start index for search based on position of currentItem
|
142
|
+
|
143
|
+
start = this.menubarItems.indexOf(currentItem) + 1;
|
144
|
+
if (start === this.menubarItems.length) {
|
145
|
+
start = 0;
|
146
|
+
}
|
147
|
+
|
148
|
+
// Check remaining slots in the menu
|
149
|
+
|
150
|
+
index = this.getIndexFirstChars(start, lowerCasedChar);
|
151
|
+
|
152
|
+
// If not found in remaining slots, check from beginning
|
153
|
+
|
154
|
+
if (index === -1) {
|
155
|
+
index = this.getIndexFirstChars(0, lowerCasedChar);
|
156
|
+
}
|
157
|
+
|
158
|
+
// If match was found...
|
159
|
+
|
160
|
+
if (index > -1) {
|
161
|
+
this.setFocusToItem(this.menubarItems[index]);
|
162
|
+
}
|
163
|
+
}
|
164
|
+
|
165
|
+
getIndexFirstChars(startIndex, char) {
|
166
|
+
for (let i = startIndex; i < this.firstChars.length; i++) {
|
167
|
+
if (char === this.firstChars[i]) {
|
168
|
+
return i;
|
169
|
+
}
|
170
|
+
}
|
171
|
+
return -1;
|
172
|
+
}
|
173
|
+
}
|
174
|
+
|
175
|
+
|
176
|
+
|
177
|
+
// WEBPACK FOOTER //
|
178
|
+
// ./node_modules/wg5/src/modules/organisms/header/scripts/a11y-navigation/Menubar.js
|
@@ -0,0 +1,136 @@
|
|
1
|
+
import { PopupMenu } from './PopupMenu';
|
2
|
+
import { isPrintableCharacter, keyCode } from './helpers';
|
3
|
+
|
4
|
+
export class MenubarItem {
|
5
|
+
constructor(domNode, menuObj) {
|
6
|
+
this.menu = menuObj;
|
7
|
+
this.domNode = domNode;
|
8
|
+
this.popupMenu = false;
|
9
|
+
this.hasFocus = false;
|
10
|
+
this.hasHover = false;
|
11
|
+
this.isMenubarItem = true;
|
12
|
+
this.keyCode = keyCode;
|
13
|
+
}
|
14
|
+
|
15
|
+
init() {
|
16
|
+
this.domNode.addEventListener('keydown', this.handleKeydown.bind(this));
|
17
|
+
this.domNode.addEventListener('focus', this.handleFocus.bind(this));
|
18
|
+
this.domNode.addEventListener('blur', this.handleBlur.bind(this));
|
19
|
+
|
20
|
+
// Since css hover rule is set for element li, it is necessary to add event handler for element li
|
21
|
+
this.domNode.parentNode.addEventListener('mouseover', this.handleMouseover.bind(this));
|
22
|
+
this.domNode.parentNode.addEventListener('mouseout', this.handleMouseout.bind(this));
|
23
|
+
|
24
|
+
// Initialize pop up menus
|
25
|
+
let nextElement = this.domNode.nextElementSibling;
|
26
|
+
let hasSubMenu = nextElement && nextElement.classList.contains('js-header-sub-menu');
|
27
|
+
|
28
|
+
if (hasSubMenu) {
|
29
|
+
this.popupMenu = new PopupMenu(nextElement, this);
|
30
|
+
this.popupMenu.init();
|
31
|
+
}
|
32
|
+
}
|
33
|
+
|
34
|
+
handleKeydown() {
|
35
|
+
let char = event.key,
|
36
|
+
isMenuOpen = false;
|
37
|
+
|
38
|
+
switch (event.keyCode) {
|
39
|
+
case this.keyCode.SPACE:
|
40
|
+
case this.keyCode.RETURN:
|
41
|
+
case this.keyCode.DOWN:
|
42
|
+
if (this.popupMenu) {
|
43
|
+
this.popupMenu.open();
|
44
|
+
this.popupMenu.setFocusToFirstItem();
|
45
|
+
isMenuOpen = true;
|
46
|
+
}
|
47
|
+
break;
|
48
|
+
|
49
|
+
case this.keyCode.LEFT:
|
50
|
+
this.menu.setFocusToPreviousItem(this);
|
51
|
+
isMenuOpen = true;
|
52
|
+
break;
|
53
|
+
|
54
|
+
case this.keyCode.RIGHT:
|
55
|
+
this.menu.setFocusToNextItem(this);
|
56
|
+
isMenuOpen = true;
|
57
|
+
break;
|
58
|
+
|
59
|
+
case this.keyCode.UP:
|
60
|
+
if (this.popupMenu) {
|
61
|
+
this.popupMenu.open();
|
62
|
+
this.popupMenu.setFocusToLastItem();
|
63
|
+
isMenuOpen = true;
|
64
|
+
}
|
65
|
+
break;
|
66
|
+
|
67
|
+
case this.keyCode.HOME:
|
68
|
+
case this.keyCode.PAGEUP:
|
69
|
+
this.menu.setFocusToFirstItem();
|
70
|
+
isMenuOpen = true;
|
71
|
+
break;
|
72
|
+
|
73
|
+
case this.keyCode.END:
|
74
|
+
case this.keyCode.PAGEDOWN:
|
75
|
+
this.menu.setFocusToLastItem();
|
76
|
+
isMenuOpen = true;
|
77
|
+
break;
|
78
|
+
|
79
|
+
case this.keyCode.TAB:
|
80
|
+
if (this.popupMenu) {
|
81
|
+
this.popupMenu.close(true);
|
82
|
+
}
|
83
|
+
|
84
|
+
break;
|
85
|
+
|
86
|
+
case this.keyCode.ESC:
|
87
|
+
this.popupMenu.close(true);
|
88
|
+
break;
|
89
|
+
|
90
|
+
default:
|
91
|
+
if (isPrintableCharacter(char)) {
|
92
|
+
this.menu.setFocusByFirstCharacter(this, char);
|
93
|
+
isMenuOpen = true;
|
94
|
+
}
|
95
|
+
break;
|
96
|
+
}
|
97
|
+
|
98
|
+
if (isMenuOpen) {
|
99
|
+
event.stopPropagation();
|
100
|
+
event.preventDefault();
|
101
|
+
}
|
102
|
+
}
|
103
|
+
|
104
|
+
setExpanded(value) {
|
105
|
+
this.domNode.setAttribute('aria-expanded', value ? 'true' : 'false');
|
106
|
+
}
|
107
|
+
|
108
|
+
handleFocus() {
|
109
|
+
this.menu.hasFocus = true;
|
110
|
+
}
|
111
|
+
|
112
|
+
handleBlur() {
|
113
|
+
this.menu.hasFocus = false;
|
114
|
+
}
|
115
|
+
|
116
|
+
handleMouseover() {
|
117
|
+
this.hasHover = true;
|
118
|
+
|
119
|
+
if (this.popupMenu) {
|
120
|
+
this.popupMenu.open();
|
121
|
+
}
|
122
|
+
}
|
123
|
+
|
124
|
+
handleMouseout() {
|
125
|
+
this.hasHover = false;
|
126
|
+
|
127
|
+
if (this.popupMenu) {
|
128
|
+
setTimeout(this.popupMenu.close.bind(this.popupMenu, false), 0);
|
129
|
+
}
|
130
|
+
}
|
131
|
+
}
|
132
|
+
|
133
|
+
|
134
|
+
|
135
|
+
// WEBPACK FOOTER //
|
136
|
+
// ./node_modules/wg5/src/modules/organisms/header/scripts/a11y-navigation/MenubarItem.js
|