PageSourceSearch

https://www.tokyodome-hotels.co.jp/assets/hotel/scripts/modules/registrationStateIndicator.js

js tokyodome-hotels.co.jp collected 2026-09-24 10:46:31 UTC 4,741 bytes, 150 lines download raw bytes

1import { parseLocalDateTime } from '../../../common/scripts/utility/date.js';
2/**
3 * 受付ステータスの表示制御
4 */
5export class RegistrationStateIndicator {
6    elements;
7    /**
8     * 現在日時(デバイス時刻)
9     */
10    #now = new Date();
11    /**
12     * 受付開始日時(要素ごと)
13     */
14    #startAtByElement = new WeakMap();
15    /**
16     * 受付終了日時(要素ごと)
17     */
18    #endAtByElement = new WeakMap();
19    /**
20     * ラベル用のテンプレート要素
21     */
22    #labelTemplates;
23    /**
24     * @constructor
25     * @param elements 機能の起点となる要素
26     */
27    constructor(elements) {
28        this.elements = elements;
29        this.#labelTemplates = document.querySelectorAll('.fn-registration-label__template');
30    }
31    init() {
32        this.#indexScheduleByElement();
33        this.#renderAll();
34    }
35    /**
36     * 開始日時・終了日時を取得して`Date`に変換し、`WeakMap`に格納
37     */
38    #indexScheduleByElement() {
39        for (const element of this.elements) {
40            const startRaw = element.dataset.registrationStart;
41            const endRaw = element.dataset.registrationEnd;
42            const startAt = parseLocalDateTime(startRaw);
43            const endAt = parseLocalDateTime(endRaw);
44            if (startAt) {
45                this.#startAtByElement.set(element, startAt);
46            }
47            if (endAt) {
48                this.#endAtByElement.set(element, endAt);
49            }
50        }
51    }
52    /**
53     * すべての要素に対して状態判定 → ラベル描画
54     */
55    #renderAll() {
56        for (const element of this.elements) {
57            const state = this.#resolveStateFor(element);
58            this.#renderLabelFor(element, state);
59        }
60    }
61    /**
62     * 境界判定
63     * - none: 開始日時または終了日時が未設定
64     * - open:`start <= now < end`
65     * - closed:`now >= end`
66     * - upcoming:`now < start`
67     */
68    #resolveStateFor(element) {
69        const now = this.#now;
70        const start = this.#startAtByElement.get(element);
71        const end = this.#endAtByElement.get(element);
72        if (!start || !end) {
73            return 'none';
74        }
75        if (start && now < start) {
76            return 'upcoming';
77        }
78        if (end && now >= end) {
79            return 'closed';
80        }
81        return 'open';
82    }
83    /**
84     * 状態に応じて不要ラベルを削除した`DocumentFragment`を返す
85     */
86    #chooseLabelFragment(state, id) {
87        // NOTE: 日時未設定(none)または受付前(upcoming)はラベル挿入しない
88        if (!this.#labelTemplates.length ||
89            state === 'none' ||
90            state === 'upcoming') {
91            return null;
92        }
93        const template = this.#getTemplateByID(id);
94        // NOTE: テンプレートが見つからなかった場合はラベル挿入しない
95        if (!template) {
96            return null;
97        }
98        const fragment = template.content.cloneNode(true);
99        const openLabel = fragment.querySelector('.fn-registration-label__open');
100        const closedLabel = fragment.querySelector('.fn-registration-label__closed');
101        if (!openLabel || !closedLabel) {
102            return null;
103        }
104        if (state === 'closed') {
105            openLabel.remove();
106        }
107        else {
108            closedLabel.remove();
109        }
110        return fragment;
111    }
112    /**
113     * `DocumentFragment`を挿入位置に応じて`slot`に追加
114     */
115    #renderLabelFor(element, state) {
116        const slot = element.querySelector('.fn-registration-label__slot');
117        if (!slot) {
118            return;
119        }
120        const fragment = this.#chooseLabelFragment(state, element.dataset.templateId);
121        if (!fragment) {
122            return;
123        }
124        const insertFirst = slot.classList.contains('fn-registration-label__insertFirst');
125        if (insertFirst) {
126            slot.insertBefore(fragment, slot.firstElementChild);
127        }
128        else {
129            slot.appendChild(fragment);
130        }
131    }
132    /**
133     * 指定IDに対応するテンプレート要素を返す
134     */
135    #getTemplateByID(id) {
136        const templates = Array.from(this.#labelTemplates);
137        if (!id) {
138            return templates.find((template) => !template.hasAttribute('id'));
139        }
140        return templates.find((template) => template.id === id);
141    }
142}
143(() => {
144    const elements = document.querySelectorAll('.js-registration-label');
145    if (elements.length === 0) {
146        return;
147    }
148    const registrationStateIndicator = new RegistrationStateIndicator(elements);
149    registrationStateIndicator.init();
150})();

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.