PageSourceSearch

https://www.ceyapt.com/wp-content/themes/cy_web_theme/assets/js/reservation.js?ver=1789820026

js ceyapt.com collected 2026-09-24 14:40:13 UTC 16,498 bytes, 348 lines download raw bytes

1(function () {
2    "use strict";
3
4    const API = "https://api.ceyapt.com";
5    const STORAGE_KEY = "reservationData";
6    const TIMEOUT_MS = 12000;
7    let epoch = 0;
8    let refreshFlight = null;
9    let loginFlight = null;
10    let watching = false;
11    let sessionNotice = null;
12
13    function isItalian() {
14        const main = document.querySelector("main[data-language]");
15        const language = main && main.getAttribute("data-language");
16        if (language === "it" || language === "en") return language === "it";
17        return /\/your-reservation(?:-login)?-it(?:\/|$)/i.test(window.location.pathname || "");
18    }
19
20    function words(english, italian) { return isItalian() ? italian : english; }
21    function loginPath() { return "/your-reservation-login" + (isItalian() ? "-it" : "") + "/"; }
22    function guestPath() { return "/your-reservation" + (isItalian() ? "-it" : "") + "/"; }
23    function isGuestPage() { return !!document.querySelector("main.reservation-page"); }
24    function failure(code, status) { return Object.assign(new Error(code), { code: code, status: status || 0 }); }
25
26    function storedText() {
27        try { return window.sessionStorage.getItem(STORAGE_KEY); }
28        catch (_) { return null; }
29    }
30
31    function validGuest(value) {
32        return !!value && typeof value === "object" && !Array.isArray(value) &&
33            value.success === true && value.app === "roma" && value.role === "guest" &&
34            (typeof value.guestId === "string" || typeof value.guestId === "number") &&
35            String(value.guestId).trim() !== "" && typeof value.structure === "string" &&
36            typeof value.authToken === "string" && value.authToken.length > 0 &&
37            value.authToken.length <= 16000 && !/\s/.test(value.authToken);
38    }
39
40    function read() {
41        try {
42            const value = JSON.parse(storedText());
43            return validGuest(value) ? value : null;
44        } catch (_) { return null; }
45    }
46
47    function save(guest) {
48        try { window.sessionStorage.setItem(STORAGE_KEY, JSON.stringify(guest)); }
49        catch (_) { throw failure("session_storage_unavailable"); }
50    }
51
52    function clear() {
53        try { window.sessionStorage.removeItem(STORAGE_KEY); }
54        catch (_) { /* A blocked storage area contains no readable usable session. */ }
55    }
56
57    function sameSession(snapshot) {
58        const current = read();
59        return epoch === snapshot.epoch && !!current &&
60            String(current.guestId) === snapshot.guestId && current.authToken === snapshot.token;
61    }
62
63    function cancelPending() {
64        if (refreshFlight) refreshFlight.controller.abort();
65        if (loginFlight) loginFlight.controller.abort();
66        refreshFlight = null;
67        loginFlight = null;
68    }
69
70    function expireSession(snapshot) {
71        if (snapshot && !sameSession(snapshot)) return;
72        epoch += 1;
73        clear();
74        cancelPending();
75        if (isGuestPage()) window.location.replace(loginPath());
76    }
77
78    async function requestJSON(path, options, controller) {
79        let timedOut = false;
80        const timer = window.setTimeout(function () {
81            timedOut = true;
82            controller.abort();
83        }, TIMEOUT_MS);
84        try {
85            const response = await window.fetch(API + path, Object.assign({
86                credentials: "omit", cache: "no-store", signal: controller.signal
87            }, options));
88            if (!response.ok) throw failure("session_http_error", response.status);
89            let data;
90            try { data = await response.json(); }
91            catch (_) { throw failure("invalid_session_response"); }
92            return data;
93        } catch (error) {
94            if (timedOut) throw failure("session_timeout");
95            if (controller.signal.aborted) throw failure("session_superseded");
96            if (error.code) throw error;
97            throw failure("session_connection_error");
98        } finally { window.clearTimeout(timer); }
99    }
100
101    function notice(message, busy) {
102        if (!isGuestPage()) return;
103        if (!sessionNotice && !message) return;
104        if (!sessionNotice) {
105            const main = document.querySelector("main.reservation-page");
106            const box = document.createElement("div");
107            box.id = "cyReservationSessionStatus";
108            box.className = "reservation-container cy-session-status";
109            box.setAttribute("role", "status");
110            box.setAttribute("aria-live", "polite");
111            box.style.padding = "16px";
112            box.style.background = "#f6f3ee";
113            box.style.color = "#716551";
114            const messageNode = document.createElement("span");
115            const retry = document.createElement("button");
116            retry.type = "button";
117            retry.textContent = words("Retry", "Riprova");
118            retry.style.marginLeft = "12px";
119            retry.addEventListener("click", function () {
120                notice(words("Updating your reservation…", "Aggiornamento della prenotazione…"), true);
121                refresh().catch(function () {});
122            });
123            box.appendChild(messageNode);
124            box.appendChild(retry);
125            main.insertBefore(box, main.firstChild);
126            sessionNotice = { box: box, message: messageNode, retry: retry };
127        }
128        sessionNotice.box.hidden = !message;
129        sessionNotice.message.textContent = message || "";
130        sessionNotice.retry.disabled = !!busy;
131    }
132
133    function showRefreshError(error) {
134        if (error.code === "session_superseded" || error.status === 401 || error.status === 403) return;
135        const message = error.code === "session_storage_unavailable" ?
136            words("Your browser could not save the updated reservation. Allow site data and retry.",
137                "Il browser non riesce a salvare la prenotazione aggiornata. Consenti i dati del sito e riprova.") :
138            words("We could not update your reservation. The details shown may be out of date. Please retry.",
139                "Non è stato possibile aggiornare la prenotazione. I dati mostrati potrebbero non essere aggiornati. Riprova.");
140        notice(message, false);
141    }
142
143    function refresh() {
144        const guest = read();
145        if (!guest) {
146            expireSession();
147            return Promise.reject(failure("guest_session_required", 401));
148        }
149        const snapshot = { epoch: epoch, guestId: String(guest.guestId), token: guest.authToken };
150        if (refreshFlight && refreshFlight.snapshot.epoch === snapshot.epoch &&
151                refreshFlight.snapshot.guestId === snapshot.guestId && refreshFlight.snapshot.token === snapshot.token) {
152            return refreshFlight.promise;
153        }
154        if (refreshFlight) refreshFlight.controller.abort();
155        const flight = { snapshot: snapshot, controller: new AbortController(), promise: null };
156        refreshFlight = flight;
157        flight.promise = (async function () {
158            try {
159                const updated = await requestJSON("/roma/auth/session", {
160                    method: "GET", headers: { "Authorization": "Bearer " + snapshot.token, "Accept": "application/json" }
161                }, flight.controller);
162                // A reply for an earlier login must never restore a logged-out or replaced session.
163                if (!sameSession(snapshot)) throw failure("session_superseded");
164                if (!validGuest(updated) || String(updated.guestId) !== snapshot.guestId ||
165                        updated.structure.trim().toLowerCase() !== guest.structure.trim().toLowerCase()) {
166                    throw failure("invalid_session_response");
167                }
168                const current = read();
169                // Another authenticated module may have received a newer admin revision meanwhile.
170                const currentRevision = Number(current.guestRevision) || 0;
171                const incomingRevision = Number(updated.guestRevision) || 0;
172                const result = incomingRevision < currentRevision ?
173                    Object.assign({}, current, { authToken: updated.authToken }) : updated;
174                save(result);
175                notice("", false);
176                window.dispatchEvent(new CustomEvent("cy:reservation-updated", { detail: result }));
177                return result;
178            } catch (error) {
179                if (!sameSession(snapshot)) throw failure("session_superseded");
180                if (error.status === 401 || error.status === 403) expireSession(snapshot);
181                else showRefreshError(error);
182                throw error;
183            } finally {
184                if (refreshFlight === flight) refreshFlight = null;
185            }
186        }());
187        return flight.promise;
188    }
189
190    function paintGuest(guest) {
191        if (!guest) return;
192        const values = {
193            guestName: [guest.name, guest.surname].filter(Boolean).join(" "),
194            guestStructure: "C&Y Roma " + (guest.structure || ""),
195            checkIn: guest.checkIn, checkOut: guest.checkOut, inTime: guest.inTime, outTime: guest.outTime
196        };
197        Object.keys(values).forEach(function (id) {
198            const element = document.getElementById(id);
199            if (element) element.textContent = values[id] || "—";
200        });
201        const pictures = {
202            trastevere: ["trastevereterrace1.jpg", "trasteverehousemap.png"],
203            vaticano: ["vaticano1.jpg", "vaticanohousemap.png"],
204            prati: ["prati1.jpg", "pratihousemap.png"]
205        };
206        const propertyKey = String(guest.structure || "").trim().toLowerCase();
207        const pair = Object.prototype.hasOwnProperty.call(pictures, propertyKey) ? pictures[propertyKey] : null;
208        const hero = document.getElementById("reservationHero");
209        const map = document.getElementById("houseMapImage");
210        const prefix = "/wp-content/uploads/2026/02/";
211        if (hero) hero.style.backgroundImage = pair ? "url('" + prefix + pair[0] + "')" : "";
212        if (map) {
213            if (pair) map.src = prefix + pair[1];
214            else map.removeAttribute("src");
215            map.hidden = !pair;
216            const section = map.closest(".house-map-section");
217            if (section) section.hidden = !pair;
218        }
219    }
220
221    function backgroundRefresh() {
222        if (isGuestPage() && !document.hidden) refresh().catch(function () {});
223    }
224
225    function watchGuestPage() {
226        if (watching || !isGuestPage()) return;
227        watching = true;
228        window.addEventListener("focus", backgroundRefresh);
229        document.addEventListener("visibilitychange", backgroundRefresh);
230        window.setInterval(backgroundRefresh, 60000);
231    }
232
233    function loadReservationPage() {
234        if (!isGuestPage()) return Promise.resolve(null);
235        const guest = read();
236        if (!guest) {
237            expireSession();
238            return Promise.resolve(null);
239        }
240        paintGuest(guest);
241        watchGuestPage();
242        // The public page initializer is safe for existing callers that do not await it.
243        return refresh().catch(function () { return null; });
244    }
245
246    function loginError(error) {
247        if (error.status === 401 || error.status === 400) return words(
248            "Check your booking number and the last four digits of your phone number.",
249            "Controlla il numero di prenotazione e le ultime quattro cifre del telefono.");
250        if (error.status === 403) return words(
251            "This reservation is not currently available for access. Please contact C&Y for assistance.",
252            "L’accesso non è disponibile per questa prenotazione. Contatta C&Y per assistenza.");
253        if (error.code === "session_storage_unavailable") return words(
254            "Your browser could not save the session. Allow site data and retry.",
255            "Il browser non riesce a salvare la sessione. Consenti i dati del sito e riprova.");
256        if (error.code === "session_timeout") return words(
257            "The server took too long to respond. Please retry.", "Il server non ha risposto in tempo. Riprova.");
258        return words("We could not connect to your reservation. Please retry.",
259            "Non è stato possibile accedere alla prenotazione. Riprova.");
260    }
261
262    function reservationLogin() {
263        if (loginFlight) return loginFlight.promise;
264        const form = document.getElementById("reservationLoginForm");
265        const bookingInput = document.getElementById("booking");
266        const phoneInput = document.getElementById("phone");
267        const errorNode = document.getElementById("errorMsg");
268        if (!form || !bookingInput || !phoneInput || !errorNode) return Promise.resolve(null);
269        const booking = bookingInput.value.trim();
270        const phone = phoneInput.value.trim();
271        errorNode.textContent = "";
272        if (!booking || !/^\d{4}$/.test(phone)) {
273            errorNode.textContent = words("Enter your booking number and the last four digits of your phone number.",
274                "Inserisci il numero di prenotazione e le ultime quattro cifre del telefono.");
275            (booking ? phoneInput : bookingInput).focus();
276            return Promise.resolve(null);
277        }
278        epoch += 1;
279        if (refreshFlight) refreshFlight.controller.abort();
280        refreshFlight = null;
281        const originalStorage = storedText();
282        const flight = { epoch: epoch, controller: new AbortController(), promise: null };
283        const button = form.querySelector("button[type='submit']");
284        const previousLabel = button ? button.textContent : "";
285        if (button) {
286            button.disabled = true;
287            button.textContent = words("Signing in…", "Accesso in corso…");
288        }
289        form.setAttribute("aria-busy", "true");
290        loginFlight = flight;
291        flight.promise = (async function () {
292            try {
293                const guest = await requestJSON("/roma/auth/login", {
294                    method: "POST", headers: { "Content-Type": "application/json", "Accept": "application/json" },
295                    body: JSON.stringify({ username: booking, password: phone })
296                }, flight.controller);
297                if (epoch !== flight.epoch || storedText() !== originalStorage) throw failure("session_superseded");
298                if (!validGuest(guest)) throw failure("invalid_session_response");
299                save(guest);
300                epoch += 1;
301                window.location.assign(guestPath());
302                return guest;
303            } catch (error) {
304                if (epoch === flight.epoch && storedText() === originalStorage && error.code !== "session_superseded") {
305                    errorNode.textContent = loginError(error);
306                }
307                return null;
308            } finally {
309                if (loginFlight === flight) {
310                    loginFlight = null;
311                    form.removeAttribute("aria-busy");
312                    if (button) { button.disabled = false; button.textContent = previousLabel; }
313                }
314            }
315        }());
316        return flight.promise;
317    }
318
319    function logoutReservation() {
320        epoch += 1;
321        cancelPending();
322        clear();
323        window.location.replace(loginPath());
324    }
325
326    window.cyReservationSession = { read: read, refresh: refresh };
327    window.loadReservationPage = loadReservationPage;
328    window.reservationLogin = reservationLogin;
329    window.logoutReservation = logoutReservation;
330    window.addEventListener("cy:reservation-updated", function (event) {
331        const current = read();
332        if (current && event.detail && String(current.guestId) === String(event.detail.guestId) &&
333                current.authToken === event.detail.authToken) paintGuest(current);
334    });
335
336    function init() {
337        const form = document.getElementById("reservationLoginForm");
338        if (form && !form.hasAttribute("data-session-bound")) {
339            form.setAttribute("data-session-bound", "true");
340            form.addEventListener("submit", function (event) { event.preventDefault(); reservationLogin(); });
341            const button = form.querySelector("button[type='submit']");
342            if (button) button.disabled = false;
343        }
344        if (isGuestPage()) loadReservationPage();
345    }
346    if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", init, { once: true });
347    else init();
348}());

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.