PageSourceSearch

https://www.fujisan-climb.jp/wp-content/themes/mtfuji-climbing-off…/assets/js/top-weather-wind.js?ver=1.0

js fujisan-climb.jp collected 2026-10-02 03:18:33 UTC 9,041 bytes, 405 lines download raw bytes

1(() => {
2  const API_URL = '/wp-content/weather-data/wind.json';
3  const TABLES_WRAP_ID = 'weather-wind-tables';
4  const PREV_ID = 'weather-wind-prev';
5  const NEXT_ID = 'weather-wind-next';
6
7  /*
8   * HTMLのlang属性を取得。
9   *
10   * ja、ja-JPなど、日本語で始まる場合のみ日本語表示。
11   * en、zh-CN、koなど、それ以外はすべて英語表示。
12   */
13  const currentLang = (
14    document.documentElement.lang || 'ja'
15  ).toLowerCase();
16
17  const isJapanese = currentLang.startsWith('ja');
18
19  /*
20   * 表示文言
21   */
22  const LABELS = isJapanese
23    ? {
24        days: ['日', '月', '火', '水', '木', '金', '土'],
25        time: '時刻',
26        altitude4200: '高度4200m付近',
27        altitude3000: '高度3000m付近',
28        temperatureWind: '気温 / 風向風速',
29        precipitation: '降水量',
30        altitude880: '標高880m付近',
31        fetchError: '風速・気温データの取得に失敗しました。'
32      }
33    : {
34        days: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
35        time: 'Time',
36        altitude4200: 'Around 4,200m',
37        altitude3000: 'Around 3,000m',
38        temperatureWind: 'Temperature / Wind',
39        precipitation: 'Precipitation',
40        altitude880: 'Around 880m',
41        fetchError: 'Failed to load wind and temperature data.'
42      };
43
44  /*
45   * 日本語の風向を英語の略称へ変換。
46   *
47   * 日本語ページでは変換せず、そのまま表示する。
48   */
49  const WIND_DIRECTION_EN = {
50    '北': 'N',
51    '北北東': 'NNE',
52    '北東': 'NE',
53    '東北東': 'ENE',
54    '東': 'E',
55    '東南東': 'ESE',
56    '南東': 'SE',
57    '南南東': 'SSE',
58    '南': 'S',
59    '南南西': 'SSW',
60    '南西': 'SW',
61    '西南西': 'WSW',
62    '西': 'W',
63    '西北西': 'WNW',
64    '北西': 'NW',
65    '北北西': 'NNW',
66    '静穏': 'Calm'
67  };
68
69  /**
70   * 日付ラベルを作成
71   *
72   * 入力例:
73   * 020318 → 2月3日 18:00
74   */
75  function formatSlotLabel(dateStr) {
76    if (!dateStr || dateStr.length < 6) {
77      return {
78        top: dateStr || '',
79        bottom: ''
80      };
81    }
82
83    const month = dateStr.slice(0, 2);
84    const day = dateStr.slice(2, 4);
85    const hour = dateStr.slice(4, 6);
86
87    const now = new Date();
88    const year = now.getFullYear();
89
90    const date = new Date(
91      year,
92      Number(month) - 1,
93      Number(day)
94    );
95
96    const week = LABELS.days[date.getDay()] || '';
97
98    if (isJapanese) {
99      return {
100        top: `${Number(day)}日(${week})`,
101        bottom: `${hour}:00`
102      };
103    }
104
105    return {
106      top: `${Number(month)}/${Number(day)} (${week})`,
107      bottom: `${hour}:00`
108    };
109  }
110
111  /**
112   * 風向文字列を整形
113   */
114  function normalizeWindText(text) {
115    const normalized = String(text || '').replace(/\s+/g, '');
116
117    if (isJapanese) {
118      return normalized;
119    }
120
121    return WIND_DIRECTION_EN[normalized] || normalized;
122  }
123
124  /**
125   * 配列を指定件数ごとに分割
126   */
127  function chunkArray(arr, size) {
128    const result = [];
129
130    for (let i = 0; i < arr.length; i += size) {
131      result.push(arr.slice(i, i + size));
132    }
133
134    return result;
135  }
136
137  /**
138   * 風速・気温テーブルを生成
139   */
140  function createTable(slots4200, slots3000) {
141    const table = document.createElement('table');
142    table.className = 'weather-wind__table';
143
144    const maxCols = Math.max(
145      slots4200.length,
146      slots3000.length
147    );
148
149    /*
150     * 時刻行
151     */
152    const headRow = document.createElement('tr');
153
154    const headLabel = document.createElement('th');
155    headLabel.className = 'weather-wind__rowhead';
156    headLabel.textContent = LABELS.time;
157
158    headRow.appendChild(headLabel);
159
160    for (let i = 0; i < maxCols; i++) {
161      const th = document.createElement('th');
162      const slot = slots4200[i] || slots3000[i];
163
164      if (slot) {
165        const label = formatSlotLabel(slot.date);
166
167        th.innerHTML = `
168          <span>${label.top}</span>
169          <span class="weather-wind__label">${label.bottom}</span>
170        `;
171      } else {
172        th.innerHTML = '&nbsp;';
173      }
174
175      headRow.appendChild(th);
176    }
177
178    table.appendChild(headRow);
179
180    /*
181     * 高度4,200m付近
182     */
183    const row4200 = document.createElement('tr');
184
185    const row4200Head = document.createElement('th');
186    row4200Head.className = 'weather-wind__rowhead';
187    row4200Head.innerHTML = `
188      ${LABELS.altitude4200}
189      <span class="weather-wind__label">
190        ${LABELS.temperatureWind}
191      </span>
192    `;
193
194    row4200.appendChild(row4200Head);
195
196    for (let i = 0; i < maxCols; i++) {
197      const td = document.createElement('td');
198      const slot = slots4200[i];
199
200      if (slot) {
201        td.innerHTML = `
202          <span class="weather-wind__temp">
203            ${slot.temp}℃
204          </span>
205          <span class="weather-wind__wind">
206            ${normalizeWindText(slot.wind)}<br>
207            ${slot.ws}m/s
208          </span>
209        `;
210      } else {
211        td.innerHTML = '&nbsp;';
212      }
213
214      row4200.appendChild(td);
215    }
216
217    table.appendChild(row4200);
218
219    /*
220     * 高度3,000m付近
221     */
222    const row3000 = document.createElement('tr');
223
224    const row3000Head = document.createElement('th');
225    row3000Head.className = 'weather-wind__rowhead';
226    row3000Head.innerHTML = `
227      ${LABELS.altitude3000}
228      <span class="weather-wind__label">
229        ${LABELS.temperatureWind}
230      </span>
231    `;
232
233    row3000.appendChild(row3000Head);
234
235    for (let i = 0; i < maxCols; i++) {
236      const td = document.createElement('td');
237      const slot = slots3000[i];
238
239      if (slot) {
240        td.innerHTML = `
241          <span class="weather-wind__temp">
242            ${slot.temp}℃
243          </span>
244          <span class="weather-wind__wind">
245            ${normalizeWindText(slot.wind)}<br>
246            ${slot.ws}m/s
247          </span>
248        `;
249      } else {
250        td.innerHTML = '&nbsp;';
251      }
252
253      row3000.appendChild(td);
254    }
255
256    table.appendChild(row3000);
257
258    /*
259     * 降水量
260     */
261    const rowRain = document.createElement('tr');
262
263    const rowRainHead = document.createElement('th');
264    rowRainHead.className = 'weather-wind__rowhead';
265    rowRainHead.innerHTML = `
266      ${LABELS.precipitation}
267      <span class="weather-wind__label">
268        ${LABELS.altitude880}
269      </span>
270    `;
271
272    rowRain.appendChild(rowRainHead);
273
274    for (let i = 0; i < maxCols; i++) {
275      const td = document.createElement('td');
276      const slot = slots4200[i] || slots3000[i];
277
278      if (slot) {
279        td.innerHTML = `
280          <span class="weather-wind__rain">
281            ${slot.precipitation}mm
282          </span>
283        `;
284      } else {
285        td.innerHTML = '&nbsp;';
286      }
287
288      rowRain.appendChild(td);
289    }
290
291    table.appendChild(rowRain);
292
293    return table;
294  }
295
296  /**
297   * 横スライダーを初期化
298   */
299  function initSlider(totalPages) {
300    const wrap = document.getElementById(TABLES_WRAP_ID);
301    const prev = document.getElementById(PREV_ID);
302    const next = document.getElementById(NEXT_ID);
303
304    if (!wrap || !prev || !next) {
305      return;
306    }
307
308    let current = 0;
309
310    function update() {
311      wrap.style.transform = `translateX(-${current * 100}%)`;
312
313      prev.disabled = current <= 0;
314      next.disabled = current >= totalPages - 1;
315    }
316
317    prev.addEventListener('click', () => {
318      if (current <= 0) {
319        return;
320      }
321
322      current -= 1;
323      update();
324    });
325
326    next.addEventListener('click', () => {
327      if (current >= totalPages - 1) {
328        return;
329      }
330
331      current += 1;
332      update();
333    });
334
335    update();
336  }
337
338  /**
339   * 風速・気温表示を初期化
340   */
341  async function initWeatherWind() {
342    const wrap = document.getElementById(TABLES_WRAP_ID);
343
344    if (!wrap) {
345      return;
346    }
347
348    try {
349      const response = await fetch(
350        API_URL,
351        {
352          cache: 'no-store'
353        }
354      );
355
356      if (!response.ok) {
357        throw new Error('Failed to fetch wind data');
358      }
359
360      const json = await response.json();
361
362      const altitude3000 = json.altitude_3000 || [];
363      const altitude4200 = json.altitude_4200 || [];
364
365      /*
366       * 8コマずつで1テーブルに分割
367       */
368      const group3000 = chunkArray(altitude3000, 8);
369      const group4200 = chunkArray(altitude4200, 8);
370
371      const pages = Math.max(
372        group3000.length,
373        group4200.length
374      );
375
376      /*
377       * 再実行時に二重表示されないよう初期化
378       */
379      wrap.innerHTML = '';
380
381      for (let i = 0; i < pages; i++) {
382        const table = createTable(
383          group4200[i] || [],
384          group3000[i] || []
385        );
386
387        wrap.appendChild(table);
388      }
389
390      initSlider(pages);
391
392    } catch (error) {
393      console.error(error);
394
395      wrap.innerHTML = `
396        <p>${LABELS.fetchError}</p>
397      `;
398    }
399  }
400
401  document.addEventListener(
402    'DOMContentLoaded',
403    initWeatherWind
404  );
405})();

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.