const app = () => ({ futureTimes: [], locationId: Alpine.$persist('11104'), lastUpdated: Alpine.$persist(null), userMinutes: 0, now: new Date(), debug: location.hash === '#debug', geolocation: null, async init() { await this.refreshIfStale(); setInterval(() => { this.now = new Date(); }, 500); getUserLocation() .then(loc => { this.geolocation = {latitude: loc.latitude, longitude: loc.longitude}; this.refreshIfStale(); }) .catch(() => this.geolocation = null) }, onHash() { this.debug = location.hash === '#debug' }, get userNow() { if (!this.debug) { return this.now; } const d = new Date(); d.setHours(0, this.userMinutes, 0, 0); return d; }, get userTime() { return formatTime(this.userNow); }, async refreshIfStale() { const updatedAt = new Date(this.lastUpdated); const now = new Date(); const elapsedSeconds = (now - updatedAt) / 1000; if (this.geolocation !== null) { this.futureTimes = await fetchJSON(`/api/v1/diyanet/prayertimes?latitude=${this.geolocation.latitude}&longitude=${this.geolocation.longitude}`); } else { this.futureTimes = await fetchJSON(`/api/v1/diyanet/prayertimes?location_id=${this.locationId}`); } this.lastUpdated = now.toISOString(); }, get todayTimes() { if (this.futureTimes.length === 0) { return null; } return new PrayerTimes(this.futureTimes[0], () => this.userNow); }, translate(key, lang) { return translations[key][lang] ?? key } }); class PrayerTimes { static salaths = ['fajr', 'sunrise', 'dhuhr', 'asr', 'maghrib', 'isha']; static translations = { fajr: {tr: 'İmsak', de: 'Frühgebet', ar: 'صلاة الفجر'}, sunrise: {tr: 'Güneş', de: 'Sonnenaufgang', ar: 'الشروق'}, dhuhr: {tr: 'Öğle', de: 'Mittagsgebet', ar: 'صلاة الظهر'}, asr: {tr: 'İkindi', de: 'Nachmittagsgebet', ar: 'صلاة العصر'}, maghrib: {tr: 'Akşam', de: 'Abendgebet', ar: 'صلاة المغرب'}, isha: {tr: 'Yatsı', de: 'Nachtgebet', ar: 'صلاة العشاء'}, } constructor({date, ...rest}, clock = () => new Date()) { this.date = date; this.clock = clock this.salathTimes = rest } get times() { const now = this.clock() return PrayerTimes.salaths.map(k => { // "2023-03-05T00:00:00Z" const startsAt = new Date(this.date.replace('T00:00', `T${this.salathTimes[k]}`).replace(/Z$/, '')); return { salath: k, name: lang => PrayerTimes.translations[k][lang] ?? '??', startsAt, timeLocal: this.salathTimes[k], get untilSeconds() { let untilSeconds = (startsAt - now) / 1000; return now > startsAt ? 0 : untilSeconds; }, get untilHuman() { return formatDuration(this.untilSeconds) }, } }) } get currentSalath() { let current = this.times.filter(it => it.untilSeconds === 0).at(-1); if (current === undefined) { // we're in isha -> today's fajr is almost the same as tomorrows const prevDay = new Date(this.date); prevDay.setDate(prevDay.getDate() - 1); current = new PrayerTimes({date: prevDay.toISOString(), ...this.salathTimes}, this.clock).times.at(-1); } return current } get nextSalath() { let next = this.times .filter(it => it.untilSeconds > 0)[0] if (next === undefined) { // we're in isha -> today's fajr is almost the same as tomorrows const nextDay = new Date(this.date); nextDay.setDate(nextDay.getDate() + 1); next = new PrayerTimes({date: nextDay.toISOString(), ...this.salathTimes}, this.clock).times[0]; } return { ...next, } } } /** * @param {number} seconds * @return {string} * */ function formatDuration(seconds) { const d = new Date(0, 0, 0, 0, 0, seconds); return formatTime(d); } /** * @param {Date} then * @return {string} * */ function formatTime(then) { return new Intl.DateTimeFormat(navigator.language, { hour: "numeric", minute: "numeric", second: "numeric" }).format(then); } /** * @param {Date} then * @return {string} * */ function formatDate(then) { return new Intl.DateTimeFormat(navigator.language, { year: "numeric", month: "2-digit", day: "2-digit" }).format(then); } /** * @param {Date} then * @return {string} * */ function formatDateHijri(then) { return new Intl.DateTimeFormat("en-u-ca-islamic-umalqura-nu-latn", { year: "numeric", month: "long", day: "numeric", }).format(then); } /** * @param {string} url * @param {RequestInit} req * */ async function fetchJSON(url, req = {}) { const res = await fetch(url, { ...req, }) return res.json() } /** * @return {Promise} * */ function getUserLocation() { return new Promise((resolve, reject) => { if (!navigator.geolocation) { reject("Geolocation is not supported by this browser."); return; } navigator.geolocation.getCurrentPosition( (position) => resolve(position.coords), (error) => reject(error.message) ); }); }