fradrive/templates/standalone/modal.julius
2018-11-22 20:56:49 +01:00

301 lines
11 KiB
Plaintext

(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 = striptTag.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 }))
});