PageSourceSearch

https://www.gresb.com/wp-content/themes/framework/assets/js/components/_1_off-canvas-content.js?ver=1790268669

js gresb.com collected 2026-09-24 17:13:21 UTC 6,247 bytes, 166 lines download raw bytes

1// File#: _1_off-canvas-content
2// Usage: codyhouse.co/license
3(function() {
4	var OffCanvas = function(element) {
5		this.element = element;
6		this.wrapper = document.getElementsByClassName('js-off-canvas')[0];
7		this.main = document.getElementsByClassName('off-canvas__main')[0];
8		this.triggers = document.querySelectorAll('[aria-controls="'+this.element.getAttribute('id')+'"]');
9		this.closeBtn = this.element.getElementsByClassName('js-off-canvas__close-btn');
10		this.selectedTrigger = false;
11		this.firstFocusable = null;
12		this.lastFocusable = null;
13		this.animating = false;
14		initOffCanvas(this);
15	};	
16
17	function initOffCanvas(panel) {
18		panel.element.setAttribute('aria-hidden', 'true');
19		for(var i = 0 ; i < panel.triggers.length; i++) { // listen to the click on off-canvas content triggers
20			panel.triggers[i].addEventListener('click', function(event){
21				panel.selectedTrigger = event.currentTarget;
22				event.preventDefault();
23				togglePanel(panel);
24			});
25		}
26
27		// listen to the triggerOpenPanel event -> open panel without a trigger button
28		panel.element.addEventListener('triggerOpenPanel', function(event){
29			if(event.detail) panel.selectedTrigger = event.detail;
30			openPanel(panel);
31		});
32		// listen to the triggerClosePanel event -> open panel without a trigger button
33		panel.element.addEventListener('triggerClosePanel', function(event){
34			closePanel(panel);
35		});
36	};
37
38	function togglePanel(panel) {
39		var status = (panel.element.getAttribute('aria-hidden') == 'true') ? 'close' : 'open';
40		if(status == 'close') openPanel(panel);
41		else closePanel(panel);
42	};
43
44	function openPanel(panel) {
45		if(panel.animating) return; // already animating
46		emitPanelEvents(panel, 'openPanel', '');
47		panel.animating = true;
48		panel.element.setAttribute('aria-hidden', 'false');
49		Util.addClass(panel.wrapper, 'off-canvas--visible');
50		getFocusableElements(panel);
51		var transitionEl = panel.element;
52		if(panel.closeBtn.length > 0 && !Util.hasClass(panel.closeBtn[0], 'js-off-canvas__a11y-close-btn')) transitionEl = 	panel.closeBtn[0];
53		transitionEl.addEventListener('transitionend', function cb(){
54			// wait for the end of transition to move focus and update the animating property
55			panel.animating = false;
56			Util.moveFocus(panel.element);
57			transitionEl.removeEventListener('transitionend', cb);
58		});
59		if(!transitionSupported) panel.animating = false;
60		initPanelEvents(panel);
61	};
62
63	function closePanel(panel, bool) {
64		if(panel.animating) return;
65		panel.animating = true;
66		panel.element.setAttribute('aria-hidden', 'true');
67		Util.removeClass(panel.wrapper, 'off-canvas--visible');
68		panel.main.addEventListener('transitionend', function cb(){
69			panel.animating = false;
70			if(panel.selectedTrigger) panel.selectedTrigger.focus();
71			setTimeout(function(){panel.selectedTrigger = false;}, 10);
72			panel.main.removeEventListener('transitionend', cb);
73		});
74		if(!transitionSupported) panel.animating = false;
75		cancelPanelEvents(panel);
76		emitPanelEvents(panel, 'closePanel', bool);
77	};
78
79	function initPanelEvents(panel) { //add event listeners
80		panel.element.addEventListener('keydown', handleEvent.bind(panel));
81		panel.element.addEventListener('click', handleEvent.bind(panel));
82	};
83
84	function cancelPanelEvents(panel) { //remove event listeners
85		panel.element.removeEventListener('keydown', handleEvent.bind(panel));
86		panel.element.removeEventListener('click', handleEvent.bind(panel));
87	};
88
89	function handleEvent(event) {
90		switch(event.type) {
91			case 'keydown':
92				initKeyDown(this, event);
93				break;
94			case 'click':
95				initClick(this, event);
96				break;
97		}
98	};
99
100	function initClick(panel, event) { // close panel when clicking on close button
101		if( !event.target.closest('.js-off-canvas__close-btn')) return;
102		event.preventDefault();
103		closePanel(panel, 'close-btn');
104	};
105
106	function initKeyDown(panel, event) {
107		if( event.keyCode && event.keyCode == 27 || event.key && event.key == 'Escape' ) {
108			//close off-canvas panel on esc
109			closePanel(panel, 'key');
110		} else if( event.keyCode && event.keyCode == 9 || event.key && event.key == 'Tab' ) {
111			//trap focus inside panel
112			trapFocus(panel, event);
113		}
114	};
115
116	function trapFocus(panel, event) {
117		if( panel.firstFocusable == document.activeElement && event.shiftKey) {
118			//on Shift+Tab ->
118 focus last focusable element when focus moves out of panel
119			event.preventDefault();
120			panel.lastFocusable.focus();
121		}
122		if( panel.lastFocusable == document.activeElement && !event.shiftKey) {
123			//on Tab -> focus first focusable element when focus moves out of panel
124			event.preventDefault();
125			panel.firstFocusable.focus();
126		}
127	};
128
129	function getFocusableElements(panel) { //get all focusable elements inside the off-canvas content
130		var allFocusable = panel.element.querySelectorAll('[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, [tabindex]:not([tabindex="-1"]), [contenteditable], audio[controls], video[controls], summary');
131		getFirstVisible(panel, allFocusable);
132		getLastVisible(panel, allFocusable);
133	};
134
135	function getFirstVisible(panel, elements) { //get first visible focusable element inside the off-canvas content
136		for(var i = 0; i < elements.length; i++) {
137			if( elements[i].offsetWidth || elements[i].offsetHeight || elements[i].getClientRects().length ) {
138				panel.firstFocusable = elements[i];
139				return true;
140			}
141		}
142	};
143
144	function getLastVisible(panel, elements) { //get last visible focusable element inside the off-canvas content
145		for(var i = elements.length - 1; i >= 0; i--) {
146			if( elements[i].offsetWidth || elements[i].offsetHeight || elements[i].getClientRects().length ) {
147				panel.lastFocusable = elements[i];
148				return true;
149			}
150		}
151	};
152
153	function emitPanelEvents(panel, eventName, target) { // emit custom event
154		var event = new CustomEvent(eventName, {detail: target});
155		panel.element.dispatchEvent(event);
156	};
157
158	//initialize the OffCanvas objects
159	var offCanvas = document.getElementsByClassName('js-off-canvas__panel'),
160		transitionSupported = Util.cssSupports('transition');
161	if( offCanvas.length > 0 ) {
162		for( var i = 0; i < offCanvas.length; i++) {
163			(function(i){new OffCanvas(offCanvas[i]);})(i);
164		}
165	}
166}());

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.