1// cookieconsent-config.js 2 3import * as CookieConsent from "/assets/js/cookieconsent.esm.js"; 4 5/** 6 * Per-site URLs â customize before deploy (see README section 10). 7 */ 8const SITE_CONFIG = { 9 privacyPolicyUrl: "/privacy-policy", 10 contactUrl: "/contact/", 11}; 12 13const CAT_NECESSARY = "necessary"; 14const CAT_ANALYTICS = "analytics"; 15const CAT_ADVERTISEMENT = "advertisement"; 16const CAT_FUNCTIONALITY = "functionality"; 17const CAT_SECURITY = "security"; 18 19const SERVICE_AD_STORAGE = "ad_storage"; 20const SERVICE_AD_USER_DATA = "ad_user_data"; 21const SERVICE_AD_PERSONALIZATION = "ad_personalization"; 22const SERVICE_ANALYTICS_STORAGE = "analytics_storage"; 23const SERVICE_FUNCTIONALITY_STORAGE = "functionality_storage"; 24const SERVICE_PERSONALIZATION_STORAGE = "personalization_storage"; 25const SERVICE_SECURITY_STORAGE = "security_storage"; 26 27const im = iframemanager(); 28 29const TRADINGVIEW_MARKET_OVERVIEW_EMBED_URL = `https://s.tradingview.com/embed-widget/market-overview/?locale=en#${encodeURIComponent( 30 JSON.stringify({ 31 colorTheme: "light", 32 dateRange: "12M", 33 showChart: false, 34 locale: "en", 35 largeChartUrl: "", 36 isTransparent: false, 37 showSymbolLogo: true, 38 width: "280", 39 height: "140", 40 tabs: [ 41 { 42 title: "Indices", 43 symbols: [ 44 { s: "FOREXCOM:SPXUSD", d: "S&P 500" }, 45 { s: "FOREXCOM:NSXUSD", d: "Nasdaq 100" }, 46 { s: "FOREXCOM:DJI", d: "Dow 30" }, 47 ], 48 originalTitle: "Indices", 49 }, 50 ], 51 }), 52)}`; 53 54/** 55 * Convert legacy embed iframes pasted in CMS content to IframeManager placeholders. 56 */ 57function getYoutubeVideoId(url) { 58 if (!url) { 59 return null; 60 } 61 62 try { 63 const parsed = new URL(url, window.location.origin); 64 const host = parsed.hostname.replace(/^www\./, ""); 65 66 if (host === "youtu.be") { 67 return parsed.pathname.slice(1).split("/")[0] || null; 68 } 69 70 if (host.includes("youtube.com") || host.includes("youtube-nocookie.com")) { 71 if (parsed.pathname.startsWith("/embed/")) { 72 return parsed.pathname.split("/")[2] || null; 73 } 74 75 return parsed.searchParams.get("v"); 76 } 77 } catch (error) { 78 return null; 79 } 80 81 return null; 82} 83 84function getGoogleMapsPbId(url) { 85 if (!url) { 86 return null; 87 } 88 89 const pbMatch = url.match(/[?&]pb=([^&]*)/); 90 91 if (!pbMatch) { 92 return null; 93 } 94 95 // Keep percent-encoding intact â URL.searchParams decodes %23 to # which 96 // breaks embed URLs for places like "Shalom Tire Shop #2". 97 return pbMatch[1] || null; 98} 99 100function getEmbedQueryParams(url) { 101 if (!url) { 102 return ""; 103 } 104 105 try { 106 return new URL(url, window.location.origin).searchParams.toString(); 107 } catch (error) { 108 return ""; 109 } 110} 111 112function getLegacyEmbedTarget(iframe) { 113 const src = iframe.getAttribute("src") || iframe.getAttribute("data-src"); 114 115 if (!src) { 116 return null; 117 } 118 119 const youtubeId = getYoutubeVideoId(src); 120 121 if (youtubeId) { 122 return { 123 service: "youtube", 124 id: youtubeId, 125 params: getEmbedQueryParams(src), 126 }; 127 } 128 129 const pb = src.includes("google.com/maps/embed") 130 ? getGoogleMapsPbId(src) 131 : null; 132 133 if (pb) { 134 return { 135 service: "googlemaps", 136 id: pb, 137 params: "", 138 }; 139 } 140 141 return null; 142} 143 144function copyIframeAttributesToPlaceholder(iframe, placeholder) { 145 const passthroughAttributes = ["title", "loading", "referrerpolicy"]; 146 147 passthroughAttributes.forEach((attribute) => { 148 const value = iframe.getAttribute(attribute); 149 150 if (!value) { 151 return; 152 } 153 154 if (attribute === "title") { 155 placeholder.setAttribute("data-title", value); 156 return; 157 } 158 159 placeholder.setAttribute(`data-iframe-${attribute}`, value); 160 }); 161 162 iframe.getAttributeNames().forEach((attribute) => { 163 if (!attribute.startsWith("data-iframe-")) { 164 return; 165 } 166 167 placeholder.setAttribute(attribute, iframe.getAttribute(attribute)); 168 }); 169} 170 171function migrateLegacyCmsEmbeds() { 172 document 173 .querySelectorAll("iframe[src], iframe[data-src]") 174 .forEach((iframe) => { 175 if (iframe.closest("[data-service]")) { 176 return; 177 } 178 179 const target = getLegacyEmbedTarget(iframe); 180 181 if (!target) { 182 return; 183 } 184 185 const placeholder = document.createElement("div"); 186 placeholder.setAttribute("data-service", target.service); 187 placeholder.setAttribute("data-id", target.id); 188 189 const width = iframe.getAttribute("width"); 190 const height = iframe.getAttribute("height"); 191 const isFixedSize = 192 width && height && width !== "100%" && !width.includes("%"); 193 194 if (isFixedSize) { 195 placeholder.style.width = /^\d+$/.test(width) ? `${width}px` : width; 196 placeholder.style.height = /^\d+$/.test(height) 197 ? `${height}px` 198 : height; 199 placeholder.style.margin = "0 auto"; 200 } else { 201 placeholder.setAttribute("data-autoscale", ""); 202 } 203 204 if (target.params) { 205 placeholder.setAttribute("data-params", target.params); 206 } 207 208 copyIframeAttributesToPlaceholder(iframe, placeholder); 209 iframe.replaceWith(placeholder); 210 }); 211} 212 213migrateLegacyCmsEmbeds(); 214 215/** 216 * Update gtag consent according to the users choices made in CookieConsent UI 217 */ 218function updateGtagConsent() { 219 if (typeof gtag !== "function") return; 220 221 gtag("consent", "update", { 222 [SERVICE_ANALYTICS_STORAGE]: CookieConsent.acceptedService( 223 SERVICE_ANALYTICS_STORAGE, 224 CAT_ANALYTICS, 225 ) 226 ? "granted" 227 : "denied", 228 [SERVICE_AD_STORAGE]: CookieConsent.acceptedService( 229 SERVICE_AD_STORAGE, 230 CAT_ADVERTISEMENT, 231 ) 232 ? "granted" 233 : "denied", 234 [SERVICE_AD_USER_DATA]: CookieConsent.acceptedService( 235 SERVICE_AD_USER_DATA, 236 CAT_ADVERTISEMENT, 237 ) 238 ? "granted" 239 : "denied", 240 [SERVICE_AD_PERSONALIZATION]: CookieConsent.acceptedService( 241 SERVICE_AD_PERSONALIZATION, 242 CAT_ADVERTISEMENT, 243 ) 244 ? "granted" 245 : "denied", 246 [SERVICE_FUNCTIONALITY_STORAGE]: CookieConsent.acceptedService( 247 SERVICE_FUNCTIONALITY_STORAGE, 248 CAT_FUNCTIONALITY, 249 ) 250 ? "granted" 251 : "denied", 252 [SERVICE_PERSONALIZATION_STORAGE]: CookieConsent.acceptedService( 253 SERVICE_PERSONALIZATION_STORAGE, 254 CAT_FUNCTIONALITY, 255 ) 256 ? "granted" 257 : "denied", 258 [SERVICE_SECURITY_STORAGE]: CookieConsent.acceptedService( 259 SERVICE_SECURITY_STORAGE, 260 CAT_SECURITY, 261 ) 262 ? "granted" 263 : "denied", 264 }); 265} 266 267function updateWidgetPlaceholders() { 268 document.querySelectorAll("[data-cc-placeholder]").forEach((el) => { 269 const category = el.getAttribute("data-cc-placeholder"); 270 const service = el.getAttribute("data-cc-service"); 271 272 let accepted = false;
273 if (category) { 274 accepted = CookieConsent.acceptedCategory(category); 275 if (service && !accepted) { 276 accepted = CookieConsent.acceptedService(service, category); 277 } 278 } 279 280 el.toggleAttribute("hidden", accepted); 281 }); 282} 283 284function onConsentChange() { 285 updateGtagConsent(); 286 updateWidgetPlaceholders(); 287} 288 289im.run({ 290 currLang: "en", 291 292 onChange: ({ changedServices, eventSource }) => { 293 if (eventSource.type === "click") { 294 const servicesToAccept = [ 295 ...(CookieConsent.getUserPreferences().acceptedServices[ 296 CAT_FUNCTIONALITY 297 ] || []), 298 ...changedServices, 299 ]; 300 301 CookieConsent.acceptService(servicesToAccept, CAT_FUNCTIONALITY); 302 } 303 }, 304 305 services: { 306 youtube: { 307 embedUrl: "https://www.youtube-nocookie.com/embed/{data-id}", 308 thumbnailUrl: "https://i3.ytimg.com/vi/{data-id}/hqdefault.jpg", 309 iframe: { 310 allow: 311 "accelerometer; encrypted-media; gyroscope; picture-in-picture; fullscreen;", 312 loading: "lazy", 313 frameborder: "0", 314 }, 315 languages: { 316 en: { 317 notice: 318 'This content is hosted by a third party. By showing the external content you accept the <a rel="noreferrer noopener" href="https://www.youtube.com/t/terms" target="_blank">terms and conditions</a> of youtube.com.', 319 loadBtn: "Load video", 320 loadAllBtn: "Don't ask again", 321 }, 322 }, 323 }, 324 325 googlemaps: { 326 embedUrl: "https://www.google.com/maps/embed?pb={data-id}", 327 iframe: { 328 allow: "picture-in-picture; fullscreen;", 329 loading: "lazy", 330 referrerpolicy: "no-referrer-when-downgrade", 331 frameborder: "0", 332 }, 333 languages: { 334 en: { 335 notice: 336 'This content is hosted by a third party. By showing the external content you accept the <a rel="noreferrer noopener" href="https://cloud.google.com/maps-platform/terms" target="_blank">terms and conditions</a> of Google Maps.', 337 loadBtn: "Load map", 338 loadAllBtn: "Don't ask again", 339 }, 340 }, 341 }, 342 343 apmex: { 344 embedUrl: 345 "https://widgets.apmex.com/widget/spotprice/?w=280&h=120&mtls=GSPL&arf=True&rint=1&srf=False&tId=2&cId=6d59eebb-6dd0-42d3-8a06-bf77298a83c5&wId=1", 346 iframe: { 347 frameborder: "0", 348 scrolling: "no", 349 loading: "lazy", 350 }, 351 languages: { 352 en: { 353 notice: 354 'This content is hosted by a third party. By showing the external content you accept the <a rel="noreferrer noopener" href="https://www.apmex.com/" target="_blank">terms and conditions</a> of APMEX.', 355 loadBtn: "Load prices", 356 loadAllBtn: "Don't ask again", 357 }, 358 }, 359 }, 360 361 tradingview: { 362 embedUrl: TRADINGVIEW_MARKET_OVERVIEW_EMBED_URL, 363 iframe: { 364 frameborder: "0", 365 scrolling: "no", 366 loading: "lazy", 367 }, 368 languages: { 369 en: { 370 notice: 371 'This content is hosted by a third party. By showing the external content you accept the <a rel="noreferrer noopener" href="https://www.tradingview.com/policies/" target="_blank">terms and conditions</a> of TradingView.', 372 loadBtn: "Load prices", 373 loadAllBtn: "Don't ask again", 374 }, 375 }, 376 }, 377 }, 378}); 379 380CookieConsent.run({ 381 revision: 6, 382 383 cookie: { 384 secure: window.location.protocol === "https:", 385 }, 386 387 guiOptions: { 388 consentModal: { 389 layout: "box", 390 position: "bottom right", 391 }, 392 preferencesModal: { 393 layout: "box", 394 }, 395 }, 396 397 onFirstConsent: () => { 398 onConsentChange(); 399 }, 400 onConsent: () => { 401 onConsentChange(); 402 }, 403 onChange: () => { 404 onConsentChange(); 405 }, 406 407 categories: { 408 [CAT_NECESSARY]: { 409 enabled: true, 410 readOnly: true, 411 }, 412 [CAT_ANALYTICS]: { 413 autoClear: { 414 cookies: [ 415 { 416 name: /^_ga/, 417 }, 418 { 419 name: "_gid", 420 }, 421 ], 422 }, 423 services: { 424 [SERVICE_ANALYTICS_STORAGE]: { 425 label: 426 "Enables storage (such as cookies) related to analytics e.g. visit duration.", 427 }, 428 }, 429 }, 430 [CAT_ADVERTISEMENT]: { 431 services: { 432 [SERVICE_AD_STORAGE]: { 433 label: "Enables storage (such as cookies) related to advertising.", 434 }, 435 [SERVICE_AD_USER_DATA]: { 436 label: 437 "Sets consent for sending user data related to advertising to Google.", 438 }, 439 [SERVICE_AD_PERSONALIZATION]: { 440 label: "Sets consent for personalized advertising.", 441 }, 442 }, 443 }, 444 [CAT_FUNCTIONALITY]: { 445 services: { 446 [SERVICE_FUNCTIONALITY_STORAGE]: { 447 label: 448 "Enables storage related to site functionality such as language preferences.", 449 }, 450 youtube: { 451 label: 452 "Embeds YouTube videos, which may set cookies for playback and preferences.", 453 onAccept: () => im.acceptService("youtube"), 454 onReject: () => im.rejectService("youtube"), 455 }, 456 googlemaps: { 457 label: "Embeds an interactive Google Maps view of our location.", 458 onAccept: () => im.acceptService("googlemaps"), 459 onReject: () => im.rejectService("googlemaps"), 460 }, 461 apmex: { 462 label: 463 "Displays live precious metals spot prices from APMEX.", 464 onAccept: () => im.acceptService("apmex"), 465 onReject: () => im.rejectService("apmex"), 466 }, 467 tradingview: { 468 label: 469 "Displays live stock index prices from TradingView.", 470 onAccept: () => im.acceptService("tradingview"), 471 onReject: () => im.rejectService("tradingview"), 472 }, 473 }, 474 }, 475 }, 476 477 language: { 478 default: "en", 479 translations: { 480 en: { 481 consentModal: { 482 title: "We use cookies", 483 description: 484 "We use cookies for specific <strong>purposes</strong> â essential operation, analytics, and functionality. Optional cookies may involve <strong>third parties</strong> such as Google, APMEX, and TradingView; you can <strong>withdraw</strong> consent anytime via Manage preferences or Cookie Settings.", 485 acceptAllBtn: "Accept all", 486 acceptNecessaryBtn: "Reject all", 487 showPreferencesBtn: "Manage preferences", 488 footer: ` 489 <a href="${SITE_CONFIG.privacyPolicyUrl}">Privacy Policy</a> 490 <a href="${SITE_CONFIG.contactUrl}">Contact</a> 491 `, 492 }, 493 preferencesModal: { 494 title: "Manage cookie preferences", 495 acceptAllBtn: "Accept all", 496 acceptNecessaryBtn: "Reject all", 497 savePreferencesBtn: "Accept current selection", 498 closeIconLabel: "Close modal", 499 sections: [ 500 { 501 title: "Cookie usage", 502 description: 503 "We use cookies for specific purposes: essential site operation, analytics, and enhanced functionality. When you enable optional categories, data may be processed by third-parties such as Google Analytics, APMEX, and TradingView. See our privacy policy for full details.", 504 }, 505 { 506 title: "Strictly necessary cookies", 507 description: 508 "These cookies are essential for the proper functioning of the website, for example for user authentication.", 509 linkedCategory: CAT_NECESSARY, 510 }, 511 { 512 title: "Analytics", 513 description: 514 "Cookies used for analytics help collect data that allows services to understand how users interact with a particular service. Data may be shared with third-parties such as Google Analytics.", 515 linkedCategory: CAT_ANALYTICS, 516 cookieTable: { 517 headers: { 518 name: "Name", 519 domain: "Service", 520 description: "Description", 521 expiration: "Expiration", 522 }, 523 body: [ 524 { 525 name: "_ga", 526 domain: "Google Analytics", 527 description: 528 'Cookie set by <a href="https://business.safety.google/adscookies/">Google Analytics</a>', 529 expiration: "Expires after 12 days", 530 }, 531 { 532 name: "_gid", 533 domain: "Google Analytics", 534 description: 535 'Cookie set by <a href="https://business.safety.google/adscookies/">Google Analytics</a>', 536 expiration: "Session", 537 }, 538 ], 539 }, 540 }, 541 {
542 title: "Advertising", 543 description: 544 "These settings control Google Consent Mode storage keys for advertising-related data. We do not currently use third-party advertising trackers such as Meta or LinkedIn on this site.", 545 linkedCategory: CAT_ADVERTISEMENT, 546 }, 547 { 548 title: "Functionality", 549 description: 550 "Cookies used for functionality enable embedded YouTube and Google Maps content, APMEX precious metals prices, and TradingView stock market data. These third-parties may set their own cookies when enabled.", 551 linkedCategory: CAT_FUNCTIONALITY, 552 }, 553 { 554 title: "More information", 555 description: `Read our <a href="${SITE_CONFIG.privacyPolicyUrl}">Privacy Policy</a> for full details on purposes, third-parties, and data retention. You may withdraw or change your consent at any time using Manage preferences above or the Cookie Settings link in our site footer. For other queries, <a href="${SITE_CONFIG.contactUrl}">contact us</a>.`, 556 }, 557 ], 558 }, 559 }, 560 }, 561 }, 562}); 563 564window.showCookiePreferences = () => CookieConsent.showPreferences(); 565updateWidgetPlaceholders();
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.