{"id":9201,"date":"2026-01-07T13:17:30","date_gmt":"2026-01-07T18:17:30","guid":{"rendered":"https:\/\/planyukon.ca\/?page_id=9201"},"modified":"2026-01-07T13:18:44","modified_gmt":"2026-01-07T18:18:44","slug":"test-page","status":"publish","type":"post","link":"https:\/\/planyukon.ca\/?p=9201","title":{"rendered":"Test Page"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"9201\" class=\"elementor elementor-9201\" data-elementor-post-type=\"post\">\n\t\t\t\t<div class=\"elementor-element elementor-element-f54bb7c e-flex e-con-boxed e-con e-parent\" data-id=\"f54bb7c\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-e34ebb8 elementor-widget elementor-widget-html\" data-id=\"e34ebb8\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<style>\n  \/* === UI FIXES === *\/\n  \/* Remove browser default focus outlines for accessibility on click *\/\n  #yukon-map:focus,\n  .leaflet-interactive:focus,\n  .leaflet-container:focus {\n    outline: none !important;\n    box-shadow: none !important;\n  }\n  \n  \/* Ensure map panes stack correctly within Divi\/Elementor context *\/\n  .leaflet-pane.leaflet-map-pane {\n    z-index: 10 !important;\n  }\n\n  \/* === CONTAINER STYLES === *\/\n  #yukon-map {\n    height: 520px;               \/* Vertical dimension *\/\n    width: 100%;                 \/* Responsive width *\/\n    border-radius: 8px;\n    background: transparent;     \/* Seamless integration with page bg *\/\n  }\n  \n  \/* === INTERACTION STYLES === *\/\n  .leaflet-interactive {\n    cursor: pointer;\n    transition: fill-opacity .15s ease, fill .15s ease, stroke .15s ease;\n  }\n  \n  \/* === POPUP STYLES === *\/\n  .ylupc-popup h4 { margin: 0 0 .25rem; font-size: 1.05rem; line-height: 1.2; }\n  .ylupc-popup p  { margin: 0 0 .5rem; }\n  \n  \/* Action button style *\/\n  .ylupc-popup a.button {\n    display: inline-block; \n    padding: .45rem .75rem; \n    border-radius: 6px; \n    text-decoration: none;\n    border: 2px solid #80c7bc; \n    color: #0a0a0a; \n    font-weight: 600;\n  }\n  .ylupc-popup a.button:hover { \n    background: rgba(128, 199, 188, 0.1); \n  }\n  \n  \/* Responsive popup sizing *\/\n  #yukon-map .leaflet-popup {\n    max-width: 260px; \n  }\n  @media (max-width: 600px) {\n    #yukon-map .ylupc-popup {\n        font-size: 0.9rem;\n    }\n  }\n\n  \/* === CONTROL VISIBILITY === *\/\n  \/* Hide default attribution\/zoom to reduce visual noise *\/\n  .leaflet-control-attribution,\n  .leaflet-control-zoom {\n    display: none !important;\n  }\n  \n  \/* Custom Recenter Control *\/\n  .ylupc-recenter-control {\n    background: transparent;\n    box-shadow: none;\n  }\n  .ylupc-recenter-control .ylupc-recenter-button {\n    display: block;\n    width: 26px;\n    height: 26px;\n    line-height: 24px;\n    text-align: center;\n    text-decoration: none;\n    font-size: 16px;\n    border-radius: 6px;\n    background: rgba(255, 255, 255, 0.2);\n    color: #333;\n    border: none;\n    cursor: pointer;\n  }\n  .ylupc-recenter-control .ylupc-recenter-button:hover {\n    background: rgba(255, 255, 255, 0.8);\n  }\n<\/style>\n\n<!-- Map Container -->\n<div id=\"yukon-map\" aria-label=\"Yukon planning regions map\"><\/div>\n\n<script>\n(function () {\n  \/**\n   * ========================================================================\n   * CONFIGURATION & CONSTANTS\n   * ========================================================================\n   *\/\n  \n  \/\/ Path to the GeoJSON data file (Relative path for environment portability)\n  const GEOJSON_URL = '\/wp-content\/uploads\/2025\/12\/YUKON_PLANNING_REGION_YT_250K_simplified.json'; \n  \n  \/\/ Animation and Viewport settings\n  const ZOOM_BUMP   = 0.0;       \n  const FIT_PADDING = [0, 0];    \n\n  \/**\n   * REGION SLUG MAPPING\n   * Maps the \"PLANNING_REGION_NAME\" from the JSON to the internal WordPress page slug.\n   * Keys must match exactly the 'name' property in the GeoJSON.\n   *\/\n  const REGION_LINKS = {\n    'Dawson':            '\/dawson\/',\n    'Inuvialuit':        '\/yukon-north-slope\/',\n    'Kaska Dena':        '\/kaska\/',\n    'Kluane':            '\/kluane\/',\n    'North Yukon':       '\/north-yukon\/',\n    'Northern Tutchone': '\/northern-tutchone\/',\n    'Peel Watershed':    '\/peel-watershed\/',\n    'Teslin':            '\/teslin\/',\n    'Whitehorse':        '\/whitehorse\/',\n    'White River':       '\/white-river\/',\n    'Na-Cho Nyak Dun':   '\/na-cho-nyak-dun\/'\n  };\n\n  \/**\n   * HTML Sanitizer\n   * Prevents XSS when rendering dynamic content in popups.\n   *\/\n  const esc = (s='') => String(s).replace(\/[&<>\"']\/g, m => (\n    {'&':'&','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[m]\n  ));\n\n  \/**\n   * Main Initialization Function\n   *\/\n  function initMap() {\n    \/\/ 1. Initialize Leaflet instance\n    const map = L.map('yukon-map', {\n      zoomControl: false,\n      scrollWheelZoom: false,\n      dragging: false,\n      doubleClickZoom: false,\n      boxZoom: false,\n      keyboard: true,\n      tap: true,\n      zoomSnap: 0.1\n    }).setView([64.5, -135.8], 4.5); \n\n    \/\/ 2. Fetch and Process Data\n    fetch(GEOJSON_URL, { cache: 'no-cache' })\n      .then(r => r.json())\n      .then(gj => {\n        \n        \/\/ Transform GeoJSON into a leaner internal structure\n        const lean = {\n          type: 'FeatureCollection',\n          features: (gj.features || []).map(f => {\n            \/\/ Determine region name safely\n            const regionName = f.properties?.name ?? f.properties?.PLANNING_REGION_NAME ?? 'Region';\n            \n            \/\/ Resolve URL: Lookup in REGION_LINKS first, fallback to JSON property, then hash\n            const resolvedUrl = REGION_LINKS[regionName] ?? f.properties?.link_url ?? '#';\n\n            return {\n              type: 'Feature',\n              geometry: f.geometry,\n              properties: {\n                name:      regionName,\n                link_url:  resolvedUrl,\n                status:    f.properties?.status ?? f.properties?.PLANNING_REGION_STATUS ?? '',\n                bndstatus: f.properties?.bndstatus ?? f.properties?.BOUNDARY_STATUS ?? 'approved'\n              }\n            };\n          })\n        };\n\n        \/\/ 3. Create Layer Filters\n        const tentativeFC = {\n          type: 'FeatureCollection',\n          features: lean.features.filter(f => (f.properties?.bndstatus || '').toLowerCase() === 'tentative')\n        };\n\n        const approvedFC = {\n          type: 'FeatureCollection',\n          features: lean.features.filter(f => (f.properties?.bndstatus || '').toLowerCase() !== 'tentative')\n        };\n\n        \/**\n         * Style and Interaction Binder\n         *\/\n        function bindFeature(feat, lyr) {\n          const { name, link_url, status, bndstatus } = feat.properties;\n          const isTentative = (bndstatus || '').toLowerCase() === 'tentative';\n          \n          \/\/ Visual constants\n          const BASE_WEIGHT = 2;\n          const HOVER_WEIGHT = BASE_WEIGHT * 2;\n          const baseLine  = isTentative ? '#7d7d7d'  : '#000000';\n          const hoverLine = isTentative ? '#7d7d7d' : '#000000'; \n\n          const base  = { fillColor: '#ffffff', fillOpacity: 1.0, color: baseLine, weight: BASE_WEIGHT };\n          const hover = { fillColor: '#80c7bc', fillOpacity: 0.35, color: hoverLine, weight: HOVER_WEIGHT };\n\n          \/\/ Event Listeners\n          lyr.on('mouseover', e => e.target.setStyle(hover));\n          lyr.on('mouseout',  e => e.target.setStyle(base));\n          lyr.bindTooltip(name, { sticky: true });\n\n          \/\/ Popup Construction\n          const html = `\n            <div class=\"ylupc-popup\">\n              <h4>${esc(name)}<\/h4>\n              ${status ? `<p><strong>Status:<\/strong> ${esc(status)}<\/p> ` : ``}\n              ${bndstatus ? `<p><strong>Boundary:<\/strong> ${esc(bndstatus)}<\/p> ` : ``}\n              <a class=\"button\" href=\"${esc(link_url)}\" aria-label=\"Open ${esc(name)} page\">Open region page<\/a>\n            <\/div>`;\n            \n          const POPUP_OPTIONS = {\n            maxWidth: 260, \n            autoPan: true,\n            keepInView: true,\n            autoPanPaddingTopLeft: [10, 80],\n            autoPanPaddingBottomRight: [10, 20]\n          };\n          lyr.bindPopup(html, POPUP_OPTIONS);\n        }\n\n        \/\/ 4. Add Layers to Map\n        \/\/ Tentative layer first (render order)\n        const tentativeLayer = L.geoJSON(tentativeFC, {\n          style: feat => ({ color: '#7d7d7d', weight: 2, fillColor: '#ffffff', fillOpacity: 1.0 }),\n          onEachFeature: bindFeature\n        }).addTo(map);\n\n        \/\/ Approved layer second\n        const approvedLayer = L.geoJSON(approvedFC, {\n          style: feat => ({ color: '#000000', weight: 2, fillColor: '#ffffff', fillOpacity: 1.0 }),\n          onEachFeature: bindFeature\n        }).addTo(map);\n\n        \/\/ 5. Fit View to Content\n        const group = L.featureGroup([tentativeLayer, approvedLayer]);\n        const b = group.getBounds?.();\n\n        function resetView() {\n            if (b && b.isValid()) {\n            map.fitBounds(b, { padding: FIT_PADDING, animate: true });\n            }\n        }\n        resetView();\n\n        \/\/ 6. Add Custom Controls\n        \/\/ Recenter Button\n        const recenterControl = L.control({ position: 'bottomright' });\n        recenterControl.onAdd = function (map) {\n            const container = L.DomUtil.create('div', 'leaflet-bar ylupc-recenter-control');\n            const btn = L.DomUtil.create('button', 'ylupc-recenter-button', container);\n            btn.type = 'button';\n            btn.title = 'Reset map view';\n            btn.innerHTML = '\u27f3'; \n            L.DomEvent.disableClickPropagation(container);\n            L.DomEvent.on(btn, 'click', function (e) {\n                L.DomEvent.preventDefault(e);\n                map.closePopup();\n                resetView(); \n            });\n            return container;\n        };\n        recenterControl.addTo(map);\n\n        \/\/ Static Legend\n        const legend = L.control({ position: 'topright' });\n        legend.onAdd = function () {\n          const div = L.DomUtil.create('div', 'ylupc-legend');\n          div.innerHTML = `\n            <div class=\"legend-title\">Boundary Status<\/div>\n            <div class=\"legend-row\"><span class=\"legend-swatch approved\"><\/span><span>Approved<\/span><\/div>\n            <div class=\"legend-row\"><span class=\"legend-swatch tentative\"><\/span><span>Tentative<\/span><\/div>\n          `;\n          return div;\n        };\n        legend.addTo(map);\n\n        \/\/ Inject Legend Styles\n        const legendCSS = `\n          .ylupc-legend { background: rgba(255,255,255,0.95); padding: 8px 10px; border-radius: 8px; font: 14px\/1.2 \"Noto Sans\", sans-serif; color: #000; box-shadow: 0 1px 4px rgba(0,0,0,0.25); }\n          .ylupc-legend .legend-title { font-weight: 700; margin-bottom: 6px; }\n          .ylupc-legend .legend-row { display: flex; align-items: center; gap: 8px; margin: 4px 0; white-space: nowrap; }\n          .ylupc-legend .legend-swatch { width: 20px; height: 12px; border: 2px solid #000; background: #fff; display: inline-block; border-radius: 2px; }\n          .ylupc-legend .legend-swatch.tentative { border-color: #7d7d7d; }\n          @media (max-width: 600px) { .ylupc-legend { padding: 4px 6px; font-size: 12px; } }\n        `;\n        const styleTag = document.createElement('style');\n        styleTag.innerHTML = legendCSS;\n        document.head.appendChild(styleTag);\n\n        \/\/ 7. Interaction Cleanup\n        \/\/ Disable interactions that interfere with page scrolling\n        map.dragging.disable();\n        map.scrollWheelZoom.disable();\n        map.doubleClickZoom.disable();\n        map.boxZoom.disable();\n        map.keyboard.disable();\n      })\n      .catch(err => console.error('GeoJSON load error:', err));\n\n    \/\/ Responsive Handling\n    const ro = new ResizeObserver(() => map.invalidateSize());\n    ro.observe(document.getElementById('yukon-map'));\n  }\n\n  \/\/ 8. Performance Optimization: Lazy Load\n  const target = document.getElementById('yukon-map');\n  const io = new IntersectionObserver(([entry]) => {\n    if (entry.isIntersecting) { initMap(); io.disconnect(); }\n  }, { rootMargin: '100px' });\n  io.observe(target);\n})();\n<\/script>\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\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"","protected":false},"author":5,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-9201","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/planyukon.ca\/index.php?rest_route=\/wp\/v2\/posts\/9201","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/planyukon.ca\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/planyukon.ca\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/planyukon.ca\/index.php?rest_route=\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/planyukon.ca\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=9201"}],"version-history":[{"count":0,"href":"https:\/\/planyukon.ca\/index.php?rest_route=\/wp\/v2\/posts\/9201\/revisions"}],"wp:attachment":[{"href":"https:\/\/planyukon.ca\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=9201"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/planyukon.ca\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=9201"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/planyukon.ca\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=9201"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}