PageSourceSearch

https://openmaptiles.org/assets/js/languages.js

js openmaptiles.org collected 2026-10-02 04:11:26 UTC 4,006 bytes, 127 lines download raw bytes

1// Multilingual map preview for the language picker.
2//
3// The SDK is the vendored UMD build (assets/js/vendor/maptiler-sdk.umd.js), ~379 KB
4// gzipped, attached to window.maptilersdk. initHero calls this on the hero dialog's
5// first open, NOT when the markup is found — every page on the home layout carries the
6// picker and only /languages/:code/ opens the dialog, so gating on presence downloaded
7// the SDK site-wide for a map that could not be shown.
8
9const ZURICH_CENTER = [8.5417, 47.3769];
10const ZURICH_ZOOM = 10.5;
11
12const parseBbox = (bbox) => bbox.split(",").map(parseFloat);
13
14let sdkPromise;
15
16function loadSdk(base) {
17  if (sdkPromise) return sdkPromise;
18
19  sdkPromise = new Promise((resolve, reject) => {
20    const css = document.createElement("link");
21    css.rel = "stylesheet";
22    css.href = `${base}/assets/js/vendor/maptiler-sdk.css`;
23    document.head.appendChild(css);
24
25    const script = document.createElement("script");
26    script.src = `${base}/assets/js/vendor/maptiler-sdk.umd.js`;
27    script.onload = () => resolve(window.maptilersdk);
28    script.onerror = () => reject(new Error("Failed to load MapTiler SDK"));
29    document.head.appendChild(script);
30  });
31
32  return sdkPromise;
33}
34
35function hasWebGL() {
36  const canvas = document.createElement("canvas");
37  return Boolean(
38    canvas.getContext("webgl") || canvas.getContext("experimental-webgl")
39  );
40}
41
42// Mirrors detectInitialCode(): explicit route language, else the first browser
43// preference the site supports, else English.
44function initialCode(root, languages) {
45  const active = root.getAttribute("data-active-language");
46  if (active && languages.some((l) => l.code === active)) return active;
47
48  const supported = new Set(languages.map((l) => l.code));
49  for (const pref of navigator.languages || [navigator.language]) {
50    const code = String(pref).split("-")[0];
51    if (supported.has(code)) return code;
52  }
53  return "en";
54}
55
56let started = false;
57
58export async function initLanguages(config) {
59  // Set before the first await: the dialog can be reopened, and two opens in the same
60  // tick would both pass a flag raised only after the SDK finished loading.
61  if (started) return;
62  started = true;
63
64  const root = document.querySelector("[data-languages-map]");
65  if (!root) return;
66
67  const container = root.querySelector("[data-languages-canvas]");
68  const titleEl = root.querySelector("[data-languages-title]");
69  const noWebgl = root.querySelector("[data-languages-nowebgl]");
70
71  const languages = config.languages || [];
72  const code = initialCode(root, languages);
73  const current =
74    languages.find((l) => l.code === code) ||
75    languages.find((l) => l.code === "en") ||
76    {};
77
78  if (titleEl) titleEl.textContent = current.titlelocalized || current.title || "";
79
80  if (!hasWebGL()) {
81    if (noWebgl) noWebgl.removeAttribute("hidden");
82    return;
83  }
84
85  let sdk;
86  try {
87    sdk = await loadSdk(config.baseurl || "");
88  } catch (err) {
89    console.error(err);
90    return;
91  }
92
93  sdk.config.apiKey = config.apiKey;
94
95  const map = new sdk.Map({
96    container,
97    style: sdk.MapStyle.OPENSTREETMAP,
98    center: ZURICH_CENTER,
99    zoom: ZURICH_ZOOM,
100    attributionControl: {},
101    // `hash` was tied to the non-embedded variant, which the site never renders.
102    hash: false,
103    navigationControl: false,
104    geolocateControl: false,
105    maptilerLogo: false,
106  });
107
108  const applyLanguage = (value) => {
109    try {
110      map.setLanguage(value === "native" ? sdk.Language.LOCAL : value);
111    } catch {
112      // Language code the SDK does not know — ignored rather than treated as an error.
113    }
114  };
115
116  map.on("load", () => {
117    map.resize();
118    applyLanguage(code);
119
120    // The original deliberately stayed on Zurich for the initial load and only
121    // used a language's bbox on subsequent changes.
122    const explicit = root.getAttribute("data-active-language");
123    if (explicit && current.bbox) {
124      map.fitBounds(parseBbox(current.bbox), { animate: true, padding: 10 });
125    }
126  });
127}

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.