import Choices from "choices.js"; import { Sidebar } from "./js/sidebar"; import "./scss/app.scss"; export interface choiceMap { [details: string]: Choices; } let choiceObjects: choiceMap = {}; let boat_in_ottensheim = true; let boat_reserved_today= true; 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; boat_in_ottensheim: boolean; boat_reserved_today: boolean; }; }; } 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; boat_reserved_today = event.detail.customProperties.boat_reserved_today; if (boat_reserved_today){ alert(event.detail.label.trim()+' wurde heute reserviert. Bitte kontrolliere, dass du die Reservierung nicht störst.'); } boat_in_ottensheim = event.detail.customProperties.boat_in_ottensheim; 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; const distinput = ( document.querySelector("#distance_in_km") ); distinput.value = ""; const destinput = ( document.querySelector("#destination") ); destinput.dispatchEvent(new Event("input")); }, 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; is_racing: 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, { searchFields: ['label', 'value', 'customProperties.searchableText'], 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 (boat_in_ottensheim && obj.customProperties) { if (obj.customProperties.is_racing) { 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); } } 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 (boat_in_ottensheim && event.detail.customProperties.is_racing) { if (event.detail.customProperties.is_racing) { const coxSelect = ( document.querySelector("#shipmaster-" + select.id + "js") ); if (coxSelect) { coxSelect.add(new Option(name, user_id)); } } } 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; i < coxSelect.length; i++) { if (coxSelect.options[i].value == user_id) coxSelect.remove(i); } } const steeringSelect = ( document.querySelector("#steering_person-" + select.id + "js") ); if (steeringSelect) { for (var i = 0; i < steeringSelect.length; i++) { if (steeringSelect.options[i].value == user_id) steeringSelect.remove(i); } } }, false, ); choiceObjects[select.id] = choice; } function initToggle() { // get filter btns & set object sessionStorage const btns = >( 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; } } } 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 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"); }); }