PageSourceSearch

https://www.bodrum-museum.com/assets/bodrum-destination-map/bodrum-destination-map-v4.js?ver=1789074617

js bodrum-museum.com collected 2026-10-02 07:58:24 UTC 10,839 bytes, 315 lines download raw bytes

1(() => {
2  'use strict';
3
4  const SVG_NS = 'http://www.w3.org/2000/svg';
5  const assetRequests = new Map();
6
7  const loadAsset = (url) => {
8    if (!assetRequests.has(url)) {
9      assetRequests.set(
10        url,
11        fetch(url, { credentials: 'same-origin' })
12          .then((response) => {
13            if (!response.ok) throw new Error('Map request failed.');
14            return response.text();
15          })
16          .then((markup) => {
17            const asset = new DOMParser().parseFromString(markup, 'image/svg+xml');
18            if (asset.querySelector('parsererror')) throw new Error('Invalid map SVG.');
19            return asset;
20          })
21      );
22    }
23
24    return assetRequests.get(url);
25  };
26
27  const parseConfig = (widget) => {
28    const node = widget.querySelector('.bdm-v4__data');
29    if (!node) return null;
30
31    try {
32      return JSON.parse(node.textContent);
33    } catch (error) {
34      return null;
35    }
36  };
37
38  const showLoadError = (widget) => {
39    const map = widget.querySelector('.bdm-v4__map');
40    if (!map) return;
41
42    map.setAttribute('aria-busy', 'false');
43    const message = document.createElement('p');
44    message.className = 'bdm-v4__load-error';
45    message.textContent = 'The Bodrum map could not be loaded.';
46    map.replaceChildren(message);
47  };
48
49  const makePlace = (place) => {
50    const item = document.createElement(place.url ? 'a' : 'div');
51    item.className = 'bdm-v4__place';
52    item.textContent = place.name;
53    if (place.url) item.href = place.url;
54    return item;
55  };
56
57  const updateTabs = (widget, viewKey) => {
58    widget.querySelectorAll('.bdm-v4__tab').forEach((button) => {
59      const active = button.dataset.view === viewKey;
60      button.setAttribute('aria-selected', active ? 'true' : 'false');
61      button.tabIndex = active ? 0 : -1;
62    });
63  };
64
65  const clearSelection = (widget) => {
66    widget.querySelectorAll('.bdm-v4-marker').forEach((marker) => {
67      marker.classList.remove('is-selected');
68      marker.setAttribute('aria-pressed', 'false');
69    });
70
71    widget.querySelectorAll('.bdm-v4__legend-button').forEach((button) => {
72      button.setAttribute('aria-pressed', 'false');
73    });
74  };
75
76  const showDefaultPanel = (widget, state) => {
77    const view = state.config.views[state.viewKey];
78    const title = state.viewKey === 'museum' ? 'Bodrum Museum' : view.label;
79
80    widget.querySelector('[data-bdm-v4-kicker]').textContent = view.panelLabel;
81    widget.querySelector('[data-bdm-v4-title]').textContent = title;
82    widget.querySelector('[data-bdm-v4-default]').textContent = view.prompt;
83    widget.querySelector('[data-bdm-v4-default]').hidden = false;
84    widget.querySelector('[data-bdm-v4-selected]').hidden = true;
85    clearSelection(widget);
86  };
87
88  const buildLegend = (widget, state) => {
89    const view = state.config.views[state.viewKey];
90    const legend = widget.querySelector('[data-bdm-v4-legend]');
91    const destinations = Object.entries(view.destinations)
92      .filter(([, destination]) => destination.kind !== 'starting-point');
93
94    const buttons = destinations.map(([destinationId, destination], index) => {
95      const button = document.createElement('button');
96      button.className = 'bdm-v4__legend-button';
97      button.type = 'button';
98      button.dataset.destination = destinationId;
99      button.setAttribute('aria-pressed', 'false');
100
101      const number = document.createElement('span');
102      number.className = 'bdm-v4__legend-number';
103      number.setAttribute('aria-hidden', 'true');
104      number.textContent = String(index + 1);
105
106      const label = document.createElement('span');
107      label.className = 'bdm-v4__legend-label';
108      label.textContent = destination.name;
109
110      button.append(number, label);
111      return button;
112    });
113
114    legend.style.setProperty('--bdm-v4-legend-count', String(buttons.length));
115    legend.setAttribute('aria-label', `${view.label} destinations`);
116    legend.replaceChildren(...buttons);
117  };
118
119  const prepareMarkers = (widget, state) => {
120    const view = state.config.views[state.viewKey];
121
122    widget.querySelectorAll('.bdm-v4-marker[data-destination]').forEach((marker) => {
123      const destination = view.destinations[marker.dataset.destination];
124      if (!destination) return;
125
126      marker.setAttribute('role', 'button');
127      marker.setAttribute('tabindex', '0');
128      marker.setAttribute('aria-label', destination.name);
129      marker.setAttribute('aria-pressed', 'false');
130    });
131  };
132
133  const renderView = (widget, state, viewKey) => {
134    const view = state.config.views[viewKey];
135    if (!view || !state.asset) return;
136
137    const symbol = state.asset.getElementById(view.symbol);
138    if (!symbol) {
139      showLoadError(widget);
140      return;
141    }
142
143    const svg = document.createElementNS(SVG_NS, 'svg');
144    svg.setAttribute('viewBox', symbol.getAttribute('viewBox'));
145    svg.setAttribute('preserveAspectRatio', 'xMidYMid meet');
146    svg.setAttribute('role', 'img');
147    svg.setAttribute('aria-label', view.mapLabel);
148    Array.from(symbol.childNodes).forEach((node) => svg.append(node.cloneNode(true)));
149
150    state.viewKey = viewKey;
151    widget.dataset.view = viewKey;
152    const map = widget.querySelector('.bdm-v4__map');
153    map.replaceChildren(svg);
154    map.setAttribute('aria-label', view.mapLabel);
155    map.setAttribute('aria-busy', 'false');
156
157    updateTabs(widget, viewKey);
158    buildLegend(widget, state);
159    prepareMarkers(widget, state);
160    showDefaultPanel(widget, state);
161  };
162
163  const scrollToPanel = (widget) => {
164    if (!window.matchMedia('(max-width: 1020px)').matches) return;
165
166    const panel = widget.querySelector('.bdm-v4__panel');
167    const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
168    window.requestAnimationFrame(() => {
169      panel.scrollIntoView({
170        behavior: reduceMotion ? 'auto' : 'smooth',
171        block: 'start'
172      });
173    });
174  };
175
176  const selectDestination = (widget, state, destinationId) => {
177    const view = state.config.views[state.viewKey];
178    const destination = view.destinations[destinationId];
179    if (!destination) return;
180
181    const kind = destination.kind || 'destination';
182    const description = widget.querySelector('[data-bdm-v4-description]');
183    const placesHeading = widget.querySelector('[data-bdm-v4-places-heading]');
184    const places = widget.querySelector('[data-bdm-v4-places]');
185    const button = widget.querySelector('[data-bdm-v4-button]');
186
187    widget.querySelector('[data-bdm-v4-kicker]').textContent =
188      destination.panelLabel || view.panelLabel;
189    widget.querySelector('[data-bdm-v4-title]').textContent = destination.name;
190    widget.querySelector('[data-bdm-v4-default]').hidden = true;
191    widget.querySelector('[data-bdm-v4-selected]').hidden = false;
192
193    description.hidden = true;
194    description.textContent = '';
195    placesHeading.hidden = true;
196    places.hidden = true;
197    places.replaceChildren();
198
199    if (kind === 'department') {
200      description.textContent = destination.description || '';
201      description.hidden = false;
202    } else if (kind === 'starting-point') {
203      places.replaceChildren(makePlace({ name: destination.message || 'You are here', url: '' }));
204      places.hidden = false;
205    } else {
206      placesHeading.textContent = destination.placesHeading || 'Places to explore';
207      placesHeading.hidden = false;
208      places.replaceChildren(...(destination.places || []).map(makePlace));
209      places.hidden = false;
210    }
211
212    if (kind !== 'starting-point' && destination.url) {
213      button.href = destination.url;
214      button.textContent = destination.buttonLabel || 'Learn more';
215      button.hidden = false;
216    } else {
217      button.hidden = true;
218      button.removeAttribute('href');
219      button.textContent = '';
220    }
221
222    widget.querySelectorAll('.bdm-v4-marker').forEach((marker) => {
223      const active = marker.dataset.destination === destinationId;
224      marker.classList.toggle('is-selected', active);
225      marker.setAttribute('aria-pressed', active ? 'true' : 'false');
226    });
227
228    widget.querySelectorAll('.bdm-v4__legend-button').forEach((legendButton) => {
229      legendButton.setAttribute(
230        'aria-pressed',
231        legendButton.dataset.destination === destinationId ? 'true' : 'false'
232      );
233    });
234
235    scrollToPanel(widget);
236  };
237
238  const moveTabFocus = (widget, current, direction) => {
239    const tabs = Array.from(widget.querySelectorAll('.bdm-v4__tab'));
240    const index = tabs.indexOf(current);
241    if (index < 0) return;
242    tabs[(index + direction + tabs.length) % tabs.length].focus();
243  };
244
245  const bindWidget = (widget) => {
246    if (widget.dataset.bdmV4Ready === 'true') return;
247    widget.dataset.bdmV4Ready = 'true';
248
249    const config = parseConfig(widget);
250    const mapUrl = widget.dataset.mapSrc;
251    if (!config || !config.views || !mapUrl) {
252      showLoadError(widget);
253      return;
254    }
255
256    const state = {
257      asset: null,
258      config,
259      viewKey: config.defaultView || Object.keys(config.views)[0]
260    };
261
262    widget.addEventListener('click', (event) => {
263      const tab = event.target.closest('.bdm-v4__tab');
264      if (tab && widget.contains(tab)) {
265        renderView(widget, state, tab.dataset.view);
266        return;
267      }
268
269      const control = event.target.closest('[data-destination]');
270      if (control && widget.contains(control)) {
271        selectDestination(widget, state, control.dataset.destination);
272      }
273    });
274
275    widget.addEventListener('keydown', (event) => {
276      const tab = event.target.closest('.bdm-v4__tab');
277      if (tab && widget.contains(tab)) {
278        if (event.key === 'ArrowRight' || event.key === 'ArrowDown') {
279          event.preventDefault();
280          moveTabFocus(widget, tab, 1);
281        } else if (event.key === 'ArrowLeft' || event.key === 'ArrowUp') {
282          event.preventDefault();
283          moveTabFocus(widget, tab, -1);
284        } else if (event.key === 'Enter' || event.key === ' ') {
285          event.preventDefault();
286          renderView(widget, state, tab.dataset.view);
287        }
288        return;
289      }
290
291      const marker = event.target.closest('.bdm-v4-marker[data-destination]');
292      if (!marker || !widget.contains(marker)) return;
293      if (event.key !== 'Enter' && event.key !== ' ') return;
294      event.preventDefault();
295      selectDestination(widget, state, marker.dataset.destination);
296    });
297
298    loadAsset(mapUrl)
299      .then((asset) => {
300        state.asset = asset;
301        renderView(widget, state, state.viewKey);
302      })
303      .catch(() => showLoadError(widget));
304  };
305
306  const initialize = () => {
307    document.querySelectorAll('[data-bdm-v4]').forEach(bindWidget);
308  };
309
310  if (document.readyState === 'loading') {
311    document.addEventListener('DOMContentLoaded', initialize, { once: true });
312  } else {
313    initialize();
314  }
315})();

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.