{"id":442,"date":"2025-11-28T22:59:49","date_gmt":"2025-11-28T22:59:49","guid":{"rendered":"https:\/\/homenorthbaseball.ca\/LNH\/?page_id=442"},"modified":"2025-12-08T19:58:53","modified_gmt":"2025-12-08T19:58:53","slug":"2025-player-stats","status":"publish","type":"page","link":"https:\/\/homenorthbaseball.ca\/LNH\/2025-player-stats\/","title":{"rendered":"2025 PLAYER STATS"},"content":{"rendered":"\n<h2 class=\"wp-block-heading has-text-align-center\">2025 REGULAR SEASON PLAYER STATS<\/h2>\n\n\n\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>LNH Slo-Pitch Stats<\/title>\n    <link href=\"https:\/\/fonts.googleapis.com\/css2?family=Roboto:wght@400;500;700&#038;display=swap\" rel=\"stylesheet\">\n    <style>\n        body {\n            font-family: 'Roboto', Arial, sans-serif;\n            margin: 0px;\n            background: #f8f9fa;\n            color: #000;\n            font-size: 14px;\n            line-height: 1.4;\n        }\n        .controls {\n            text-align: center;\n            margin: 20px 0;\n        }\n        #filterInput {\n            padding: 10px 14px;\n            width: 340px;\n            max-width: 90%;\n            font-size: 15px;\n            border: 1px solid #999;\n            border-radius: 6px;\n        }\n        table {\n            width: 100%;\n            border-collapse: collapse;\n            background: white;\n            box-shadow: 0 2px 12px rgba(0,0,0,0.1);\n        }\n        th, td {\n            padding: 10px 6px;\n            text-align: center;\n            border: 1px solid #999;\n            color: #000;\n            white-space: nowrap;\n        }\nth {\n            background: #f0c53c;\n            color: #000000;\n            font-weight: 700;\n            cursor: pointer;\n            user-select: none;\n        }\n\n        \/* HOVER: black background + WHITE text so it\u2019s readable *\/\n        th:hover {\n            background: #000000 !important;\n            color: #ffffff !important;\n        }\n\n        \/* Rank column (first column) \u2013 stays yellow normally, black on hover *\/\n        th:first-child {\n            background: #f0c53c !important;\n            color: #000000 !important;\n            font-weight: 700;\n            cursor: default !important;\n        }\n        th:first-child:hover {\n            background: #000000 !important;\n            color: #ffffff !important;\n        }\n\n        \/* Body rank cells \u2013 always white background, bold black text *\/\n        td:first-child {\n            font-weight: 800;\n            color: #000 !important;\n            background: #ffffff !important;\n        }\n\n        \/* Header Rank cell stays black *\/\n       th:first-child {\n    background: #f0c53c !important;   \/* normal state \u2013 orange *\/\n    color: #000000 !important;\n    font-weight: 700;\n    cursor: default;                  \/* no pointer cursor since it\u2019s not sortable *\/\n}\n\n\n\/* Body rank column stays exactly as we want it *\/\ntd:first-child {\n    font-weight: 700;\n    color: #000 !important;\n    background: #ffffff !important;\n}        \nfont-weight: 700;\n            color: #ffffff;\n            background: inherit !important; \/* Inherits row background *\/\n        }\n\n        \/* Wider Name & Team columns *\/\n        .wide-col { width: 170px; min-width: 170px; }\n\n        \/* Row striping *\/\n        tr:nth-child(even) td { \n            background-color: #f9f9f9; \n        }\n        tr:hover td { \n            background-color: #d9d9d9 !important; \n        }\n\n        .sort-arrow { margin-left: 5px; font-size: 0.8em; }\n        .pagination {\n            text-align: center;\n            margin: 25px 0;\n        }\n        .pagination button {\n            padding: 8px 13px;\n            margin: 0 4px;\n            border: 1px solid #999;\n            background: white;\n            font-size: 14px;\n            border-radius: 4px;\n            cursor: pointer;\n        }\n        .pagination button.active {\n            background: #f0c53c;\n            color: #000000;\n            font-weight: bold;\n        }\n\n\/* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   MOBILE-ONLY HORIZONTAL SCROLL\n   (Desktop remains exactly the same)\n   \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 *\/\n@media (max-width: 768px) {\n    \/* Wrap the entire table in a scrollable container *\/\n    #statsTable {\n        display: block;\n        overflow-x: auto;\n        -webkit-overflow-scrolling: touch; \/* smooth scrolling on iOS *\/\n        white-space: nowrap;\n    }\n\n    \/* Make sure the table itself stays full-width inside the scroller *\/\n    #statsTable table {\n        display: table;\n        width: 100%;\n        min-width: 900px; \/* forces horizontal scroll when screen is narrow *\/\n    }\n\n    \/* Optional: add a subtle shadow to indicate it\u2019s scrollable *\/\n    #statsTable::after {\n        content: '';\n        position: absolute;\n        top: 0;\n        right: 0;\n        bottom: 0;\n        width: 12px;\n        background: linear-gradient(to left, rgba(0,0,0,0.15), transparent);\n        pointer-events: none;\n    }\n}\n\/* FREEZE FIRST TWO COLUMNS (Rank + Player Name) \u2014 MOBILE ONLY *\/\n@media (max-width: 768px) {\n    \/* Required for sticky columns to work properly *\/\n    table {\n        border-collapse: separate !important;\n        border-spacing: 0;\n    }\n\n    \/* First column \u2014 RANK *\/\n    th:nth-child(1),\n    td:nth-child(1) {\n        position: sticky;\n        left: 0;\n        background: white;\n        z-index: 10;\n        min-width: 50px;\n        box-shadow: 3px 0 6px -2px rgba(0,0,0,0.25);\n    }\n    th:nth-child(1) {\n        background: #f0c53c !important;\n        color: black !important;\n        z-index: 12;\n    }\n\n    \/* Second column \u2014 Player Name (or whatever your second column is) *\/\n    th:nth-child(2),\n    td:nth-child(2) {\n        position: sticky;\n        left: 50px;                    \/* must equal the width of the first column *\/\n        background: white;\n        z-index: 10;\n        min-width: 170px;\n        box-shadow: 3px 0 6px -2px rgba(0,0,0,0.25);\n    }\n    th:nth-child(2) {\n        background: #f0c53c !important;\n        color: black !important;\n        z-index: 12;\n    }\n\n    \/* Fix header hover color for the two frozen columns *\/\n    th:nth-child(1):hover,\n    th:nth-child(2):hover {\n        background: #000000 !important;\n        color: #ffffff;\n    }\n\n    \/* Optional: make the Rank column in body slightly bolder *\/\n    td:nth-child(1) {\n        font-weight: 800;\n        color: #ffffff;\n    }\n}\n    <\/style>\n<\/head>\n<body>\n<div class=\"controls\">\n    <input type=\"text\" id=\"filterInput\" placeholder=\"Search player or team...\" autocomplete=\"off\">\n<\/div>\n<table id=\"statsTable\">\n    <thead><tr id=\"headerRow\"><\/tr><\/thead>\n    <tbody id=\"tableBody\"><\/tbody>\n<\/table>\n<div class=\"pagination\" id=\"pagination\"><\/div>\n\n<script>\n    const csvUrl = 'https:\/\/docs.google.com\/spreadsheets\/d\/e\/2PACX-1vQ9UPrNtLTGq4TisHjvKCskSRJtf3EOGG2WXqTSMPQUIoKzxzg37Eym9jlXViFeyrKgy07KyiXMxOqD\/pub?gid=724629549&single=true&output=csv';\n    const PAGE_SIZE = 30;\n    let headers = [];\n    let originalData = [];\n    let filteredData = [];\n    let currentSortCol = -1;\n    let currentSortAsc = true;\n    let currentPage = 1;\n\n    function getWideColumnIndices() {\n        const namePatterns = [\/player\/i, \/name\/i];\n        const teamPatterns = [\/team\/i, \/tm\/i, \/^t$\/i];\n        const nameIdx = headers.findIndex(h => namePatterns.some(p => p.test(h)));\n        const teamIdx = headers.findIndex(h => teamPatterns.some(p => p.test(h)));\n        return { nameIdx, teamIdx };\n    }\n\n    function parseCSV(text) {\n        const lines = text.trim().split(\/\\r?\\n\/);\n        const h = lines[0].split(',').map(s => s.trim());\n        const rows = lines.slice(1)\n            .map(l => l.split(',').map(s => s.trim()))\n            .filter(r => r.length === h.length);\n        return { headers: h, data: rows };\n    }\n\n    function render() {\n        const filter = document.getElementById('filterInput').value.toLowerCase();\n        filteredData = originalData.filter(row =>\n            row.some(cell => cell.toLowerCase().includes(filter))\n        );\n\n        if (currentSortCol > 0) {\n            const idx = currentSortCol - 1;\n            filteredData.sort((a, b) => {\n                const va = a[idx], vb = b[idx];\n                const na = parseFloat(va), nb = parseFloat(vb);\n                if (!isNaN(na) && !isNaN(nb)) return (na - nb) * (currentSortAsc ? 1 : -1);\n                return va.localeCompare(vb) * (currentSortAsc ? 1 : -1);\n            });\n        }\n\n        const totalPages = Math.max(1, Math.ceil(filteredData.length \/ PAGE_SIZE));\n        currentPage = Math.min(currentPage, totalPages);\n        const start = (currentPage - 1) * PAGE_SIZE;\n        const pageData = filteredData.slice(start, start + PAGE_SIZE);\n\n        \/\/ Header\n        const headerRow = document.getElementById('headerRow');\n        headerRow.innerHTML = '';\n        const { nameIdx, teamIdx } = getWideColumnIndices();\n        ['RANK', ...headers].forEach((h, i) => {\n            const th = document.createElement('th');\n            if ((i-1) === nameIdx || (i-1) === teamIdx) th.classList.add('wide-col');\n            th.textContent = h;\n            if (i > 0) {\n                th.onclick = () => {\n                    const newAsc = (currentSortCol === i) ? !currentSortAsc : false;\n                    currentSortCol = i;\n                    currentSortAsc = newAsc;\n                    currentPage = 1;\n                    render();\n                };\n            }\n            if (i === currentSortCol) {\n                const arrow = document.createElement('span');\n                arrow.className = 'sort-arrow';\n                arrow.textContent = currentSortAsc ? '\u25b2' : '\u25bc';\n                th.appendChild(arrow);\n            }\n            headerRow.appendChild(th);\n        });\n\n        \/\/ Body\n        const tbody = document.getElementById('tableBody');\n        tbody.innerHTML = '';\n        pageData.forEach((row, i) => {\n            const tr = document.createElement('tr');\n            const rankTd = document.createElement('td');\n            rankTd.textContent = start + i + 1;\n            tr.appendChild(rankTd);\n\n            row.forEach((cell, colIdx) => {\n                const td = document.createElement('td');\n                if (colIdx === nameIdx || colIdx === teamIdx) td.classList.add('wide-col');\n                td.textContent = cell;\n                tr.appendChild(td);\n            });\n            tbody.appendChild(tr);\n        });\n\n        \/\/ Pagination\n        const pag = document.getElementById('pagination');\n        pag.innerHTML = '';\n        for (let p = 1; p <= totalPages; p++) {\n            const btn = document.createElement('button');\n            btn.textContent = p;\n            if (p === currentPage) btn.classList.add('active');\n            btn.onclick = () => { currentPage = p; render(); };\n            pag.appendChild(btn);\n        }\n\n        if (filteredData.length === 0) {\n            tbody.innerHTML = '<tr><td colspan=\"20\" style=\"padding:30px;\">No players found<\/td><\/tr>';\n        }\n    }\n\n    \/\/ Load data\n    fetch(csvUrl)\n        .then(r => r.text())\n        .then(text => {\n            const parsed = parseCSV(text);\n            headers = parsed.headers;\n            originalData = parsed.data;\n            const avgIdx = headers.indexOf('AVG');\n            if (avgIdx !== -1) {\n                currentSortCol = avgIdx + 1;\n                currentSortAsc = false;\n            }\n            render();\n        })\n        .catch(err => {\n            document.getElementById('tableBody').innerHTML =\n                '<tr><td colspan=\"20\">Error loading stats<\/td><\/tr>';\n            console.error(err);\n        });\n\n    document.getElementById('filterInput').addEventListener('input', () => {\n        currentPage = 1;\n        render();\n    });\n<\/script>\n<\/body>\n<\/html>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>2025 REGULAR SEASON PLAYER STATS LNH Slo-Pitch Stats<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-442","page","type-page","status-publish","hentry"],"blocksy_meta":[],"_links":{"self":[{"href":"https:\/\/homenorthbaseball.ca\/LNH\/wp-json\/wp\/v2\/pages\/442","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/homenorthbaseball.ca\/LNH\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/homenorthbaseball.ca\/LNH\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/homenorthbaseball.ca\/LNH\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/homenorthbaseball.ca\/LNH\/wp-json\/wp\/v2\/comments?post=442"}],"version-history":[{"count":98,"href":"https:\/\/homenorthbaseball.ca\/LNH\/wp-json\/wp\/v2\/pages\/442\/revisions"}],"predecessor-version":[{"id":819,"href":"https:\/\/homenorthbaseball.ca\/LNH\/wp-json\/wp\/v2\/pages\/442\/revisions\/819"}],"wp:attachment":[{"href":"https:\/\/homenorthbaseball.ca\/LNH\/wp-json\/wp\/v2\/media?parent=442"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}