289 lines
9.2 KiB
JavaScript
289 lines
9.2 KiB
JavaScript
/**
|
|
* SPDX-FileCopyrightText: 2023 jordi fita mas <jordi@tandem.blog>
|
|
* SPDX-License-Identifier: AGPL-3.0-only
|
|
*/
|
|
|
|
function ready(fn) {
|
|
if (document.readyState !== 'loading') {
|
|
fn();
|
|
} else {
|
|
document.addEventListener('DOMContentLoaded', fn);
|
|
}
|
|
}
|
|
|
|
ready(function () {
|
|
const snackBar = Object.assign(document.body.appendChild(document.createElement('section')), {
|
|
id: 'snackbar',
|
|
});
|
|
|
|
const errorMessage = snackBar.appendChild(document.createElement('div'));
|
|
errorMessage.setAttribute('role', 'alert');
|
|
|
|
const openClass = 'open';
|
|
const toasts = [];
|
|
let timeoutId = null;
|
|
|
|
function showError(message) {
|
|
toasts.push(message);
|
|
popUp();
|
|
}
|
|
|
|
function popUp() {
|
|
if (toasts.length === 0) {
|
|
return;
|
|
}
|
|
if (errorMessage.classList.contains(openClass)) {
|
|
dismiss();
|
|
return;
|
|
}
|
|
if (errorMessage.innerText !== "") {
|
|
// it will show after remove calls popUp again.
|
|
return;
|
|
}
|
|
errorMessage.innerText = toasts[0];
|
|
errorMessage.classList.add(openClass);
|
|
timeoutId = setTimeout(dismiss, 4000);
|
|
}
|
|
|
|
function dismiss() {
|
|
if (!errorMessage.classList.contains(openClass)) {
|
|
// already dismissed
|
|
return;
|
|
}
|
|
errorMessage.classList.remove(openClass);
|
|
clearTimeout(timeoutId);
|
|
timeoutId = setTimeout(remove, 350);
|
|
}
|
|
|
|
function remove() {
|
|
clearTimeout(timeoutId);
|
|
toasts.splice(0, 1);
|
|
errorMessage.innerText = "";
|
|
popUp();
|
|
}
|
|
|
|
document.body.addEventListener('htmx:error', function (evt) {
|
|
const errorInfo = evt.detail.errorInfo;
|
|
const error = errorInfo.xhr && errorInfo.xhr.responseText || errorInfo.error;
|
|
showError(error);
|
|
});
|
|
})
|
|
|
|
ready(function () {
|
|
const textareas = document.querySelectorAll('textarea.html');
|
|
if (textareas.length > 0) {
|
|
const language = document.documentElement.getAttribute('lang');
|
|
const csrfHeader = JSON.parse(document.querySelector('meta[name="csrf-header"]').getAttribute('content'));
|
|
const script = document.head.appendChild(Object.assign(document.createElement('script'), {
|
|
src: '/static/ckeditor5@40.2.0/ckeditor.js',
|
|
}));
|
|
if (language !== 'en') {
|
|
document.head.appendChild(Object.assign(document.createElement('script'), {
|
|
src: '/static/ckeditor5@40.2.0/translations/' + language + '.js',
|
|
}));
|
|
}
|
|
const editorConfig = {
|
|
language,
|
|
image: {
|
|
toolbar: [
|
|
'imageTextAlternative',
|
|
'toggleImageCaption',
|
|
'|',
|
|
'imageStyle:inline',
|
|
'imageStyle:wrapText',
|
|
'imageStyle:breakText',
|
|
'|',
|
|
'resizeImage',
|
|
],
|
|
},
|
|
simpleUpload: {
|
|
uploadUrl: '/admin/media',
|
|
headers: Object.assign(csrfHeader, {
|
|
Accept: 'application/vnd.ckeditor+json',
|
|
}),
|
|
},
|
|
};
|
|
script.addEventListener('load', function () {
|
|
for (const textarea of textareas) {
|
|
const canvas = document.createElement('div');
|
|
textarea.parentNode.insertBefore(canvas, textarea.nextSibling);
|
|
textarea.style.display = 'none';
|
|
|
|
ClassicEditor
|
|
.create(canvas, editorConfig)
|
|
.then(editor => {
|
|
const xml = document.createElement('div');
|
|
const serializer = new XMLSerializer();
|
|
editor.setData(textarea.value);
|
|
editor.ui.focusTracker.on('change:isFocused', (event, name, focused) => {
|
|
if (!focused) {
|
|
xml.innerHTML = editor.getData();
|
|
let text = serializer.serializeToString(xml);
|
|
text = text.replace(' ', ' ');
|
|
text = text.substring(text.indexOf('>') + 1, text.lastIndexOf('<'));
|
|
textarea.value = text;
|
|
}
|
|
});
|
|
})
|
|
.catch(error => {
|
|
console.error(error);
|
|
});
|
|
}
|
|
});
|
|
}
|
|
})
|
|
|
|
export function camperUploadForm(el) {
|
|
const progress = el.querySelector('progress');
|
|
htmx.on(el, 'drop', function (evt) {
|
|
evt.preventDefault();
|
|
[...evt.dataTransfer.items].forEach(function (i) {
|
|
console.log(i);
|
|
i.getAsString(console.log)
|
|
});
|
|
});
|
|
htmx.on(el, 'dragover', function (evt) {
|
|
evt.preventDefault();
|
|
});
|
|
htmx.on(el, 'dragleave', function (evt) {
|
|
evt.preventDefault();
|
|
});
|
|
htmx.on(el, 'htmx:xhr:progress', function (evt) {
|
|
if (progress && evt.detail.lengthComputable) {
|
|
progress.setAttribute('value', evt.detail.loaded / evt.detail.total * 100);
|
|
}
|
|
});
|
|
}
|
|
|
|
export function setupCampgroundMap(map) {
|
|
if (!map) {
|
|
return;
|
|
}
|
|
const prefix = "cp_";
|
|
for (const campsite of Array.from(map.querySelectorAll(`[id^="${prefix}"]`))) {
|
|
const label = campsite.id.substring(prefix.length);
|
|
if (!label) {
|
|
continue;
|
|
}
|
|
const link = document.createElementNS('http://www.w3.org/2000/svg', 'a');
|
|
link.setAttributeNS('http://www.w3.org/1999/xlink', 'href', '/admin/campsites/' + label);
|
|
link.append(...campsite.childNodes);
|
|
campsite.appendChild(link);
|
|
}
|
|
}
|
|
|
|
export function setupIconInput(icon) {
|
|
if (!icon) {
|
|
return;
|
|
}
|
|
const input = icon.querySelector('input[type="hidden"]')
|
|
if (!input) {
|
|
return;
|
|
}
|
|
const buttons = Array.from(icon.querySelectorAll('button[data-icon-name]'));
|
|
const updateValue = function (iconName) {
|
|
input.value = iconName;
|
|
for (const button of buttons) {
|
|
button.setAttribute('aria-pressed', button.dataset.iconName === iconName);
|
|
}
|
|
}
|
|
for (const button of buttons) {
|
|
button.addEventListener('click', function (e) {
|
|
updateValue(e.target.dataset.iconName);
|
|
})
|
|
}
|
|
updateValue(input.value);
|
|
}
|
|
|
|
export function setupCalendar(calendar) {
|
|
const startDate = calendar.querySelector('input[name="start_date"]');
|
|
const endDate = calendar.querySelector('input[name="end_date"]');
|
|
const days = Array.from(calendar.querySelectorAll('time'));
|
|
const dialog = calendar.querySelector('dialog');
|
|
|
|
const clear = function () {
|
|
startDate.value = endDate.value = "";
|
|
days.forEach((e) => e.removeAttribute('aria-checked'));
|
|
}
|
|
|
|
dialog.addEventListener('close', clear);
|
|
dialog.querySelector('button').addEventListener('click', function (e) {
|
|
e.preventDefault();
|
|
dialog.close();
|
|
});
|
|
|
|
const selectDate = function (e) {
|
|
e.preventDefault();
|
|
const date = e.currentTarget.dateTime;
|
|
if (!date) {
|
|
return;
|
|
}
|
|
if (!startDate.value) {
|
|
startDate.value = date;
|
|
e.currentTarget.setAttribute('aria-checked', true);
|
|
return;
|
|
} else if (startDate.value > date) {
|
|
endDate.value = startDate.value;
|
|
startDate.value = date;
|
|
} else {
|
|
endDate.value = date;
|
|
}
|
|
for (const day of days) {
|
|
if (day.dateTime >= startDate.value && day.dateTime <= endDate.value) {
|
|
day.setAttribute('aria-checked', true);
|
|
} else {
|
|
day.removeAttribute('aria-checked');
|
|
}
|
|
}
|
|
dialog.showModal();
|
|
}
|
|
|
|
for (const day of days) {
|
|
day.addEventListener('click', selectDate);
|
|
}
|
|
|
|
clear();
|
|
}
|
|
|
|
htmx.onLoad((target) => {
|
|
if (target.tagName === 'DIALOG') {
|
|
target.showModal();
|
|
}
|
|
})
|
|
|
|
htmx.onLoad((content) => {
|
|
const sortables = Array.from(content.querySelectorAll('.sortable table tbody'));
|
|
for (const sortable of sortables) {
|
|
const sortableInstance = new Sortable(sortable, {
|
|
animation: 150,
|
|
draggable: '>tr',
|
|
handle: '.handle',
|
|
onMove: (evt) => evt.related.className.indexOf('htmx-indicator') === -1,
|
|
onEnd: function () {
|
|
this.option('disabled', true);
|
|
},
|
|
});
|
|
|
|
sortable.addEventListener('htmx:afterSwap', function () {
|
|
sortableInstance.option('disabled', false);
|
|
});
|
|
}
|
|
})
|
|
|
|
htmx.on('htmx:configRequest', function (e) {
|
|
const element = e.detail.elt;
|
|
if (element && element.nodeName === 'FORM') {
|
|
let submitter = e.detail.triggeringEvent.submitter;
|
|
if (submitter) {
|
|
const action = submitter.attributes['formaction'];
|
|
if (action && action.value) {
|
|
e.detail.path = action.value;
|
|
}
|
|
const method = submitter.attributes['formmethod'];
|
|
if (method && method.value) {
|
|
e.detail.verb = method.value;
|
|
}
|
|
}
|
|
}
|
|
})
|