// === グローバル変数 ===
let japaneseHolidays = {};
let cafeteriaMenuData = {};
const trainStatusList = [
{
lineName: 'JR京都線',
jsonPath: './delayInfoJson/delay_info_jrKyotoLine.json',
elementIds: ['jr-status-area']
},
{
lineName: '阪急千里線',
jsonPath: './delayInfoJson/delay_info_hankyusenriLine.json',
elementIds: ['hankyu-suita-status-area', 'hankyu-toyotsu-status-area']
}
];
// === DOM要素取得 ===
const currentTimeElem = document.getElementById('currentTime');
const currentSecondElem = document.getElementById('currentSecond');
const dateInfoElem = document.getElementById('dateInfo');
const weatherInfoDiv = document.getElementById('weather-info');
const weatherImageDiv = document.getElementById('weather-image');
const jrSuitaTbody = document.getElementById('jr-suita-tbody');
const hankyuSuitaTbody = document.getElementById('hankyu-suita-tbody');
const hankyuToyotsuTbody = document.getElementById('hankyu-toyotsu-tbody');
const busTbody = document.getElementById('bus-tbody');
const cafeteriaMenuTitleElem = document.getElementById('cafeteria-menu-title');
const cafeteriaMenuListElem = document.getElementById('cafeteria-menu-list');
// === 時刻表データ格納用 ===
let jrKyotoTimetable = [], jrOsakaTimetable = [];
let hankyuSuitaKitasenriTimetable = [], hankyuSuitaUmedaTimetable = [];
let hankyuToyotsuKitasenriTimetable = [], hankyuToyotsuUmedaTimetable = [];
let busAsahigaokaTimetable = [], busJrSuitaTimetable = [];
// === 基本機能:時刻・日付・天気 ===
function updateTime() {
const now = new Date();
currentTimeElem.textContent = String(now.getHours()).padStart(2, '0') + ':' + String(now.getMinutes()).padStart(2, '0');
currentSecondElem.textContent = String(now.getSeconds()).padStart(2, '0');
}
function updateDateInfo() {
const now = new Date();
const monthNames = ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"];
const dayNames = ["日", "月", "火", "水", "木", "金", "土"];
dateInfoElem.textContent = `${monthNames[now.getMonth()]} ${now.getDate()}日 (${dayNames[now.getDay()]})`;
}
function fetchWeatherData() {
fetch(`https://weather.tsukumijima.net/api/forecast/city/270000`,{ cache: 'no-store' })
.then(response => response.json())
.then(data => {
if (data.forecasts && data.forecasts[1]) {
const tomorrow = data.forecasts[1];
weatherInfoDiv.innerHTML = `<b>大阪 明日</b> <div>${tomorrow.temperature?.max?.celsius || '---'}°C</div><div>降水:${tomorrow.chanceOfRain?.T06_12 || '---'}</div>`;
if (tomorrow.image?.url) weatherImageDiv.innerHTML = `<img src="${tomorrow.image.url}" alt="${tomorrow.image.title}">`;
}
})
.catch(error => {
console.error('天気情報の取得に失敗:', error);
weatherInfoDiv.textContent = '天気情報 取得失敗';
});
}
// === 休日判定ロジック ===
function isTodayHoliday() {
const now = new Date();
const dayOfWeek = now.getDay();
if (dayOfWeek === 0 || dayOfWeek === 6) return true;
const yyyy = now.getFullYear();
const mm = String(now.getMonth() + 1).padStart(2, '0');
const dd = String(now.getDate()).padStart(2, '0');
const todayFormatted = `${yyyy}-${mm}-${dd}`;
return !!japaneseHolidays[todayFormatted];
}
// === データ取得汎用関数 ===
async function fetchJson(url) {
const response = await fetch(url,{ cache: 'no-store' });
if (!response.ok) throw new Error(`ファイル取得失敗 Status: ${response.status}, URL: ${url}`);
return response.json();
}
// === 食堂メニュー関連 ===
async function fetchCafeteriaMenu() {
try {
cafeteriaMenuData = (await fetchJson('./YamatoFoodJson/yamato_menu.json'))['今週の食堂'];
console.log("食堂メニューデータを取得しました。");
} catch (error) {
console.error("食堂メニューデータの読み込みに失敗しました:", error);
cafeteriaMenuListElem.innerHTML = '<li>メニュー取得に失敗</li>';
}
}
function updateCafeteriaMenu() {
if (Object.keys(cafeteriaMenuData).length === 0) return;
const now = new Date();
let targetDate = new Date(now);
let titleText = "食堂 本日の献立";
let noMenuMessage = '<li>今日のメニューはありません。</li>';
// 14時以降は明日の献立を表示する
if (now.getHours() >= 14) {
targetDate.setDate(targetDate.getDate() + 1);
titleText = "食堂 明日の献立";
noMenuMessage = '<li>明日のメニューはありません。</li>';
}
/*
* 修正点: 土日場合に自動で月曜日のメニューを探しにいく処理を削除します。
* これにより、土日にはその日のメニューがない場合に「メニューはありません」と正しく表示されます。
const dayOfWeek = targetDate.getDay();
if (dayOfWeek === 6) targetDate.setDate(targetDate.getDate() + 2);
else if (dayOfWeek === 0) targetDate.setDate(targetDate.getDate() + 1);
*/
const menuKey = `${targetDate.getMonth() + 1}/${targetDate.getDate()}(${["日", "月", "火", "水", "木", "金", "土"][targetDate.getDay()]})`;
const menuItems = cafeteriaMenuData[menuKey];
cafeteriaMenuTitleElem.textContent = titleText;
if (menuItems && menuItems.length > 0) {
cafeteriaMenuListElem.innerHTML = menuItems.map(item => `<li>${item.name}</li>`).join('');
} else {
// 状況に応じた「メニューなし」メッセージを表示
cafeteriaMenuListElem.innerHTML = noMenuMessage;
}
}
// === 時刻表・運行情報関連 ===
function fetchTimeTableData(url, processFunc) {
return fetchJson(url).then(data => data).catch(error => {
console.error(`${url}の読み込みに失敗:`, error);
return []; // エラー時は空配列を返す
});
}
function getUpcomingTrains(timetable, count) {
if (!Array.isArray(timetable)) return [];
const now = new Date();
const nowMinutes = now.getHours() * 60 + now.getMinutes();
return timetable.map(train => {
const depMinutes = parseInt(train.hour) * 60 + parseInt(train.minute);
let diff = depMinutes - nowMinutes;
// 日付をまたぐ深夜便の処理
if (parseInt(train.hour) < 4 && now.getHours() > 20) {
diff += 24 * 60;
}
return { ...train, diff };
}).filter(train => train.diff >= 0).sort((a, b) => a.diff - b.diff).slice(0, count);
}
function createTimetableRowHtml(dep, isBus = false) {
if (!dep) return isBus ? '<td></td><td></td><td></td><td></td>' : '';
const timeStr = `${String(dep.hour).padStart(2, '0')}:${String(dep.minute).padStart(2, '0')}`;
const destStr = (dep.destination || '').replace(/JR京都線\s*|JR神戸線\s*|JR宝塚線\s*|阪急千里線\s*|[阪急]\s*|【始発】\s*|行\s*$/g, '').trim();
const diffStr = dep.diff <= 0 ? "到着済み" : `${dep.diff}分`;
if (isBus) return `<td>${dep.route || ''}</td><td>${timeStr}</td><td>${destStr}</td><td>${diffStr}</td>`;
return `<td>${timeStr}</td><td>${destStr}</td><td>${diffStr}</td>`;
}
function renderTimetable(tbody, leftData, rightData, isBus = false) {
if (!tbody) return;
const upcomingLeft = getUpcomingTrains(leftData, 3);
const upcomingRight = getUpcomingTrains(rightData, 3);
let tableHTML = '';
for (let i = 0; i < 3; i++) {
tableHTML += '<tr>' + createTimetableRowHtml(upcomingLeft[i], isBus) + createTimetableRowHtml(upcomingRight[i]) + '</tr>';
}
tbody.innerHTML = tableHTML;
}
function fetchAllTimetables() {
const todayIsHoliday = isTodayHoliday();
const busFiles = todayIsHoliday ? ['katayama_asahigaoka_holidays.json', 'katayama_jrsuita_holidays.json'] : ['katayama_asahigaoka_weekdays.json', 'katayama_jrsuita_weekdays.json'];
Promise.all([
fetchTimeTableData('keepJson/jr/suita/toTakatuki_jrSuita.json'),
fetchTimeTableData('keepJson/jr/suita/toOsaka_jrSuita.json'),
fetchTimeTableData('keepJson/hankyu/suita/toKitasenri_hankyuSuita.json'),
fetchTimeTableData('keepJson/hankyu/suita/toUmeda_hankyuSuita.json'),
fetchTimeTableData('keepJson/hankyu/toyotu/toKitasenri_hankyuToyotu.json'),
fetchTimeTableData('keepJson/hankyu/toyotu/toUmeda_hankyuToyotu.json'),
fetchTimeTableData(`keepJson/bus/${busFiles[0]}`),
]).then(results => {
[jrKyotoTimetable, jrOsakaTimetable, hankyuSuitaKitasenriTimetable, hankyuSuitaUmedaTimetable, hankyuToyotsuKitasenriTimetable, hankyuToyotsuUmedaTimetable, busAsahigaokaTimetable] = results;
updateAllTimetables();
setInterval(updateAllTimetables, 60000);
});
}
function updateAllTimetables() {
renderTimetable(jrSuitaTbody, jrKyotoTimetable, jrOsakaTimetable);
renderTimetable(hankyuSuitaTbody, hankyuSuitaKitasenriTimetable, hankyuSuitaUmedaTimetable);
renderTimetable(hankyuToyotsuTbody, hankyuToyotsuKitasenriTimetable, hankyuToyotsuUmedaTimetable);
renderTimetable(busTbody, busAsahigaokaTimetable, busJrSuitaTimetable, true);
}
function fetchAndDisplayTrainStatus() {
trainStatusList.forEach(train => {
fetchJson(train.jsonPath).then(data => {
const isTrouble = data.運行状況.ステータス !== '平常運転';
train.elementIds.forEach(id => {
const elem = document.getElementById(id);
if (elem) {
elem.className = 'status-area ' + (isTrouble ? 'status-trouble' : 'status-normal');
elem.innerHTML = `<span class="status-title">${data.運行状況.ステータス}</span>`;
}
});
}).catch(error => {
console.error(`${train.lineName}の運行情報取得に失敗:`, error);
train.elementIds.forEach(id => {
const elem = document.getElementById(id);
if(elem) elem.innerHTML = `<p>${train.lineName} 運行情報取得失敗</p>`;
});
});
});
}
// HTMLに表示する要素のID
const targetElementId = 'scrollingText';
// 読み込むJSONファイルのリスト
const jsonFiles = [
'/delayInfoJson/delay_info_jrKyotoLine.json',
'/delayInfoJson/delay_info_hankyusenriLine.json'
];
// ページの読み込みが完了したら実行
document.addEventListener('DOMContentLoaded', () => {
// すべてのJSONファイルを並行して読み込むPromiseを作成
const fetchPromises = jsonFiles.map(file =>
fetch(file)
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status} for ${file}`);
}
return response.json();
})
.catch(error => {
console.error(`Error fetching or parsing ${file}:`, error);
return null; // エラーが発生しても処理を続行するためにnullを返す
})
);
// すべてのPromiseが解決するのを待つ
Promise.all(fetchPromises)
.then(results => {
let combinedDetails = [];
// 各JSONの結果から「詳細」情報を抽出
results.forEach((data, index) => {
if (data && data.運行状況 && data.運行状況.詳細) {
const routeName = data.列車情報 ? data.列車情報.路線名 : `ファイル${index + 1}`;
const detail = data.運行状況.詳細;
combinedDetails.push(`${routeName}: ${detail}`);
} else {
console.warn(`ファイル ${jsonFiles[index]} から詳細情報を取得できませんでした。`);
}
});
// 取得した詳細情報を結合(ここでは全角スペースで区切る例)
const finalDisplayText = combinedDetails.join(' '); // 複数の詳細情報を全角スペース2つで区切る
// 指定したIDのHTML要素を取得
const displayElement = document.getElementById(targetElementId);
// 要素が存在するか確認し、テキストを設定
if (displayElement) {
displayElement.textContent = finalDisplayText;
} else {
console.error(`IDが '${targetElementId}' の要素が見つかりません。HTMLに正しいIDを持つ要素が存在することを確認してください。`);
}
})
.catch(error => {
console.error('Promise.allでエラーが発生しました:', error);
});
});
// === 全画面表示 ===
function initializeFullscreenButton() {
const button = document.getElementById('fullscreen-button');
if (!button) return;
button.addEventListener('click', () => {
if (!document.fullscreenElement) {
document.documentElement.requestFullscreen().catch(err => console.error(err));
} else {
document.exitFullscreen();
}
});
document.addEventListener('fullscreenchange', () => {
button.style.display = document.fullscreenElement ? 'none' : 'block';
});
}
// === アプリケーション初期化処理 ===
async function initializeApp() {
// 1. 静的データの取得
await Promise.all([
fetchJson(`https://holidays-jp.github.io/api/v1/${new Date().getFullYear()}/date.json`,{ cache: 'no-store' }).then(data => {
japaneseHolidays = data;
console.log("日本の祝日データを取得しました。");
}).catch(err => console.error("祝日データの取得に失敗:", err)),
fetchCafeteriaMenu()
]);
// 2. 初期表示
updateTime();
updateDateInfo();
fetchWeatherData();
fetchAllTimetables();
fetchAndDisplayTrainStatus();
updateCafeteriaMenu();
initializeFullscreenButton();
// 3. 定期更新の設定
setInterval(updateTime, 1000);
setInterval(updateDateInfo, 60000);
setInterval(fetchWeatherData, 3600000); // 1時間ごと
setInterval(fetchAndDisplayTrainStatus, 300000); // 5分ごと
setInterval(updateCafeteriaMenu, 60000);
}
// --- 初期実行 ---
initializeApp();
timetable2.0 javascript
投稿日時: 2025年6月25日 05:10