Repository navigation
Expand file tree
/
Copy pathscript.js
More file actions
479 lines (433 loc) · 20.1 KB
/
Copy pathscript.js
File metadata and controls
479 lines (433 loc) · 20.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
const translations = {
es: {
meta: {
title: 'Ayane & Alejandro | Información de boda para invitados',
description:
'Sitio web de información de boda con cronograma, sedes, detalles de viaje e instrucciones de RSVP para invitados.',
},
skip: 'Saltar al contenido principal',
language: {
label: 'Idioma - 言語',
selectAria: 'Seleccionar idioma',
spanish: 'Español',
japanese: '日本語',
},
nav: {
ariaLabel: 'Navegación principal',
menuButton: 'Menú',
home: 'Inicio',
details: 'Detalles',
venues: 'Sedes',
schedule: 'Cronograma',
rsvp: 'Asistencia',
confirmAttendance: 'Confirmar asistencia',
travel: 'Viaje',
faq: 'Preguntas',
message: 'Mensaje',
},
countdown: {
ariaLabel: 'Cuenta regresiva para la boda',
text: 'días para la Boda',
},
hero: {
ariaLabel: 'Introducción de la boda',
date: '2026.Oct.01',
},
details: {
title: 'Detalles importantes',
dateTitle: 'Fecha',
dateValue: 'Jueves, 1 de octubre de 2026',
ceremonyTitle: 'Ceremonia',
ceremonyValue: 'La ceremonia religiosa se llevará a cabo a las 5:00 p. m. (hora de México) en la <strong><a href="https://maps.app.goo.gl/3U4JRh3GeCaUGPL46" class="inline-link" target="_blank" rel="noopener noreferrer">Basílica Colegiata de Nuestra Señora de Guanajuato, Gto.</a></strong>',
photoSessionTitle: 'Sesión de fotos',
photoSessionValue: 'Después de la ceremonia religiosa, alrededor de las 6:10 p. m., nos reuniremos frente a la iglesia, en: <strong><a href="https://maps.app.goo.gl/Rm2HHoDUS6qW7fgV8" class="inline-link" target="_blank" rel="noopener noreferrer">Plaza de la Paz</a></strong>, para la sesión de fotos.',
receptionTitle: 'Recepción',
receptionValue: 'La recepción se llevará a cabo a las 8:00 p. m. (hora de México) en <strong><a href="https://www.google.com/maps/search/?api=1&query=Jdn.%20de%20la%20Uni%C3%B3n%203%2C%20Zona%20Centro%2C%2036000%20Guanajuato%2C%20Gto.%2C%20Mexico" class="inline-link" target="_blank" rel="noopener noreferrer">Casa Valadez</a></strong>',
dressTitle: 'Código de vestimenta',
dressValue: 'Vestimenta formal',
},
venues: {
title: 'Sedes',
ceremonyTitle: 'Sede de la ceremonia',
ceremonyName: 'Basílica Colegiata de Nuestra Señora de Guanajuato',
ceremonyAddress: 'C. Ponciano Aguilar 7, Zona Centro, 36000 Guanajuato, Gto., Mexico',
mapsLink: 'Google Maps',
ceremonyNote: 'La ceremonia religiosa es a las 5:00 p. m. (hora de México). Por favor llega 10 minutos antes.',
receptionTitle: 'Sede de la recepción',
receptionName: 'Casa Valadez',
receptionAddress: 'Jdn. de la Unión 3, Zona Centro, 36000 Guanajuato, Gto., Mexico',
receptionNote: 'La recepción es a las 8:00 p. m. (hora de México).',
},
schedule: {
title: 'Cronograma del día de la boda',
ceremony: 'Ceremonia de boda en la iglesia',
photos: 'Sesión de fotos con invitados y familia',
receptionBegins: 'Inicio de la recepción en el restaurante',
welcome: 'Bienvenida de los novios',
},
rsvp: {
title: 'Confirmación de asistencia',
line1Html:
'Confirma tu asistencia antes del <strong>sábado 1 de agosto de 2026</strong> para finalizar asientos y catering.',
line2: 'Si recibiste una invitación, responde usando el formulario.',
button: 'Abrir formulario RSVP',
buttonAria: 'Abrir formulario de RSVP',
nameLabel: 'Nombre completo',
attendanceLabel: 'Asistencia',
attendancePlaceholder: 'Selecciona una opción',
attendanceYes: 'Asistiré',
attendanceNo: 'No asistiré',
submitButton: 'Enviar',
messages: {
required: 'Por favor completa tu nombre completo y estado de asistencia.',
success: '¡Gracias! Tu reservación ha sido enviada.',
error: 'Algo salió mal. Por favor inténtalo de nuevo.',
},
},
travel: {
title: 'Alojamiento y Estacionamiento',
hotelsTitle: 'Recomendaciones de hotel',
hotelSanDiegoHtml:
'Opción más cercana: <a href="https://sandiegohotel.mx/" class="inline-link" target="_blank" rel="noopener noreferrer">Hotel San Diego</a>, ubicado en Jardín de la Unión #1.',
hotelSanDiegoMapsHtml:
'<a href="https://www.google.com/maps/search/?api=1&query=Hotel+San+Diego%2C+Jard%C3%ADn+de+la+Uni%C3%B3n+1%2C+Guanajuato" class="inline-link" target="_blank" rel="noopener noreferrer">Google Maps</a>.',
hotel1850Html:
'Otra opción a pocos pasos: <a href="https://www.hotel1850.com/" class="inline-link" target="_blank" rel="noopener noreferrer">Hotel Boutique 1850</a>, ubicado en Jardín de la Unión #7.',
hotel1850MapsHtml:
'<a href="https://www.google.com/maps/search/?api=1&query=Hotel+Boutique+1850%2C+Jard%C3%ADn+de+la+Uni%C3%B3n+7%2C+Guanajuato" class="inline-link" target="_blank" rel="noopener noreferrer">Google Maps</a>.',
transportTitle: 'Información de transporte',
transportCeremony:
'Para la ceremonia religiosa, te recomendamos llegar con tiempo y buscar un estacionamiento público cerca de Plaza de la Paz o del centro de Guanajuato.',
transportReception: 'Casa Valadez se encuentra frente al Teatro Juárez, muy cerca de la iglesia.',
},
faq: {
title: 'Preguntas frecuentes',
q1: '¿Puedo llevar acompañante?',
a1: 'Revisa tu invitación. Si tienes dudas, contáctanos directamente.',
q2: '¿Están invitados los niños?',
a2: 'Les agradecemos, su comprensión; esta será una recepción exclusiva para adultos.',
q3: '¿Qué debo usar?',
a3: 'Ropa formal.',
q4: '¿Dónde debo estacionarme?',
a4: 'Habrá estacionamiento en sitio con personal en la sede de recepción.',
q5: '¿A qué hora debo llegar?',
a5: 'Por favor llegar entre 10 y 15 min antes de que inicie la ceremonia o la recepción.',
q6: '¿A quién contacto si tengo dudas?',
a6: 'Envía un mensaje a la persona que compartió la invitación contigo.',
},
message: {
note: 'Gracias de todo corazón por acompañarnos y por ser parte de esta etapa tan importante en nuestras vidas. Su presencia, cariño y buenos deseos significan muchísimo para nosotros y harán de este día un recuerdo aún más especial. Con mucho amor y gratitud, Ayane y Alejandro.',
},
footer: {
date: '20 de junio de 2026',
top: 'Volver arriba',
},
},
ja: {
meta: {
title: 'Ayane & Alejandro | 結婚式ゲスト向け情報',
description:
'結婚式のスケジュール、会場、旅行情報、RSVP案内をまとめたゲスト向けサイトです。',
},
skip: 'メインコンテンツへ移動',
language: {
label: '言語',
selectAria: '言語を選択',
spanish: 'Español',
japanese: '日本語',
},
nav: {
ariaLabel: 'メインナビゲーション',
menuButton: 'メニュー',
home: 'ホーム',
details: '詳細',
venues: '会場',
schedule: 'スケジュール',
rsvp: '出欠確認',
confirmAttendance: '出欠を確認する',
travel: '旅行情報',
faq: 'よくある質問',
message: 'メッセージ',
},
countdown: {
ariaLabel: '結婚式までのカウントダウン',
text: '日後に結婚式',
},
hero: {
ariaLabel: '結婚式のご案内',
date: '2026.Oct.01',
},
details: {
title: '重要なお知らせ',
dateTitle: '日付',
dateValue: '2026年10月1日(木)',
ceremonyTitle: '挙式',
ceremonyValue: '挙式は午後5:00(メキシコ時間)に<strong><a href="https://maps.app.goo.gl/3U4JRh3GeCaUGPL46" class="inline-link" target="_blank" rel="noopener noreferrer">Basílica Colegiata de Nuestra Señora de Guanajuato, Gto.</a></strong>で行います。',
photoSessionTitle: '写真セッション',
photoSessionValue: '宗教挙式の後、18:10ごろに教会前の<strong><a href="https://maps.app.goo.gl/Rm2HHoDUS6qW7fgV8" class="inline-link" target="_blank" rel="noopener noreferrer">Plaza de la Paz</a></strong>に集合し、写真セッションを行います。',
receptionTitle: '披露宴',
receptionValue: '披露宴は午後8:00(メキシコ時間)に<strong><a href="https://www.google.com/maps/search/?api=1&query=Jdn.%20de%20la%20Uni%C3%B3n%203%2C%20Zona%20Centro%2C%2036000%20Guanajuato%2C%20Gto.%2C%20Mexico" class="inline-link" target="_blank" rel="noopener noreferrer">Casa Valadez</a></strong>で行います。',
dressTitle: 'ドレスコード',
dressValue: '未定',
},
venues: {
title: '会場',
ceremonyTitle: '挙式会場',
ceremonyName: 'Basílica Colegiata de Nuestra Señora de Guanajuato',
ceremonyAddress: 'C. Ponciano Aguilar 7, Zona Centro, 36000 Guanajuato, Gto., Mexico',
mapsLink: 'Googleマップ(仮リンク)',
ceremonyNote: '宗教挙式は午後5:00(メキシコ時間)開始です。10分前までにお越しください。',
receptionTitle: '披露宴会場',
receptionName: 'Casa Valadez',
receptionAddress: 'Jdn. de la Unión 3, Zona Centro, 36000 Guanajuato, Gto., Mexico',
receptionNote: '披露宴は午後8:00(メキシコ時間)開始です。',
},
schedule: {
title: '結婚式当日のスケジュール',
ceremony: '教会での挙式',
photos: 'ゲスト・家族との写真撮影',
receptionBegins: 'レストランで披露宴開始',
welcome: '新郎新婦の歓迎セレモニー',
},
rsvp: {
title: '出欠確認',
line1Html:
'<strong>2026年8月1日(土)</strong> までに出欠をご回答ください。席次とケータリングの準備に必要です。',
line2: '招待状を受け取った方は、以下のRSVPフォームからご回答ください。',
button: 'RSVPフォームを開く',
buttonAria: 'RSVPフォームを開く',
nameLabel: '氏名',
attendanceLabel: '出欠',
attendancePlaceholder: 'オプションを選択してください',
attendanceYes: '出席します',
attendanceNo: '欠席します',
submitButton: '送信',
messages: {
required: '氏名と出欠を入力してください。',
success: 'ありがとうございます。ご予約内容を送信しました。',
error: 'エラーが発生しました。もう一度お試しください。',
},
},
travel: {
title: '宿泊・駐車場案内',
hotelsTitle: 'ホテルのおすすめ',
hotelSanDiegoHtml:
'最も近いホテルは、Jardín de la Unión #1にある<a href="https://sandiegohotel.mx/" class="inline-link" target="_blank" rel="noopener noreferrer">Hotel San Diego</a>です。',
hotelSanDiegoMapsHtml:
'<a href="https://www.google.com/maps/search/?api=1&query=Hotel+San+Diego%2C+Jard%C3%ADn+de+la+Uni%C3%B3n+1%2C+Guanajuato" class="inline-link" target="_blank" rel="noopener noreferrer">Google Maps</a>。',
hotel1850Html:
'もう一つの徒歩圏内のホテルは、Jardín de la Unión #7にある<a href="https://www.hotel1850.com/" class="inline-link" target="_blank" rel="noopener noreferrer">Hotel Boutique 1850</a>です。',
hotel1850MapsHtml:
'<a href="https://www.google.com/maps/search/?api=1&query=Hotel+Boutique+1850%2C+Jard%C3%ADn+de+la+Uni%C3%B3n+7%2C+Guanajuato" class="inline-link" target="_blank" rel="noopener noreferrer">Google Maps</a>。',
transportTitle: '交通情報',
transportCeremony: '挙式へ車でお越しの場合は、Plaza de la Pazまたはグアナファト中心部周辺の公共駐車場をご利用ください。',
transportReception: 'Casa ValadezはTeatro Juárezの向かい側にあり、教会からもすぐ近くです。',
},
faq: {
title: 'よくある質問',
q1: '同伴者を連れて行けますか?',
a1: '招待状をご確認ください。不明な場合は直接ご連絡ください。',
q2: '子どもの参加は可能ですか?',
a2: 'お子さまも大歓迎です。招待状の記載内容をご確認ください。',
q3: '服装は何がよいですか?',
a3: 'フォーマルまたはセミフォーマルで、動きやすい服装でお願いします。',
q4: '駐車場はどこですか?',
a4: '披露宴会場にスタッフ付きの駐車場があります。',
q5: '何時に到着すればいいですか?',
a5: '挙式または披露宴の開始10〜15分前までにお越しください。',
q6: '質問がある場合の連絡先は?',
a6: '招待を共有した方へメッセージしてください。',
},
message: {
note: '私たちの人生にとって大切な節目に寄り添い、共にお祝いしてくださることを心より感謝しています。皆さまの存在、温かいお気持ち、そして祝福の言葉は、私たちにとって何よりの宝物です。大好きな皆さまに囲まれてこの日を迎えられることを、とても幸せに思います。たくさんの愛と感謝を込めて、Ayane と Alejandroより。',
},
footer: {
date: '2026年6月20日',
top: 'ページ上部へ戻る',
},
},
};
const siteHeader = document.querySelector('.site-header');
const navToggle = document.querySelector('.nav-toggle');
const navMenu = document.querySelector('.nav-menu');
const languageToggle = document.querySelector('#language-toggle');
const countdownDays = document.querySelector('#countdown-days');
const countdownLabel = document.querySelector('#countdown-label');
const scrollTopLink = document.querySelector('[data-scroll-top]');
const targetDate = new Date('2026-10-01T17:00:00-06:00');
const millisecondsPerDay = 1000 * 60 * 60 * 24;
const mobileNavMediaQuery = window.matchMedia('(max-width: 800px)');
const scrollDeltaBeforeToggle = 8;
let currentLanguage = 'es';
let activeRsvpMessage = null;
let lastScrollPosition = window.scrollY;
const getTranslation = (lang, key) => {
return key.split('.').reduce((value, part) => value?.[part], translations[lang]);
};
const applyTranslations = (lang) => {
currentLanguage = translations[lang] ? lang : 'es';
document.documentElement.lang = currentLanguage;
document.querySelectorAll('[data-i18n]').forEach((element) => {
const key = element.dataset.i18n;
const translation = getTranslation(currentLanguage, key);
if (!translation) {
return;
}
if (element.dataset.i18nHtml === 'true') {
element.innerHTML = translation;
} else {
element.textContent = translation;
}
});
document.querySelectorAll('[data-i18n-attr]').forEach((element) => {
const pairs = element.dataset.i18nAttr.split('|');
pairs.forEach((pair) => {
const [attribute, key] = pair.split(':');
const translation = getTranslation(currentLanguage, key);
if (attribute && translation) {
element.setAttribute(attribute.trim(), translation);
}
});
});
updateCountdown();
if (activeRsvpMessage) {
setTranslatedRsvpMessage(activeRsvpMessage.key, activeRsvpMessage.isError);
}
};
const updateCountdown = () => {
if (!countdownDays || !countdownLabel) {
return;
}
const now = new Date();
const difference = targetDate.getTime() - now.getTime();
countdownDays.textContent = difference <= 0 ? '0' : String(Math.ceil(difference / millisecondsPerDay));
countdownLabel.textContent = getTranslation(currentLanguage, 'countdown.text');
};
if (navToggle && navMenu) {
navToggle.addEventListener('click', () => {
const isOpen = navMenu.classList.toggle('open');
navToggle.setAttribute('aria-expanded', String(isOpen));
});
navMenu.querySelectorAll('a').forEach((link) => {
link.addEventListener('click', () => {
navMenu.classList.remove('open');
navToggle.setAttribute('aria-expanded', 'false');
});
});
}
window.addEventListener('keydown', (event) => {
if (event.key === 'Escape' && navMenu?.classList.contains('open')) {
navMenu.classList.remove('open');
navToggle?.setAttribute('aria-expanded', 'false');
navToggle?.focus();
}
});
const closeMobileMenu = () => {
if (!navMenu?.classList.contains('open')) {
return;
}
navMenu.classList.remove('open');
navToggle?.setAttribute('aria-expanded', 'false');
};
const updateHeaderVisibility = () => {
if (!siteHeader) {
return;
}
const currentScrollPosition = window.scrollY;
const isMobile = mobileNavMediaQuery.matches;
const isScrollingDown = currentScrollPosition > lastScrollPosition;
const hasScrolledEnough = Math.abs(currentScrollPosition - lastScrollPosition) > scrollDeltaBeforeToggle;
if (!isMobile) {
siteHeader.classList.remove('header-hidden');
lastScrollPosition = currentScrollPosition;
return;
}
if (currentScrollPosition <= 0) {
siteHeader.classList.remove('header-hidden');
} else if (hasScrolledEnough && isScrollingDown) {
siteHeader.classList.add('header-hidden');
closeMobileMenu();
} else if (hasScrolledEnough) {
siteHeader.classList.remove('header-hidden');
}
if (hasScrolledEnough || currentScrollPosition <= 0) {
lastScrollPosition = currentScrollPosition;
}
};
window.addEventListener('scroll', updateHeaderVisibility, { passive: true });
if (scrollTopLink) {
scrollTopLink.addEventListener('click', (event) => {
event.preventDefault();
siteHeader?.classList.remove('header-hidden');
lastScrollPosition = window.scrollY;
window.scrollTo({ top: 0, behavior: 'smooth' });
history.replaceState(null, '', `${window.location.pathname}${window.location.search}`);
});
}
if (typeof mobileNavMediaQuery.addEventListener === 'function') {
mobileNavMediaQuery.addEventListener('change', updateHeaderVisibility);
} else {
mobileNavMediaQuery.addListener(updateHeaderVisibility);
}
if (languageToggle) {
languageToggle.value = 'es';
languageToggle.addEventListener('change', (event) => {
applyTranslations(event.target.value);
});
}
applyTranslations('es');
setInterval(updateCountdown, 60 * 1000);
const rsvpForm = document.querySelector('#rsvp-form');
const rsvpMessage = document.querySelector('#rsvp-message');
const rsvpSubmitButton = document.querySelector('#rsvp-submit');
// Paste your Google Apps Script Web App URL here if it changes.
const scriptURL = 'https://script.google.com/macros/s/AKfycbzglbZEObJ8d4tNLIW0imaGybsgA8f8Ga7JbyVdOfE8RJQkQfZavPM7CfJL0kdUUkdH/exec';
const setRsvpMessage = (message, isError = false) => {
activeRsvpMessage = null;
if (!rsvpMessage) {
return;
}
rsvpMessage.textContent = message;
rsvpMessage.classList.toggle('error', isError);
rsvpMessage.classList.toggle('success', !isError && Boolean(message));
};
const setTranslatedRsvpMessage = (key, isError = false) => {
activeRsvpMessage = { key, isError };
if (!rsvpMessage) {
return;
}
const message = getTranslation(currentLanguage, `rsvp.messages.${key}`) || '';
rsvpMessage.textContent = message;
rsvpMessage.classList.toggle('error', isError);
rsvpMessage.classList.toggle('success', !isError && Boolean(message));
};
if (rsvpForm && rsvpSubmitButton) {
rsvpForm.addEventListener('submit', async (event) => {
event.preventDefault();
const nameInput = rsvpForm.querySelector('[name="name"]');
const attendanceInput = rsvpForm.querySelector('[name="attendance"]');
const name = String(nameInput?.value || '').trim();
const attendance = String(attendanceInput?.value || '').trim();
if (!name || !attendance) {
setTranslatedRsvpMessage('required', true);
return;
}
const formData = new FormData(rsvpForm);
rsvpSubmitButton.disabled = true;
setRsvpMessage('');
try {
await fetch(scriptURL, {
method: 'POST',
mode: 'no-cors',
body: formData,
});
setTranslatedRsvpMessage('success');
rsvpForm.reset();
} catch (error) {
console.error(error);
setTranslatedRsvpMessage('error', true);
} finally {
rsvpSubmitButton.disabled = false;
}
});
}