import { INTERVALS, API_ENDPOINTS } from '@/constants';
type InjectJavascriptData = (
a: string,
b: string,
c: string,
d: string,
e: string,
f: string
) => string;
export const injectJavascriptData: InjectJavascriptData = (
mapSwitch,
iconSetting,
stationMenu,
trainMenu,
uiSetting,
useUnyohub
) => {
// 一番上のメニュー非表示 地図スイッチによって切り替え
const topMenu =
mapSwitch != "true"
? `
document.querySelector('#header a').style.display = 'none';
document.querySelector('#main').style.left = '0px';
document.querySelector('#header').style.height = '50px';
document.querySelector('#main').style.paddingTop = '54px';
document.querySelector('#headerStr').style.display = 'none';
`
: `
document.querySelector('.accordionClass').style.display = 'none';
document.querySelector('#header').style.display = 'none';
document.querySelector('#main').style.left = '0px';
document.querySelector('#main').style.paddingTop = '0px';
document.querySelector('#headerStr').style.display = 'none';
`;
// 上部ヘッダーの取り扱い、自動再読み込み、setStringsの実行
const bootData = `
// Font Awesome は FA6/7 ともに CSS Custom Properties (content:var(--fa)) 依存で
// 古い WebView (Chrome 49未満) では表示されないため、インライン SVG に置き換え済み。
// CDN依存・外部リソースロードも不要になった。
// 軽量な変更検出ユーティリティ (lodash_.isEqual の代替)
const _hashes = {};
const hasChanged = (key, newVal) => {
const h = JSON.stringify(newVal);
if (_hashes[key] === h) return false;
_hashes[key] = h;
return true;
};
// データ変数の宣言
let stationList = {};
let trainDataList = [];
let operationList = [];
let trainDiagramData2 = {};
let probremsData = [];
let unyohubData = [];
const useUnyohubSetting = ${useUnyohub === "true"};
const setReload = () => {
try {
document.getElementById('refreshIcon').click();
setStrings();
} catch(e) {}
};
// ポーリング処理 (Phase 3 以降)
const startPolling = () => {
const DatalistUpdate = () => {
try {
fetch("${API_ENDPOINTS.TRAIN_DATA_API}").then(r => r.json())
.then(data => {
if (hasChanged('trainDataList', data.data)) {
trainDataList = data.data;
setReload();
}
});
} catch(e) {}
setTimeout(DatalistUpdate, ${INTERVALS.DELAY_UPDATE});
};
DatalistUpdate();
const operationListUpdate = () => {
try {
fetch("${API_ENDPOINTS.OPERATION_LOGS}").then(r => r.json())
.then(data => {
if (data?.data === null) return;
const filtered = (data.data || []).filter(d => d.state !== 100);
if (hasChanged('operationList', filtered)) {
operationList = filtered;
setReload();
}
});
} catch(e) {}
setTimeout(operationListUpdate, ${INTERVALS.DELAY_UPDATE});
};
operationListUpdate();
const TrainDiagramData2Update = () => {
try {
fetch("${API_ENDPOINTS.DIAGRAM_TODAY}").then(r => r.json())
.then(res => {
const data = {};
res.forEach(d => { const keys = Object.keys(d); data[keys] = d[keys]; });
if (hasChanged('trainDiagramData2', data)) {
trainDiagramData2 = data;
setReload();
}
});
} catch(e) {}
setTimeout(TrainDiagramData2Update, ${INTERVALS.DELAY_UPDATE});
};
TrainDiagramData2Update();
const getProblemsData = () => {
try {
fetch("${API_ENDPOINTS.POSITION_PROBLEMS}").then(r => r.json())
.then(data => {
if (hasChanged('probremsData', data?.data)) {
probremsData = data.data;
setReload();
}
});
} catch(e) {}
setTimeout(getProblemsData, ${INTERVALS.FETCH_DIAGRAM});
};
getProblemsData();
// unyohub は Phase 3 で遅延スタート (他の読み込みを邪魔しない)
const unyohubDataUpdate = () => {
if (!useUnyohubSetting) {
setTimeout(unyohubDataUpdate, ${INTERVALS.DELAY_UPDATE});
return;
}
try {
fetch("${API_ENDPOINTS.UNYOHUB_DATA}" + '?_=' + Date.now())
.then(r => r.json())
.then(data => {
if (hasChanged('unyohubData', data)) {
unyohubData = data;
console.log('[UnyoHub] Data updated:', unyohubData.length, 'items');
setReload();
}
})
.catch(e => console.error('[UnyoHub] Fetch error:', e));
} catch(e) {
console.error('[UnyoHub] Fetch error:', e);
}
setTimeout(unyohubDataUpdate, ${INTERVALS.DELAY_UPDATE});
};
unyohubDataUpdate();
};
// === Phase 1: 軽量API (合計~210ms) を並列取得 → setReload 1回 ===
// OPERATION_LOGS: 0.07s/3KB, STATION_LIST: 0.18s/80KB, POSITION_PROBLEMS: 0.21s/11B
Promise.allSettled([
fetch("${API_ENDPOINTS.STATION_LIST}").then(r => r.json()).then(data => {
stationList = data;
}).catch(() => {}),
fetch("${API_ENDPOINTS.OPERATION_LOGS}").then(r => r.json()).then(data => {
if (data?.data != null) {
const filtered = (data.data || []).filter(d => d.state !== 100);
operationList = filtered;
_hashes['operationList'] = JSON.stringify(filtered);
}
}).catch(() => {}),
fetch("${API_ENDPOINTS.POSITION_PROBLEMS}").then(r => r.json()).then(data => {
probremsData = data?.data ?? [];
_hashes['probremsData'] = JSON.stringify(probremsData);
}).catch(() => {}),
]).then(() => {
setReload(); // Phase 1 完了: 1回目の描画更新
// === Phase 2: 重いAPI (合計~840ms) を並列取得 → setReload 1回 ===
// TRAIN_DATA_API: 0.84s/1MB, DIAGRAM_TODAY: 0.27s/522KB
Promise.allSettled([
fetch("${API_ENDPOINTS.TRAIN_DATA_API}").then(r => r.json()).then(data => {
trainDataList = data.data ?? [];
_hashes['trainDataList'] = JSON.stringify(trainDataList);
}).catch(() => {}),
fetch("${API_ENDPOINTS.DIAGRAM_TODAY}").then(r => r.json()).then(res => {
const data = {};
res.forEach(d => { const keys = Object.keys(d); data[keys] = d[keys]; });
trainDiagramData2 = data;
_hashes['trainDiagramData2'] = JSON.stringify(data);
}).catch(() => {}),
]).then(() => {
setReload(); // Phase 2 完了: 2回目の描画更新
startPolling(); // Phase 3: ポーリング開始
});
});
const getUnyohubFormation = (trainNumber) => {
if (!${useUnyohub === "true"} || !unyohubData || unyohubData.length === 0) {
return null;
}
const foundUnyos = [];
for (const unyo of unyohubData) {
if (!unyo.trains) {
continue;
}
const found = unyo.trains.find(train => train.train_number === trainNumber);
if (found) {
foundUnyos.push({
formations: unyo.formations,
position_forward: found.position_forward,
position_rear: found.position_rear,
});
}
}
if (foundUnyos.length === 0) return null;
// position_forward順にソート
foundUnyos.sort((a, b) => a.position_forward - b.position_forward);
const result = foundUnyos.map(u => u.formations + '(' + u.position_forward + '-' + u.position_rear + ')').join(', ');
return result;
};
const sortOperationalList = (a, b,targetTrainID) => {
// trainIdからカンマ以降の数字を抽出する関数
const extractOrderNumber = (trainId) => {
const parts = trainId.split(',');
if (parts.length > 1) {
const num = parseInt(parts[1].trim(), 10);
return isNaN(num) ? Infinity : num;
}
return Infinity; // カンマなし = 末尾に移動
};
// data.trainNumと一致するtrainIdを探す関数
const findMatchingTrainId = (operation)=> {
const allTrainIds = [
...(operation.train_ids || []),
...(operation.related_train_ids || []),
];
// data.trainNumの接頭辞と一致するものを探す
for (const trainId of allTrainIds) {
const prefix = trainId.split(',')[0]; // カンマ前の部分
if (prefix === targetTrainID) {
return trainId;
}
}
return null;
};
const aTrainId = findMatchingTrainId(a);
const bTrainId = findMatchingTrainId(b);
// マッチしたものがない場合は元の順序を保持
if (!aTrainId || !bTrainId) {
return aTrainId ? -1 : bTrainId ? 1 : 0;
}
const aOrder = extractOrderNumber(aTrainId);
const bOrder = extractOrderNumber(bTrainId);
return aOrder - bOrder;
};
`;
// 左か右かを判定してアイコンを設置する
const trainIcon = `
const setStationIcon = (setIconElem,img,hasProblem,backCount = 100) =>{
const position = setIconElem.getAttribute("style").includes("left");
let marginData = ${uiSetting === "tokyo" ? `"5px"`: `"2px"`};
let backgroundColor = "transparent";
let heightData = "22px";
if(backCount == 0){
marginData = position ? ${uiSetting === "tokyo" ? `"0px 0px -10px 0px" : "-10px 0px 0px 0px"`: `"0px 2px 0px 0px" : "0px 2px 0px 0px"`};
heightData = "16px";
}
setIconElem.insertAdjacentHTML('beforebegin', "");
if (backCount == 0 || backCount == 100) setIconElem.remove();
}
const setTrainIcon = (列番データ) => {
switch (列番データ) {
//しおかぜメイン
//8000 ノーマル
case "2M":
case "4M":
case "6M":
case "14M":
case "16M":
case "18M":
case "26M":
case "28M":
case "30M":
case "1M":
case "3M":
case "5M":
case "13M":
case "15M":
case "17M":
case "25M":
case "27M":
case "29M":
return "https://storage.haruk.in/s8000nr.png";
//8000 アンパン
case "10M":
case "22M":
case "9M":
case "21M":
return "https://n8n.haruk.in/webhook/anpanman-pictures.png?trainNum=" +
列番データ;
//8600
case "8M":
case "12M":
case "20M":
case "24M":
case "7M":
case "11M":
case "19M":
case "23M":
return "https://storage.haruk.in/s8600.png";
//いしづちメイン
//8000 ノーマル
case "1004M":
case "1006M":
case "1014M":
case "1016M":
case "1018M":
case "1026M":
case "1028M":
case "1030M":
case "1001M":
case "1003M":
case "1005M":
case "1013M":
case "1015M":
case "1017M":
case "1025M":
case "1027M":
case "1029M":
return "https://storage.haruk.in/s8000no.png";
//8000 アンパン
case "1010M":
case "1022M":
case "1009M":
case "1021M":
return "https://n8n.haruk.in/webhook/anpanman-pictures.png?trainNum=" +
列番データ;
//8600
case "1008M":
case "1012M":
case "1020M":
case "1024M":
case "1007M":
case "1011M":
case "1019M":
case "1023M":
return "https://storage.haruk.in/s8600_isz.png";
//MEXP
//8000
case "1092M":
return "https://storage.haruk.in/s8000nr.png";
//8600
case "1091M":
return "https://storage.haruk.in/s8600_isz.png";
//三桁いしづち
//8000 アンパン
case "1041M":
case "1044M":
return "https://n8n.haruk.in/webhook/anpanman-pictures.png?trainNum=" +
列番データ;
//8600
case "1043M":
case "1042M":
case "1046M":
return "https://storage.haruk.in/s8600_isz.png";
//南風 2700ノーマル
case "34D":
case "38D":
case "40D":
case "42D":
case "46D":
case "50D":
case "52D":
case "54D":
case "58D":
case "31D":
case "35D":
case "39D":
case "41D":
case "43D":
case "47D":
case "51D":
case "53D":
case "55D":
return "https://storage.haruk.in/s2700.png";
//2700アンパン
case "32D":
case "36D":
case "44D":
case "48D":
case "56D":
case "33D":
case "37D":
case "45D":
case "49D":
case "57D":
return "https://n8n.haruk.in/webhook/anpanman-pictures.png?trainNum=" +
列番データ;
//うずしお
//2700
case "3004D":
case "3006D":
case "3010D":
case "3014D":
case "3016D":
case "3022D":
case "3028D":
case "3003D":
case "3007D":
case "3013D":
case "3019D":
case "3025D":
case "3031D":
return "https://storage.haruk.in/s2700_uzu.png";
//2700 二両編成
case "3008D":
case "3020D":
case "3026D":
case "3001D":
case "3005D":
case "3011D":
case "3017D":
case "3023D":
case "3029D":
return "https://storage.haruk.in/s2700_uzu.png";
//2600
case "3002D":
case "3012D":
case "3018D":
case "3024D":
case "3030D":
case "3009D":
case "3015D":
case "3021D":
case "3027D":
case "3033D":
return "https://storage.haruk.in/s2600.png";
//マリンライナー
case "3104M":
case "3106M":
case "3108M":
case "3110M":
case "3112M":
case "3114M":
case "3116M":
case "3118M":
case "3120M":
case "3122M":
case "3124M":
case "3126M":
case "3128M":
case "3130M":
case "3132M":
case "3134M":
case "3136M":
case "3138M":
case "3140M":
case "3142M":
case "3144M":
case "3146M":
case "3148M":
case "3150M":
case "3152M":
case "3154M":
case "3156M":
case "3158M":
case "3160M":
case "3162M":
case "3164M":
case "3166M":
case "3168M":
case "3170M":
case "3105M":
case "3107M":
case "3109M":
case "3111M":
case "3113M":
case "3115M":
case "3117M":
case "3119M":
case "3121M":
case "3123M":
case "3125M":
case "3127M":
case "3129M":
case "3131M":
case "3133M":
case "3135M":
case "3137M":
case "3139M":
case "3141M":
case "3143M":
case "3145M":
case "3147M":
case "3149M":
case "3151M":
case "3153M":
case "3155M":
case "3157M":
case "3159M":
case "3161M":
case "3163M":
case "3165M":
case "3167M":
case "3169M":
case "3175M":
return "https://storage.haruk.in/s5001.png";
case "3102M":
case "3101M":
case "3103M":
case "3171M":
case "3173M":
return "https://storage.haruk.in/s5001k.png";
//サンライズ瀬戸
case "5032M":
case "5031M":
case "8041M": //琴平延長高松迄
case "8031M": //琴平延長高松以降
return "https://storage.haruk.in/w285.png";
//宇和海
//2000 ノーマル
case "1052D":
case "1054D":
case "1056D":
case "1060D":
case "1062D":
case "1064D":
case "1068D":
case "1070D":
case "1072D":
case "1076D":
case "1078D":
case "1080D":
case "1082D":
case "1051D":
case "1055D":
case "1057D":
case "1061D":
case "1063D":
case "1065D":
case "1069D":
case "1071D":
case "1073D":
case "1075D":
case "1077D":
case "1079D":
case "1081D":
return "https://storage.haruk.in/s2000_uwa.png";
//2000 アンパン込み
case "1058D":
case "1066D":
case "1074D":
case "1053D":
case "1059D":
case "1067D":
return "https://n8n.haruk.in/webhook/anpanman-pictures.png?trainNum=" +
列番データ;
//しまんと
case "2002D":
case "2004D":
case "2001D":
case "2003D":
return "https://storage.haruk.in/s2000_smn.png";
//あしずり 2000
case "2074D":
case "2076D":
case "2080D":
case "2082D":
case "2071D":
case "2073D":
case "2079D":
case "2081D":
return "https://n8n.haruk.in/webhook/anpanman-pictures.png?trainNum=" +
列番データ;
//あしずり 2700
case "2072D":
case "2078D":
case "2084D":
case "2075D":
case "2077D":
case "2083D":
return "https://storage.haruk.in/s2700_asi.png";
//剣山
case "4002D":
case "4004D":
case "4006D":
case "4001D":
case "4003D":
case "4005D":
case "4007D":
return "https://storage.haruk.in/s185tu.png";
//よしのがわトロッコ
case "8452D":
case "8451D":
return "https://storage.haruk.in/s185to_ai.png";
//岡山高松アントロ
case "8176D":
case "8179D":
//岡山琴平アントロ
case "8277D":
case "8278D":
return "https://storage.haruk.in/s32to4.png";
//千年ものがたり
case "8021D":
case "8022D":
return "https://storage.haruk.in/s185mm1.png";
//夜明けものがたり
case "8082D":
case "8083D":
case "8073D":
case "8074D":
return "https://storage.haruk.in/s185ym1.png";
//ラマルドボア
case "9253M":
case "9256M":
return "https://storage.haruk.in/w213w.png";
// 貨物
case "74":
case "75":
case "70":
case "71":
case "73":
case "76":
case "3070":
case "3071":
case "3072":
case "3073":
case "3076":
case "3077":
case "3078":
case "3079":
case "8070":
case "8071":
case "8072":
case "8077":
return "https://storage.haruk.in/ef210a.png";
// 伊予灘ものがたり 赤
case "8091D":
case "8093D":
return "https://storage.haruk.in/s185iyor.png";
// 伊予灘ものがたり 黄
case "8092D":
case "8094D":
return "https://storage.haruk.in/s185iyoy.png";
// 高徳線、徳島線、牟岐線、鳴門線普通列車系統
// キハ40・47
case "4303D":
case "371D":
case "316D":
case "362D":
case "4376D":
case "951D":
case "953D":
case "955D":
case "973D":
case "975D":
case "977D":
case "979D":
case "981D":
case "950D":
case "968D":
case "970D":
case "972D":
case "974D":
case "976D":
case "980D":
case "982D":
return "https://storage.haruk.in/s40.png";
// 1000形
case "4311D":
case "363D":
case "356D":
case "4374D":
case "433D":
case "4447D":
case "451D":
case "450D":
case "4458D":
case "474D":
return "https://storage.haruk.in/s1000.png";
// 1200形
case "4301D":
case "4327D":
case "4329D":
case "4343D":
case "353D":
case "355D":
case "367D":
case "310D":
case "4326D":
case "4334D":
case "4342D":
case "358D":
case "364D":
case "4453D":
case "4455D":
case "4457D":
case "463D":
case "475D":
case "477D":
case "485D":
case "4430D":
case "434D":
case "438D":
case "4460D":
case "4464D":
case "4466D":
case "478D":
case "484D":
case "957D":
case "4959D":
case "4963D":
case "4967D":
case "4971D":
case "952D":
case "4954D":
case "4958D":
case "4962D":
case "4966D":
return "https://storage.haruk.in/s1200n.png";
// 半定期臨時列車
case "9174M":
return "https://storage.haruk.in/s5001.png";
case "9395D":
return "https://storage.haruk.in/s1500.png";
default: //指定車両でなく、Regexでの指定に移動
// // 琴平直通系普通電車
// if(new RegExp(/^(1|4|5)2\\d\\dM$/).test(列番データ)){
// setStationIcon(e.querySelector("img"),'https://storage.haruk.in/s7200.png',hasProblem);
// break;
// }
// else if(new RegExp(/^(1|4|5)(1|5|6)\\d\\dM$/).test(列番データ)){
// setStationIcon(e.querySelector("img"),'https://storage.haruk.in/s7000.png',hasProblem);
// break;
// }
// else if(new RegExp(/^(1|5|6)\\d\\dM$/).test(列番データ)){
// setStationIcon(e.querySelector("img"),'https://storage.haruk.in/s7000.png',hasProblem);
// break;
// }// 高徳線 普通列車系統
// else
if (new RegExp(/^(4|5)3\\d\\dD$/).test(列番データ)) {
return "https://storage.haruk.in/s1500.png";
} else if (new RegExp(/^3\\d\\dD$/).test(列番データ)) {
return "https://storage.haruk.in/s1500.png";
} // 徳島線 普通列車系統
else if (new RegExp(/^(4|5)4\\d\\dD$/).test(列番データ)) {
return "https://storage.haruk.in/s1500.png";
} else if (new RegExp(/^4\\d\\dD$/).test(列番データ)) {
return "https://storage.haruk.in/s1500.png";
} // 鳴門線普通列車系統
else if (new RegExp(/^(4|5)9(5|6|7|8)\\dD$/).test(列番データ)) {
return "https://storage.haruk.in/s1500.png";
} else if (new RegExp(/^9(5|6|7|8)\\dD$/).test(列番データ)) {
return "https://storage.haruk.in/s1500.png";
}
break;
}
}
`;
const normal_train_name = `
const nameReplace = (列車名データ,列番データ,行き先情報,hasProblem,isLeft) =>{
let isWanman = false;
let trainName = "";
let trainType = "";
let trainTypeColor = "black";
let viaData = "";
let ToData = "";
let TrainNumber = 列番データ;
let isEdit = false;
let isSeason = false;
let TrainNumberOverride;
let optionalText = "";
try{
const diagram = trainDiagramData2[列番データ] || trainTimeInfo[列番データ];
if(diagram){
const diagramData = diagram.split("#");
ToData = diagramData[diagramData.length - 2].split(",")[0];
}
}catch(e){}
if(列車名データ.split(":")[1]){
const textBase = 列車名データ.split(":")[1].replace("\\r","");
trainName = textBase;
}
if(列車名データ.match("サンポート")){
const textBase = 列車名データ.split(":")[1].replace("\\r","");
trainName = textBase;
}
if(new RegExp(/^4[1-9]\\d\\d[DM]$/).test(列番データ) || new RegExp(/^5[1-7]\\d\\d[DM]$/).test(列番データ) || new RegExp(/^3[2-9]\\d\\d[DM]$/).test(TrainNumber) ){
flag=true;
isWanman = true;
}
if(new RegExp(/^49[0-4]\\dD$/).test(列番データ) || new RegExp(/^9[0-4]\\dD$/).test(列番データ)){
viaData = "(海経由)";
}
if(new RegExp(/^46\\d\\dD$/).test(列番データ) || new RegExp(/^6\\d\\dD$/).test(列番データ)){
viaData = "(内子経由)";
}
const getThrew = num =>{
switch(num){
//牟岐線直通列車情報
//徳島線発牟岐線行き
case "468D":
case "478D":
case "484D":
viaData = "牟岐線直通";
ToData = "牟岐";
break;
case "4430D":
case "4472D":
viaData = "牟岐線直通";
isWanman = true;
ToData = "牟岐";
break;
case "434D":
case "474D":
case "476D":
case "480D":
viaData = "牟岐線直通";
ToData = "阿南";
break;
case "4452D":
case "4466D":
case "4470D":
viaData = "牟岐線直通";
isWanman = true;
ToData = "阿南";
break;
case "4456D":
viaData = "牟岐線直通";
isWanman = true;
ToData = "阿波海南"
break;
//鳴門線発牟岐線行き
case "951D":
viaData = "牟岐線直通";
ToData = "桑野";
break;
//牟岐線発高徳線行き
case "358D":
viaData = "高徳線直通";
break;
case "4314D":
case "4326D":
case "4334D":
case "4342D":
case "4350D":
case "4368D":
viaData = "高徳線直通";
isWanman = true;
break;
//牟岐線発徳島線行き
case "451D":
case "475D":
viaData = "徳島線直通";
break;
case "4447D":
case "4455D":
case "5467D":
case "5471D":
case "5479D":
viaData = "徳島線直通";
isWanman = true;
break;
//牟岐線発鳴門線行き
case "952D":
viaData = "鳴門線直通";
break;
case "4954D":
case "4978D":
viaData = "鳴門線直通";
isWanman = true;
break;
//安芸行と併結列車を個別に表示、それ以外をdefaultで下りなら既定の行き先を、上りなら奈半利行を設定
case "5814D":
case "5816D":
viaData = "ごめん・なはり線[快速]";
ToData = "奈半利";
break;
case "5812D":
viaData = "ごめん・なはり線[快速]";
ToData = "安芸";
break;
case "5874D":
case "5882D":
viaData = "ごめん・なはり線[各停]";
ToData = "安芸";
break;
case "248D":
case "250D":
viaData = "ごめん・なはり線[快速]";
ToData = "(後免にて解結)\\n土佐山田/奈半利";
break;
default:
if(new RegExp(/^58[1-3][1,3,5,7,9][DM]$/).test(列番データ)){
viaData = "ごめん・なはり線[快速]";
break;
}
else if(new RegExp(/^58[4-9][1,3,5,7,9][DM]$/).test(列番データ)){
viaData = "ごめん・なはり線[各停]";
break;
}
else if(new RegExp(/^58[3-4][0,2,4,6,8][DM]$/).test(列番データ)){
viaData = "ごめん・なはり線[快速]";
ToData = "奈半利";
break;
}
else if(new RegExp(/^58[5-9][0,2,4,6,8][DM]$/).test(列番データ)){
viaData = "ごめん・なはり線[各停]";
ToData = "奈半利";
break;
}
}
}
getThrew(列番データ);
if(trainDataList.find(e => e.train_id === 列番データ) !== undefined){
const data = trainDataList.find(e => e.train_id === 列番データ);
switch(data.type){
case "Normal":
trainTypeColor = "black";
isWanman = false;
trainType = "普通";
break;
case "OneMan":
trainTypeColor = "black";
isWanman = true;
trainType = "普通";
break;
case "Rapid":
trainTypeColor = "rgba(0, 140, 255, 1)";
isWanman = false;
trainType = "快速";
break;
case "OneManRapid":
trainTypeColor = "rgba(0, 140, 255, 1)";
isWanman = true;
trainType = "快速";
break;
case "LTDEXP":
trainTypeColor = "red";
isWanman = false;
trainType = "特急";
break;
case "NightLTDEXP":
trainTypeColor = "#d300b0ff";
isWanman = false;
trainType = "寝台特急";
break;
case "SPCL":
case "SPCL_Normal":
trainTypeColor = "#008d07ff";
isWanman = false;
trainType = "臨時";
break;
case "SPCL_Rapid":
trainTypeColor = "rgba(0, 81, 255, 1)";
isWanman = false;
trainType = "臨時快速";
break;
case "SPCL_EXP":
trainTypeColor = "#a52e2eff";
isWanman = false;
trainType = "臨時特急";
break;
case "Party":
trainTypeColor = "#ff7300ff";
isWanman = false;
trainType = "団体臨時";
break;
case "Freight":
trainTypeColor = "#00869ecc";
isWanman = false;
trainType = "貨物";
break;
case "Forwarding":
trainTypeColor = "#727272cc";
isWanman = false;
trainType = "回送";
break;
case "Trial":
trainTypeColor = "#727272cc";
isWanman = false;
trainType = "試運転";
break;
case "Construction":
trainTypeColor = "#727272cc";
isWanman = false;
trainType = "工事";
break;
case "FreightForwarding":
trainTypeColor = "#727272cc";
isWanman = false;
trainType = "単機回送";
break;
default:
break;
}
isEdit = data.priority == 400;
isSeason = data.priority == 300;
operationList
.sort((a,b)=>sortOperationalList(a,b,data.train_id))
.forEach(e => {
if (e.train_ids?.length > 0) {
const trainIds = e.train_ids.map((x) => x.split(",")[0]);
if (trainIds.includes(data.train_id.toString())) {
//returnData.push(e);
isEdit = true;
}
} else if (e.related_train_ids?.length > 0) {
const trainIds = e.related_train_ids.map(
(x) => x.split(",")[0]
);
if (trainIds.includes(data.train_id.toString())) {
//returnData.push(e);
isEdit = true;
}
}
});
if(data.train_name != ""){
trainName = data.train_name;
if(data.train_num_distance != ""){
trainName += (parseInt(列番データ.replace("M", "").replace("D", "")) - parseInt(data.train_num_distance))+"号";
}
}
if(data.via_data != ""){
viaData = data.via_data;
}
if(data.to_data != ""){
ToData = data.to_data;
}
if(data.train_number_override){
TrainNumberOverride = data.train_number_override;
}
if(data.optional_text){
optionalText = data.optional_text;
}
}
//列番付与
const returnText1 = (isWanman ? "ワンマン " : "") + trainName + viaData;
行き先情報.innerText = "";
${uiSetting === "tokyo" ? `
let stationIDs = [];
let stationLines = [];
Object.keys(stationList).forEach((key) => {
const data = stationList[key].find(e => e.Station_JP === ToData )?.StationNumber;
if(data){
stationIDs.push(data);
stationLines.push(key);
}
});
let getColors = [];
// to_data_colorが配列で値があればそれを使用
if(trainDataList.find(e => e.train_id === 列番データ) !== undefined){
const data = trainDataList.find(e => e.train_id === 列番データ);
if(data.to_data_color && Array.isArray(data.to_data_color) && data.to_data_color.length > 0){
getColors = data.to_data_color;
}
}
// getColorsが空の場合は既存の路線色を使用(駅番号の最初の1文字で色を取得)
if(getColors.length === 0){
if(stationLines.length === 0){
getColors = ["rgba(97, 96, 96, 0.81)"];
}else{
getColors = stationLines.map(e => GetLineBarColor(e));
}
}
let yosan2Color = undefined;
switch(viaData){
case "(内子経由)":
yosan2Color = "#F5AC13";
break;
case "(海経由)":
yosan2Color = "#9AA7D7";
break;
case "牟岐線直通":
yosan2Color = "#00b8bb";
break;
case "徳島線直通":
yosan2Color = "#2d506e";
break;
case "高徳線直通":
yosan2Color = "#87CA3B";
break;
case "鳴門線直通":
yosan2Color = "#881F61";
break;
case "予土線":
yosan2Color = "#008a5a";
break;
default:
break;
}
// 複数色に対応したグラデーション生成
let gradient;
if(getColors.length > 1){
const colorStops = [];
const step = 100 / getColors.length;
getColors.forEach((color, index) => {
const start = step * index;
const end = step * (index + 1);
colorStops.push(color + " " + start + "%");
colorStops.push(color + " " + end + "%");
});
gradient = "linear-gradient(130deg, " + colorStops.join(", ") + ")";
}else{
gradient = getColors[0];
}
const optionalTextColor = optionalText.includes("最終") ? "red" : "black";
const unyohubFormation = getUnyohubFormation(列番データ);
const hasUnyohub = unyohubFormation !== null;
if(hasUnyohub) {
console.log('[UnyoHub] Badge shown for', 列番データ, ':', unyohubFormation);
}
// バッジHTMLを構築(複数のバッジを上下に配置)
let badgeHtml = "";
const badgePosition = isLeft ? "right" : "left";
const badgeVerticalPos = isLeft ? "bottom" : "top";
// コミュニティバッジ(青)- fa-user-group をインライン SVG で代替 (全 WebView 対応)
if(isEdit) {
badgeHtml += "
" + (TrainNumberOverride ? TrainNumberOverride : TrainNumber) + "
" + (isWanman ? "ワンマン " : "") + "
" + viaData + "
" + optionalText + "
" + trainName + "
" + (ToData ? ToData + "行" : ToData) + "
" + trainType + "
" + (hasProblem ? "‼️停止中‼️" : "") + "
" + returnText1 + "
"); 行き先情報.insertAdjacentHTML('beforebegin', "" + (ToData ? ToData + "行 " : ToData) + "
" + (TrainNumberOverride ? TrainNumberOverride : TrainNumber) + "
" + (hasProblem ? "‼️停止中‼️" : "") + "
"); `} } `; const textInsert = ` const setNewTrainItem = (element,hasProblem,type)=>{ var 列番データ = element.getAttribute('offclick').split('"')[1]; if(trainDataList.find(e => e.train_id === 列番データ) !== undefined){ const data = trainDataList.find(e => e.train_id === 列番データ); switch (data.type) { case "Normal": element.style.borderColor = "black"; element.style.backgroundColor = '#ffffffcc'; break; case "OneMan": element.style.borderColor = "black"; element.style.backgroundColor = '#ffffffcc'; break; case "Rapid": element.style.borderColor = "rgba(0, 140, 255, 1)"; element.style.backgroundColor = '#ffffffcc'; break; case "OneManRapid": element.style.borderColor = "rgba(0, 140, 255, 1)"; element.style.backgroundColor = '#ffffffcc'; break; case "LTDEXP": element.style.borderColor = "red"; element.style.backgroundColor = '#ffffffcc'; break; case "NightLTDEXP": element.style.borderColor = "#d300b0ff"; element.style.backgroundColor = '#ffffffcc'; break; case "SPCL": case "SPCL_Normal": element.style.borderColor = "#008d07ff"; element.style.backgroundColor = '#ffffffcc'; break; case "SPCL_Rapid": element.style.borderColor = "#0051ffff"; element.style.backgroundColor = '#ffffffcc'; break; case "SPCL_EXP": element.style.borderColor = "#a52e2eff"; element.style.backgroundColor = '#ffffffcc'; break; case "Party": element.style.borderColor = "#ff7300ff"; element.style.backgroundColor = '#ffd0a9ff'; break; case "Freight": element.style.borderColor = "#00869ecc"; element.style.backgroundColor = '#c7c7c7cc'; break; case "Forwarding": element.style.borderColor = "#727272cc"; element.style.backgroundColor = '#c7c7c7cc'; break; case "Trial": element.style.borderColor = "#727272cc"; element.style.backgroundColor = '#c7c7c7cc'; break; case "Construction": element.style.borderColor = "#727272cc"; element.style.backgroundColor = '#c7c7c7cc'; break; case "FreightForwarding": element.style.borderColor = "#727272cc"; element.style.backgroundColor = '#c7c7c7cc'; break; default: element.style.borderColor = 'black'; element.style.backgroundColor = '#ffffffcc'; break; } }else{ if(element.getAttribute('offclick').includes("express")){ element.style.borderColor = '#ff0000ff'; }else if(element.getAttribute('offclick').includes("rapid")){ element.style.borderColor = '#008cffff'; }else{ element.style.borderColor = 'black'; } } element.style.borderWidth = '2px'; element.style.borderStyle = 'solid'; element.style.borderRadius = '10%'; if(hasProblem){ element.style.boxShadow = '0 0 10px rgba(255, 0, 0, 0.9)'; }else{ element.style.boxShadow = '0 0 4px rgba(0, 0, 0, 0.2)'; } element.style.margin = '2px'; element.style.display = 'flex'; element.style.alignItems = 'center'; element.style.justifyContent = 'center'; element.style.width = '4.5em'; element.style.minHeight = '80px'; element.style.height = '100%'; element.getElementsByTagName("img")[0].style.float = 'unset'; element.style.webkitTapHighlightColor = 'rgba(0, 0, 0, 0)'; element.style.transition = 'transform 0.1s ease-in-out'; element.addEventListener('touchstart', () => element.style.transform = 'scale(0.8)'); element.addEventListener('touchend', () => element.style.transform = 'scale(1)'); if(element.getAttribute("style").includes("left")){ // borderを使って五角形を生成 下り element.style.borderRadius = '30px 30px 120px 120px'; element.style.flexDirection = 'column-reverse'; } else if(element.getAttribute("style").includes("right")){ // borderを使って五角形を生成 上り element.style.borderRadius = '120px 120px 30px 30px'; element.style.flexDirection = 'column'; } } //列番付与 const setStrings = () =>{ try { const elements = document.querySelectorAll('#disp > div > div > div[onclick]'); const setNewTrainItemUI = ()=>{ const aaa = (x2,pos) => { x2.style.display = 'flex'; x2.style.flexDirection = 'row'; if(pos == "right"){ x2.style.alignItems = 'flex-start'; x2.style.justifyContent = 'flex-start'; }else if(pos == "left"){ x2.style.alignItems = 'flex-end'; x2.style.justifyContent = 'flex-end'; } x2.style.flexWrap = 'wrap'; x2.style.width = '100%'; x2.style.height = "100%"; } const aaa2 = (x2) => { x2.style.display = 'flex'; x2.style.flexDirection = 'row'; x2.style.alignItems = 'center'; x2.style.justifyContent = 'center'; x2.style.flexWrap = 'wrap'; x2.style.width = '100%'; x2.style.height = "unset"; const x3 = x2.querySelectorAll(":scope > div"); x3.forEach(i=>{ i.style.position = "unset"; i.style.display = "flex"; i.style.flexDirection = "column"; i.style.alignItems = "center"; i.style.justifyContent = "center"; i.style.flex = "1"; i.style.backgroundColor = "#00000000"; i.querySelectorAll(":scope > *").forEach(j=>{ j.style.display = "flex"; j.style.flex = "1"; j.style.textAlign = "center"; j.style.margin = "5px"; j.style.padding = "5px"; }); }); } const layoutBase = (e)=>{ e.style.display = 'flex'; e.style.height = "unset"; e.style.flexDirection = 'row'; e.style.justifyContent = 'center'; } const elementBaseBase = document.querySelectorAll('[id^="stationBlock"]'); const elementNotBase = document.querySelectorAll('#disp > [id*="~"]'); elementNotBase.forEach(e=>{ layoutBase(e); const x = e.querySelectorAll(':scope > [id^="Up"], :scope > [id^="Id"], :scope > [id^="Down"]');//配下のdiv要素を選択 aaa(x[0],"left"); aaa2(x[1]); aaa(x[2],"right"); const upTrainCrossBarElement = e.querySelector(':scope > [id="upTrainCrossBar"]'); if (upTrainCrossBarElement) { upTrainCrossBarElement.style.left = '0px'; } }); elementBaseBase.forEach(e=>{ //それぞれの駅ブロック横一列 layoutBase(e); const x = e.querySelectorAll(':scope > div');//配下のdiv要素を選択 //x[0] 登りブロック x[2] 下りブロック x[1] 駅ブロック aaa(x[0],"left"); aaa2(x[1]); aaa(x[2],"right"); }); } ${uiSetting === "tokyo" ? `setNewTrainItemUI();`: ``} for (let element of elements) { if(element.getAttribute('offclick')){ continue; } element.setAttribute('offclick',element.getAttribute('onclick')) var 行き先情報 = element.getElementsByTagName("p")[0]; ${uiSetting === "tokyo" ? ` element.querySelector("img").insertAdjacentHTML('beforebegin',""); element.querySelector("img").insertAdjacentHTML('afterend',""); element.querySelector("img").style.padding = '5px'; element.style.position = 'relative'; if(element.getElementsByTagName("p")[1] != undefined){ element.getElementsByTagName("p")[1].innerText = element.getElementsByTagName("p")[1].innerText.replace("(","").replace(")",""); element.getElementsByTagName("p")[1].style.position = 'absolute'; element.getElementsByTagName("p")[1].style.backgroundColor = 'red'; element.getElementsByTagName("p")[1].style.color = 'white'; element.getElementsByTagName("p")[1].style.fontSize = '10px'; element.getElementsByTagName("p")[1].style.fontWeight = 'bold'; element.getElementsByTagName("p")[1].style.padding = '2px'; element.getElementsByTagName("p")[1].style.textAlign = 'center'; element.getElementsByTagName("p")[1].style.borderRadius = '10px'; if(element.getAttribute("style").includes("left")){ element.getElementsByTagName("p")[1].style.bottom = '0px'; element.getElementsByTagName("p")[1].style.left = '0px'; } else if(element.getAttribute("style").includes("right")){ element.getElementsByTagName("p")[1].style.right = '0px'; element.getElementsByTagName("p")[1].style.top = '0px'; } }`: ``} const isLeft = element.getAttribute("style").includes("left"); var 列番データ = element.getAttribute('offclick').split('"')[1]; var 列車名データ = element.getAttribute('offclick').split('"')[3]; const trainData = trainPositionDatas.filter(e=>!(e.Pos && e.Pos.includes("予告窓"))).find(e => e.TrainNum == 列番データ); const hasProblem = probremsData.find((e)=>{ return e.TrainNum == trainData.TrainNum && e.Pos == trainData.Pos; }); var flag=false; var TrainType = undefined; setTrainMenuDialog(element) ${iconSetting == "true" ? ` let trainIconUrl = []; operationList .sort((a,b)=>sortOperationalList(a,b,列番データ.toString())) .reverse() .forEach(e => { if (e.train_ids?.length > 0) { const trainIds = e.train_ids.map((x) => x.split(",")[0]); if (trainIds.includes(列番データ.toString())) { let iconTrainDirection = parseInt(列番データ.toString().replace(/[^\\d]/g,""))%2 == 0 ? true : false; const {directions} = trainDataList.find(e => e.train_id === 列番データ); if(directions != null && directions != undefined){ iconTrainDirection = directions ? true : false; } alert(directions+":"+列番データ+":"+(iconTrainDirection ? "right":"left")); if(iconTrainDirection){ if(e.vehicle_img) trainIconUrl.push(e.vehicle_img); else if(e.vehicle_img_right) trainIconUrl.push(e.vehicle_img_right); }else{ if(e.vehicle_img_right) trainIconUrl.push(e.vehicle_img_right); else if(e.vehicle_img) trainIconUrl.push(e.vehicle_img); } } } else if (e.related_train_ids?.length > 0) { const trainIds = e.related_train_ids.map( (x) => x.split(",")[0] ); if (trainIds.includes(列番データ.toString())) { let iconTrainDirection = parseInt(列番データ.toString().replace(/[^\\d]/g,""))%2 === 0 ? true : false; const {directions} = trainDataList.find(e => e.train_id === 列番データ); //alert(directions+":"+列番データ+":"+iconTrainDirection); if(directions != null && directions != undefined){ iconTrainDirection = directions ? true : false; } if(iconTrainDirection){ if(e.vehicle_img) trainIconUrl.push(e.vehicle_img); else if(e.vehicle_img_right) trainIconUrl.push(e.vehicle_img_right); }else{ if(e.vehicle_img_right) trainIconUrl.push(e.vehicle_img_right); else if(e.vehicle_img) trainIconUrl.push(e.vehicle_img); } } } }); if(trainIconUrl.length > 0){ [trainIconUrl[0], trainIconUrl[trainIconUrl.length - 1]].forEach((url,index,array) => { if(url && url != ""){ setStationIcon(element.querySelector("img"),url,hasProblem,trainIconUrl.length == 1 ? 100 : index); } }); } else{ if(trainDataList.find(e => e.train_id === 列番データ) !== undefined){ const trainIconUrl = [trainDataList.find(e => e.train_id === 列番データ).train_info_img]; if(trainIconUrl.length > 0){ trainIconUrl.forEach((url,index,array) => { if(url && url != ""){ setStationIcon(element.querySelector("img"),url,hasProblem); } }); } } else{ const trainIconUrl = [setTrainIcon(列番データ)]; if(trainIconUrl.length > 0){ if(trainIconUrl[0] && trainIconUrl[0] != ""){ setStationIcon(element.querySelector("img"),trainIconUrl[0],hasProblem); } } } } ` : ""} nameReplace(列車名データ,列番データ,行き先情報,hasProblem,isLeft); ${uiSetting === "tokyo" ? `setNewTrainItem(element,hasProblem);`: ``} } try{ for(let d of document.getElementById('disp').childNodes){ switch(d.id){ case 'pMENU_2': case 'pMENU_2_En': case 'pMENU_3': case 'pMENU_3_En': case 'pMENU_k': case 'pMENU_k_En': continue; default: break; } d.style.width = '100vw'; for(let f of d.childNodes){ try{ if(f.style.alignItems || f.style.textAlign){ f.style.width = '38vw'; } else{ if(f.id == 'upTrainCrossBar'){ f.style.width = '38vw'; } else if(f.id == 'dwTrainCrossBar'){ f.style.left = '62vw'; f.style.width = '38vw'; } else { f.style.width = '0vw'; } } if(f.style.textAlign == 'center'){ f.style.width = '24vw'; f.style.display = 'flex'; f.childNodes.forEach(i =>{ i.style.width = 'unset'; i.style.left = 'unset'; i.style.top = 'unset'; i.style.position = 'unset'; i.style.flex = '1'; i.style.margin = '5px' if(i.style.backgroundColor != 'rgb(247, 247, 247)'){ i.childNodes.forEach(m=> m.style.width = '20vw') } }) } }catch(e){} } } document.querySelector('#pMENU_2').style.borderStyle='solid'; document.querySelector('#pMENU_2').style.borderColor='#00d3e8'; document.querySelector('#pMENU_2').style.borderWidth='2px'; document.querySelector('#pMENU_2').style.borderRadius='10%'; document.querySelector('#pMENU_3').style.borderStyle='solid'; document.querySelector('#pMENU_3').style.borderColor='#00d3e8'; document.querySelector('#pMENU_3').style.borderWidth='2px'; document.querySelector('#pMENU_3').style.borderRadius='10%'; document.querySelectorAll('#pMENU_2 div').forEach((d)=>d.style.padding = '10px'); document.querySelectorAll('#pMENU_3 div').forEach((d)=>d.style.padding = '10px'); document.querySelectorAll('#topHeader div').forEach((d)=>d.style.width = '100vw'); document.querySelectorAll('#disp div')[0].style.width = '100vw'; document.getElementById('disp').style.width = '100vw'; document.getElementById('disp').style.overflowX = 'hidden'; }catch(e){ alert("本家サーバーからのデータ取得に失敗しました。"); window.ReactNativeWebView.postMessage(JSON.stringify({type:"LoadError"})); } } catch (e) {} } const textInsert = new MutationObserver( (mutations) =>{ setStrings(); const currentLines = document.querySelector('#topHeader div').innerText; window.ReactNativeWebView.postMessage(JSON.stringify({type:"currentLines",currentLines})); }); // 監視を開始 textInsert.observe(document.getElementById('disp'), { attributes: true, // 属性変化の監視 //attributeOldValue: true, // 変化前の属性値を matation.oldValue に格納する //characterData: true, // テキストノードの変化を監視 //characterDataOldValue: true, // 変化前のテキストを matation.oldValue に格納する childList: true, // 子ノードの変化を監視 //subtree: true // 子孫ノードも監視対象に含める }); `; // 列車メニュー表示の起動用スクリプト const makeTrainView = ` const makeTrainView = new MutationObserver( (mutations) => { for(let d of modal_content.getElementsByTagName("button") ){ if(d.getAttribute('data-rn-handled')) continue; d.setAttribute('data-rn-handled','1'); // getAttribute で生のHTML属性文字列を取得 (toString()はWebViewごとに書式が異なる) const raw = d.getAttribute('onclick') || ''; const data = raw.split('"')[1] || raw.split("'")[1] || ''; if(!data) continue; d.removeAttribute('onclick'); // 古いWebViewで属性が再評価されるのを防ぐ d.onclick = (e) => { e && e.stopPropagation && e.stopPropagation(); window.ReactNativeWebView.postMessage(data); }; } }); // 監視を開始 makeTrainView.observe(document.getElementById('modal_content'), { //attributes: true, // 属性変化の監視 //attributeOldValue: true, // 変化前の属性値を matation.oldValue に格納する //characterData: true, // テキストノードの変化を監視 //characterDataOldValue: true, // 変化前のテキストを matation.oldValue に格納する childList: true, // 子ノードの変化を監視 //subtree: true // 子孫ノードも監視対象に含める }); `; const makeTrainMenu = trainMenu == "true" ? ` // これの中身抽出ShowTrainTimeInfo("1228M","normal") // ShowTrainTimeInfo("142M","rapid:サンポート南風リレー") function setTrainMenuDialog(d){ try{ const offclick = d.getAttribute('offclick'); if(!offclick) return; // シングル/ダブルクォート両対応で引数を抽出 const s = offclick.replace('ShowTrainTimeInfo(','').replaceAll('"','').replaceAll("'",'').replace(')','').split(','); const returnData = {type:"ShowTrainTimeInfo",trainNum:s[0],limited:s[1]}; d.removeAttribute('onclick'); d.onclick = null; // capture=true でターゲットフェーズより先に発火 + stopImmediatePropagation で元ハンドラを封じる d.addEventListener('click', function(e){ e.stopImmediatePropagation(); e.preventDefault(); window.ReactNativeWebView.postMessage(JSON.stringify(returnData)); return false; }, true); }catch(e){ } } // Object.defineProperty でページ側JSが後から上書きできないようにロック // Chrome 81 等の古いWebViewでも onclick属性から呼ばれる関数の乗っ取りが確実になる try { Object.defineProperty(window, 'ShowTrainTimeInfo', { configurable: false, writable: false, value: function(trainNum, limited){ window.ReactNativeWebView.postMessage(JSON.stringify({type:"ShowTrainTimeInfo",trainNum:trainNum,limited:limited})); } }); } catch(e) { /* 既にnon-configurableで定義済みの場合は無視 */ } ` : `function setTrainMenuDialog(d){}`; const makeStationMenu = stationMenu == "true" ? ` //駅メニューダイアログの配置 // PopUpMenu もObject.definePropertyでロック: onclick属性から呼ばれても確実に横取りできる try { Object.defineProperty(window, 'PopUpMenu', { configurable: false, writable: false, value: function(event, id, name, pdf, map, url, chk){ window.ReactNativeWebView.postMessage(JSON.stringify({type:"PopUpMenu",event:String(event||''),id:String(id||''),name:String(name||''),pdf:String(pdf||''),map:String(map||''),url:String(url||''),chk:String(chk||'')})); } }); } catch(e) { /* 既にnon-configurableで定義済みの場合は無視 */ } const StationData = [ {"StationName":"高松","StationNumber":"Y00","Feature":'{"Midori":{"style":"normal"},"IC":true}'}, {"StationName":"香西","StationNumber":"Y01","Feature":'{"Midori":{"style":"none"},"IC":true}'}, {"StationName":"鬼無","StationNumber":"Y02","Feature":'{"Midori":{"style":"none"},"IC":true}'}, {"StationName":"端岡","StationNumber":"Y03","Feature":'{"Midori":{"style":"none"},"IC":true}'}, {"StationName":"国分","StationNumber":"Y04","Feature":'{"Midori":{"style":"none"},"IC":true}'}, {"StationName":"讃岐府中","StationNumber":"Y05","Feature":'{"Midori":{"style":"none"},"IC":true}'}, {"StationName":"鴨川","StationNumber":"Y06","Feature":'{"Midori":{"style":"none"},"IC":true}'}, {"StationName":"八十場","StationNumber":"Y07","Feature":'{"Midori":{"style":"none"},"IC":true}'}, {"StationName":"坂出","StationNumber":"Y08","Feature":'{"Midori":{"style":"normal"},"IC":true}'}, {"StationName":"宇多津","StationNumber":"Y09","Feature":'{"Midori":{"style":"normal"},"IC":true}'}, {"StationName":"丸亀","StationNumber":"Y10","Feature":'{"Midori":{"style":"normal"},"IC":true}'}, {"StationName":"讃岐塩屋","StationNumber":"Y11","Feature":'{"Midori":{"style":"none"},"IC":true}'}, {"StationName":"多度津","StationNumber":"Y12","Feature":'{"Midori":{"style":"normal"},"IC":true}'}, {"StationName":"詫間","StationNumber":"Y14","Feature":'{"Midori":{"style":"plus"},"IC":true}'}, {"StationName":"観音寺","StationNumber":"Y19","Feature":'{"Midori":{"style":"normal"},"IC":true}'}, {"StationName":"川之江","StationNumber":"Y22","Feature":'{"Midori":{"style":"plus"},"IC":false}'}, {"StationName":"伊予三島","StationNumber":"Y23","Feature":'{"Midori":{"style":"normal"},"IC":false}'}, {"StationName":"新居浜","StationNumber":"Y29","Feature":'{"Midori":{"style":"normal"},"IC":false}'}, {"StationName":"伊予西条","StationNumber":"Y31","Feature":'{"Midori":{"style":"normal"},"IC":false}'}, {"StationName":"壬生川","StationNumber":"Y36","Feature":'{"Midori":{"style":"plus"},"IC":false}'}, {"StationName":"今治","StationNumber":"Y40","Feature":'{"Midori":{"style":"plus"},"IC":false}'}, {"StationName":"伊予北条","StationNumber":"Y48","Feature":'{"Midori":{"style":"plus"},"IC":false}'}, {"StationName":"松山","StationNumber":"Y55","Feature":'{"Midori":{"style":"normal"},"IC":false}'}, {"StationName":"内子","StationNumber":"U10","Feature":'{"Midori":{"style":"plus"},"IC":false}'}, {"StationName":"伊予大洲","StationNumber":"U14","Feature":'{"Midori":{"style":"plus"},"IC":false}'}, {"StationName":"伊予大洲","StationNumber":"S18","Feature":'{"Midori":{"style":"plus"},"IC":false}'}, {"StationName":"八幡浜","StationNumber":"U18","Feature":'{"Midori":{"style":"normal"},"IC":false}'}, {"StationName":"宇和島","StationNumber":"U28","Feature":'{"Midori":{"style":"plus"},"IC":false}'}, {"StationName":"多度津","StationNumber":"D12","Feature":'{"Midori":{"style":"normal"},"IC":true}'}, {"StationName":"善通寺","StationNumber":"D14","Feature":'{"Midori":{"style":"plus"},"IC":true}'}, {"StationName":"琴平","StationNumber":"D15","Feature":'{"Midori":{"style":"plus"},"IC":true}'}, {"StationName":"阿波池田","StationNumber":"D22","Feature":'{"Midori":{"style":"plus"},"IC":false}'}, {"StationName":"土佐山田","StationNumber":"D37","Feature":'{"Midori":{"style":"plus"},"IC":false}'}, {"StationName":"後免","StationNumber":"D40","Feature":'{"Midori":{"style":"plus"},"IC":false}'}, {"StationName":"高知","StationNumber":"D45","Feature":'{"Midori":{"style":"normal"},"IC":false}'}, {"StationName":"高知","StationNumber":"K00","Feature":'{"Midori":{"style":"normal"},"IC":false}'}, {"StationName":"朝倉","StationNumber":"K05","Feature":'{"Midori":{"style":"plus"},"IC":false}'}, {"StationName":"須崎","StationNumber":"K19","Feature":'{"Midori":{"style":"plus"},"IC":false}'}, {"StationName":"窪川","StationNumber":"K26","Feature":'{"Midori":{"style":"normal"},"IC":false}'}, {"StationName":"高松","StationNumber":"T28","Feature":'{"Midori":{"style":"normal"},"IC":true}'}, {"StationName":"栗林公園北口","StationNumber":"T26","Feature":'{"Midori":{"style":"none"},"IC":true}'}, {"StationName":"栗林","StationNumber":"T25","Feature":'{"Midori":{"style":"plus"},"IC":true}'}, {"StationName":"屋島","StationNumber":"T24","Feature":'{"Midori":{"style":"none"},"IC":true}'}, {"StationName":"志度","StationNumber":"T19","Feature":'{"Midori":{"style":"plus"},"IC":false}'}, {"StationName":"三本松","StationNumber":"T12","Feature":'{"Midori":{"style":"plus"},"IC":false}'}, {"StationName":"徳島","StationNumber":"T00","Feature":'{"Midori":{"style":"normal"},"IC":false}'}, {"StationName":"鳴門","StationNumber":"N10","Feature":'{"Midori":{"style":"plus"},"IC":false}'}, {"StationName":"阿南","StationNumber":"","Feature":'{"Midori":{"style":"normal"},"IC":false}'}, {"StationName":"牟岐","StationNumber":"","Feature":'{"Midori":{"style":"normal"},"IC":false}'}, {"StationName":"鴨島","StationNumber":"B09","Feature":'{"Midori":{"style":"plus"},"IC":false}'}, {"StationName":"穴吹","StationNumber":"B16","Feature":'{"Midori":{"style":"plus"},"IC":false}'}, ]; const setStationMenuDialog = new MutationObserver( (mutations) => { const data =[]; document.querySelectorAll('#disp div div').forEach(d=>d.id.indexOf("st")!= -1 && data.push(d)); for(let d of data ){ if(!d.offclick){ // getAttribute で生の属性文字列を取得 (toString() は WebView ごとに書式が異なる) d.offclick = d.getAttribute('onclick') || d.onclick.toString(); } const s = d.offclick.replace('(event)','').replaceAll("'", "").split('(')[1].split(')')[0].split(','); const stationBadge = StationData.find(e=>e.StationName === s[2])?.Feature; if(stationBadge){ const midoriStyle = JSON.parse(stationBadge).Midori.style; const IC = JSON.parse(stationBadge).IC; if(!d.childNodes[0].childNodes[0].childNodes[1]){ d.childNodes[0].childNodes[0].childNodes[0].insertAdjacentHTML('afterend',"