import Choices from "choices.js"; import { Sidebar } from './js/sidebar'; import './scss/app.scss' export interface choiceMap { [details: string]: Choices; } let choiceObjects: choiceMap = {}; document.addEventListener('DOMContentLoaded', function() { changeTheme(); initcolorTheme(); initSearch(); initSidebar(); initToggle(); replaceStrings(); initChoices(); initBoatActions(); selectBoatChange(); addRelationMagic(document.querySelector('body')); reloadPage(); setCurrentdate(document.querySelector('#departure')); }); function changeTheme() { let toggleBtn = document.querySelector('#theme-toggle-js'); if(toggleBtn) { toggleBtn.addEventListener('click', function() { if(toggleBtn.dataset.theme === 'light') { setTheme('dark', true); } else { setTheme('light', true); } }); } } /*** * init javascript * 1) detect native color scheme or use set theme in local storage * 2) detect view (desktop or responsive) if on mobile device with touch screen * 3) set base font size to 112.5% -> 18px */ function initcolorTheme() { colorThemeWatcher(); let theme = localStorage.getItem('theme'); if (theme == null || theme === 'auto') { if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { setTheme('dark', false); } else { setTheme('light', false); } } else { setTheme(theme) } } /*** * Listener operating system native color configuration */ function colorThemeWatcher() { try { window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => { setTheme(e.matches ? 'dark' : 'light'); }); } catch { console.warn('color theme watcher not supported'); } } /** * Define color scheme, colors without losing the base font size configuration * and add data-theme to html tag * @param theme */ function setTheme(theme: string, setLocalStorage = true) { let toggleBtn = document.querySelector('#theme-toggle-js'); if (setLocalStorage) { localStorage.setItem('theme', theme); } if (toggleBtn) toggleBtn.setAttribute('data-theme', theme); if (document.documentElement.classList.contains('dark') && theme === 'light') { document.documentElement.classList.remove('dark'); } else if(theme === 'dark'){ document.documentElement.classList.add('dark'); } } function setCurrentdate(input: HTMLInputElement) { if(input) { const now = new Date(); const formattedDateTime = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}T${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`; input.value = formattedDateTime; } } interface ChoiceBoatEvent extends Event{ detail: { value: string; label: string, customProperties: { amount_seats: number, owner: number, default_destination: string, } }; } function selectBoatChange() { const boatSelect = document.querySelector('#boat_id'); if(boatSelect) { const boatChoice = new Choices(boatSelect, { loadingText: 'Wird geladen...', noResultsText: 'Keine Ergebnisse gefunden', noChoicesText: 'Keine Ergebnisse gefunden', itemSelectText: 'Zum Auswählen klicken', } as any); boatSelect.addEventListener('addItem', function(e) { const event = e as ChoiceBoatEvent; const amount_seats = event.detail.customProperties.amount_seats; setMaxAmountRowers("newrower", amount_seats); const destination = document.querySelector('#destination'); destination.value = event.detail.customProperties.default_destination; if (event.detail.customProperties.owner){ choiceObjects["newrower"].setChoiceByValue(event.detail.customProperties.owner+""); } const inputElement = document.getElementById("departure") as HTMLInputElement; const now = new Date(); const formattedDateTime = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}T${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`; inputElement.value = formattedDateTime; },false); choiceObjects[boatSelect.id] = boatChoice; choiceObjects["boat_id"] = boatChoice; } } function reloadPage() { if (!window.location.href.includes("ergo")){ let pageTitle = document.title; let attentionMessage = 'Riemen- und Dollenbruch'; document.addEventListener('visibilitychange', function() { let isPageActive = !document.hidden; if(!isPageActive){ document.title = attentionMessage; } else { document.title = pageTitle; location.reload(); } }); } } function setMaxAmountRowers(name: string, rowers: number) { if(choiceObjects[name]) { choiceObjects[name].removeActiveItems(-1); //let curSelection = choiceObjects[name].getValue(true); //let amount_to_delete = (curSelection).length - rowers; //if (amount_to_delete > 0){ // let to_delete = (curSelection).slice(-amount_to_delete); // for (let del of to_delete) { // choiceObjects[name].removeActiveItemsByValue(del); // } //} let input = document.querySelector('#'+name); if(input) { choiceObjects[name].config.maxItemCount = rowers; if (rowers === 0) { choiceObjects[name].disable() input.parentElement?.parentElement?.parentElement?.classList.add('hidden'); input.parentElement?.parentElement?.parentElement?.classList.add('md:block'); input.parentElement?.parentElement?.parentElement?.classList.add('opacity-50'); } else{ choiceObjects[name].enable(); input.parentElement?.parentElement?.parentElement?.classList.remove('hidden'); input.parentElement?.parentElement?.parentElement?.classList.remove('md:block'); input.parentElement?.parentElement?.parentElement?.classList.remove('opacity-50'); } } //let only_steering = document.querySelector('#shipmaster_only_steering'); //if(only_steering) { // if(isShipmasterSteering == 'true') { // only_steering.removeAttribute('disabled'); // only_steering.setAttribute('checked', 'true'); // only_steering.parentElement?.parentElement?.parentElement?.classList.remove('hidden'); // only_steering.parentElement?.parentElement?.parentElement?.classList.remove('md:block'); // only_steering.parentElement?.parentElement?.parentElement?.classList.remove('opacity-50'); // } else { // only_steering.setAttribute('disabled', 'disabled'); // only_steering.removeAttribute('checked'); // only_steering.parentElement?.parentElement?.parentElement?.classList.add('hidden'); // only_steering.parentElement?.parentElement?.parentElement?.classList.add('md:block'); // only_steering.parentElement?.parentElement?.parentElement?.classList.add('opacity-50'); // } //} let shipmaster = document.querySelector('#shipmaster-'+name+'js'); let steering_person = document.querySelector('#steering_person-'+name+'js'); if (rowers == 1){ if (shipmaster.parentNode) { (shipmaster.parentNode).classList.add('hidden'); } shipmaster.removeAttribute('required'); if (steering_person.parentNode){ (steering_person.parentNode).classList.add('hidden'); } steering_person.removeAttribute('required'); }else{ if (shipmaster.parentNode){ (shipmaster.parentNode).classList.remove('hidden'); } shipmaster.setAttribute('required', 'required'); if (steering_person.parentNode){ (steering_person.parentNode).classList.remove('hidden'); } steering_person.setAttribute('required', 'required'); } } } function initBoatActions() { const boatSelects = document.querySelectorAll('.boats-js[data-onclick="true"]'); if(boatSelects) { Array.prototype.forEach.call(boatSelects, (select: HTMLInputElement) => { select.addEventListener('click', function() { if(select.dataset.seats) { if (select.dataset.id) { choiceObjects['boat_id'].setChoiceByValue(select.dataset.id); } window.scrollTo(0, 0); } }); }); } } function initChoices() { const selects = document.querySelectorAll('select[data-init="true"]'); if(selects) { Array.prototype.forEach.call(selects, (select: HTMLInputElement) => { initNewChoice(select); }); } } interface ChoiceEvent extends Event{ detail: { value: string; label: string, customProperties: { is_cox: boolean, steers: boolean, cox_on_boat: boolean, } }; } function initNewChoice(select: HTMLInputElement) { let seats = 0; if (select.dataset && select.dataset.seats) { seats = +select.dataset.seats; } let shipmaster = document.querySelector('#shipmaster-'+select.id+'js'); let steering_person = document.querySelector('#steering_person-'+select.id+'js'); if (seats == 1){ if (shipmaster.parentNode) { (shipmaster.parentNode).classList.add('hidden'); } shipmaster.removeAttribute('required'); if (steering_person.parentNode){ (steering_person.parentNode).classList.add('hidden'); } steering_person.removeAttribute('required'); }else{ if (shipmaster.parentNode){ (shipmaster.parentNode).classList.remove('hidden'); } shipmaster.setAttribute('required', 'required'); if (steering_person.parentNode){ (steering_person.parentNode).classList.remove('hidden'); } steering_person.setAttribute('required', 'required'); } const choice = new Choices(select, { removeItemButton: true, loadingText: 'Wird geladen...', noResultsText: 'Keine Ergebnisse gefunden', noChoicesText: 'Keine Ergebnisse gefunden', itemSelectText: 'Zum Auswählen klicken', placeholderValue: 'Ruderer auswählen', maxItemCount: seats, maxItemText: (maxItemCount) => { return `Nur ${maxItemCount} Ruderer können hinzugefügt werden`; }, callbackOnInit: function() { this._currentState.items.forEach(function(obj){ if (obj.customProperties && obj.customProperties.is_cox){ const coxSelect = document.querySelector('#shipmaster-' + select.id + 'js'); var new_option = new Option(obj.label, obj.value); if (obj.customProperties.cox_on_boat){ new_option.selected = true; } coxSelect.add(new_option); } const steeringSelect = document.querySelector('#steering_person-' + select.id + 'js'); if (steeringSelect) { var new_option = new Option(obj.label, obj.value); if (obj.customProperties && obj.customProperties.steers){ new_option.selected = true; } steeringSelect.add(new_option); } }); } }); choiceObjects[select.id] = choice; select.addEventListener('addItem', function(e) { const event = e as ChoiceEvent; const user_id = event.detail.value; const name = event.detail.label; if (event.detail.customProperties.is_cox) { const coxSelect = document.querySelector('#shipmaster-' + select.id + 'js'); if (coxSelect){ coxSelect.add(new Option(name, user_id)); } } const steeringSelect = document.querySelector('#steering_person-' + select.id + 'js'); if (steeringSelect) { steeringSelect.add(new Option(name, user_id)); } },false); select.addEventListener('removeItem', function(e) { const event = e as ChoiceEvent; const user_id = event.detail.value; const coxSelect = document.querySelector('#shipmaster-' + select.id + 'js'); if (coxSelect) { for (var i=0; idocument.querySelector('#steering_person-' + select.id + 'js'); if (steeringSelect) { for (var i=0; i>document.querySelectorAll('.filter-trips-js'); let filterObject = new Map(); if(btns) { Array.prototype.forEach.call(btns, (btn: HTMLButtonElement) => { filterObject.set(btn.dataset.action, btn.ariaPressed); btn.addEventListener('click', () => { let filter = sessionStorage.getItem('tripsFilter'); if(filter) { let filterMap = new Map(JSON.parse(filter)); for (let entry of filterMap.entries()) { if(entry[0] === btn.dataset.action && entry[1] !== 'true') { filterMap.set(entry[0],'true'); } else { filterMap.set(entry[0],'false'); } } sessionStorage.setItem('tripsFilter', JSON.stringify( Array.from(filterMap.entries()))); } resetFilteredElements(); if(btn.getAttribute('aria-pressed') === 'false'){ Array.prototype.forEach.call(btns, (b: HTMLButtonElement) => { b.setAttribute('aria-pressed', 'false'); }); triggerFilterAction(btn.dataset.action); } else { btn.setAttribute('aria-pressed', 'false'); } }); }); } let filter = sessionStorage.getItem('tripsFilter'); if(filter) { let filterMap = new Map(JSON.parse(filter)); for (let entry of filterMap.entries()) { if(entry[1] === 'true') { triggerFilterAction(entry[0]); } } } else { sessionStorage.setItem('tripsFilter', JSON.stringify( Array.from(filterObject.entries()))); } } function resetFilteredElements() { const hiddenElements = document.querySelectorAll('.reset-js.hidden'); if(hiddenElements) { Array.prototype.forEach.call(hiddenElements, (hiddenElement: HTMLButtonElement) => { hiddenElement.classList.remove('hidden'); }); } } function triggerFilterAction(activeFilter: any) { const activeBtn = document.querySelector('button[data-action="' + activeFilter + '"]'); if(activeBtn) { activeBtn.setAttribute('aria-pressed', 'true'); filterAction(activeFilter); } } function filterAction(activeFilter: string) { switch(activeFilter) { case 'filter-days': { filterDays(); break; } case 'filter-coxs': { filterCoxs(); break; } case 'filter-months': { filterMonths(activeFilter) break; } } } function filterDays() { const daysNoTrips = document.querySelectorAll('div[data-trips="0"]'); Array.prototype.forEach.call(daysNoTrips, (day: HTMLElement) => { day.classList.toggle('hidden'); }); } function filterCoxs() { const noCoxNeeded = document.querySelectorAll('div[data-coxneeded="false"]'); Array.prototype.forEach.call(noCoxNeeded, (notNeeded: HTMLElement) => { notNeeded.classList.toggle('hidden'); }); } function filterMonths(action: string) { const months = ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12']; const monthToggle = document.querySelector('button[data-action="' + action + '"]'); if(monthToggle) { const currentMonth = monthToggle.dataset.month; if(currentMonth) { const index = months.indexOf(currentMonth); if (index > -1) { // only splice array when item is found months.splice(index, 1); // 2nd parameter means remove one item only } Array.prototype.forEach.call(months, (month: HTMLElement) => { const notThisMonth = document.querySelectorAll('div[data-month="' + month + '"]'); Array.prototype.forEach.call(notThisMonth, (notThisMonth: HTMLElement) => { notThisMonth.classList.toggle('hidden'); }); }); } } } function initSearch() { const input = document.querySelector('#filter-js'); if(input) { filterElements(input.value); input.addEventListener('input', () => { filterElements(input.value); }); } } function filterElements(input: string) { const elements = document.querySelectorAll('div[data-filterable="true"]'); let resultWrapper = document.querySelector('#filter-result-js'), amountShownElements = 0; Array.prototype.forEach.call(elements, (element: HTMLElement) => { // set both strings (input & dataset filter) to lowercase to not be case sensitive let filterString = element.dataset.filter?.toLocaleLowerCase(); // bulk hide all elements element.style.display = 'none'; // show if input matches if(filterString?.includes(input.toLocaleLowerCase())) { element.style.display = 'flex'; amountShownElements ++; } }); if(resultWrapper) { resultWrapper.innerHTML = (amountShownElements === 0 ? 'Kein Ergebnis gefunden' : '' + amountShownElements + '' + (amountShownElements > 1 ? ' Ergebnisse' : ' Ergebnis') + ' gefunden'); } } function initSidebar() { const sidebarTrigger = >document.querySelectorAll('[data-trigger]'); if(sidebarTrigger) { Array.prototype.forEach.call(sidebarTrigger, (triggerElement: HTMLElement) => { if(triggerElement.dataset.trigger) { const sidebar = new Sidebar(triggerElement.dataset.trigger); triggerElement.addEventListener('click', (e) => { e.preventDefault(); if(triggerElement.dataset.trigger === 'sidebar') { initTripSidebar(triggerElement); } sidebar.toggle(); }); } }); } } function initTripSidebar(triggerElement: HTMLElement) { const sidebarElement = document.querySelector('#sidebar'); if(sidebarElement && triggerElement.dataset.body && triggerElement.dataset.header) { let body = document.querySelector(triggerElement.dataset.body); let bodyElement = body.cloneNode(true); let bodyContainerElement = sidebarElement.querySelector('.body-js'); /* Quickfix duplicate ids checkboxes */ const checkboxes = >bodyElement.querySelectorAll('input[type="checkbox"]'); Array.prototype.forEach.call(checkboxes, (checkbox: HTMLElement) => { if(checkbox) { checkbox.parentElement?.setAttribute('for', checkbox.id + 'js'); checkbox.id += 'js'; } }); const prefixedContent = >bodyElement.querySelectorAll('.change-id-js'); Array.prototype.forEach.call(prefixedContent, (content: HTMLElement) => { if(content) { content.id += 'js'; if(content.dataset.relation){ content.dataset.relation += 'js'; } } }); if(bodyContainerElement) { bodyContainerElement.innerHTML = ''; bodyContainerElement.append(bodyElement); addRelationMagic(bodyElement); } if(triggerElement.dataset.day) { let hiddenElement = bodyElement.querySelector('.day-js'); if(hiddenElement) { hiddenElement.value = triggerElement.dataset.day; } } let headerElement = sidebarElement.querySelector('.header-js'); if(headerElement) { headerElement.innerHTML = triggerElement.dataset.header; } const selects = bodyElement.querySelectorAll('select[multiple]'); if(selects) { Array.prototype.forEach.call(selects, (select: HTMLInputElement) => { initNewChoice(select); }); } } const defaultDateTimes = >document.querySelectorAll('.current-date-time'); defaultDateTimes.forEach((defaultDateTime) => { setCurrentdate(defaultDateTime); }); } function addRelationMagic(bodyElement: HTMLElement) { const fields = bodyElement.querySelectorAll('.set-distance-js'); if(fields) { Array.prototype.forEach.call(fields, (field: HTMLInputElement) => { if(field.dataset.relation){ const relatedField = bodyElement.querySelector('#' + field.dataset.relation); if(relatedField) { field.addEventListener('input', (e) => { e.preventDefault(); const dataList = document.querySelector('#destinations'); if(dataList) { var option = Array.prototype.find.call(dataList.options, function(option) { return option.value === field.value; }); // Get distance const distance = option.getAttribute('distance'); if(distance) relatedField.value = distance; } }); } } }); } } function replaceStrings() { const weekdays = document.querySelectorAll('.weekday-js'); Array.prototype.forEach.call(weekdays, (weekday: HTMLElement) => { weekday.innerHTML = weekday.innerHTML.replace('Freitag', 'Markttag'); }); }