1(function () { 2 let validSlugs = null; 3 const CACHE_KEY = 'cec_valid_slugs_data'; 4 const CACHE_EXPIRY = 24 * 60 * 60 * 1000; // 24 hours 5 6 /** 7 * 1. Hardened Fetching with Defensive Filtering 8 */ 9 async function fetchValidSlugs() { 10 if (validSlugs !== null) return validSlugs; 11 12 try { 13 const cached = localStorage.getItem(CACHE_KEY); 14 if (cached) { 15 const { slugs, timestamp } = JSON.parse(cached); 16 if (Date.now() - timestamp < CACHE_EXPIRY && Array.isArray(slugs)) { 17 validSlugs = slugs; 18 return validSlugs; 19 } 20 } 21 } catch (_) {} 22 23 try { 24 const response = await fetch('/wp-content/uploads/location-slugs.json', { cache: "no-cache" }); 25 if (!response.ok) throw new Error("Network error"); 26 const data = await response.json(); 27 28 if (Array.isArray(data.valid_slugs)) { 29 // Filter out bad data and normalize to lowercase 30 validSlugs = data.valid_slugs 31 .filter(s => s && typeof s.slug === "string") 32 .map(s => ({ ...s, slug: s.slug.toLowerCase() })); 33 34 try { 35 localStorage.setItem(CACHE_KEY, JSON.stringify({ slugs: validSlugs, timestamp: Date.now() })); 36 } catch (_) {} 37 return validSlugs; 38 } 39 } catch (error) { 40 console.error("Location fetch failed:", error); 41 } 42 return []; 43 } 44 45 function getCookie(name) { 46 const matches = document.cookie.match(new RegExp("(?:^|; )" + name.replace(/([.*+?^${}()|[\]\\])/g, '\\$1') + "=([^;]*)")); 47 return matches ? decodeURIComponent(matches[1]) : undefined; 48 } 49 50 function setCookie(name, value, days) { 51 let expires = ""; 52 if (days >= 0) { 53 let date = new Date(); 54 date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); 55 expires = "; expires=" + date.toUTCString(); 56 } else { 57 expires = "; expires=Thu, 01 Jan 1970 00:00:00 GMT"; 58 } 59 const host = window.location.hostname; 60 const isLocal = 61 host === "localhost" || 62 host === "127.0.0.1" || 63 host.endsWith(".local") || 64 host.endsWith(".test"); 65 const isChuck = host.endsWith("chuckecheese.com"); 66 const isHttps = window.location.protocol === "https:"; 67 const domainAttr = isChuck ? "; domain=.chuckecheese.com" : ""; 68 const secureAttr = !isLocal && isHttps ? "; Secure" : ""; 69 document.cookie = `${name}=${encodeURIComponent(value)}${expires}; path=/${domainAttr}; SameSite=Lax${secureAttr}`; 70 } 71 72 function lockLocationPickerFlag(value) { 73 try { 74 Object.defineProperty(window, "CEC_SHOW_LOCATION_PICKER", { 75 value: !!value, 76 writable: false, 77 configurable: false, 78 enumerable: true, 79 }); 80 } catch (_) { 81 window.CEC_SHOW_LOCATION_PICKER = !!value; 82 } 83 } 84 85 function normalizePath(path) { 86 if (!path || path === "") return "/"; 87 return path.startsWith("/") ? path : `/${path}`; 88 } 89 90 function isUtilityPath(pathname) { 91 return ["/select-location/", "/select-location"].includes(pathname); 92 } 93 94 function stripSlugFromPath(pathname, validSlugsList) { 95 const normalizedPath = normalizePath(pathname); 96 const segments = normalizedPath.split("/").filter(Boolean); 97 98 if (segments.length === 0) return "/"; 99 100 const firstSegment = (segments[0] || "").toLowerCase(); 101 const isSlugged = validSlugsList.some(s => s.slug === firstSegment); 102 103 if (!isSlugged) return normalizedPath; 104 105 const stripped = `/${segments.slice(1).join("/")}`; 106 return stripped === "/" || stripped === "" ? "/" : stripped; 107 } 108 109 async function getCurrentReturnPath() { 110 const slugs = await fetchValidSlugs(); 111 const { pathname, search, hash } = window.location; 112 113 // If slugs failed to load, don't try to strip â return bare path 114 if (!slugs || slugs.length === 0) { 115 const cleanPath = isUtilityPath(pathname) ? "/" : pathname; 116 return `${cleanPath}${search}${hash}`; 117 } 118 119 let cleanPath = pathname; 120 121 if (!isUtilityPath(pathname)) { 122 cleanPath = stripSlugFromPath(pathname, slugs); 123 } else { 124 cleanPath = "/"; 125 } 126 127 return `${cleanPath}${search}${hash}`; 128 } 129 130 function is404Page() { 131 return window.CEC_IS_404 === true; 132 } 133 134 function shouldAutoOpenLocationPicker(pathname) { 135 const utilityPaths = ["/", "/select-location/", "/select-location"]; 136 if (is404Page()) return false; 137 return !utilityPaths.includes(pathname); 138 } 139 140 141 /** 142 * Helper: Clean localization for a single anchor 143 */ 144 function localizeLink(link, userSlug, validSlugsList, origin) { 145 const href = link.getAttribute("href"); 146 if (!href 147 || href.startsWith("#") 148 || href.startsWith("mailto:") 149 || href.startsWith("tel:") 150 || href.includes("wp-admin") 151 || href.includes("wp-content") 152 ) return; 153 154 let url; 155 try { url = new URL(href, origin); } catch { return; } 156 157 if (url.origin !== origin) return; 158
159 const pathSegments = url.pathname.split("/").filter(Boolean); 160 const firstSegment = (pathSegments[0] || "").toLowerCase(); 161 162 if (firstSegment === userSlug) return; 163 164 const isSlugged = validSlugsList.some(s => s.slug === firstSegment); 165 const utilityPaths = ["/", "/select-location/", "/select-location"]; 166 const isUtility = utilityPaths.includes(url.pathname); 167 168 if (!isSlugged && !isUtility) { 169 url.pathname = `/${userSlug}${url.pathname}`; 170 link.href = url.toString(); 171 } 172 } 173 174 /** 175 * 2. Hardened Redirection with Cookie Normalization 176 */ 177 async function handleRedirection() { 178 const slugs = await fetchValidSlugs(); 179 if (!Array.isArray(slugs) || slugs.length === 0) return; 180 181 const cookie = getCookie("cecLocation"); 182 const { pathname, search, hash, origin } = window.location; 183 const isAsset = pathname.startsWith("/wp-content/") 184 || /\.(css|js|map|json|xml|txt|png|jpg|jpeg|gif|svg|webp|ico|pdf)$/i.test(pathname); 185 if (isAsset) return; 186 const firstSegment = (pathname.split("/").filter(Boolean)[0] || "").toLowerCase(); 187 const isSlugged = slugs.some(s => s.slug === firstSegment); 188 189 // SYNC: URL overrides Cookie 190 if (isSlugged) { 191 const urlMatch = slugs.find(s => s.slug === firstSegment); 192 if (urlMatch) { 193 setCookie('cecLocation', JSON.stringify(urlMatch), 365); 194 lockLocationPickerFlag(false); 195 } 196 return; 197 } 198 199 if (cookie) { 200 try { 201 const locationData = JSON.parse(cookie); 202 203 // NORMALIZE: Ensure cookie slug is lowercase for validation 204 const cookieSlug = (locationData.slug || "").toLowerCase(); 205 const cookieSlugValid = slugs.some(s => s.slug === cookieSlug); 206 207 if (!cookieSlugValid) { 208 setCookie('cecLocation', '', -1); 209 if (shouldAutoOpenLocationPicker(pathname)) { 210 setLocationPickerFlag(true); 211 } 212 return; 213 } 214 215 // Valid cookie should suppress the picker redirect 216 lockLocationPickerFlag(false); 217 218 // Global utility paths should not be slug-forced 219 const utilityPaths = ["/select-location/", "/select-location"]; 220 if (utilityPaths.includes(pathname)) return; 221 222 if (pathname === "/") { 223 window.location.replace(`${origin}/${cookieSlug}${search}${hash}`); 224 return; 225 } 226 227 window.location.replace(`${origin}/${cookieSlug}${pathname}${search}${hash}`); 228 } catch (e) { console.error("Cookie sync error", e); } 229 } else { 230 if (shouldAutoOpenLocationPicker(pathname)) { 231 setLocationPickerFlag(true); 232 } 233 } 234 } 235 236 async function updateLocalLinks() { 237 const slugs = await fetchValidSlugs(); 238 const cookie = getCookie("cecLocation"); 239 if (!cookie) return; 240 241 try { 242 // NORMALIZE: User slug for link localization 243 const { slug } = JSON.parse(cookie); 244 const userSlug = (slug || "").toLowerCase(); 245 const origin = window.location.origin; 246 247 document.querySelectorAll("a[href]").forEach(link => { 248 localizeLink(link, userSlug, slugs, origin); 249 }); 250 251 const observer = new MutationObserver((mutations) => { 252 mutations.forEach((m) => { 253 m.addedNodes.forEach((node) => { 254 if (node.nodeType !== 1) return; 255 if (node.tagName === 'A') localizeLink(node, userSlug, slugs, origin); 256 node.querySelectorAll("a[href]").forEach(l => localizeLink(l, userSlug, slugs, origin)); 257 }); 258 }); 259 }); 260 observer.observe(document.body, { childList: true, subtree: true });
261 } catch (e) { console.error("Link update failed:", e); } 262 } 263 264 async function init() { 265 if (window.location.search === "?undefined") { 266 window.history.replaceState({}, "", window.location.pathname + window.location.hash); 267 } 268 269 await handleRedirection(); 270 271 if (document.readyState === "loading") { 272 document.addEventListener("DOMContentLoaded", updateLocalLinks); 273 } else { 274 updateLocalLinks(); 275 } 276 } 277 278 window.CEC_getCurrentReturnPath = getCurrentReturnPath; 279 init(); 280})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.