### PWA Install Prompt and App Installed Event Source: https://lightpollutionmap.app/it/ Handles the 'beforeinstallprompt' event to allow users to install the PWA, and logs a message when the app is successfully installed. The 'deferredPrompt' variable stores the event for later use. ```javascript window.addEventListener("beforeinstallprompt",(e=>{ e.preventDefault(), deferredPrompt=e, console.log("[PWA] Install prompt available") })), window.addEventListener("appinstalled",(()=>{ console.log("[PWA] App installed successfully"), deferredPrompt=null })) ``` -------------------------------- ### JavaScript Translations Setup Source: https://lightpollutionmap.app/es/ Sets up a global translations object in JavaScript, dynamically loading translations based on the detected document language. It includes error handling for missing translation keys. ```javascript window.translations || (window.translations = {}); const e = document.documentElement.lang || "en"; window.translations[e] || (window.translations[e] = {}); ["lunar.title", "lunar.open", "lunar.close", "lunar.currentPhase", "lunar.age", "lunar.ageDays", "lunar.illumination", "lunar.distance", "lunar.altitude", "lunar.moonTimes", "lunar.moonrise", "lunar.moonset", "lunar.localTime", "lunar.currentTime", "lunar.utcTime", "lunar.noRise", "lunar.noSet", "lunar.nextPhases", "lunar.newMoon", "lunar.waxingCrescent", "lunar.firstQuarter", "lunar.waxingGibbous", "lunar.fullMoon", "lunar.waningGibbous", "lunar.lastQuarter", "lunar.waningCrescent", "lunar.hemisphere.north", "lunar.hemisphere.south", "lunar.coordinates", "lunar.latitude", "lunar.longitude", "lunar.timezone", "time.hoursAgo", "time.minutesAgo", "time.hoursLater", "time.minutesLater", "time.now", "time.passed", "time.upcoming"].forEach((o => { try { a && a[o] ? window.translations[e][o] = a[o] : console.warn(`Lunar translation missing for key: ${o}`); } catch (i) { console.warn(`Error setting lunar translation for key ${o}:`, i); } })), console.debug(`Lunar translations loaded for ${e}:`, Object.keys(window.translations[e]).filter((a => a.startsWith("lunar."))).length, "lunar keys")} ``` -------------------------------- ### PWA Install Prompt Handling Source: https://lightpollutionmap.app/ Manages the beforeinstallprompt event to allow users to install the Progressive Web App (PWA) and logs the app installation success. It captures the prompt event for later display and confirms when the app is installed. ```javascript window.addEventListener("beforeinstallprompt",(e=>{ e.preventDefault(), deferredPrompt=e, console.log("[PWA] Install prompt available") })), window.addEventListener("appinstalled",(()=>{ console.log("[PWA] App installed successfully"), deferredPrompt=null })) ``` -------------------------------- ### Matomo Analytics Tracking Setup Source: https://lightpollutionmap.app/ko/ This JavaScript code initializes and configures Matomo web analytics tracking for the website. It sets the tracker URL and site ID, and asynchronously loads the Matomo JavaScript tracker script. ```javascript var _paq=window._paq=window._paq||[]; _paq.push(["trackPageView"]), _paq.push(["enableLinkTracking"]), function(){ var a="https://matomotech.com/"; _paq.push(["setTrackerUrl",a+"matomo.php"]), _paq.push(["setSiteId","24"]); var e=document, t=e.createElement("script"), p=e.getElementsByTagName("script")[0]; t.async=!0, t.src=a+"matomo.js", p.parentNode?.insertBefore(t,p) }() ``` -------------------------------- ### JavaScript Translation Setup Source: https://lightpollutionmap.app/de/ Initializes a global translations object and populates it based on the detected browser language. It iterates through a predefined list of translation keys, attempting to assign values from a provided translation object 'e'. If a key is missing, it logs a warning. This snippet is crucial for internationalizing the application's text content. ```javascript rt"};window.translations||(window.translations={});const t=document.documentElement.lang||"en";window.translations\[t\]|| (window.translations\[t\]={}); ["pollution.category.veryLow","pollution.category.low","pollution.category.moderate","pollution.category.high","pollution.category.veryHigh","pollution.description.veryLow","pollution.description.low","pollution.description.moderate","pollution.description.high","pollution.description.veryHigh","pollution.milkyWay.excellent","pollution.milkyWay.good","pollution.milkyWay.fair","pollution.milkyWay.poor","pollution.milkyWay.none","pollution.stars.6000plus","pollution.stars.3000to5000","pollution.stars.1000to2000","pollution.stars.200to500","pollution.stars.under200","trend.analyzing","trend.dataInsufficient","trend.status.improving","trend.status.worsening","trend.status.stable","trend.prediction.label","trend.prediction.compared.worse","trend.prediction.compared.better","trend.brightness.ratio","trend.brightness.ratio.period","trend.brightness.change","trend.confidence.high","trend.confidence.medium","trend.confidence.low","trend.direction.increasing","trend.direction.decreasing","trend.confidence.label","chart.axisLabel.bortleScale","chart.tooltip.yearPrefix","chart.tooltip.predictedSuffix","location.bortleScale","location.sqm","location.sqmUnit","location.lightPollution","location.pollutionHistory","location.clickToExpand","weather.title","weather.description","weather.expand","weather.collapse","weather.loading","weather.error","weather.retry","weather.noData","weather.lastDays","weather.today","weather.yesterday","weather.condition.clear","weather.condition.partly-cloudy","weather.condition.cloudy","weather.condition.fog","weather.condition.drizzle","weather.condition.freezing-drizzle","weather.condition.rain","weather.condition.freezing-rain","weather.condition.snow","weather.condition.showers","weather.condition.snow-showers","weather.condition.thunderstorm","weather.condition.unknown","weather.temperature","weather.cloudCover","weather.airQuality","weather.unit.celsius","weather.unit.percent","weather.aqi.good","weather.aqi.fair","weather.aqi.moderate","weather.aqi.poor","weather.aqi.veryPoor","weather.aqi.unknown","weather.source","weather.updated","aurora.probability","aurora.activity.low","aurora.activity.moderate","aurora.activity.high","aurora.description.low","aurora.description.moderate","aurora.description.high","aurora.updated","aurora.live","aurora.label","aurora.forecastTime","legend.aurora.low","legend.aurora.moderate","legend.aurora.high","legend.aurora.veryHigh","controls.aurora.status.active","controls.aurora.status.updated","location.history","history.location.title","history.location.empty","history.location.emptyHint","history.location.recentCount","history.location.clearAll","history.location.clearConfirm","history.search.title","history.search.empty","history.search.clear","history.search.clearConfirm","history.time.justNow","history.time.minuteAgo","history.time.minutesAgo","history.time.hourAgo","history.time.hoursAgo","history.time.dayAgo","history.time.daysAgo"].forEach((n=>{ try{ e&&e\[n\]?window.translations\[t\]\[n\]=e\[n\]:console.warn(`Translation missing for key: ${n}`) }catch(i){console.warn(`Error setting translation for key ${n}:`,i)} })),console.debug("Translation for location.clickToExpand:",window.translations\[t\]\["location.clickToExpand"\]),console.debug(`Translations loaded for ${t}:`,Object.keys(window.translations\[t\]).length,"keys")}() ``` -------------------------------- ### JavaScript: PWA, Performance, and Cleanup Utilities Source: https://lightpollutionmap.app/nb/ This script handles Progressive Web App (PWA) installation prompts, unregisters old service workers, cleans up caches, and monitors various performance metrics like LCP, FID, CLS, and map interaction times. It also includes SEO event tracking for user interactions and external link clicks, along with general error and promise rejection logging. ```JavaScript let deferredPrompt; "serviceWorker" in navigator && window.addEventListener("load", async () =>{ try { const registrations = await navigator.serviceWorker.getRegistrations(); for (const registration of registrations) { console.log("[Cleanup] Unregistering Service Worker:", registration.scope); await registration.unregister(); } if ("caches" in window) { const cacheKeys = await caches.keys(); for (const key of cacheKeys) { if (key.startsWith("lpm-")) { console.log("[Cleanup] Deleting cache:", key); await caches.delete(key); } } } localStorage.removeItem("lpm-version"); console.log("[Cleanup] Service Worker and cache cleanup completed") } catch (e) { console.log("[Cleanup] Service Worker cleanup failed:", e) } }); window.addEventListener("beforeinstallprompt", (e) => { e.preventDefault(); deferredPrompt = e; console.log("[PWA] Install prompt available") }); window.addEventListener("appinstalled", (() => { console.log("[PWA] App installed successfully"); deferredPrompt = null; })) !function() { if ("PerformanceObserver" in window) { // Largest Contentful Paint (LCP) !function() { const observer = new PerformanceObserver((entries => { const entriesList = entries.getEntries(); const lastEntry = entriesList[entriesList.length - 1]; if (lastEntry && lastEntry.startTime) { console.log("[Performance] LCP:", lastEntry.startTime); if (typeof gtag !== "undefined") gtag("event", "web_vitals", { name: "LCP", value: Math.round(lastEntry.startTime), event_category: "Performance" }); } })); try { observer.observe({ entryTypes: ["largest-contentful-paint"] }); } catch (e) { console.log("[Performance] LCP observation not supported") } }(); // First Input Delay (FID) !function() { const observer = new PerformanceObserver((entries => { entries.getEntries().forEach((entry => { console.log("[Performance] FID:", entry.processingStart - entry.startTime); if (typeof gtag !== "undefined") gtag("event", "web_vitals", { name: "FID", value: Math.round(entry.processingStart - entry.startTime), event_category: "Performance" }); })) })); try { observer.observe({ entryTypes: ["first-input"] }); } catch (e) { console.log("[Performance] FID observation not supported") } }(); // Cumulative Layout Shift (CLS) !function() { let clsValue = 0; const observer = new PerformanceObserver((entries => { entries.getEntries().forEach((entry => { !entry.hadRecentInput && (clsValue += entry.value); })); console.log("[Performance] CLS:", clsValue); if (typeof gtag !== "undefined") gtag("event", "web_vitals", { name: "CLS", value: Math.round(1e3 * clsValue), event_category: "Performance" }); })); try { observer.observe({ entryTypes: ["layout-shift"] }); } catch (e) { console.log("[Performance] CLS observation not supported") } }() } function monitorMapPerformance() { let lastInteractionTime = 0; document.addEventListener("wheel", (e => { e.target.closest(".leaflet-container") && (lastInteractionTime = performance.now()); })); document.addEventListener("mousedown", (e => { e.target.closest(".leaflet-container") && (lastInteractionTime = performance.now()); })); document.addEventListener("mouseup", (() => { if (lastInteractionTime > 0) { const interactionTime = performance.now() - lastInteractionTime; console.log("[Performance] Map Interaction Time:", interactionTime); if (typeof gtag !== "undefined" && interactionTime > 100) gtag("event", "map_interaction", { interaction_time: Math.round(interactionTime), event_category: "User Experience" }); lastInteractionTime = 0; } })) } function trackSEOEvents() { document.addEventListener("input", (e => { e.target.matches("[data-search]") && "undefined" != typeof gtag && gtag("event", "search_interaction", { event_category: "SEO" }); })); document.addEventListener("click", (e => { if (e.target.matches("[data-lang-switch]")) { const lang = e.target.dataset.lang; "undefined" != typeof gtag && gtag("event", "language_change", { language: lang, event_category: "SEO" }); } })); document.addEventListener("click", (e => { const anchor = e.target.closest('a[href^="http"]'); if (anchor && !anchor.href.includes(window.location.hostname)) { "undefined" != typeof gtag && gtag("event", "external_link_click", { link_url: anchor.href, event_category: "SEO" }); } })) } window.addEventListener("load", (() => { setTimeout((() => { if ("performance" in window) { const navigationEntries = performance.getEntriesByType("navigation")[0]; if (navigationEntries) { const metrics = { dns: navigationEntries.domainLookupEnd - navigationEntries.domainLookupStart, tcp: navigationEntries.connectEnd - navigationEntries.connectStart, ttfb: navigationEntries.responseStart - navigationEntries.requestStart, download: navigationEntries.responseEnd - navigationEntries.responseStart, domReady: navigationEntries.domContentLoadedEventEnd - navigationEntries.navigationStart, loadComplete: navigationEntries.loadEventEnd - navigationEntries.navigationStart }; console.log("[Performance] Navigation Metrics:", metrics); if (typeof gtag !== "undefined") gtag("event", "page_load_metrics", { ttfb: Math.round(metrics.ttfb), dom_ready: Math.round(metrics.domReady), load_complete: Math.round(metrics.loadComplete), event_category: "Performance" }); } } }), 1e3) })); window.addEventListener("error", (e => { console.error("[Error] JavaScript Error:", e.error); if (typeof gtag !== "undefined") gtag("event", "javascript_error", { error_message: e.message, error_filename: e.filename, error_lineno: e.lineno, event_category: "Error" }); })); window.addEventListener("unhandledrejection", (e => { console.error("[Error] Unhandled Promise Rejection:", e.reason); if (typeof gtag !== "undefined") gtag("event", "promise_rejection", { error_message: e.reason?.toString() || "Unknown promise rejection", event_category: "Error" }); })); monitorMapPerformance(); trackSEOEvents(); }() ``` -------------------------------- ### Agent Initialization and Feature Management Source: https://www.darksky.org/ This snippet details the core logic for initializing an agent, managing its features, and handling dependencies. It includes setting up performance observers, wrapping console logs, and dynamically loading features based on configuration and runtime conditions. The code demonstrates a robust system for agent lifecycle management and feature activation. ```javascript ions.enabled&&(gt.Zp.forEach((e=>(0,O.sp)(e,(e=>(0,s.p)("ua",\[e\],void 0,this.featureName,this.ee)),!0))),gt.qN.forEach((e=>{const t=(0,x.s)((e=>{(0,s.p)("ua",\[e\],void 0,this.featureName,this.ee)}),500,{leading:!0});(0,O.sp)(e,t)}))),e.init.performance.resources.enabled&&y.gm.PerformanceObserver?.supportedEntryTypes.includes("resource"))){new PerformanceObserver((e=>{e.getEntries().forEach((e=>{(0,s.p)("browserPerformance.resource",\[e\],void 0,this.featureName,this.ee)}))})).observe({type:"resource",buffered:!0})}t.some((e=>e))?this.importAggregator(e,(()=>i.e(478).then(i.bind(i,8019)))):this.deregisterDrain()}}var vt=i(2646);const bt=new Map;function yt(e,t,r,n){if("object"!=typeof t||!t||"string"!=typeof r||!r||"function"!=typeof t\[r\])return(0,l.R)(29);const i=function(e){return(e||ee.ee).get("logger")}(e),o=(0,te.YM)(i),a=new vt.y(ee.P);a.level=n.level,a.customAttributes=n.customAttributes;const s=t\[r\]?.[te.Jt]||t\[r\];return bt.set(s,a),o.inPlace(t,\[r\],"wrap-logger-",(()=>bt.get(s))),i}class wt extends T{static featureName=W.TZ;constructor(e){var t;super(e,W.TZ),t=e,p(u.$9,((e,r)=>G(e,r,t)),t),function(e){p(u.Wb,((t,r,{customAttributes:n={},level:i=W.p\_.INFO}={})=>{yt(e.ee,t,r,{customAttributes:n,level:i})}),e)}(e),z(e);const r=this.ee;yt(r,y.gm.console,"log",{level:"info"}),yt(r,y.gm.console,"error",{level:"error"}),yt(r,y.gm.console,"warn",{level:"warn"}),yt(r,y.gm.console,"info",{level:"info"}),yt(r,y.gm.console,"debug",{level:"debug"}),yt(r,y.gm.console,"trace",{level:"trace"}),this.ee.on("wrap-logger-end",(function([e]){const{level:t,customAttributes:n}=this;(0,B.R)(r,e,n,t)}))},this.importAggregator(e,(()=>i.e(478).then(i.bind(i,5288))))}}new class extends r{constructor(e){var t;(super(),y.gm)?(this.features={},(0,_.bQ)(this.agentIdentifier,this),this.desiredFeatures=new Set(e.features||\[\]),this.desiredFeatures.add(S),this.runSoftNavOverSpa=\[...this.desiredFeatures\].some((e=>e.featureName===n.K7.softNav)),(0,a.j)(this,e,e.loaderType||"agent"),t=this,p(u.cD,(function(e,r,n=!1){if("string"==typeof e){if(\["string","number","boolean"\]).includes(typeof r)||null===r)return g(t,e,r,u.cD,n);(0,l.R)(40,typeof r)}else(0,l.R)(39,typeof e)}),t),function(e){p(u.Dl,(function(t){if("string"==typeof t||null===t)return g(e,"enduser.id",t,u.Dl,!0);(0,l.R)(41,typeof t)}),e)}(this),function(e){p(u.nb,(function(t){if("string"==typeof t||null===t)return g(e,"application.version",t,u.nb,!1);(0,l.R)(42,typeof t)}),e)}(this),function(e){p(u.d3,(function(){e.ee.emit("manual-start-all")}),e)}(this),this.run()):(0,l.R)(21)}get config(){return{info:this.info,init:this.init,loader_config:this.loader_config,runtime:this.runtime}}get api(){return this}run(){try{const e=function(e){const t={};return o.forEach((r=>{t\[r\]=!!e\[r\]?.enabled})),t}(this.init),t=\[...this.desiredFeatures\];t.sort(((e,t)=>n.P3\[e.featureName\]-n.P3\[t.featureName\])),t.forEach((t=>{if(!e\[t.featureName\]&&t.featureName!==n.K7.pageViewEvent)return;if(this.runSoftNavOverSpa&&t.featureName===n.K7.spa)return;if(!this.runSoftNavOverSpa&&t.featureName===n.K7.softNav)return;const r=function(e){switch(e){case n.K7.ajax:return\[n.K7.jserrors\];case n.K7.sessionTrace:return\[n.K7.ajax,n.K7.pageViewEvent\];case n.K7.sessionReplay:return\[n.K7.sessionTrace\];case n.K7.pageViewTiming:return\[n.K7.pageViewEvent\];default:return\[\]}}(t.featureName).filter((e=>!(e in this.features)));r.length>0&&(0,l.R)(36,{targetFeature:t.featureName,missingDependencies:r}),this.features\[t.featureName\]=new t(this)}))}catch(e){(0,l.R)(22,e);for(const e in this.features)this.features\[e].abortHandler?.();const t=(0,_.Zm)();delete t.initializedAgents\[this.agentIdentifier\]?.features,delete this.sharedAggregator;return t.ee.get(this.agentIdentifier).abort(),!1}}}({features:\[xe,S,P,He,Ke,j,Z,mt,wt,We,pt\],loaderType:"spa"})})() ``` -------------------------------- ### API: Utility - Get Agent Instance Source: https://www.darksky.org/ Retrieves the agent instance associated with a given identifier, creating it if it doesn't exist. ```APIDOC API: Utility - Get Agent Instance Provides a way to retrieve or create an agent instance based on a provided identifier. Methods: 1. `getAgentInstance(agent, identifier, createFn)` * Retrieves an agent instance from a collection or creates a new one using a factory function. * Parameters: * `agent` (object): The collection or object holding agent instances. * `identifier` (string): The unique identifier for the agent instance. * `createFn` (function): A factory function that returns a new agent instance when called. * Returns: * (object): The agent instance. * Usage: ```javascript // Assuming 'agents' is an object storing agent instances const agentId = 'my-agent-1'; const agentInstance = getAgentInstance(agents, agentId, () => new MyAgent(agentId)); ``` Internal Usage: * This function is used internally to manage multiple agent instances, ensuring that each has a unique identifier and is created only once. ``` -------------------------------- ### Initialize Performance and SEO Event Monitoring Source: https://lightpollutionmap.app/ja/ Sets up event listeners for DOMContentLoaded and visibilitychange to monitor map performance and track SEO-related events. Integrates with analytics (gtag) for event reporting. Logs initialization status. ```javascript function monitorMapPerformance() { const resource_url = e.target.src || e.target.href || "unknown"; const resource_type = e.target.tagName || "unknown"; const event_category = "Error"; "undefined" != typeof gtag && gtag("event", "resource_error", { resource_url, resource_type, event_category }); } "loading" === document.readyState ? document.addEventListener("DOMContentLoaded", monitorMapPerformance) : monitorMapPerformance(); "loading" === document.readyState ? document.addEventListener("DOMContentLoaded", trackSEOEvents) : trackSEOEvents(); document.addEventListener("visibilitychange", (() => { "undefined" != typeof gtag && gtag("event", "page_visibility_change", { visibility_state: document.visibilityState, event_category: "User Experience" }); })); console.log("[Performance] Performance monitoring initialized"); ``` -------------------------------- ### Search Modal and Event Handling Source: https://lightpollutionmap.app/nb/ Initializes the search modal UI and sets up event listeners for opening/closing the modal, triggering searches via button click or Enter key press, and handling window resize events to center the modal. ```javascript document.addEventListener("DOMContentLoaded",(()=>{ const e=document.getElementById("search-modal"), t=document.getElementById("close-search-modal"), n=document.getElementById("search-input"), s=document.getElementById("search-button"); document.getElementById("search-results"), document.getElementById("search-loading"); window.addEventListener("map-controls:open-search",(()=>{ e&&(e.classList.remove("hidden"),e.classList.remove("opacity-0","pointer-events-none"),e.style.opacity="1",e.style.pointerEvents="auto",setTimeout(((()=>{ n?.focus() })()),100), window.dispatchEvent(new CustomEvent("search-modal:show")), document.body.style.overflow="hidden") })), t?.addEventListener("click",(()=>{ e&&(e.classList.add("opacity-0","pointer-events-none"),e.style.opacity="0",e.style.pointerEvents="none",setTimeout(((()=>{ e.classList.add("hidden"), window.dispatchEvent(new CustomEvent("search-modal:hide")), document.body.style.overflow="" })()),300)) })), s?.addEventListener("click",(()=>{ n&&searchLocation(n.value) })), n?.addEventListener("keypress",(e=>{ "Enter"===e.key&&searchLocation(n.value) })), window.addEventListener("resize",(()=>{ if(e&&!e.classList.contains("hidden")){ const t=e.querySelector(".search-modal-content"); if(t instanceof HTMLElement){ const e=t.offsetWidth, n=t.offsetHeight, s=window.innerWidth, r=window.innerHeight; t.style.left=(s-e)/2+"px", t.style.top=(r-n)/2+"px" } } })) })); ``` -------------------------------- ### API Initialization and Customization Source: https://www.darksky.org/ Sets up the application's API interface, allowing for custom attributes, user IDs, and application versions. It also provides methods for adding page actions and logging, with mechanisms for handling connection status. ```javascript function z(e) { p(u.eY, (function(t) { return function(e, t) { const r = {}; let i, o; (0, l.R)(54, "newrelic.register"), e.init.api.allow_registered_children || (i = () => (0, l.R)(55)); t && (0, F.I)(t) || (i = () => (0, l.R)(48, t)); const a = { addPageAction: (n, i = {}) => { u(V, [n, { ...r, ...i }, e], t) }, log: (n, i = {}) => { u(G, [n, { ...i, customAttributes: { ...r, ...i.customAttributes || {} } }, e], t) }, noticeError: (n, i = {}) => { u(U, [n, { ...r, ...i }, e], t) }, setApplicationVersion: e => { r["application.version"] = e }, setCustomAttribute: (e, t) => { r[e] = t }, setUserId: e => { r["enduser.id"] = e }, metadata: { customAttributes: r, target: t, get connected() { return o || Promise.reject(new Error("Failed to connect")) } } }; i ? i() : o = new Promise(((n, i) => { try { const o = e.runtime?.entityManager; ``` -------------------------------- ### How to Use TianWenTong's Stargazing Index Source: https://laysky.com/ An English guide on how to use the TianWenTong APP's stargazing index, explaining its reliance on Clear Sky Chart and ECMWF meteorological data. ```markdown How to Use TianWenTong's Stargazing Index? Tonight's Stargazing Forecast Guide ============================================================================== > TianWenTong serves as a professional stargazing tool offering all-in-one astronomical services. It calculates stargazing indices based on Clear Sky Chart and ECMWF meteorological data. This guide explains how to use TianWenTong's stargazing index forecasts. * * * ``` -------------------------------- ### Search Modal and Event Handling Source: https://lightpollutionmap.app/ja/ Initializes the search modal UI and sets up event listeners for opening/closing the modal, triggering searches via button click or Enter key press, and handling window resize events to center the modal. ```javascript document.addEventListener("DOMContentLoaded",(()=>{ const e=document.getElementById("search-modal"), t=document.getElementById("close-search-modal"), n=document.getElementById("search-input"), s=document.getElementById("search-button"); document.getElementById("search-results"), document.getElementById("search-loading"); window.addEventListener("map-controls:open-search",(()=>{ e&&(e.classList.remove("hidden"),e.classList.remove("opacity-0","pointer-events-none"),e.style.opacity="1",e.style.pointerEvents="auto",setTimeout(((()=>{ n?.focus() })()),100), window.dispatchEvent(new CustomEvent("search-modal:show")), document.body.style.overflow="hidden") })), t?.addEventListener("click",(()=>{ e&&(e.classList.add("opacity-0","pointer-events-none"),e.style.opacity="0",e.style.pointerEvents="none",setTimeout(((()=>{ e.classList.add("hidden"), window.dispatchEvent(new CustomEvent("search-modal:hide")), document.body.style.overflow="" })()),300)) })), s?.addEventListener("click",(()=>{ n&&searchLocation(n.value) })), n?.addEventListener("keypress",(e=>{ "Enter"===e.key&&searchLocation(n.value) })), window.addEventListener("resize",(()=>{ if(e&&!e.classList.contains("hidden")){ const t=e.querySelector(".search-modal-content"); if(t instanceof HTMLElement){ const e=t.offsetWidth, n=t.offsetHeight, s=window.innerWidth, r=window.innerHeight; t.style.left=(s-e)/2+"px", t.style.top=(r-n)/2+"px" } } })) })); ``` -------------------------------- ### Search Modal and Event Handling Source: https://lightpollutionmap.app/ko/ Initializes the search modal UI and sets up event listeners for opening/closing the modal, triggering searches via button click or Enter key press, and handling window resize events to center the modal. ```javascript document.addEventListener("DOMContentLoaded",(()=>{ const e=document.getElementById("search-modal"), t=document.getElementById("close-search-modal"), n=document.getElementById("search-input"), s=document.getElementById("search-button"); document.getElementById("search-results"), document.getElementById("search-loading"); window.addEventListener("map-controls:open-search",(()=>{ e&&(e.classList.remove("hidden"),e.classList.remove("opacity-0","pointer-events-none"),e.style.opacity="1",e.style.pointerEvents="auto",setTimeout(((()=>{ n?.focus() })()),100), window.dispatchEvent(new CustomEvent("search-modal:show")), document.body.style.overflow="hidden") })), t?.addEventListener("click",(()=>{ e&&(e.classList.add("opacity-0","pointer-events-none"),e.style.opacity="0",e.style.pointerEvents="none",setTimeout(((()=>{ e.classList.add("hidden"), window.dispatchEvent(new CustomEvent("search-modal:hide")), document.body.style.overflow="" })()),300)) })), s?.addEventListener("click",(()=>{ n&&searchLocation(n.value) })), n?.addEventListener("keypress",(e=>{ "Enter"===e.key&&searchLocation(n.value) })), window.addEventListener("resize",(()=>{ if(e&&!e.classList.contains("hidden")){ const t=e.querySelector(".search-modal-content"); if(t instanceof HTMLElement){ const e=t.offsetWidth, n=t.offsetHeight, s=window.innerWidth, r=window.innerHeight; t.style.left=(s-e)/2+"px", t.style.top=(r-n)/2+"px" } } })) })); ``` -------------------------------- ### Service Worker and Cache Cleanup Source: https://lightpollutionmap.app/it/ This script unregisters all service workers and deletes caches starting with 'lpm-' upon page load. It also removes a specific local storage item. This is useful for cleaning up old PWA assets. ```javascript let deferredPrompt; "serviceWorker"in navigator&&window.addEventListener("load",(async()=>{ try{ const e=await navigator.serviceWorker.getRegistrations(); for(const o of e) console.log("[Cleanup] Unregistering Service Worker:",o.scope), await o.unregister(); if("caches"in window){ const e=await caches.keys(); for(const o of e) o.startsWith("lpm-")&& (console.log("[Cleanup] Deleting cache:",o), await caches.delete(o)) } localStorage.removeItem("lpm-version"), console.log("[Cleanup] Service Worker and cache cleanup completed") }catch(e){ console.log("[Cleanup] Service Worker cleanup failed:",e) } })) ``` -------------------------------- ### Search Modal and Event Handling Source: https://lightpollutionmap.app/tr/ Initializes the search modal UI and sets up event listeners for opening/closing the modal, triggering searches via button click or Enter key press, and handling window resize events to center the modal. ```javascript document.addEventListener("DOMContentLoaded",(()=>{ const e=document.getElementById("search-modal"), t=document.getElementById("close-search-modal"), n=document.getElementById("search-input"), s=document.getElementById("search-button"); document.getElementById("search-results"), document.getElementById("search-loading"); window.addEventListener("map-controls:open-search",(()=>{ e&&(e.classList.remove("hidden"),e.classList.remove("opacity-0","pointer-events-none"),e.style.opacity="1",e.style.pointerEvents="auto",setTimeout(((()=>{ n?.focus() })()),100), window.dispatchEvent(new CustomEvent("search-modal:show")), document.body.style.overflow="hidden") })), t?.addEventListener("click",(()=>{ e&&(e.classList.add("opacity-0","pointer-events-none"),e.style.opacity="0",e.style.pointerEvents="none",setTimeout(((()=>{ e.classList.add("hidden"), window.dispatchEvent(new CustomEvent("search-modal:hide")), document.body.style.overflow="" })()),300)) })), s?.addEventListener("click",(()=>{ n&&searchLocation(n.value) })), n?.addEventListener("keypress",(e=>{ "Enter"===e.key&&searchLocation(n.value) })), window.addEventListener("resize",(()=>{ if(e&&!e.classList.contains("hidden")){ const t=e.querySelector(".search-modal-content"); if(t instanceof HTMLElement){ const e=t.offsetWidth, n=t.offsetHeight, s=window.innerWidth, r=window.innerHeight; t.style.left=(s-e)/2+"px", t.style.top=(r-n)/2+"px" } } })) })); ``` -------------------------------- ### JavaScript Translation Setup Source: https://lightpollutionmap.app/ This snippet demonstrates how translations are loaded and assigned to the global `window.translations` object, specifically for the 'exposure' module. It includes error handling for missing translations. ```javascript function(){ "undefined"!=typeof window&&(window.translations=window.translations||{},window.translations\[e\]=window.translations\[e\]||{}); ["exposure.title","exposure.open","exposure.close","exposure.referenceTime","exposure.hours","exposure.inputType","exposure.bortleClass","exposure.sqmValue","exposure.currentConditions","exposure.calculate","exposure.clear","exposure.result","exposure.results","exposure.explanation","exposure.multiplier","exposure.reference","exposure.equivalent","exposure.conditions","exposure.bortleRange","exposure.sqmRange","exposure.timeRange","exposure.example","exposure.description","exposure.error.invalidReference","exposure.error.invalidCondition","exposure.error.calculation","exposure.help","exposure.autofill","exposure.autofilled"].forEach((t=>{ try{ o&&o\[t\]?window.translations\[e\]\[t\]=o\[t\]:console.warn(`Exposure translation missing for key: ${t}`) }catch(i){ console.warn(`Error setting exposure translation for key ${t}:`,i) } })) } ``` -------------------------------- ### JavaScript Instrumentation Core Source: https://www.darksky.org/ This snippet initializes core instrumentation logic, including event propagation, timer wrapping, and executor error handling. It sets up listeners for various events like 'propagate', 'executor-start', and 'cb-end' to capture and process execution details. ```javascript ch((function(r){const n=i[r];e[r]=function(e){let i=!1;[...e||[]].forEach((e=>{this.resolve(e).then(a("all"===r),a(!1))}));const o=n.apply(this,arguments);return o;function a(e){return function(){t.emit("propagate",[null,!i],o,!1,!1),i=i||!e}}}})),["resolve","reject"].forEach((function(r){const n=i[r];e[r]=function(e){const r=n.apply(this,arguments);return e!==r&&t.emit("propagate",[e,!0],r,!1,!1),r}})),e.prototype=i.prototype;const o=i.prototype.then;i.prototype.then=function(...e){var i=this,a=r(i);a.promise=i,e[0]=n(e[0],"cb-",a,null,!1),e[1]=n(e[1],"cb-",a,null,!1);const s=o.apply(this,e);return a.nextPromise=s,t.emit("propagate",[i,!0],s,!1,!1),s},i.prototype.then[te.Jt]=o,t.on("executor-start",(function(e){e[0]=n(e[0],"resolve-",this,null,!1),e[1]=n(e[1],"resolve-",this,null,!1)})),t.on("executor-err",(function(e,t,r){e[1](r)})),t.on("cb-end",(function(e,r,n){t.emit("propagate",[n,!0],this.nextPromise,!1,!1)})),t.on("propagate",(function(e,r,n){this.getCtx&&!r||(this.getCtx=function(){if(e instanceof Promise)var r=t.context(e);return r&&r.getCtx?r.getCtx():this})}))}(),t} ``` -------------------------------- ### JavaScript Translation Setup Source: https://lightpollutionmap.app/ A JavaScript code snippet that initializes a global translation object and populates it with language-specific strings. It detects the document's language and creates a nested object for storing translations. ```javascript window.translations || (window.translations={}); const o=document.documentElement.lang||"en"; window.translations[o]||(window.translations[o]={}); ``` -------------------------------- ### Event Wrapper for DOM and XHR Source: https://www.darksky.org/ Wraps DOM event listeners and XMLHttpRequest methods to enable instrumentation and tracing. It uses a custom wrapper (`nr@wrapped:`) to identify instrumented functions and provides hooks for event start, end, and errors, facilitating performance monitoring. ```javascript const WRAPPED_KEY = "nr@wrapped:"; const eventListeners = {}; // Cache for event listeners const XHR = window.XMLHttpRequest; const ADD_EVENT_LISTENER = "addEventListener"; const REMOVE_EVENT_LISTENER = "removeEventListener"; function wrapEventListeners(agent) { const ee = agent.get("events"); if (eventListeners[ee.debugId]++) return ee; eventListeners[ee.debugId] = 1; const originalAdd = ee.wrap(window, ADD_EVENT_LISTENER, "-", listenerWrapper); const originalRemove = ee.wrap(window, REMOVE_EVENT_LISTENER, "-", listenerWrapper); if (Object.getPrototypeOf && Object.keys) { let proto = Object.getPrototypeOf(window); while (proto && proto !== Object.prototype) { if (proto.hasOwnProperty(ADD_EVENT_LISTENER)) { ee.wrap(proto, ADD_EVENT_LISTENER, "-", listenerWrapper); ee.wrap(proto, REMOVE_EVENT_LISTENER, "-", listenerWrapper); } proto = Object.getPrototypeOf(proto); } } if (XHR) { ee.wrap(XHR.prototype, ADD_EVENT_LISTENER, "-", listenerWrapper); ee.wrap(XHR.prototype, REMOVE_EVENT_LISTENER, "-", listenerWrapper); } ee.on(ADD_EVENT_LISTENER + "-start", function(args) { const originalListener = args[1]; if (originalListener && (typeof originalListener === "function" || typeof originalListener === "object")) { // Wrap the listener if it's a function or object with handleEvent const wrappedListener = ee.wrap(originalListener, WRAPPED_KEY, function(listener, context) { return listener.handleEvent ? listener.handleEvent.apply(listener, arguments) : listener.apply(context, arguments); }, originalListener.name || "anonymous"); args[1] = wrappedListener; } }); ee.on(REMOVE_EVENT_LISTENER + "-start", function(args) { args[1] = this.wrapped || args[1]; // Use the wrapped listener if available }); return ee; } function listenerWrapper(originalListener, key, wrapperFn, name) { const wrapped = wrapperFn(originalListener, key, name); return wrapped; } // Helper to apply wrapping to prototypes function applyPrototypeWrapping(target, method, wrapper, key) { let proto = target; while (proto && proto !== Object.prototype) { if (proto.hasOwnProperty(method)) { wrapper(proto, method, key); } proto = Object.getPrototypeOf(proto); } } // Example usage within an agent module: // wrapEventListeners(agentInstance); ``` -------------------------------- ### Agent Configuration and Initialization Source: https://www.darksky.org/ Defines default configuration options for an agent, including beacon URLs, license keys, and application IDs. It provides a function to validate if essential configuration properties are present and another function to apply the configuration, merging it with defaults. ```javascript const o={beacon:n.NT.beacon,errorBeacon:n.NT.errorBeacon,licenseKey:void 0,applicationID:void 0,sa:void 0,queueTime:void 0,applicationTime:void 0,ttGuid:void 0,user:void 0,account:void 0,product:void 0,extra:void 0,jsAttributes:{},userAttributes:void 0,atts:void 0,transactionName:void 0,tNamePlain:void 0}; function a(e){try{return!!e.licenseKey&&!!e.errorBeacon&&!!e.applicationID}catch(e){return!1}} const s=e=>(0,i.a)(e,o) ``` -------------------------------- ### Service Worker and Cache Cleanup Source: https://lightpollutionmap.app/ Cleans up old service workers and caches when the application loads. This ensures a fresh state for the web application by unregistering service workers and deleting cache entries starting with 'lpm-'. ```javascript let deferredPrompt; "serviceWorker"in navigator&&window.addEventListener("load",(async()=>{ try{ const e=await navigator.serviceWorker.getRegistrations(); for(const o of e) console.log("[Cleanup] Unregistering Service Worker:",o.scope), await o.unregister(); if("caches"in window){ const e=await caches.keys(); for(const o of e) o.startsWith("lpm-")&& (console.log("[Cleanup] Deleting cache:",o), await caches.delete(o)) } localStorage.removeItem("lpm-version"), console.log("[Cleanup] Service Worker and cache cleanup completed") }catch(e){ console.log("[Cleanup] Service Worker cleanup failed:",e) } })) ``` -------------------------------- ### Initialize Responsive Elements Source: https://nightsky.jpl.nasa.gov/ Initializes responsive behavior for various UI elements by linking main containers with desktop and mobile views. This utility helps manage layout changes based on screen size. ```javascript mb_utils.initResponsiveElement('#clubs_events', '#clubs_events_container_desktop', '#clubs_events_container_mobile'); mb_utils.initResponsiveElement('#search_module', '#search_container_desktop', '#search_container_mobile'); mb_utils.initResponsiveElement('.share_follow', '#share_follow_desktop', '#share_follow_mobile'); mb_utils.initResponsiveElement('.open_items', '#desktop_open_items', '#mobile_open_items'); ``` -------------------------------- ### Performance and Error Monitoring Source: https://lightpollutionmap.app/fr/ Sets up event listeners for unhandled promise rejections and resource load errors. It also initializes performance monitoring and SEO event tracking based on the document's ready state. ```javascript (() => { window.addEventListener("unhandledrejection", (e) => { console.error("[Error] Unhandled Promise Rejection:", e.reason?.toString() || "Unknown promise rejection", { event_category: "Error" }); }); window.addEventListener("error", (e) => { e.target !== window && (console.error("[Error] Resource Load Error:", e.target.src || e.target.href), "undefined" != typeof gtag && gtag("event", "resource_error", { resource_url: e.target.src || e.target.href || "unknown", resource_type: e.target.tagName || "unknown", event_category: "Error" })); }, !0); "loading" === document.readyState ? document.addEventListener("DOMContentLoaded", monitorMapPerformance) : monitorMapPerformance(); "loading" === document.readyState ? document.addEventListener("DOMContentLoaded", trackSEOEvents) : trackSEOEvents(); document.addEventListener("visibilitychange", (() => { "undefined" != typeof gtag && gtag("event", "page_visibility_change", { visibility_state: document.visibilityState, event_category: "User Experience" }); })); console.log("[Performance] Performance monitoring initialized"); })() ```