{"id":473,"date":"2025-04-18T01:01:37","date_gmt":"2025-04-17T23:01:37","guid":{"rendered":"http:\/\/www.mittwochsregatta-neustadt.de\/mittwochsregatta\/?page_id=473"},"modified":"2025-06-30T18:31:56","modified_gmt":"2025-06-30T16:31:56","slug":"ergebnisse-2","status":"publish","type":"page","link":"http:\/\/www.mittwochsregatta-neustadt.de\/mittwochsregatta\/ergebnisse-2\/","title":{"rendered":"Ergebnisse"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"473\" class=\"elementor elementor-473\">\n\t\t\t\t<div class=\"elementor-element elementor-element-f3e0c70 e-flex e-con-boxed wpr-particle-no wpr-jarallax-no wpr-parallax-no wpr-sticky-section-no e-con e-parent\" data-id=\"f3e0c70\" data-element_type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-c776221 elementor-widget elementor-widget-text-editor\" data-id=\"c776221\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p data-start=\"91\" data-end=\"105\">\ud83d\udca1 <strong data-start=\"94\" data-end=\"103\">Tipp:<\/strong><\/p><ul data-start=\"106\" data-end=\"343\"><li data-start=\"106\" data-end=\"231\"><p data-start=\"108\" data-end=\"231\">Klicke auf eine Spalten\u00fcberschrift, um die Tabelle auf- oder absteigend zu sortieren (z.\u202fB. nach Startzeit oder Platzierung).<\/p><\/li><li data-start=\"232\" data-end=\"343\"><p data-start=\"234\" data-end=\"343\">Mit einem Klick auf <strong data-start=\"254\" data-end=\"279\">\u201eFilter zur\u00fccksetzen\u201c<\/strong> werden alle Sortierungen und Filtereinstellungen zur\u00fcckgesetzt.<\/p><\/li><\/ul>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-2b24007 e-con-full e-flex wpr-particle-no wpr-jarallax-no wpr-parallax-no wpr-sticky-section-no e-con e-parent\" data-id=\"2b24007\" data-element_type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-ddfc98c elementor-widget__width-initial elementor-widget elementor-widget-html\" data-id=\"ddfc98c\" data-element_type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<!DOCTYPE html>\n<html lang=\"de\">\n<head>\n    <meta charset=\"UTF-8\" \/>\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" \/>\n  <title>Mittwochsregatta Ergebnisse \u2013 JSONP mit Suche<\/title>\n  <style>\n    :root {\n      --gray-light: #f3f4f6;\n      --gray-border: #d1d5db;\n      --gray-dark: #111827;\n      --blue: #3b82f6;\n    }\n\n    html, body {\n      margin: 0;\n      padding: 0;\n      font-family: 'Open Sans', sans-serif;\n      background-color: white;\n    }\n\n    .page-container {\n      max-width: 1600px;\n      margin: 0 auto;\n      padding: 0 12px;\n      display: flex;\n      flex-direction: column;\n      min-height: 100vh;\n      box-sizing: border-box;\n    }\n\n    .control-box {\n      display: flex;\n      flex-wrap: wrap;\n      gap: 10px;\n      padding: 12px;\n      border: 1px solid var(--gray-border);\n      background-color: white;\n      margin-bottom: 12px;\n      align-items: center;\n      flex-shrink: 0;\n    }\n\n    .search-container {\n      position: relative;\n      flex: 1 1 300px;\n    }\n\n    .search-container input {\n      width: 100%;\n      padding: 10px 16px 10px 40px;\n      font-size: 16px;\n      border: 1px solid var(--gray-border);\n      border-radius: 12px;\n      background-color: var(--gray-light);\n      box-shadow: 0 2px 4px rgba(0, 0, 0, 0.04);\n      color: #374151;\n      appearance: none;\n    }\n\n    .search-container::before {\n      content: \"\ud83d\udd0d\";\n      position: absolute;\n      left: 14px;\n      top: 50%;\n      transform: translateY(-50%);\n      font-size: 18px;\n      color: #6b7280;\n    }\n\n    .download-button {\n      flex-shrink: 0;\n    }\n\n    .download-button button {\n      padding: 10px 20px;\n      font-size: 16px;\n      border-radius: 0;\n      background-color: var(--gray-light);\n      color: #374151;\n      border: 1px solid var(--gray-border);\n      cursor: pointer;\n      box-shadow: 0 2px 4px rgba(0, 0, 0, 0.04);\n    }\n\n    .reset-button {\n      flex-basis: 100%;\n    }\n\n    .reset-button button {\n      width: 100%;\n      margin-top: 10px;\n      padding: 12px;\n      font-size: 16px;\n      background-color: var(--gray-light);\n      border: 1px solid var(--gray-border);\n      color: #374151;\n      cursor: pointer;\n      box-shadow: 0 2px 4px rgba(0, 0, 0, 0.04);\n    }\n\n    .scroll-container {\n      overflow-x: auto;\n      overflow-y: auto;\n      max-height: calc(100vh - 180px);\n      border: 1px solid var(--gray-border);\n      background-color: white;\n      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);\n      width: 100%;\n    }\n\n    .clone-scroll {\n      height: 20px;\n      overflow-x: auto;\n      overflow-y: hidden;\n      border: none;\n      margin-bottom: 4px;\n    }\n\n    .clone-scroll::-webkit-scrollbar {\n      height: 12px;\n    }\n\n    .clone-scroll::-webkit-scrollbar-thumb {\n      background-color: var(--gray-border);\n    }\n\n    .regatta-table {\n      border-collapse: separate;\n      border-spacing: 0;\n      width: 100%;\n      min-width: 800px;\n      font-size: 14px;\n    }\n\n    th, td {\n      padding: 8px 12px;\n      border: 1px solid var(--gray-border);\n      white-space: nowrap;\n      background-color: white;\n    }\n\n    thead th {\n      position: sticky;\n      top: 0;\n      background-color: var(--gray-light);\n      color: var(--gray-dark);\n      font-weight: 600;\n      z-index: 20;\n      font-size: 17px;\n      text-align: left;\n      cursor: pointer;\n    }\n\n    tbody td:first-child,\n    thead th:first-child {\n      position: sticky;\n      left: 0;\n      background-color: var(--gray-light);\n      z-index: 30;\n      min-width: 60px;\n    }\n\n    tbody td:nth-child(2),\n    thead th:nth-child(2) {\n      position: sticky;\n      left: 60px;\n      background-color: var(--gray-light);\n      z-index: 30;\n      min-width: 180px;\n    }\n\n    tbody td:nth-child(8),\n    thead th:nth-child(8) {\n      min-width: 50px;\n    }\n\n    tr:nth-child(even) td {\n      background-color: #f9fafb;\n    }\n\n    .highlight {\n      background-color: #bfdbfe !important;\n      font-weight: bold;\n      transition: background 0.3s ease;\n    }\n\n    #loader {\n      display: flex;\n      flex-direction: column;\n      align-items: center;\n      margin: 30px;\n    }\n\n    .spinner {\n      width: 48px;\n      height: 48px;\n      border: 5px solid #e5e7eb;\n      border-top: 5px solid var(--blue);\n      border-radius: 50%;\n      animation: spin 1s linear infinite;\n    }\n\n    @keyframes spin {\n      to {\n        transform: rotate(360deg);\n      }\n    }\n\n    .loader-text {\n      margin-top: 10px;\n      font-size: 14px;\n      color: #6b7280;\n    }\n\n    @media (max-width: 768px) {\n      .download-button button {\n        width: 100%;\n      }\n    }\n  <\/style>\n<\/head>\n<body>\n  <div id=\"loader\">\n    <div class=\"spinner\"><\/div>\n    <div class=\"loader-text\">Lade Daten...<\/div>\n  <\/div>\n\n  <div class=\"page-container\">\n    <div class=\"control-box\">\n      <div class=\"search-container\">\n        <input list=\"bootsliste\" id=\"searchInput\" oninput=\"scrollToRow()\" placeholder=\"Bootsnamen oder Startnummer eingeben...\" \/>\n        <datalist id=\"bootsliste\"><\/datalist>\n      <\/div>\n      <div class=\"download-button\">\n        <button id=\"pdfButton\">PDF herunterladen<\/button>\n      <\/div>\n      <div class=\"reset-button\">\n        <button id=\"resetButton\">Filter zur\u00fccksetzen<\/button>\n      <\/div>\n    <\/div>\n\n    <div class=\"scroll-container clone-scroll\" id=\"scroll-clone\"><\/div>\n    <div class=\"scroll-container\" id=\"regatta-data\"><\/div>\n  <\/div>\n\n  <script>\n    let currentSortColumn = null;\n    let currentSortDirection = 1;\n    let originalData = null;\n\n    function parseTimeToSeconds(timeStr) {\n      const parts = timeStr.replace(',', '.').split(':');\n      if (parts.length === 3) {\n        return (+parts[0]) * 3600 + (+parts[1]) * 60 + (+parts[2]);\n      }\n      return NaN;\n    }\nfunction downloadTableAsPDF() {\n  const { jsPDF } = window.jspdf;\n  const doc = new jsPDF('landscape', 'mm', 'a3');\n\n  const img = new Image();\n  img.crossOrigin = 'Anonymous';\n  img.src = 'https:\/\/www.mittwochsregatta-neustadt.de\/mittwochsregatta\/wp-content\/uploads\/2025\/04\/logo.png';\n\n  let isDone = false;\n\n  function generatePDF(withLogo) {\n    if (withLogo) {\n      doc.addImage(img, 'PNG', 10, 10, 15, 15);\n      doc.text('Mittwochsregatta Ergebnisse', 30, 20);\n    } else {\n      doc.text('Mittwochsregatta Ergebnisse', 14, 20);\n    }\n\n    doc.autoTable({\n      html: '.regatta-table',\n      startY: 30,\n      theme: 'striped',\n      styles: {\n        fontSize: 7,\n        cellPadding: 2,\n        lineWidth: 0.1,\n        lineColor: [200, 200, 200]\n      },\n      headStyles: {\n        fillColor: [59, 130, 246]\n      },\n      margin: { top: 30 },\n      columnStyles: {\n        7: { cellWidth: 20 }\n      }\n    });\n\n    doc.save('Mittwochsregatta_Ergebnisse.pdf');\n    isDone = true;\n  }\n\n  img.onload = () => { if (!isDone) generatePDF(true); };\n  img.onerror = () => {\n    alert('Logo konnte nicht geladen werden. PDF wird ohne Logo generiert.');\n    if (!isDone) generatePDF(false);\n  };\n  setTimeout(() => { if (!isDone) generatePDF(false); }, 3000);\n}\n\n    function parseShortTimeToSeconds(timeStr) {\n      const parts = timeStr.split(':');\n      if (parts.length === 2) {\n        return (+parts[0]) * 3600 + (+parts[1]) * 60;\n      }\n      return NaN;\n    }\n\n    function highlightColumn(columnIndex) {\n      const rows = document.querySelectorAll('.regatta-table tr');\n      rows.forEach(row => {\n        const cells = row.querySelectorAll('td, th');\n        cells.forEach(cell => cell.classList.remove('highlight'));\n        if (cells[columnIndex]) {\n          cells[columnIndex].classList.add('highlight');\n        }\n      });\n    }\n\n    function zeigeTabelle(data) {\n      if (!originalData) {\n        originalData = JSON.parse(JSON.stringify(data));\n      }\n\n      const container = document.getElementById(\"regatta-data\");\n      const cloneScroll = document.getElementById(\"scroll-clone\");\n\n      container.innerHTML = \"\";\n      cloneScroll.innerHTML = \"\";\n\n      const table = document.createElement(\"table\");\n      table.className = \"regatta-table\";\n\n      const thead = document.createElement(\"thead\");\n      const headRow = document.createElement(\"tr\");\n\n      const highlightStartIndex = 8;\n      const highlightEndIndex = 27;\n\n      data[0].forEach((cell, columnIndex) => {\n        const th = document.createElement(\"th\");\n        th.textContent = cell;\n\n        const isHighlightable = columnIndex >= highlightStartIndex && columnIndex <= highlightEndIndex;\n\n        if (![1, 2, 3, 4, 5].includes(columnIndex)) {\n          th.addEventListener(\"click\", () => {\n            sortTableByColumn(table, columnIndex);\n            if (isHighlightable) highlightColumn(columnIndex);\n          });\n        } else if (isHighlightable) {\n          th.addEventListener(\"click\", () => highlightColumn(columnIndex));\n        } else {\n          th.style.cursor = \"default\";\n        }\n\n        headRow.appendChild(th);\n      });\n\n      thead.appendChild(headRow);\n      table.appendChild(thead);\n\n      const tbody = document.createElement(\"tbody\");\n\n      for (let i = 1; i < data.length; i++) {\n        const rowData = data[i];\n        if (rowData.every(cell => cell.trim() === '')) continue;\n        const row = document.createElement(\"tr\");\n        rowData.forEach(cell => {\n          const td = document.createElement(\"td\");\n          td.textContent = cell;\n          row.appendChild(td);\n        });\n        tbody.appendChild(row);\n      }\n\n      table.appendChild(tbody);\n      container.appendChild(table);\n\n      const clone = document.createElement(\"div\");\n      clone.style.height = \"1px\";\n      clone.style.visibility = \"hidden\";\n      clone.style.pointerEvents = \"none\";\n      clone.style.userSelect = \"none\";\n      clone.style.minWidth = table.scrollWidth + \"px\";\n      cloneScroll.appendChild(clone);\n\n      cloneScroll.onscroll = () => container.scrollLeft = cloneScroll.scrollLeft;\n      container.onscroll = () => cloneScroll.scrollLeft = container.scrollLeft;\n\n      populateDropdown();\n      document.getElementById('loader').style.display = 'none';\n    }\n\n    function sortTableByColumn(table, columnIndex) {\n      const tbody = table.querySelector(\"tbody\");\n      const rowsArray = Array.from(tbody.rows);\n\n      if (currentSortColumn === columnIndex) {\n        currentSortDirection *= -1;\n      } else {\n        currentSortColumn = columnIndex;\n        currentSortDirection = 1;\n      }\n\n      const mainRows = rowsArray.slice(0, -5);\n      const fixedRows = rowsArray.slice(-5);\n\n      mainRows.sort((a, b) => {\n        const valA = a.cells[columnIndex].textContent.trim().toLowerCase();\n        const valB = b.cells[columnIndex].textContent.trim().toLowerCase();\n\n        if (valA.match(\/^\\d{1,2}:\\d{2}$\/) && valB.match(\/^\\d{1,2}:\\d{2}$\/)) {\n          const timeA = parseShortTimeToSeconds(valA);\n          const timeB = parseShortTimeToSeconds(valB);\n          return (timeA - timeB) * currentSortDirection;\n        }\n\n        if (columnIndex === 7) {\n          const timeA = parseTimeToSeconds(valA);\n          const timeB = parseTimeToSeconds(valB);\n          return (timeA - timeB) * currentSortDirection;\n        }\n\n        const numA = parseFloat(valA.replace(',', '.'));\n        const numB = parseFloat(valB.replace(',', '.'));\n        const isNumberA = !isNaN(numA);\n        const isNumberB = !isNaN(numB);\n\n        const isDnfA = valA === 'dnf';\n        const isDnfB = valB === 'dnf';\n        const isDsqA = valA === 'dsq';\n        const isDsqB = valB === 'dsq';\n        const isEmptyA = valA === '';\n        const isEmptyB = valB === '';\n\n        if (isNumberA && isNumberB) return (numA - numB) * currentSortDirection;\n        if (isNumberA && isDnfB) return -1 * currentSortDirection;\n        if (isDnfA && isNumberB) return 1 * currentSortDirection;\n        if (isDnfA && isDnfB) return 0;\n        if (isNumberA || isDnfA) return -1;\n        if (isNumberB || isDnfB) return 1;\n        if (isDsqA && !isDsqB) return 1;\n        if (!isDsqA && isDsqB) return -1;\n        if (isEmptyA && !isEmptyB) return 1;\n        if (!isEmptyA && isEmptyB) return -1;\n\n        return valA.localeCompare(valB) * currentSortDirection;\n      });\n\n      [...mainRows, ...fixedRows].forEach(row => tbody.appendChild(row));\n    }\n\n    function scrollToRow() {\n      const input = document.getElementById('searchInput').value.toLowerCase().trim();\n      const rows = document.querySelectorAll('.regatta-table tbody tr');\n      rows.forEach(row => row.querySelectorAll('td').forEach(cell => cell.classList.remove('highlight')));\n      if (!input) return;\n      rows.forEach(row => {\n        const cells = row.querySelectorAll('td');\n        if (cells.length > 1) {\n          const nr = cells[0].innerText.toLowerCase();\n          const ship = cells[1].innerText.toLowerCase();\n          if (nr === input || ship === input) {\n            row.scrollIntoView({ behavior: 'smooth', block: 'center' });\n            cells.forEach(cell => cell.classList.add('highlight'));\n          }\n        }\n      });\n    }\n\n    function populateDropdown() {\n      const datalist = document.getElementById('bootsliste');\n      if (!datalist) return;\n      datalist.innerHTML = '';\n      const rows = document.querySelectorAll('.regatta-table tbody tr');\n      const seen = new Set();\n      rows.forEach(row => {\n        const cells = row.querySelectorAll('td');\n        if (cells.length > 1) {\n          const nr = cells[0].innerText.trim();\n          const ship = cells[1].innerText.trim();\n          if (nr && !seen.has(nr)) {\n            datalist.innerHTML += `<option value=\"${nr}\">`;\n            seen.add(nr);\n          }\n          if (ship && !seen.has(ship)) {\n            datalist.innerHTML += `<option value=\"${ship}\">`;\n            seen.add(ship);\n          }\n        }\n      });\n    }\n\n    function ladeJSONP(url) {\n      const script = document.createElement('script');\n      script.src = url;\n      document.body.appendChild(script);\n    }\n\n    document.getElementById('resetButton').addEventListener('click', () => {\n      document.getElementById('searchInput').value = '';\n      scrollToRow();\n      currentSortColumn = null;\n      currentSortDirection = 1;\n      zeigeTabelle(originalData);\n    });\ndocument.getElementById('pdfButton').addEventListener('click', downloadTableAsPDF);\n\n    ladeJSONP(\"https:\/\/www.mittwochsregatta-neustadt.de\/mittwochsregatta\/csv_proxy_results_jasonP.php?callback=zeigeTabelle\");\n  <\/script>\n  <script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/jspdf\/2.5.1\/jspdf.umd.min.js\"><\/script>\n<script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/jspdf-autotable\/3.5.25\/jspdf.plugin.autotable.min.js\"><\/script>\n<\/body>\n<\/html>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>\ud83d\udca1 Tipp: Klicke auf eine Spalten\u00fcberschrift, um die Tabelle auf- oder absteigend zu sortieren (z.\u202fB. nach Startzeit oder Platzierung). Mit einem Klick auf \u201eFilter zur\u00fccksetzen\u201c werden alle Sortierungen und Filtereinstellungen zur\u00fcckgesetzt. Mittwochsregatta Ergebnisse \u2013 JSONP mit Suche Lade Daten&#8230; PDF herunterladen Filter zur\u00fccksetzen<\/p>\n","protected":false},"author":2,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-473","page","type-page","status-publish","hentry"],"acf":[],"_links":{"self":[{"href":"http:\/\/www.mittwochsregatta-neustadt.de\/mittwochsregatta\/wp-json\/wp\/v2\/pages\/473","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/www.mittwochsregatta-neustadt.de\/mittwochsregatta\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"http:\/\/www.mittwochsregatta-neustadt.de\/mittwochsregatta\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"http:\/\/www.mittwochsregatta-neustadt.de\/mittwochsregatta\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"http:\/\/www.mittwochsregatta-neustadt.de\/mittwochsregatta\/wp-json\/wp\/v2\/comments?post=473"}],"version-history":[{"count":238,"href":"http:\/\/www.mittwochsregatta-neustadt.de\/mittwochsregatta\/wp-json\/wp\/v2\/pages\/473\/revisions"}],"predecessor-version":[{"id":1435,"href":"http:\/\/www.mittwochsregatta-neustadt.de\/mittwochsregatta\/wp-json\/wp\/v2\/pages\/473\/revisions\/1435"}],"wp:attachment":[{"href":"http:\/\/www.mittwochsregatta-neustadt.de\/mittwochsregatta\/wp-json\/wp\/v2\/media?parent=473"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}