const toggleBackgroundElements = (hide) => { $('main, header, footer, [data-aria-hide-on-modal]').attr('aria-hidden', hide ? 'true' : 'false'); }; const trapFocus = (modal) => { const focusableElements = modal.find('a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])').get(); const firstElement = focusableElements[0]; const lastElement = focusableElements[focusableElements.length - 1]; modal.on('keydown', function (e) { if (e.key === 'Tab') { if (e.shiftKey) { // Shift+Tab if (document.activeElement === firstElement) { e.preventDefault(); lastElement.focus(); } } else { // Tab if (document.activeElement === lastElement) { e.preventDefault(); firstElement.focus(); } } } }); }; wpj.focus = { focusName: ADMIN ? "focusAdmin" : "focus", create: (options) => { let focusName = ADMIN ? "focusAdmin" : "focus"; let $wpjFocuses = wpj.focus.getFocuses(); const template = $(wpj.focus.getFocusLoadingTemplate()); $wpjFocuses.append(template); const $focus = template[focusName](options); $focus[focusName]('load'); return $focus[focusName]('instance'); }, createDeduplicated: (endpoint, ajaxSubmit = false, removeOnClose = false, focusCustomClass = null, focusTriggerEventName = null) => { // zaridim, ze existuje wrapper, ve kterem budu focusy vytvaret let $wpjFocuses = wpj.focus.getFocuses(); let focusName = ADMIN ? "focusAdmin" : "focus"; // pokud jsem focus uz jednou vytvoril, tak otevru ten uz vytvoreny const $existingFocus = $wpjFocuses.find('.focus[data-focus-endpoint="'+ endpoint +'"]'); if ($existingFocus.length) { $existingFocus[focusName]('show'); return; } // pridam novy focus s vychozi loading sablonou $wpjFocuses.append( wpj.focus.getFocusLoadingTemplate(endpoint) ); // inicializuju focus a zavolam na nem load, aby se naloadoval const $focus = $wpjFocuses.find('.focus[data-focus-endpoint="'+ endpoint +'"]')[focusName]({ opened: true, onDemandEndpoint: endpoint, ajaxSubmit: ajaxSubmit, removeOnClose: removeOnClose, focusCustomClass: focusCustomClass, focusTriggerEventName: focusTriggerEventName }); $focus[focusName]('load'); }, getFocusLoadingTemplate: (endpoint) => { if (endpoint){ return '
'; } return '
'; }, getFocuses: () => { let $wpjFocuses = $('[data-wpj-focuses]'); if (!$wpjFocuses.length) { // vytvorim si wrapper $('body').append('
'); $wpjFocuses = $('[data-wpj-focuses]'); } return $wpjFocuses; } }; $.widget("wpj." + wpj.focus.focusName, { options: { opened: false, closeOnBgClick: true, addBodyPadding: false, onDemandEndpoint: null, ajaxSubmit: false, removeOnClose: false, show: null, hide: null, }, _create: function() { var me = this; this.element.on('click', '[data-focus=close]', function() { return me.hide(); }); this._super(); if (this.options.opened) { this.show(true); } }, toggle: function(show) { if (typeof show === 'undefined') { show = !this.element.is('.active'); } return show ? this.show() : this.hide(); }, show: function(immediate) { this.element.addClass('active').attr('tabindex', '-1').focus(); const focusableElements = this.element.find('a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])'); var me = this, $body = $('body'); if (this.options.addBodyPadding && wpj.domUtils.getScrollbarWidth()) { this.element.add('body').css('padding-right', wpj.domUtils.getScrollbarWidth()); } $body .on('mousedown.focus', function(e) { // pac-item => results in googlemaps input autocomplete if (me.element.has(e.target).length || !me.options.closeOnBgClick || $(e.target).hasClass('pac-item') || $(e.target).hasClass('pac-item-query') || $(e.target).parents('.pac-item').length) { return; } me.hide(); }) .on('keydown.focus', function(e) { if (e.key !== 'Escape') { return true; } me.hide(); return false; }) .removeClass('focus-closed') .addClass('focus-opened'); toggleBackgroundElements(true); trapFocus(this.element); setTimeout(() => { const firstFocusable = focusableElements.length > 0 ? focusableElements.get(0) : this.element.get(0); if (firstFocusable && typeof firstFocusable.focus === 'function') firstFocusable.focus(); }, 50); if (!immediate) { $body.addClass('focus-transition'); } if (this.options.ajaxSubmit) { this.registerAjaxSubmit(); } this._trigger( "show" ); return false; }, hide: function() { this.element.removeClass('active'); $('body') .off('mousedown.focus') .off('keydown.focus') .removeClass('focus-opened') .addClass('focus-closed'); if (this.options.addBodyPadding && wpj.domUtils.getScrollbarWidth()) { this.element.add('body').css('padding-right', 0); } this.element.trigger('focusClosed'); toggleBackgroundElements(false); const lastFocused = this.element.data('last-focused'); if (lastFocused && typeof lastFocused.focus === 'function') { lastFocused.focus(); } else { document.body.focus(); } if (this.options.ajaxSubmit) { this.element.off('submit', 'form'); } if (this.options.removeOnClose) { this.element.remove(); } this._trigger( "hide" ); return false; }, load: function () { if (!this.options.onDemandEndpoint) { return; } const me = this; if (this.options.focusCustomClass) { me.element.addClass(this.options.focusCustomClass);//('focus-delivery-widgets'); } // Handle callback if (typeof this.options.onDemandEndpoint === "function") { this.options.onDemandEndpoint(me.element.find('.focus-content')[0]); return; } // Handle global function if (typeof window[this.options.onDemandEndpoint] === "function") { window[this.options.onDemandEndpoint](me.element.find('.focus-content')[0]); return; } // nacteni ondemand focusu fetch(this.options.onDemandEndpoint) .then((response) => response.text()) .then((response) => { const focusClasses = [...$(response).prop('classList'), ...['active']]; // replacnu obsah focusu naloadovanym obsahem me.element.html($(response).html()); // nastavim focusu classy podle nacteneho focusu me.element.attr('class', focusClasses.join(' ')) if (me.options.focusTriggerEventName) { $('body').trigger(me.options.focusTriggerEventName, me); } }); }, registerAjaxSubmit: function () { const me = this; this.element.on('submit', 'form', function (e) { e.preventDefault(); const $this = $(e.target); const data = $this.serialize(); const url = $this.attr('action'); wpj.domUtils.reloadPartsFromUrl(url, me.element.find('[data-reload]'), data); }); this.element.on('click', '[data-ondemand]', function(e) { const $this = $(e.target); const url = $this.attr('href'); wpj.domUtils.reloadPartsFromUrl(url, me.element.find('[data-reload]'), undefined, () => {}); return false; }); } }); $(document).on('click', '[data-wpj-focus]', function () { const endpoint = $(this).data('wpj-focus'); const ajaxSubmit = $(this).data('wpj-focus-ajax'); const removeOnClose = $(this).data('wpj-focus-remove-on-close'); const focusCustomClass = $(this).data('wpj-focus-custom-class'); const focusTriggerEventName = $(this).data('wpj-focus-trigger-event-name'); if (endpoint) { wpj.focus.createDeduplicated(endpoint, !!ajaxSubmit, removeOnClose, focusCustomClass, focusTriggerEventName); } return false; }); // Pro nacteni fallback focusu pri nacteni stranky (nikotino - vyber prodejny) if (MODULES.COMPONENTS) { window.dispatchEvent(new Event("wpjFocusLoaded")); }