(function() { 'use strict'; window.utils = window.utils || {}; window.utils.modal = function(modal) { var overlay = document.createElement('div'); var closer = document.createElement('div'); var trigger = document.querySelector('#' + modal.dataset.trigger); // var origParent = modal.parentNode; function open(event) { if (!modal.classList.contains('js-modal-initialized')) { return; } // disable modals for narrow screens if (event) { event.preventDefault(); } modal.classList.add('modal--open'); overlay.classList.add('modal__overlay'); // document.body.insertBefore(modal, null); document.body.insertBefore(overlay, modal); overlay.classList.add('modal__overlay--open'); if ('closeable' in modal.dataset) { closer.classList.add('modal__closer'); modal.insertBefore(closer, null); closer.addEventListener('click', close, false); overlay.addEventListener('click', close, false); } } // you can open this modal via event // example: document.dispatchEvent(new CustomEvent('modal-open', { details: { for: 'modal-[id]' }})) function openOnEvent(event) { if (event.detail.for === modal.getAttribute('id')) { open(); } } function close(event) { overlay.remove(); // origParent.insertBefore(modal, null); modal.classList.remove('modal--open'); closer.removeEventListener('click', close, false); }; function setup() { document.body.insertBefore(modal, null); // every modal can be openend via document-wide event, see openOnEvent document.addEventListener('modal-open', openOnEvent, false); if ('dynamic' in modal.dataset) { function fetchModal(url, init) { function responseHtml(body) { var modalContent = document.createElement('div'); modalContent.innerHTML = body; var contentBody = modalContent.querySelector('.main__content-body'); var scriptTags = []; if (contentBody) { modalContent.querySelectorAll('script').forEach(function(scriptTag) { var existsAlready = Array.from(document.body.querySelectorAll('script')).some(function(haystack) { if (haystack.text === scriptTag.text && haystack.getAttribute('src') === scriptTag.getAttribute('src')) { scriptTags.push(haystack); return true; } else { return false; } }); if (existsAlready) return; var scriptClone = document.createElement('script'); if (scriptTag.text) scriptClone.text = scriptTag.text; if (scriptTag.hasAttributes()) { var attrs = scriptTag.attributes; for (var i = attrs.length - 1; i >= 0; i--) { scriptClone.setAttribute(attrs[i].name, attrs[i].value); } } document.body.insertBefore(scriptClone, null); scriptTags.push(scriptClone); }); modalContent.querySelectorAll('style').forEach(function(styleTag) { if (Array.from(document.head.querySelectorAll('style')).some(function(haystack) { return haystack.innerText === styleTag.innerText; })) { return } document.head.insertBefore(styleTag, null); }); modalContent.querySelectorAll('link').forEach(function(linkTag) { if (linkTag.getAttribute('rel') !== 'stylesheet') return; if (Array.from(document.head.querySelectorAll('link')).some(function(haystack) { return haystack.getAttribute('href') === linkTag.getAttribute('href'); })) { return } document.head.insertBefore(linkTag, null); }); var modalAlertsEl = modalContent.querySelector('#alerts'); var alertsEl = document.body.querySelector('#alerts'); if (alertsEl && modalAlertsEl) { var modalAlerts = Array.from(modalAlertsEl.childNodes); modalAlerts.forEach(function(alertEl) { alertsEl.insertBefore(alertEl, alertsEl.querySelector('.alerts__toggler')); }); if (modalAlerts.length !== 0) document.dispatchEvent(new CustomEvent('setup', { detail: { scope: alertsEl } })); contentBody.removeChild(modalAlertsEl); } modalContent = contentBody; } modalContent.classList.add('modal__content'); var nudgeAttr = function(attr, x) { var oldVal = x.getAttribute(attr); var newVal = modal.getAttribute('id') + '__' + oldVal; // console.log(oldVal, newVal); x.setAttribute(attr, newVal); }; var idAttrs = ['id', 'for', 'data-conditional-id']; idAttrs.map(function(attr) { modalContent.querySelectorAll('[' + attr + ']').forEach(function(x) { nudgeAttr(attr, x); }); }); modal.querySelectorAll('.modal__content').forEach(function(prev) { modal.removeChild(prev); }); modal.insertBefore(modalContent, null); var triggerContentLoad = function() { console.log('contentReady', modal); document.dispatchEvent(new CustomEvent('setup', { detail: { scope: modal }, bubbles: true, cancelable: true })); } scriptTags.forEach(function(t) { t.addEventListener('load', triggerContentLoad); }); triggerContentLoad(); return 'html'; } function responseJson(data) { var alertsEl = document.querySelector('#alerts'); if (!alertsEl) return null; for (var i = 0; i < data.length; i++) { var alert = document.createElement('div'); alert.classList.add('alert', 'alert-' + data[i].class); var alertContent = document.createElement('div'); alertContent.classList.add('alert__content'); alertContent.innerHTML = data[i].content; alert.appendChild(alertContent); alertsEl.insertBefore(alert, alertsEl.querySelector('.alerts__toggler')); } document.dispatchEvent(new CustomEvent('setup', { detail: { scope: alertsEl }, bubbles: true, cancelable: true })); return 'json'; } return fetch(url, init).then(function(response) { var contentType = response.headers.get('Content-Type') if (contentType && contentType.includes('text/html')) { return response.text().then(responseHtml); } else if (contentType && contentType.includes('application/json')) { return response.json().then(responseJson); } else { console.log(response); return null; } }); }; modal.addEventListener('modal-fetch', function(event) { var dynamicContentURL = (event.detail && event.detail.url) || trigger.getAttribute('href'); var fetchInit = (event.detail && event.detail.init) || { credentials: 'same-origin', headers: { #{String (toPathPiece HeaderIsModal)}: 'True' } }; if (dynamicContentURL.length > 0) { fetchModal(dynamicContentURL, fetchInit).then((event.detail && event.detail.then) || (function(){})); } }); modal.dispatchEvent(new CustomEvent('modal-fetch', { detail: { then: (function() { if (!trigger) return; trigger.classList.add('modal__trigger'); trigger.addEventListener('click', open, false); }) } })); } else if (trigger) { // if modal has trigger assigned to it open modal on click trigger.classList.add('modal__trigger'); trigger.addEventListener('click', open, false); } // tell further modals, that this one already got initialized modal.classList.add('js-modal-initialized'); modal.addEventListener('modal-close', close); } setup(); }; window.utils.ajaxSubmit = function(modal, form) { function doSubmit(event) { event.preventDefault(); var modalContent = modal.querySelector('.modal__content'); modalContent.style.pointerEvents = 'none'; modalContent.style.opacity = 0.5; modal.dispatchEvent(new CustomEvent('modal-fetch', { detail: { url: form.target, init: { credentials: 'same-origin', headers: { #{String (toPathPiece HeaderIsModal)}: 'True' }, method: form.method, body: new FormData(form) }, then: (function(typ) { modal.dispatchEvent(new CustomEvent('modal-close')); modalContent.style.pointerEvents = 'auto'; modalContent.style.opacity = 1; if (typ === 'json') { modal.dispatchEvent(new CustomEvent('modal-fetch')); } }) }, bubbles: true, cancelable: true })); }; form.addEventListener('submit', doSubmit); form.classList.add('js-ajax-initialized'); }; })(); document.addEventListener('setup', function(e) { if (e.detail.module && e.detail.module !== 'modal') return; Array.from(e.detail.scope.querySelectorAll('.js-modal:not(.js-modal-initialized)')).forEach(function(modal) { window.utils.modal(modal); }); if (e.detail.scope.classList.contains('js-modal')) { Array.from(e.detail.scope.querySelectorAll('form[data-ajax-submit]:not(.js-ajax-initialized)')).forEach(function(form) { window.utils.ajaxSubmit(e.detail.scope, form); }); } else { Array.from(e.detail.scope.querySelectorAll('.js-modal')).map(function(modal) { Array.from(modal.querySelectorAll('form[data-ajax-submit]:not(.js-ajax-initialized)')).forEach(function(form) { window.utils.ajaxSubmit(modal, form); }); }); }; }, false); document.addEventListener('DOMContentLoaded', function() { document.dispatchEvent(new CustomEvent('setup', { detail: { scope: document.body, module: 'modal' }, bubbles: true, cancelable: true })) });