(function initTradeFairsSubpage() { const tableBody = document.getElementById('tradeFairTableBody'); const searchInput = document.getElementById('tradeFairSearchInput'); const meta = document.getElementById('tradeFairMeta'); const sortButtons = Array.from(document.querySelectorAll('.bookmark-subpage__sort[data-trade-sort]')); const SORT_STATE_KEY = 'fb_trade_fairs_sort_v1'; if (!tableBody || !searchInput || !meta || !sortButtons.length) { return; } const TRADE_FAIRS = [ { rang: 1, messe: 'bauma', thema: 'Baumaschinen, Baustoffmaschinen, Bergbau, Baufahrzeuge', stadt: 'München', bundesland: 'Bayern', termin_start: '2028-04-03', termin_ende: '2028-04-09', besucher: 605974, besucher_jahr: 2025, besucher_status: 'AUMA 2025', ausstellungsflaeche_m2: 420107, ticketpreis_we_eur: 38, ticketpreis_unterderwoche_eur: 38, notiz: 'Besucher laut AUMA. Tagesticket aus zuletzt verfuegbarem Preisstand (TradeFairDates, 2025).', quelle_homepage: 'https://bauma.de' }, { rang: 2, messe: 'IAA MOBILITY', thema: 'Mobilitaet, Auto, Tech, Open Space', stadt: 'München', bundesland: 'Bayern', termin_start: '2027-09-08', termin_ende: '2027-09-12', besucher: 500000, besucher_jahr: 2025, besucher_status: 'Veranstalterangabe 2025 (>500.000)', ausstellungsflaeche_m2: null, ticketpreis_we_eur: 39, ticketpreis_unterderwoche_eur: 29, notiz: 'Open Space teils frei; Ticketpreise laut IAA-Ticketseite (ab EUR 29, Family Pass ab EUR 39).', quelle_homepage: 'https://www.iaa-mobility.com' }, { rang: 3, messe: 'AGRITECHNICA', thema: 'Landtechnik und Agrartechnologie', stadt: 'Hannover', bundesland: 'Niedersachsen', termin_start: '2027-11-14', termin_ende: '2027-11-20', besucher: 477055, besucher_jahr: 2025, besucher_status: 'AUMA 2025', ausstellungsflaeche_m2: 229886, ticketpreis_we_eur: 29, ticketpreis_unterderwoche_eur: 29, notiz: 'Besucher laut AUMA. Ticketpreis laut Agritechnica-Ticketseite (Tagesticket EUR 29).', quelle_homepage: 'https://www.agritechnica.com' }, { rang: 4, messe: 'IdeenExpo', thema: 'Technik, Naturwissenschaften, Erlebnis, Ausbildung', stadt: 'Hannover', bundesland: 'Niedersachsen', termin_start: '2026-06-20', termin_ende: '2026-06-28', besucher: 430000, besucher_jahr: 2024, besucher_status: 'Veranstalter/Pressestand 2024', ausstellungsflaeche_m2: null, ticketpreis_we_eur: 0, ticketpreis_unterderwoche_eur: 0, notiz: 'Eintritt frei (IdeenExpo). Besucherzahl aus dem letzten veroeffentlichten Durchgang.', quelle_homepage: 'https://www.ideenexpo.de' }, { rang: 5, messe: 'gamescom', thema: 'Games, digitale Unterhaltung, Hardware/Software', stadt: 'Köln', bundesland: 'Nordrhein-Westfalen', termin_start: '2026-08-26', termin_ende: '2026-08-30', besucher: 357000, besucher_jahr: 2025, besucher_status: 'Veranstalter/Branchenpresse 2025', ausstellungsflaeche_m2: 73515, ticketpreis_we_eur: 40, ticketpreis_unterderwoche_eur: 30.5, notiz: 'Wochenende hoeher als Werktag (Preisstand 2025 aus Gaming-/Ticketberichten).', quelle_homepage: 'https://www.gamescom.global/en' }, { rang: 6, messe: 'Grüne Woche', thema: 'Ernaehrung, Landwirtschaft, Garten, Genuss', stadt: 'Berlin', bundesland: 'Berlin', termin_start: '2027-01-15', termin_ende: '2027-01-24', besucher: 307000, besucher_jahr: 2025, besucher_status: 'Veranstalterangabe 2025', ausstellungsflaeche_m2: 42500, ticketpreis_we_eur: 16, ticketpreis_unterderwoche_eur: 16, notiz: 'Tagesticket Erwachsene laut Gruene-Woche-Ticketseite (Preisstand 2025).', quelle_homepage: 'https://www.gruenewoche.de' }, { rang: 7, messe: 'CARAVAN SALON DUESSELDORF', thema: 'Reisemobile, Caravans, Camping, Touristik', stadt: 'Düsseldorf', bundesland: 'Nordrhein-Westfalen', termin_start: '2026-08-28', termin_ende: '2026-09-06', besucher: 269000, besucher_jahr: 2025, besucher_status: 'Veranstalter/Branchenpresse 2025', ausstellungsflaeche_m2: 121439, ticketpreis_we_eur: 20, ticketpreis_unterderwoche_eur: 18, notiz: 'Preisstand zuletzt verfuegbarer Ticketinfos (Weekend hoeher als Weekday).', quelle_homepage: 'https://www.caravan-salon.de' }, { rang: 8, messe: 'Maimarkt Mannheim', thema: 'Publikums-Mehrbranchenmesse', stadt: 'Mannheim', bundesland: 'Baden-Württemberg', termin_start: '2026-04-25', termin_ende: '2026-05-05', besucher: 268613, besucher_jahr: 2025, besucher_status: 'AUMA 2025', ausstellungsflaeche_m2: 52769, ticketpreis_we_eur: 10, ticketpreis_unterderwoche_eur: 10, notiz: 'Tagesticket Erwachsene an der Kasse EUR 10 (VVK guenstiger).', quelle_homepage: 'https://www.maimarkt.de' }, { rang: 9, messe: 'CMT - Die Urlaubsmesse', thema: 'Tourismus, Caravaning, Freizeit', stadt: 'Stuttgart', bundesland: 'Baden-Württemberg', termin_start: '2027-01-16', termin_ende: '2027-01-24', besucher: 261004, besucher_jahr: 2025, besucher_status: 'AUMA 2025', ausstellungsflaeche_m2: 75587, ticketpreis_we_eur: 18, ticketpreis_unterderwoche_eur: 15, notiz: 'Preisstaffel laut CMT-Partner-/Infoseiten (Wochenende teurer).', quelle_homepage: 'https://www.messe-stuttgart.de/cmt' }, { rang: 10, messe: 'Frankfurter Buchmesse', thema: 'Buecher, Kultur, Medien, Publishing', stadt: 'Frankfurt am Main', bundesland: 'Hessen', termin_start: '2026-10-07', termin_ende: '2026-10-11', besucher: 238000, besucher_jahr: 2025, besucher_status: 'Veranstalter/Branchenpresse 2025', ausstellungsflaeche_m2: null, ticketpreis_we_eur: 35, ticketpreis_unterderwoche_eur: 30, notiz: 'Preisniveau aus zuletzt veroeffentlichtem Ticketstand (Publikumstage teils teurer).', quelle_homepage: 'https://www.buchmesse.de' }, { rang: 11, messe: 'Leipziger Buchmesse / Manga Comic Con', thema: 'Buchmarkt, Medien, Manga/Comic', stadt: 'Leipzig', bundesland: 'Sachsen', termin_start: '2026-03-19', termin_ende: '2026-03-22', besucher: 222819, besucher_jahr: 2025, besucher_status: 'Veranstalter/Pressestand 2025', ausstellungsflaeche_m2: 20788, ticketpreis_we_eur: 35, ticketpreis_unterderwoche_eur: 28, notiz: 'Ticketpreise laut offizieller Leipzig-Messe-Preisliste 2026 (Sa am hoechsten).', quelle_homepage: 'https://www.leipziger-buchmesse.de' }, { rang: 12, messe: 'SPIEL Essen', thema: 'Brettspiele, Tabletop, Family Entertainment', stadt: 'Essen', bundesland: 'Nordrhein-Westfalen', termin_start: '2026-10-22', termin_ende: '2026-10-25', besucher: 220000, besucher_jahr: 2025, besucher_status: 'Veranstalterangabe 2025', ausstellungsflaeche_m2: null, ticketpreis_we_eur: 23.5, ticketpreis_unterderwoche_eur: 23.5, notiz: 'Tagesticket Erwachsene laut Ticketberichten 2025 (einheitlicher Preis).', quelle_homepage: 'https://www.spiel-essen.de' }, { rang: 13, messe: 'ESSEN MOTOR SHOW', thema: 'Performance, Tuning, Motorsport, Classic Cars', stadt: 'Essen', bundesland: 'Nordrhein-Westfalen', termin_start: '2026-11-28', termin_ende: '2026-12-06', besucher: 202827, besucher_jahr: 2024, besucher_status: 'AUMA 2024 (letzter verfuegbarer Wert)', ausstellungsflaeche_m2: 33781, ticketpreis_we_eur: 20, ticketpreis_unterderwoche_eur: 20, notiz: 'Tagesticket aus zuletzt publiziertem Ticketstand.', quelle_homepage: 'https://www.essen-motorshow.de' }, { rang: 14, messe: 'boot Duesseldorf', thema: 'Boote, Wassersport, Tauchen, Yachting', stadt: 'Düsseldorf', bundesland: 'Nordrhein-Westfalen', termin_start: '2027-01-23', termin_ende: '2027-01-31', besucher: 198339, besucher_jahr: 2025, besucher_status: 'AUMA 2025', ausstellungsflaeche_m2: 94867, ticketpreis_we_eur: 19, ticketpreis_unterderwoche_eur: 19, notiz: 'Preis aus offiziellem Ticket-/Club-Stand (2-fuer-1 Aktion auf Basis Tagesticket).', quelle_homepage: 'https://www.boot.de' }, { rang: 15, messe: 'IFA', thema: 'Consumer Electronics, Home Appliances, Tech', stadt: 'Berlin', bundesland: 'Berlin', termin_start: '2026-09-04', termin_ende: '2026-09-08', besucher: 191997, besucher_jahr: 2024, besucher_status: 'AUMA 2024 (letzter verfuegbarer Wert)', ausstellungsflaeche_m2: 107546, ticketpreis_we_eur: 19.5, ticketpreis_unterderwoche_eur: 19.5, notiz: 'Preisniveau aus Ticketberichten zum letzten verfuegbaren IFA-Durchgang.', quelle_homepage: 'https://www.ifa-berlin.com' }, { rang: 16, messe: 'FIBO', thema: 'Fitness, Wellness, Gesundheit', stadt: 'Köln', bundesland: 'Nordrhein-Westfalen', termin_start: '2026-04-16', termin_ende: '2026-04-19', besucher: 154890, besucher_jahr: 2025, besucher_status: 'AUMA 2025', ausstellungsflaeche_m2: 56904, ticketpreis_we_eur: 44, ticketpreis_unterderwoche_eur: 44, notiz: 'Tagesticket laut FIBO-Ticketseite (ab EUR 44).', quelle_homepage: 'https://www.fibo.com' }, { rang: 17, messe: 'IAA TRANSPORTATION', thema: 'Nutzfahrzeuge, Logistik, Transporttechnologie', stadt: 'Hannover', bundesland: 'Niedersachsen', termin_start: '2026-09-15', termin_ende: '2026-09-20', besucher: 145000, besucher_jahr: 2024, besucher_status: 'Veranstalterangabe 2024', ausstellungsflaeche_m2: null, ticketpreis_we_eur: 20, ticketpreis_unterderwoche_eur: 20, notiz: 'Ticketpreis laut offizieller IAA-Transportation-Ticketseite (ab EUR 20).', quelle_homepage: 'https://www.iaa-transportation.com' }, { rang: 18, messe: 'Anuga', thema: 'Lebensmittel und Getraenke', stadt: 'Köln', bundesland: 'Nordrhein-Westfalen', termin_start: '2027-10-09', termin_ende: '2027-10-13', besucher: 143432, besucher_jahr: 2025, besucher_status: 'AUMA 2025', ausstellungsflaeche_m2: 157545, ticketpreis_we_eur: 80, ticketpreis_unterderwoche_eur: 80, notiz: 'Tagesticket laut TradeFairDates (Stand zuletzt verfuegbarer Preis).', quelle_homepage: 'https://www.anuga.com' }, { rang: 19, messe: 'HANNOVER MESSE', thema: 'Industrie, Automation, Energie, Digital', stadt: 'Hannover', bundesland: 'Niedersachsen', termin_start: '2026-04-20', termin_ende: '2026-04-24', besucher: 123035, besucher_jahr: 2025, besucher_status: 'AUMA 2025', ausstellungsflaeche_m2: 101699, ticketpreis_we_eur: null, ticketpreis_unterderwoche_eur: 39, notiz: 'Messe liegt auf Werktagen; Tagesticket laut TradeFairDates EUR 39.', quelle_homepage: 'https://www.hannovermesse.de' }, { rang: 20, messe: 'CONSUMENTA', thema: 'Publikumsmesse: Haushalt, Freizeit, Bauen, Genuss', stadt: 'Nürnberg', bundesland: 'Bayern', termin_start: '2026-10-31', termin_ende: '2026-11-08', besucher: 123000, besucher_jahr: 2025, besucher_status: 'Veroeffentlichter Wert 2025', ausstellungsflaeche_m2: 30448, ticketpreis_we_eur: 16, ticketpreis_unterderwoche_eur: 16, notiz: 'Aktionstags-Preisstand vorhanden; regulaere Ticketpreise fuer 2026 noch im Rollout.', quelle_homepage: 'https://www.consumenta.de' }, { rang: 21, messe: 'f.re.e München', thema: 'Reisen, Freizeit, Caravaning, Outdoor', stadt: 'München', bundesland: 'Bayern', termin_start: '2026-02-18', termin_ende: '2026-02-22', besucher: 120000, besucher_jahr: 2025, besucher_status: 'Veroeffentlichter Wert 2025', ausstellungsflaeche_m2: null, ticketpreis_we_eur: 17, ticketpreis_unterderwoche_eur: 14, notiz: 'Preisniveau aus zuletzt veroeffentlichtem Ticketstand (Wochenende hoeher).', quelle_homepage: 'https://free-muenchen.de' }, { rang: 22, messe: 'infa', thema: 'Publikumsmesse: Lifestyle, Haushalt, Genuss', stadt: 'Hannover', bundesland: 'Niedersachsen', termin_start: '2026-10-10', termin_ende: '2026-10-18', besucher: 112228, besucher_jahr: 2025, besucher_status: 'Veroeffentlichter Wert 2025', ausstellungsflaeche_m2: 16423, ticketpreis_we_eur: 15, ticketpreis_unterderwoche_eur: 15, notiz: 'Tagesticket laut zuletzt verfuegbarem Preisstand.', quelle_homepage: 'https://www.infa.de' }, { rang: 23, messe: 'ITB Berlin', thema: 'Reiseindustrie (B2B/Fachbesucher)', stadt: 'Berlin', bundesland: 'Berlin', termin_start: '2026-03-03', termin_ende: '2026-03-05', besucher: 100000, besucher_jahr: 2025, besucher_status: 'Veroeffentlichter Wert 2025', ausstellungsflaeche_m2: 78892, ticketpreis_we_eur: null, ticketpreis_unterderwoche_eur: 65, notiz: 'B2B-Fachmesse ohne Wochenend-Tag. Ticketpreis laut offizieller ITB-Ticketseite.', quelle_homepage: 'https://www.itb.com' }, { rang: 24, messe: 'ILA Berlin', thema: 'Luft- und Raumfahrt', stadt: 'Berlin', bundesland: 'Berlin', termin_start: '2026-06-10', termin_ende: '2026-06-14', besucher: 95000, besucher_jahr: 2024, besucher_status: 'AUMA 2024 (letzter verfuegbarer Wert)', ausstellungsflaeche_m2: 32008, ticketpreis_we_eur: 22, ticketpreis_unterderwoche_eur: 22, notiz: 'Tagesticket laut ILA-Besucherinfo (EUR 22, Kinder frei).', quelle_homepage: 'https://www.ila-berlin.de' }, { rang: 25, messe: 'Heim+Handwerk', thema: 'Wohnen, Handwerk, Lifestyle', stadt: 'München', bundesland: 'Bayern', termin_start: '2026-11-25', termin_ende: '2026-11-29', besucher: 90000, besucher_jahr: 2025, besucher_status: 'Veroeffentlichter Wert 2025', ausstellungsflaeche_m2: null, ticketpreis_we_eur: 15, ticketpreis_unterderwoche_eur: 15, notiz: 'Ticketpreis als zuletzt verfuegbares Preisniveau; finale 2026-Staffel folgt i.d.R. spaeter.', quelle_homepage: 'https://www.heim-handwerk.de' }, { rang: 26, messe: 'offerta', thema: 'Einkaufs- und Erlebnismesse fuer Verbraucher', stadt: 'Karlsruhe', bundesland: 'Baden-Württemberg', termin_start: '2026-10-24', termin_ende: '2026-11-01', besucher: 88738, besucher_jahr: 2025, besucher_status: 'AUMA 2025', ausstellungsflaeche_m2: 16242, ticketpreis_we_eur: 10, ticketpreis_unterderwoche_eur: 10, notiz: 'Preisniveau aus regional veroeffentlichtem Ticketstand (offerta Karlsruhe).', quelle_homepage: 'https://www.offerta.info' }, { rang: 27, messe: 'Reise + Camping Essen', thema: 'Reisen, Camping, Fahrrad, Outdoor', stadt: 'Essen', bundesland: 'Nordrhein-Westfalen', termin_start: '2026-02-25', termin_ende: '2026-03-01', besucher: 80000, besucher_jahr: 2025, besucher_status: 'Veroeffentlichter Wert 2025', ausstellungsflaeche_m2: null, ticketpreis_we_eur: 12, ticketpreis_unterderwoche_eur: 12, notiz: 'Online-Tagesticket laut Messe-Essen-Presse (EUR 12).', quelle_homepage: 'https://www.reise-camping.de' }, { rang: 28, messe: 'RETRO CLASSICS', thema: 'Klassische Fahrzeuge und Fahrkultur', stadt: 'Stuttgart', bundesland: 'Baden-Württemberg', termin_start: '2026-02-19', termin_ende: '2026-02-22', besucher: 72386, besucher_jahr: 2025, besucher_status: 'AUMA 2025', ausstellungsflaeche_m2: 46496, ticketpreis_we_eur: 25, ticketpreis_unterderwoche_eur: 25, notiz: 'Tagesticket laut offizieller Retro-Classics-Preisliste.', quelle_homepage: 'https://www.retro-classics.de' }, { rang: 29, messe: 'Freizeit Messe Nürnberg', thema: 'Freizeit, Touristik, Garten', stadt: 'Nürnberg', bundesland: 'Bayern', termin_start: '2026-03-04', termin_ende: '2026-03-08', besucher: 71000, besucher_jahr: 2025, besucher_status: 'Veroeffentlichter Wert 2025', ausstellungsflaeche_m2: null, ticketpreis_we_eur: 16, ticketpreis_unterderwoche_eur: 15, notiz: 'Ticketpreise laut offizieller Freizeit-Messe-Preistabelle 2026.', quelle_homepage: 'https://www.freizeitmesse.de' }, { rang: 30, messe: 'Thueringen Ausstellung', thema: 'Haus, Bau, Modernisieren, Lifestyle', stadt: 'Erfurt', bundesland: 'Thüringen', termin_start: '2026-02-28', termin_ende: '2026-03-08', besucher: 69988, besucher_jahr: 2025, besucher_status: 'Veroeffentlichter Wert 2025', ausstellungsflaeche_m2: null, ticketpreis_we_eur: 14, ticketpreis_unterderwoche_eur: 14, notiz: 'Tagesticket Erwachsene laut offizieller Thueringen-Ausstellung-Preisliste.', quelle_homepage: 'https://thueringen-ausstellung.de' } ]; const SORT_TYPES = { tage_bis_start: 'number', rang: 'number', messe: 'text', thema: 'text', stadt: 'text', bundesland: 'text', termin_start: 'date', termin_ende: 'date', besucher: 'number', besucher_jahr: 'number', besucher_status: 'text', ausstellungsflaeche_m2: 'number', ticketpreis_we_eur: 'number', ticketpreis_unterderwoche_eur: 'number', notiz: 'text', quelle_homepage: 'text' }; let sortKey = 'rang'; let sortDirection = 'asc'; let searchTerm = ''; function toNumber(value) { if (typeof value === 'number' && Number.isFinite(value)) { return value; } if (typeof value === 'string' && value.trim()) { const normalized = value.replace(/\./g, '').replace(',', '.').replace(/[^\d.-]/g, ''); const parsed = Number(normalized); return Number.isFinite(parsed) ? parsed : null; } return null; } function toDate(value) { if (!value) { return null; } const parsed = new Date(value); if (Number.isNaN(parsed.getTime())) { return null; } return parsed; } function getDaysUntil(startIso) { const start = toDate(startIso); if (!start) { return null; } const now = new Date(); now.setHours(0, 0, 0, 0); start.setHours(0, 0, 0, 0); return Math.round((start.getTime() - now.getTime()) / 86400000); } function normalizeRow(row) { return { ...row, tage_bis_start: getDaysUntil(row.termin_start) }; } function getSortValue(row, key) { const type = SORT_TYPES[key] || 'text'; const value = row[key]; if (type === 'number') { const numeric = toNumber(value); return numeric === null ? Number.NEGATIVE_INFINITY : numeric; } if (type === 'date') { const date = toDate(value); return date ? date.getTime() : Number.NEGATIVE_INFINITY; } return String(value || '').toLocaleLowerCase('de-DE'); } function formatNumber(value) { if (!Number.isFinite(value)) { return 'k.A.'; } return value.toLocaleString('de-DE'); } function formatPrice(value) { if (!Number.isFinite(value)) { return 'k.A.'; } return value.toLocaleString('de-DE', { minimumFractionDigits: 2, maximumFractionDigits: 2 }); } function formatDate(iso) { const date = toDate(iso); if (!date) { return 'k.A.'; } return date.toLocaleDateString('de-DE'); } function matchesSearch(row, term) { if (!term) { return true; } const haystack = [ row.messe, row.thema, row.stadt, row.bundesland, row.notiz ].join(' ').toLocaleLowerCase('de-DE'); return haystack.includes(term); } function sortRows(rows) { return [...rows].sort((a, b) => { const aValue = getSortValue(a, sortKey); const bValue = getSortValue(b, sortKey); if (aValue === bValue) { const fallback = (a.rang || 0) - (b.rang || 0); return sortDirection === 'asc' ? fallback : -fallback; } if (aValue > bValue) { return sortDirection === 'asc' ? 1 : -1; } return sortDirection === 'asc' ? -1 : 1; }); } function normalizeSortState(rawState) { if (!rawState || typeof rawState !== 'object') { return null; } const key = rawState.sortKey; const direction = rawState.sortDirection; if (!Object.prototype.hasOwnProperty.call(SORT_TYPES, key)) { return null; } if (direction !== 'asc' && direction !== 'desc') { return null; } return { sortKey: key, sortDirection: direction }; } function loadSortState() { try { const raw = localStorage.getItem(SORT_STATE_KEY); if (!raw) { return; } const parsed = JSON.parse(raw); const normalized = normalizeSortState(parsed); if (!normalized) { return; } sortKey = normalized.sortKey; sortDirection = normalized.sortDirection; } catch (_error) { // ignore } } function persistSortState() { try { localStorage.setItem(SORT_STATE_KEY, JSON.stringify({ sortKey, sortDirection })); } catch (_error) { // ignore } } function updateSortButtonState() { sortButtons.forEach((button) => { const key = button.dataset.tradeSort; const isActive = key === sortKey; button.classList.toggle('is-active', isActive); const indicator = isActive ? (sortDirection === 'asc' ? '▲' : '▼') : '↕'; const baseLabel = button.dataset.baseLabel || button.textContent.trim(); button.textContent = `${baseLabel} ${indicator}`; }); } function createCell(content) { const td = document.createElement('td'); td.textContent = content; return td; } function buildTradeFairQueries(row) { const base = String(row.messe || '').trim(); const city = String(row.stadt || '').trim(); const topic = String(row.thema || '').split(',')[0].trim(); return [...new Set([ base, [base, city].filter(Boolean).join(' '), [base, topic].filter(Boolean).join(' ') ].map((entry) => entry.trim()).filter(Boolean))]; } function openQueryFallback(query) { const trimmed = String(query || '').trim(); if (!trimmed) { return 0; } if (typeof window.buildBookmarkSearchUrl === 'function') { const built = window.buildBookmarkSearchUrl(trimmed); if (built) { window.open(built, '_blank', 'noopener'); return 1; } } const url = new URL('https://www.facebook.com/search/posts'); url.searchParams.set('q', trimmed); window.open(url.toString(), '_blank', 'noopener'); return 1; } function openTradeFairSearch(row) { const queries = buildTradeFairQueries(row); if (!queries.length) { return; } if (typeof window.openBookmarkQueries === 'function') { const opened = window.openBookmarkQueries(queries); if (opened > 0) { return; } } queries.forEach((query) => { openQueryFallback(query); }); } function createMesseCell(row) { const td = document.createElement('td'); const button = document.createElement('button'); button.type = 'button'; button.className = 'bookmark-subpage__messe-link'; button.textContent = row.messe || 'k.A.'; button.title = 'Als Bookmark-Suche oeffnen (3 Tabs)'; button.addEventListener('click', () => { openTradeFairSearch(row); }); td.appendChild(button); return td; } function render() { const normalizedRows = TRADE_FAIRS.map(normalizeRow); const filtered = normalizedRows.filter((row) => matchesSearch(row, searchTerm)); const sorted = sortRows(filtered); tableBody.innerHTML = ''; if (!sorted.length) { const emptyRow = document.createElement('tr'); const emptyCell = document.createElement('td'); emptyCell.colSpan = 16; emptyCell.textContent = 'Keine Messe zum Suchbegriff gefunden.'; emptyRow.appendChild(emptyCell); tableBody.appendChild(emptyRow); } else { sorted.forEach((row) => { const tr = document.createElement('tr'); tr.appendChild(createCell(Number.isFinite(row.tage_bis_start) ? String(row.tage_bis_start) : 'k.A.')); tr.appendChild(createCell(String(row.rang))); tr.appendChild(createMesseCell(row)); tr.appendChild(createCell(row.thema)); tr.appendChild(createCell(row.stadt)); tr.appendChild(createCell(row.bundesland)); tr.appendChild(createCell(formatDate(row.termin_start))); tr.appendChild(createCell(formatDate(row.termin_ende))); tr.appendChild(createCell(formatNumber(row.besucher))); tr.appendChild(createCell(formatNumber(row.besucher_jahr))); tr.appendChild(createCell(row.besucher_status)); tr.appendChild(createCell(formatNumber(row.ausstellungsflaeche_m2))); tr.appendChild(createCell(formatPrice(row.ticketpreis_we_eur))); tr.appendChild(createCell(formatPrice(row.ticketpreis_unterderwoche_eur))); tr.appendChild(createCell(row.notiz)); const sourceCell = document.createElement('td'); if (row.quelle_homepage) { const link = document.createElement('a'); link.href = row.quelle_homepage; link.target = '_blank'; link.rel = 'noopener'; link.textContent = row.quelle_homepage; sourceCell.appendChild(link); } else { sourceCell.textContent = 'k.A.'; } tr.appendChild(sourceCell); tableBody.appendChild(tr); }); } const activeSortButton = sortButtons.find((button) => button.dataset.tradeSort === sortKey); const sortLabel = activeSortButton?.dataset.baseLabel || sortKey; meta.textContent = `${sorted.length} von ${TRADE_FAIRS.length} Messen | Sortierung: ${sortLabel} (${sortDirection === 'asc' ? 'aufsteigend' : 'absteigend'})`; } sortButtons.forEach((button) => { const label = button.textContent.trim(); button.dataset.baseLabel = label; button.addEventListener('click', () => { const key = button.dataset.tradeSort; if (!key) { return; } if (sortKey === key) { sortDirection = sortDirection === 'asc' ? 'desc' : 'asc'; } else { sortKey = key; sortDirection = key === 'rang' ? 'asc' : 'desc'; } persistSortState(); sortButtons.forEach((btn) => { const baseLabel = btn.dataset.baseLabel || btn.textContent; btn.textContent = baseLabel; }); updateSortButtonState(); render(); }); }); searchInput.addEventListener('input', () => { searchTerm = searchInput.value.trim().toLocaleLowerCase('de-DE'); render(); }); loadSortState(); updateSortButtonState(); render(); })();