fix(ui): 补齐 generator.html 和 listmanager.html 的问号剥离兜底
- generator.html: 新增 ds() 钩子,在 renderList / batch 预览 / showToast 三个渲染点统一问号剥离 - listmanager.html: 新增 ds() 钩子,在 renderList / renderSequence / 自定义 select 选项 / showToast 四个点统一问号剥离 - APP_VERSION_TEXT bump 到 v1.0.107
This commit is contained in:
@@ -1536,14 +1536,24 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function sanitizeDisplay(s) {
|
||||||
|
let v = String(s == null ? '' : s)
|
||||||
|
.replace(/[?¿?]+/g, '')
|
||||||
|
.replace(/\s+/g, ' ')
|
||||||
|
.trim();
|
||||||
|
return v === '' ? '-' : v;
|
||||||
|
}
|
||||||
|
|
||||||
function escapeHtml(s) {
|
function escapeHtml(s) {
|
||||||
return String(s == null ? '' : s)
|
let v = sanitizeDisplay(s);
|
||||||
|
return v
|
||||||
.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
|
.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
|
||||||
.replace(/"/g, '"').replace(/'/g, ''');
|
.replace(/"/g, '"').replace(/'/g, ''');
|
||||||
}
|
}
|
||||||
|
|
||||||
function escapeAttr(s) {
|
function escapeAttr(s) {
|
||||||
return String(s == null ? '' : s)
|
let v = sanitizeDisplay(s);
|
||||||
|
return v
|
||||||
.replace(/"/g, '"').replace(/'/g, ''');
|
.replace(/"/g, '"').replace(/'/g, ''');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -725,6 +725,14 @@
|
|||||||
<div class="toast-container" id="toast-container"></div>
|
<div class="toast-container" id="toast-container"></div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
function ds(x) {
|
||||||
|
let v = String(x == null ? '' : x)
|
||||||
|
.replace(/[?¿?]+/g, '')
|
||||||
|
.replace(/\s+/g, ' ')
|
||||||
|
.trim();
|
||||||
|
return v === '' ? '-' : v;
|
||||||
|
}
|
||||||
|
|
||||||
const TTS_MODEL = "s2.1-pro-free";
|
const TTS_MODEL = "s2.1-pro-free";
|
||||||
const ZH_REFERENCE_ID = "0aee9f0f5683480d987c9e3c5d2de04c";
|
const ZH_REFERENCE_ID = "0aee9f0f5683480d987c9e3c5d2de04c";
|
||||||
const JA_REFERENCE_ID = "38985e545cff41b79c11c32b0196c9a0";
|
const JA_REFERENCE_ID = "38985e545cff41b79c11c32b0196c9a0";
|
||||||
@@ -826,8 +834,8 @@
|
|||||||
toast.className = 'toast';
|
toast.className = 'toast';
|
||||||
toast.innerHTML = `
|
toast.innerHTML = `
|
||||||
<div>
|
<div>
|
||||||
<div style="font-weight:600; font-size:0.9rem; margin-bottom:2px">${title}</div>
|
<div style="font-weight:600; font-size:0.9rem; margin-bottom:2px">${ds(title)}</div>
|
||||||
<div style="font-size:0.8rem; color:var(--text-muted)">${message}</div>
|
<div style="font-size:0.8rem; color:var(--text-muted)">${ds(message)}</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
container.appendChild(toast);
|
container.appendChild(toast);
|
||||||
@@ -940,11 +948,15 @@
|
|||||||
item.style.alignItems = 'stretch';
|
item.style.alignItems = 'stretch';
|
||||||
item.style.gap = '8px';
|
item.style.gap = '8px';
|
||||||
|
|
||||||
|
const dsCode = ds(task.code);
|
||||||
|
const dsZh = ds(task.zh || '');
|
||||||
|
const dsJa = ds(task.en || '');
|
||||||
|
|
||||||
item.innerHTML = `
|
item.innerHTML = `
|
||||||
<div style="display:flex; justify-content:space-between; align-items:flex-start;">
|
<div style="display:flex; justify-content:space-between; align-items:flex-start;">
|
||||||
<div style="display:flex; flex-direction:column; gap:2px;">
|
<div style="display:flex; flex-direction:column; gap:2px;">
|
||||||
<span style="font-weight:600; color:var(--accent)">${task.code}</span>
|
<span style="font-weight:600; color:var(--accent)">${dsCode}</span>
|
||||||
<span class="edit-file-name" style="color:var(--text-muted)">ZH: ${task.zh} | JA: ${task.en}</span>
|
<span class="edit-file-name" style="color:var(--text-muted)">ZH: ${dsZh} | JA: ${dsJa}</span>
|
||||||
</div>
|
</div>
|
||||||
<span id="batch-status-span-${index}" style="font-size:0.8rem; color:var(--text-muted)">Pending</span>
|
<span id="batch-status-span-${index}" style="font-size:0.8rem; color:var(--text-muted)">Pending</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -1306,10 +1318,11 @@
|
|||||||
div.className = 'list-item';
|
div.className = 'list-item';
|
||||||
div.style.gridTemplateColumns = '100px 1fr auto';
|
div.style.gridTemplateColumns = '100px 1fr auto';
|
||||||
|
|
||||||
const tagsHtml = item.files.map(f => `<span style="background:var(--bg-hover); padding:2px 6px; border-radius:4px; font-size:0.8rem; margin-right:4px;">${f}</span>`).join('');
|
const dsCode = ds(item.code);
|
||||||
|
const tagsHtml = (item.files || []).map(f => `<span style="background:var(--bg-hover); padding:2px 6px; border-radius:4px; font-size:0.8rem; margin-right:4px;">${ds(f)}</span>`).join('');
|
||||||
|
|
||||||
div.innerHTML = `
|
div.innerHTML = `
|
||||||
<div style="font-family:var(--font-mono); font-weight:600;">${item.code}</div>
|
<div style="font-family:var(--font-mono); font-weight:600;">${dsCode}</div>
|
||||||
<div>${tagsHtml || '<span style="color:var(--text-muted);font-style:italic">No files</span>'}</div>
|
<div>${tagsHtml || '<span style="color:var(--text-muted);font-style:italic">No files</span>'}</div>
|
||||||
<div style="display:flex; gap:8px;">
|
<div style="display:flex; gap:8px;">
|
||||||
<button class="btn-icon" onclick="openEditModal(${index})">${ICONS.edit}</button>
|
<button class="btn-icon" onclick="openEditModal(${index})">${ICONS.edit}</button>
|
||||||
|
|||||||
@@ -1119,6 +1119,15 @@
|
|||||||
<div class="toast-container" id="toast-container"></div>
|
<div class="toast-container" id="toast-container"></div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
// --- Display sanitizer: 强制剥除所有 ? 类字符,保证 UI 零问号 ---
|
||||||
|
function ds(x) {
|
||||||
|
let v = String(x == null ? '' : x)
|
||||||
|
.replace(/[?¿?]+/g, '')
|
||||||
|
.replace(/\s+/g, ' ')
|
||||||
|
.trim();
|
||||||
|
return v === '' ? '-' : v;
|
||||||
|
}
|
||||||
|
|
||||||
// --- Icons (SVG) ---
|
// --- Icons (SVG) ---
|
||||||
const ICONS = {
|
const ICONS = {
|
||||||
folder: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="color:#fbbf24"><path d="M4 20h16a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.93a2 2 0 0 1-1.66-.9l-.82-1.2A2 2 0 0 0 7.93 2H4a2 2 0 0 0-2 2v13c0 1.1.9 2 2 2Z"/></svg>`,
|
folder: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="color:#fbbf24"><path d="M4 20h16a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.93a2 2 0 0 1-1.66-.9l-.82-1.2A2 2 0 0 0 7.93 2H4a2 2 0 0 0-2 2v13c0 1.1.9 2 2 2Z"/></svg>`,
|
||||||
@@ -1206,27 +1215,31 @@
|
|||||||
const hasCC = Array.isArray(st.tags) && st.tags.includes('CC');
|
const hasCC = Array.isArray(st.tags) && st.tags.includes('CC');
|
||||||
const stationTarget = st.target || st.name;
|
const stationTarget = st.target || st.name;
|
||||||
const memberCount = Number(st.memberCount || 1);
|
const memberCount = Number(st.memberCount || 1);
|
||||||
const stationVersion = st.version || '';
|
const stationVersion = ds(st.version || '');
|
||||||
const targetVersion = currentStationUpdate?.targetVersion || '';
|
const targetVersion = ds(currentStationUpdate?.targetVersion || '');
|
||||||
const needsUpdate = !!targetVersion && stationVersion !== targetVersion;
|
const needsUpdate = !!targetVersion && stationVersion !== targetVersion && stationVersion !== '-' && targetVersion !== '-';
|
||||||
const updateBadge = needsUpdate
|
const updateBadge = needsUpdate
|
||||||
? `<span style="font-size:0.65rem; background:rgba(245, 158, 11, 0.12); color:#fbbf24; padding:1px 5px; border-radius:4px; margin-left:6px; font-weight:normal; border:1px solid rgba(245, 158, 11, 0.25)">待更新 ${targetVersion}</span>`
|
? `<span style="font-size:0.65rem; background:rgba(245, 158, 11, 0.12); color:#fbbf24; padding:1px 5px; border-radius:4px; margin-left:6px; font-weight:normal; border:1px solid rgba(245, 158, 11, 0.25)">待更新 ${targetVersion}</span>`
|
||||||
: '';
|
: '';
|
||||||
const div = document.createElement('div');
|
const div = document.createElement('div');
|
||||||
div.className = 'status-item';
|
div.className = 'status-item';
|
||||||
div.id = `station-item-${st.name.replace(/[^a-zA-Z0-9]/g, '_')}`;
|
div.id = `station-item-${String(st.name || '').replace(/[^a-zA-Z0-9]/g, '_')}`;
|
||||||
|
const dsName = ds(st.name);
|
||||||
|
const dsCode = ds(st.code || '');
|
||||||
|
const dsPlatform = st.platform ? ds(st.platform) : '';
|
||||||
|
const dsVersion = stationVersion === '-' ? '' : stationVersion;
|
||||||
div.innerHTML = `
|
div.innerHTML = `
|
||||||
<div class="status-main">
|
<div class="status-main">
|
||||||
<div class="item-info">
|
<div class="item-info">
|
||||||
<div class="item-title">
|
<div class="item-title">
|
||||||
${st.name}
|
${dsName}
|
||||||
${hasCC ? '<span style="font-size:0.65rem; background:rgba(16, 185, 129, 0.1); color:#34d399; padding:1px 5px; border-radius:4px; margin-left:6px; font-weight:normal; border:1px solid rgba(16, 185, 129, 0.2)">CC</span>' : ''}
|
${hasCC ? '<span style="font-size:0.65rem; background:rgba(16, 185, 129, 0.1); color:#34d399; padding:1px 5px; border-radius:4px; margin-left:6px; font-weight:normal; border:1px solid rgba(16, 185, 129, 0.2)">CC</span>' : ''}
|
||||||
${updateBadge}
|
${updateBadge}
|
||||||
</div>
|
</div>
|
||||||
<div class="item-sub">${st.code}${st.platform ? ` - Platform ${st.platform}` : ''}${memberCount > 1 ? ` · Nodes ${memberCount}` : ''} · ${stationVersion}</div>
|
<div class="item-sub">${dsCode}${dsPlatform && dsPlatform !== '-' ? ` - Platform ${dsPlatform}` : ''}${memberCount > 1 ? ` · Nodes ${memberCount}` : ''}${dsVersion ? ' · ' + dsVersion : ''}</div>
|
||||||
</div>
|
</div>
|
||||||
<div style="display:flex;align-items:center;gap:10px">
|
<div style="display:flex;align-items:center;gap:10px">
|
||||||
<div class="status-dot ${dotClass}" id="station-dot-${st.name.replace(/[^a-zA-Z0-9]/g, '_')}" data-online="${isOnline}" data-name="${st.name}"></div>
|
<div class="status-dot ${dotClass}" id="station-dot-${String(st.name || '').replace(/[^a-zA-Z0-9]/g, '_')}" data-online="${isOnline}" data-name="${dsName}"></div>
|
||||||
<button class="btn-icon" onclick="deleteStation('${stationTarget}')">${ICONS.trash}</button>
|
<button class="btn-icon" onclick="deleteStation('${stationTarget}')">${ICONS.trash}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1245,12 +1258,15 @@
|
|||||||
const div = document.createElement('div');
|
const div = document.createElement('div');
|
||||||
div.className = 'status-item';
|
div.className = 'status-item';
|
||||||
const currentStCode = stationCodeMap[tr.currentStation] || tr.currentStation || '-';
|
const currentStCode = stationCodeMap[tr.currentStation] || tr.currentStation || '-';
|
||||||
|
const dsTrainId = ds(tr.trainId);
|
||||||
|
const dsStatus = ds(tr.status || '');
|
||||||
|
const dsCurCode = ds(currentStCode);
|
||||||
|
|
||||||
div.innerHTML = `
|
div.innerHTML = `
|
||||||
<div class="status-main">
|
<div class="status-main">
|
||||||
<div class="item-info">
|
<div class="item-info">
|
||||||
<div class="item-title">${tr.trainId}</div>
|
<div class="item-title">${dsTrainId}</div>
|
||||||
<div class="item-sub">${currentStCode} <span style="margin-left:8px;color:var(--text-muted)">${tr.status || '-'}</span></div>
|
<div class="item-sub">${dsCurCode} <span style="margin-left:8px;color:var(--text-muted)">${dsStatus}</span></div>
|
||||||
</div>
|
</div>
|
||||||
<div style="display:flex;align-items:center;gap:10px">
|
<div style="display:flex;align-items:center;gap:10px">
|
||||||
<div class="status-dot ${tr.status === 'At Station' ? 'dot-online' : 'dot-warn'}"></div>
|
<div class="status-dot ${tr.status === 'At Station' ? 'dot-online' : 'dot-warn'}"></div>
|
||||||
@@ -1277,7 +1293,9 @@
|
|||||||
|
|
||||||
function updateRadioStreams(station, seq, isStarting, hlsUrl) {
|
function updateRadioStreams(station, seq, isStarting, hlsUrl) {
|
||||||
const container = document.getElementById('radio-streams-container');
|
const container = document.getElementById('radio-streams-container');
|
||||||
const cardId = `stream-card-${station.replace(/[^a-zA-Z0-9]/g, '_')}`;
|
const dsStation = ds(station || '');
|
||||||
|
const stationKey = String(station || '').replace(/[^a-zA-Z0-9]/g, '_');
|
||||||
|
const cardId = `stream-card-${stationKey}`;
|
||||||
|
|
||||||
if (isStarting) {
|
if (isStarting) {
|
||||||
// Clear empty state
|
// Clear empty state
|
||||||
@@ -1293,15 +1311,20 @@
|
|||||||
container.appendChild(card);
|
container.appendChild(card);
|
||||||
}
|
}
|
||||||
|
|
||||||
const seqHtml = seq ? seq.map(s => `<span style="background:var(--bg-hover); padding:2px 6px; border-radius:4px; font-size:0.75rem; font-family:var(--font-mono); margin:2px; display:inline-block;">${s}</span>`).join('') : '';
|
const seqHtml = Array.isArray(seq) && seq.length > 0
|
||||||
|
? seq.map(s => `<span style="background:var(--bg-hover); padding:2px 6px; border-radius:4px; font-size:0.75rem; font-family:var(--font-mono); margin:2px; display:inline-block;">${ds(s)}</span>`).join('')
|
||||||
|
: '';
|
||||||
|
|
||||||
|
const dsHlsUrl = ds(hlsUrl || '');
|
||||||
|
const showHls = hlsUrl && dsHlsUrl !== '-';
|
||||||
|
|
||||||
card.innerHTML = `
|
card.innerHTML = `
|
||||||
<div style="position:absolute; top:0; left:0; right:0; height:3px; background:#3b82f6; animation:pulse 1s infinite;"></div>
|
<div style="position:absolute; top:0; left:0; right:0; height:3px; background:#3b82f6; animation:pulse 1s infinite;"></div>
|
||||||
<div style="display:flex; justify-content:space-between; align-items:center;">
|
<div style="display:flex; justify-content:space-between; align-items:center;">
|
||||||
<div style="font-weight:600; font-size:1.1rem;">${station}</div>
|
<div style="font-weight:600; font-size:1.1rem;">${dsStation}</div>
|
||||||
<div class="status-dot dot-broadcasting"></div>
|
<div class="status-dot dot-broadcasting"></div>
|
||||||
</div>
|
</div>
|
||||||
${hlsUrl ? `<div style="font-size:0.8rem; color:var(--text-muted);">HLS: <a href="${hlsUrl}" target="_blank" rel="noreferrer" style="color:#60a5fa; text-decoration:none; font-family:var(--font-mono);">${hlsUrl}</a></div>` : ''}
|
${showHls ? `<div style="font-size:0.8rem; color:var(--text-muted);">HLS: <a href="${String(hlsUrl || '')}" target="_blank" rel="noreferrer" style="color:#60a5fa; text-decoration:none; font-family:var(--font-mono);">${dsHlsUrl}</a></div>` : ''}
|
||||||
<div>
|
<div>
|
||||||
<div style="font-size:0.8rem; color:var(--text-muted); margin-bottom:8px;">PLAYING SEQUENCE:</div>
|
<div style="font-size:0.8rem; color:var(--text-muted); margin-bottom:8px;">PLAYING SEQUENCE:</div>
|
||||||
<div style="display:flex; flex-wrap:wrap; gap:4px; max-height:100px; overflow-y:auto; padding-right:4px;">
|
<div style="display:flex; flex-wrap:wrap; gap:4px; max-height:100px; overflow-y:auto; padding-right:4px;">
|
||||||
@@ -1365,8 +1388,10 @@
|
|||||||
else typeClass = 'type-info';
|
else typeClass = 'type-info';
|
||||||
|
|
||||||
content = content.replace(/^\[.*?\]\s*/, '');
|
content = content.replace(/^\[.*?\]\s*/, '');
|
||||||
|
const dsTime = ds(time);
|
||||||
|
const dsContent = ds(content);
|
||||||
|
|
||||||
div.innerHTML = `<div class="log-time">${time}</div><div class="log-msg"><span class="${typeClass}">></span> ${content}</div>`;
|
div.innerHTML = `<div class="log-time">${dsTime}</div><div class="log-msg"><span class="${typeClass}">></span> ${dsContent}</div>`;
|
||||||
container.prepend(div);
|
container.prepend(div);
|
||||||
if (container.children.length > 100) container.lastChild.remove();
|
if (container.children.length > 100) container.lastChild.remove();
|
||||||
}
|
}
|
||||||
@@ -1376,6 +1401,9 @@
|
|||||||
const toast = document.createElement('div');
|
const toast = document.createElement('div');
|
||||||
toast.className = 'toast';
|
toast.className = 'toast';
|
||||||
|
|
||||||
|
const dsTitle = ds(title || '');
|
||||||
|
const dsMsg = ds(message || '');
|
||||||
|
|
||||||
// Icon based on title/message
|
// Icon based on title/message
|
||||||
let iconColor = 'var(--text-primary)';
|
let iconColor = 'var(--text-primary)';
|
||||||
if (title.includes('Arrival')) iconColor = 'var(--success)';
|
if (title.includes('Arrival')) iconColor = 'var(--success)';
|
||||||
@@ -1386,8 +1414,8 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>
|
||||||
</div>
|
</div>
|
||||||
<div class="toast-content">
|
<div class="toast-content">
|
||||||
<div class="toast-title">${title}</div>
|
<div class="toast-title">${dsTitle}</div>
|
||||||
<div class="toast-msg">${message}</div>
|
<div class="toast-msg">${dsMsg}</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -1437,7 +1465,7 @@
|
|||||||
beep: 'Beep',
|
beep: 'Beep',
|
||||||
station: 'Station'
|
station: 'Station'
|
||||||
};
|
};
|
||||||
const name = nameMap[currentPath] || currentPath;
|
const name = ds(nameMap[currentPath] || currentPath);
|
||||||
breadcrumb.innerHTML = `
|
breadcrumb.innerHTML = `
|
||||||
<div class="breadcrumb-item" onclick="navigate('root')">Home</div>
|
<div class="breadcrumb-item" onclick="navigate('root')">Home</div>
|
||||||
<div class="breadcrumb-sep">/</div>
|
<div class="breadcrumb-sep">/</div>
|
||||||
@@ -1460,10 +1488,12 @@
|
|||||||
const row = document.createElement('div');
|
const row = document.createElement('div');
|
||||||
row.className = 'file-row';
|
row.className = 'file-row';
|
||||||
row.onclick = () => navigate(f.id);
|
row.onclick = () => navigate(f.id);
|
||||||
|
const dsFolderName = ds(f.name);
|
||||||
|
const dsCount = String(f.count || 0);
|
||||||
row.innerHTML = `
|
row.innerHTML = `
|
||||||
<div></div>
|
<div></div>
|
||||||
<div style="display:flex;align-items:center;gap:10px">${ICONS.folder}<span class="file-name">${f.name}</span></div>
|
<div style="display:flex;align-items:center;gap:10px">${ICONS.folder}<span class="file-name">${dsFolderName}</span></div>
|
||||||
<div class="file-meta">${f.count} items</div>
|
<div class="file-meta">${dsCount} items</div>
|
||||||
<div></div>
|
<div></div>
|
||||||
`;
|
`;
|
||||||
container.appendChild(row);
|
container.appendChild(row);
|
||||||
@@ -1485,6 +1515,8 @@
|
|||||||
};
|
};
|
||||||
const row = document.createElement('div');
|
const row = document.createElement('div');
|
||||||
row.className = 'file-row';
|
row.className = 'file-row';
|
||||||
|
const dsFileName = ds(f.name);
|
||||||
|
const dsTypeLabel = ds(typeLabelMap[f.type] || f.type || '');
|
||||||
// Click to preview
|
// Click to preview
|
||||||
row.onclick = (e) => {
|
row.onclick = (e) => {
|
||||||
if (e.target.type !== 'checkbox' && e.target.tagName !== 'BUTTON' && e.target.tagName !== 'path' && e.target.tagName !== 'svg') {
|
if (e.target.type !== 'checkbox' && e.target.tagName !== 'BUTTON' && e.target.tagName !== 'path' && e.target.tagName !== 'svg') {
|
||||||
@@ -1493,8 +1525,8 @@
|
|||||||
};
|
};
|
||||||
row.innerHTML = `
|
row.innerHTML = `
|
||||||
<div style="display:flex;justify-content:center"><input type="checkbox" class="file-check" value="${f.path}" onchange="updateSelection()"></div>
|
<div style="display:flex;justify-content:center"><input type="checkbox" class="file-check" value="${f.path}" onchange="updateSelection()"></div>
|
||||||
<div style="display:flex;align-items:center;gap:10px">${f.name.endsWith('.txt') ? ICONS.file : ICONS.audio}<span class="file-name">${f.name}</span></div>
|
<div style="display:flex;align-items:center;gap:10px">${f.name.endsWith('.txt') ? ICONS.file : ICONS.audio}<span class="file-name">${dsFileName}</span></div>
|
||||||
<div class="file-meta">${typeLabelMap[f.type] || f.type}</div>
|
<div class="file-meta">${dsTypeLabel}</div>
|
||||||
<div style="text-align:right">
|
<div style="text-align:right">
|
||||||
<button class="btn-icon" onclick="deleteFile('${f.path}')">${ICONS.trash}</button>
|
<button class="btn-icon" onclick="deleteFile('${f.path}')">${ICONS.trash}</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -1769,10 +1801,12 @@
|
|||||||
|
|
||||||
function renderStationUpdate(info) {
|
function renderStationUpdate(info) {
|
||||||
const el = document.getElementById('current-version');
|
const el = document.getElementById('current-version');
|
||||||
const localVersion = info?.localVersion || '-';
|
const localVersion = ds(info?.localVersion || '-');
|
||||||
const targetVersion = info?.targetVersion || '';
|
const targetVersion = ds(info?.targetVersion || '');
|
||||||
const publishedAt = info?.publishedAt ? new Date(info.publishedAt).toLocaleString() : '';
|
const publishedAt = info?.publishedAt ? ds(new Date(info.publishedAt).toLocaleString()) : '';
|
||||||
const remoteVersionHtml = info?.remoteVersion ? `<div>Gitea最新:${info.remoteVersion}</div>` : '';
|
const remoteVersionHtml = info?.remoteVersion
|
||||||
|
? `<div>Gitea最新:${ds(info.remoteVersion)}</div>`
|
||||||
|
: '';
|
||||||
|
|
||||||
el.innerHTML = `
|
el.innerHTML = `
|
||||||
<div>服务器脚本:${localVersion}</div>
|
<div>服务器脚本:${localVersion}</div>
|
||||||
@@ -1792,12 +1826,12 @@
|
|||||||
const remoteRes = await fetch('/api/remote-version');
|
const remoteRes = await fetch('/api/remote-version');
|
||||||
const remoteData = await remoteRes.json();
|
const remoteData = await remoteRes.json();
|
||||||
if (remoteData.success) {
|
if (remoteData.success) {
|
||||||
data.remoteVersion = remoteData.version;
|
data.remoteVersion = String(remoteData.version || '');
|
||||||
} else {
|
} else {
|
||||||
data.remoteVersion = '解析失败';
|
data.remoteVersion = '';
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
data.remoteVersion = '请求失败';
|
data.remoteVersion = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
renderStationUpdate(data);
|
renderStationUpdate(data);
|
||||||
|
|||||||
@@ -358,6 +358,14 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
function ds(x) {
|
||||||
|
let v = String(x == null ? '' : x)
|
||||||
|
.replace(/[?¿?]+/g, '')
|
||||||
|
.replace(/\s+/g, ' ')
|
||||||
|
.trim();
|
||||||
|
return v === '' ? '-' : v;
|
||||||
|
}
|
||||||
|
|
||||||
// --- Icons (SVG) ---
|
// --- Icons (SVG) ---
|
||||||
const ICONS = {
|
const ICONS = {
|
||||||
chevronDown: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg>`,
|
chevronDown: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg>`,
|
||||||
@@ -443,7 +451,7 @@
|
|||||||
icon = ICONS.broadcast;
|
icon = ICONS.broadcast;
|
||||||
}
|
}
|
||||||
|
|
||||||
messageLog.unshift({ time, content: msg, type, icon, important });
|
messageLog.unshift({ time: ds(time), content: ds(msg), type, icon, important });
|
||||||
if (messageLog.length > MAX_MESSAGES) messageLog.pop();
|
if (messageLog.length > MAX_MESSAGES) messageLog.pop();
|
||||||
renderMessages();
|
renderMessages();
|
||||||
}
|
}
|
||||||
@@ -486,18 +494,22 @@
|
|||||||
const isBroadcasting = broadcastingStations.has(st.name);
|
const isBroadcasting = broadcastingStations.has(st.name);
|
||||||
const dotClass = isBroadcasting ? 'dot-broadcasting' : (isOnline ? 'dot-online' : 'dot-offline');
|
const dotClass = isBroadcasting ? 'dot-broadcasting' : (isOnline ? 'dot-online' : 'dot-offline');
|
||||||
const hasCC = Array.isArray(st.tags) && st.tags.includes('CC');
|
const hasCC = Array.isArray(st.tags) && st.tags.includes('CC');
|
||||||
|
const dsName = ds(st.name);
|
||||||
|
const dsCode = ds(st.code || '');
|
||||||
|
const dsPlatform = st.platform ? ds(st.platform) : '';
|
||||||
|
const stationKey = String(st.name || '').replace(/[^a-zA-Z0-9]/g, '_');
|
||||||
|
|
||||||
const div = document.createElement('div');
|
const div = document.createElement('div');
|
||||||
div.className = 'status-item';
|
div.className = 'status-item';
|
||||||
div.innerHTML = `
|
div.innerHTML = `
|
||||||
<div class="item-info">
|
<div class="item-info">
|
||||||
<div class="item-title">
|
<div class="item-title">
|
||||||
${st.name}
|
${dsName}
|
||||||
${hasCC ? '<span style="font-size:0.65rem; background:rgba(16, 185, 129, 0.1); color:#34d399; padding:1px 5px; border-radius:4px; margin-left:6px; font-weight:normal; border:1px solid rgba(16, 185, 129, 0.2)">CC</span>' : ''}
|
${hasCC ? '<span style="font-size:0.65rem; background:rgba(16, 185, 129, 0.1); color:#34d399; padding:1px 5px; border-radius:4px; margin-left:6px; font-weight:normal; border:1px solid rgba(16, 185, 129, 0.2)">CC</span>' : ''}
|
||||||
</div>
|
</div>
|
||||||
<div class="item-sub">${st.code}${st.platform ? ` - 站台 ${st.platform}` : ''}</div>
|
<div class="item-sub">${dsCode}${dsPlatform && dsPlatform !== '-' ? ` - 站台 ${dsPlatform}` : ''}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="status-dot ${dotClass}" id="station-dot-${st.name.replace(/[^a-zA-Z0-9]/g, '_')}" data-online="${isOnline}" data-name="${st.name}"></div>
|
<div class="status-dot ${dotClass}" id="station-dot-${stationKey}" data-online="${isOnline}" data-name="${dsName}"></div>
|
||||||
`;
|
`;
|
||||||
list.appendChild(div);
|
list.appendChild(div);
|
||||||
});
|
});
|
||||||
@@ -519,13 +531,18 @@
|
|||||||
|
|
||||||
const currentStCode = stationCodeMap[tr.currentStation] || tr.currentStation || '-';
|
const currentStCode = stationCodeMap[tr.currentStation] || tr.currentStation || '-';
|
||||||
const statusColor = tr.status === 'At Station' ? 'var(--success)' : 'var(--warning)';
|
const statusColor = tr.status === 'At Station' ? 'var(--success)' : 'var(--warning)';
|
||||||
|
const dsTrainId = ds(tr.trainId);
|
||||||
|
const dsDest = ds(tr.destination || '');
|
||||||
|
const dsCurCode = ds(currentStCode);
|
||||||
|
const rawStatus = tr.status === 'At Station' ? '在站' : (tr.status === 'Departed' ? '已发车' : '运行中');
|
||||||
|
const dsStatus = ds(rawStatus);
|
||||||
|
|
||||||
div.innerHTML = `
|
div.innerHTML = `
|
||||||
<div style="width:100%">
|
<div style="width:100%">
|
||||||
<div style="display:flex; justify-content:space-between; align-items:center;">
|
<div style="display:flex; justify-content:space-between; align-items:center;">
|
||||||
<div class="item-info">
|
<div class="item-info">
|
||||||
<div class="item-title">${tr.trainId} <span style="font-size:0.7rem; color:var(--text-muted); font-weight:normal; margin-left:8px;">${tr.destination || ''}</span></div>
|
<div class="item-title">${dsTrainId}${dsDest && dsDest !== '-' ? ' <span style="font-size:0.7rem; color:var(--text-muted); font-weight:normal; margin-left:8px;">' + dsDest + '</span>' : ''}</div>
|
||||||
<div class="item-sub">${currentStCode} <span style="color:${statusColor}; margin-left:8px;">${tr.status === 'At Station' ? '在站' : (tr.status === 'Departed' ? '已发车' : '运行中')}</span></div>
|
<div class="item-sub">${dsCurCode} <span style="color:${statusColor}; margin-left:8px;">${dsStatus}</span></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="status-dot ${tr.status === 'At Station' ? 'dot-online' : 'dot-warn'}"></div>
|
<div class="status-dot ${tr.status === 'At Station' ? 'dot-online' : 'dot-warn'}"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -452,6 +452,14 @@
|
|||||||
<div class="toast-container" id="toast-container"></div>
|
<div class="toast-container" id="toast-container"></div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
function ds(x) {
|
||||||
|
let v = String(x == null ? '' : x)
|
||||||
|
.replace(/[?¿?]+/g, '')
|
||||||
|
.replace(/\s+/g, ' ')
|
||||||
|
.trim();
|
||||||
|
return v === '' ? '-' : v;
|
||||||
|
}
|
||||||
|
|
||||||
const ICONS = {
|
const ICONS = {
|
||||||
up: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="18 15 12 9 6 15"></polyline></svg>`,
|
up: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="18 15 12 9 6 15"></polyline></svg>`,
|
||||||
down: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="6 9 12 15 18 9"></polyline></svg>`,
|
down: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="6 9 12 15 18 9"></polyline></svg>`,
|
||||||
@@ -471,8 +479,8 @@
|
|||||||
toast.className = 'toast';
|
toast.className = 'toast';
|
||||||
toast.innerHTML = `
|
toast.innerHTML = `
|
||||||
<div>
|
<div>
|
||||||
<div style="font-weight:600; font-size:0.9rem; margin-bottom:2px">${title}</div>
|
<div style="font-weight:600; font-size:0.9rem; margin-bottom:2px">${ds(title)}</div>
|
||||||
<div style="font-size:0.8rem; color:var(--text-muted)">${message}</div>
|
<div style="font-size:0.8rem; color:var(--text-muted)">${ds(message)}</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
container.appendChild(toast);
|
container.appendChild(toast);
|
||||||
@@ -539,7 +547,8 @@
|
|||||||
let html = `<div class="custom-select-option ${selectedFileValue === '' ? 'selected' : ''}" data-value="" onclick="selectCustomOption('', '-- Select an audio file --')">-- Select an audio file --</div>`;
|
let html = `<div class="custom-select-option ${selectedFileValue === '' ? 'selected' : ''}" data-value="" onclick="selectCustomOption('', '-- Select an audio file --')">-- Select an audio file --</div>`;
|
||||||
availableFiles.forEach(f => {
|
availableFiles.forEach(f => {
|
||||||
const isSelected = selectedFileValue === f ? 'selected' : '';
|
const isSelected = selectedFileValue === f ? 'selected' : '';
|
||||||
html += `<div class="custom-select-option ${isSelected}" data-value="${f}" onclick="selectCustomOption('${f}', '${f}')">${f}</div>`;
|
const dsF = ds(f);
|
||||||
|
html += `<div class="custom-select-option ${isSelected}" data-value="${f}" onclick="selectCustomOption('${f}', '${dsF}')">${dsF}</div>`;
|
||||||
});
|
});
|
||||||
optionsContainer.innerHTML = html;
|
optionsContainer.innerHTML = html;
|
||||||
}
|
}
|
||||||
@@ -576,9 +585,10 @@
|
|||||||
const div = document.createElement('div');
|
const div = document.createElement('div');
|
||||||
div.className = 'list-item';
|
div.className = 'list-item';
|
||||||
|
|
||||||
const tagsHtml = (item.files || []).map(f => `<span class="tag">${f}</span>`).join(' ');
|
const dsCode = ds(item.code);
|
||||||
|
const tagsHtml = (item.files || []).map(f => `<span class="tag">${ds(f)}</span>`).join(' ');
|
||||||
div.innerHTML = `
|
div.innerHTML = `
|
||||||
<div class="code">${item.code}</div>
|
<div class="code">${dsCode}</div>
|
||||||
<div>${tagsHtml || '<span style="color:var(--text-muted); font-style: italic;">No files</span>'}</div>
|
<div>${tagsHtml || '<span style="color:var(--text-muted); font-style: italic;">No files</span>'}</div>
|
||||||
<div style="display:flex; gap:6px;">
|
<div style="display:flex; gap:6px;">
|
||||||
<button class="btn-icon" onclick="openEditModal(${index})" aria-label="Edit">${ICONS.edit}</button>
|
<button class="btn-icon" onclick="openEditModal(${index})" aria-label="Edit">${ICONS.edit}</button>
|
||||||
@@ -636,7 +646,7 @@
|
|||||||
const div = document.createElement('div');
|
const div = document.createElement('div');
|
||||||
div.className = 'edit-file-item';
|
div.className = 'edit-file-item';
|
||||||
div.innerHTML = `
|
div.innerHTML = `
|
||||||
<div class="edit-file-name">${file}</div>
|
<div class="edit-file-name">${ds(file)}</div>
|
||||||
<div class="edit-file-actions">
|
<div class="edit-file-actions">
|
||||||
<button class="btn-icon" onclick="moveFile(${idx}, -1)" ${idx === 0 ? 'disabled style="opacity:0.35"' : ''} aria-label="Up">${ICONS.up}</button>
|
<button class="btn-icon" onclick="moveFile(${idx}, -1)" ${idx === 0 ? 'disabled style="opacity:0.35"' : ''} aria-label="Up">${ICONS.up}</button>
|
||||||
<button class="btn-icon" onclick="moveFile(${idx}, 1)" ${idx === currentSequence.length - 1 ? 'disabled style="opacity:0.35"' : ''} aria-label="Down">${ICONS.down}</button>
|
<button class="btn-icon" onclick="moveFile(${idx}, 1)" ${idx === currentSequence.length - 1 ? 'disabled style="opacity:0.35"' : ''} aria-label="Down">${ICONS.down}</button>
|
||||||
|
|||||||
@@ -308,8 +308,17 @@
|
|||||||
document.getElementById("status_box").textContent = String(msg || "");
|
document.getElementById("status_box").textContent = String(msg || "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function sanitizeDisplay(s) {
|
||||||
|
let v = String(s == null ? "" : s)
|
||||||
|
.replace(/[?¿?]+/g, '')
|
||||||
|
.replace(/\s+/g, ' ')
|
||||||
|
.trim();
|
||||||
|
return v === "" ? "-" : v;
|
||||||
|
}
|
||||||
|
|
||||||
function escapeHtml(str) {
|
function escapeHtml(str) {
|
||||||
return String(str || "")
|
let v = sanitizeDisplay(str);
|
||||||
|
return v
|
||||||
.replaceAll("&", "&")
|
.replaceAll("&", "&")
|
||||||
.replaceAll("<", "<")
|
.replaceAll("<", "<")
|
||||||
.replaceAll(">", ">")
|
.replaceAll(">", ">")
|
||||||
|
|||||||
@@ -189,8 +189,17 @@
|
|||||||
return 'idle';
|
return 'idle';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function sanitizeDisplay(s) {
|
||||||
|
let v = String(s ?? '')
|
||||||
|
.replace(/[?¿?]+/g, '')
|
||||||
|
.replace(/\s+/g, ' ')
|
||||||
|
.trim();
|
||||||
|
return v === '' ? '-' : v;
|
||||||
|
}
|
||||||
|
|
||||||
function escapeHtml(s) {
|
function escapeHtml(s) {
|
||||||
return String(s ?? '')
|
let v = sanitizeDisplay(s);
|
||||||
|
return v
|
||||||
.replaceAll('&', '&')
|
.replaceAll('&', '&')
|
||||||
.replaceAll('<', '<')
|
.replaceAll('<', '<')
|
||||||
.replaceAll('>', '>')
|
.replaceAll('>', '>')
|
||||||
|
|||||||
@@ -256,8 +256,17 @@
|
|||||||
let editKey = '';
|
let editKey = '';
|
||||||
let lastData = null;
|
let lastData = null;
|
||||||
|
|
||||||
|
function sanitizeDisplay(s) {
|
||||||
|
let v = String(s ?? '')
|
||||||
|
.replace(/[?¿?]+/g, '')
|
||||||
|
.replace(/\s+/g, ' ')
|
||||||
|
.trim();
|
||||||
|
return v === '' ? '-' : v;
|
||||||
|
}
|
||||||
|
|
||||||
function escapeHtml(s) {
|
function escapeHtml(s) {
|
||||||
return String(s ?? '')
|
let v = sanitizeDisplay(s);
|
||||||
|
return v
|
||||||
.replaceAll('&', '&')
|
.replaceAll('&', '&')
|
||||||
.replaceAll('<', '<')
|
.replaceAll('<', '<')
|
||||||
.replaceAll('>', '>')
|
.replaceAll('>', '>')
|
||||||
|
|||||||
@@ -394,8 +394,17 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function sanitizeDisplay(s) {
|
||||||
|
let v = String(s == null ? "" : s)
|
||||||
|
.replace(/[?¿?]+/g, '')
|
||||||
|
.replace(/\s+/g, ' ')
|
||||||
|
.trim();
|
||||||
|
return v === "" ? "-" : v;
|
||||||
|
}
|
||||||
|
|
||||||
function escapeHtml(str) {
|
function escapeHtml(str) {
|
||||||
return String(str || "")
|
let v = sanitizeDisplay(str);
|
||||||
|
return v
|
||||||
.replaceAll("&", "&")
|
.replaceAll("&", "&")
|
||||||
.replaceAll("<", "<")
|
.replaceAll("<", "<")
|
||||||
.replaceAll(">", ">")
|
.replaceAll(">", ">")
|
||||||
|
|||||||
@@ -0,0 +1,265 @@
|
|||||||
|
Advanced Display Block NBT Dump
|
||||||
|
Generated: 2026-08-16 14:08:16
|
||||||
|
============================================================
|
||||||
|
|
||||||
|
BlockReader: right
|
||||||
|
|
||||||
|
[Block Registry Name]
|
||||||
|
createrailwaysnavigator:advanced_display_block
|
||||||
|
|
||||||
|
[Block NBT Data]
|
||||||
|
{
|
||||||
|
DisplayId = "createrailwaysnavigator:platform/focus"
|
||||||
|
DisplaySettings = {
|
||||||
|
{
|
||||||
|
BackColor = 0
|
||||||
|
FontColor = -1
|
||||||
|
PlatformWidth = -1
|
||||||
|
PlatformWidthNextStop = -1
|
||||||
|
ShowLineColor = 0
|
||||||
|
ShowTrainMultipleTimes = 1
|
||||||
|
TimeDisplay = 0
|
||||||
|
TrainNameWidth = 12
|
||||||
|
TrainNameWidthNextStop = -1
|
||||||
|
TrainText = 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EnableCT = 1
|
||||||
|
Filter = "CE-03 2"
|
||||||
|
Glowing = 0
|
||||||
|
IsController = 1
|
||||||
|
Item = {}
|
||||||
|
LastRefreshed = 1303
|
||||||
|
Material = {
|
||||||
|
{
|
||||||
|
Name = "create:copycat_base"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Platform = "2"
|
||||||
|
TrainStops = {
|
||||||
|
[1] = {
|
||||||
|
FirstStop = "??"
|
||||||
|
IsFirst = 1
|
||||||
|
IsLast = 1
|
||||||
|
IsNextSectionExcluded = 0
|
||||||
|
IsPrevSectionExcluded = 0
|
||||||
|
State = 1
|
||||||
|
Station = {
|
||||||
|
{
|
||||||
|
Destination = "??"
|
||||||
|
Index = 2
|
||||||
|
RealTimeArrival = 2247
|
||||||
|
RealTimeDeparture = 1347
|
||||||
|
RealTimeStation = {
|
||||||
|
{
|
||||||
|
Id = {
|
||||||
|
[1] = -284816241
|
||||||
|
[2] = -119452467
|
||||||
|
[3] = -2021313202
|
||||||
|
[4] = 1655163467
|
||||||
|
}
|
||||||
|
StationInfo = {
|
||||||
|
{
|
||||||
|
Platform = "2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StationName = "CE-03 2"
|
||||||
|
TagName = "??"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ScheduledArrival = 3869
|
||||||
|
ScheduledDeparture = 4769
|
||||||
|
ScheduledStation = {
|
||||||
|
{
|
||||||
|
Id = {
|
||||||
|
[1] = -284816241
|
||||||
|
[2] = -119452467
|
||||||
|
[3] = -2021313202
|
||||||
|
[4] = 1655163467
|
||||||
|
}
|
||||||
|
StationInfo = {
|
||||||
|
{
|
||||||
|
Platform = "2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StationName = "CE-03 2"
|
||||||
|
TagName = "??"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TrainName = "???"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Stopovers = {
|
||||||
|
[1] = "????"
|
||||||
|
[2] = "???"
|
||||||
|
}
|
||||||
|
Train = {
|
||||||
|
{
|
||||||
|
Cancelled = 0
|
||||||
|
Carriages = 1
|
||||||
|
Icon = "create:traditional"
|
||||||
|
Id = {
|
||||||
|
[1] = 1934164313
|
||||||
|
[2] = -793689251
|
||||||
|
[3] = -1147623584
|
||||||
|
[4] = 557989055
|
||||||
|
}
|
||||||
|
StateData = {
|
||||||
|
{
|
||||||
|
["0"] = {
|
||||||
|
{
|
||||||
|
CategoryId = {
|
||||||
|
[1] = 186313593
|
||||||
|
[2] = 376131208
|
||||||
|
[3] = -1142882515
|
||||||
|
[4] = 1849467770
|
||||||
|
}
|
||||||
|
Color = -14790494
|
||||||
|
DisplayName = "???"
|
||||||
|
LineId = {
|
||||||
|
[1] = -1767394825
|
||||||
|
[2] = 1017529725
|
||||||
|
[3] = -1140094085
|
||||||
|
[4] = 280637596
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
["1"] = {
|
||||||
|
{
|
||||||
|
CategoryId = {
|
||||||
|
[1] = 186313593
|
||||||
|
[2] = 376131208
|
||||||
|
[3] = -1142882515
|
||||||
|
[4] = 1849467770
|
||||||
|
}
|
||||||
|
Color = -14790494
|
||||||
|
DisplayName = "???"
|
||||||
|
LineId = {
|
||||||
|
[1] = -1767394825
|
||||||
|
[2] = 1017529725
|
||||||
|
[3] = -1140094085
|
||||||
|
[4] = 280637596
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Status = {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[2] = {
|
||||||
|
FirstStop = "??"
|
||||||
|
IsFirst = 1
|
||||||
|
IsLast = 1
|
||||||
|
IsNextSectionExcluded = 0
|
||||||
|
IsPrevSectionExcluded = 0
|
||||||
|
State = 1
|
||||||
|
Station = {
|
||||||
|
{
|
||||||
|
Destination = "??"
|
||||||
|
Index = 2
|
||||||
|
RealTimeArrival = 6965
|
||||||
|
RealTimeDeparture = 6065
|
||||||
|
RealTimeStation = {
|
||||||
|
{
|
||||||
|
Id = {
|
||||||
|
[1] = -284816241
|
||||||
|
[2] = -119452467
|
||||||
|
[3] = -2021313202
|
||||||
|
[4] = 1655163467
|
||||||
|
}
|
||||||
|
StationInfo = {
|
||||||
|
{
|
||||||
|
Platform = "2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StationName = "CE-03 2"
|
||||||
|
TagName = "??"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ScheduledArrival = 9856
|
||||||
|
ScheduledDeparture = 10756
|
||||||
|
ScheduledStation = {
|
||||||
|
{
|
||||||
|
Id = {
|
||||||
|
[1] = -284816241
|
||||||
|
[2] = -119452467
|
||||||
|
[3] = -2021313202
|
||||||
|
[4] = 1655163467
|
||||||
|
}
|
||||||
|
StationInfo = {
|
||||||
|
{
|
||||||
|
Platform = "2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StationName = "CE-03 2"
|
||||||
|
TagName = "??"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TrainName = "???"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Stopovers = {
|
||||||
|
[1] = "????"
|
||||||
|
[2] = "???"
|
||||||
|
}
|
||||||
|
Train = {
|
||||||
|
{
|
||||||
|
Cancelled = 0
|
||||||
|
Carriages = 1
|
||||||
|
Icon = "create:traditional"
|
||||||
|
Id = {
|
||||||
|
[1] = 2056578835
|
||||||
|
[2] = 774784604
|
||||||
|
[3] = -1876554799
|
||||||
|
[4] = 2012426381
|
||||||
|
}
|
||||||
|
StateData = {
|
||||||
|
{
|
||||||
|
["0"] = {
|
||||||
|
{
|
||||||
|
CategoryId = {
|
||||||
|
[1] = 186313593
|
||||||
|
[2] = 376131208
|
||||||
|
[3] = -1142882515
|
||||||
|
[4] = 1849467770
|
||||||
|
}
|
||||||
|
Color = -14790494
|
||||||
|
DisplayName = "???"
|
||||||
|
LineId = {
|
||||||
|
[1] = -1767394825
|
||||||
|
[2] = 1017529725
|
||||||
|
[3] = -1140094085
|
||||||
|
[4] = 280637596
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
["1"] = {
|
||||||
|
{
|
||||||
|
CategoryId = {
|
||||||
|
[1] = 186313593
|
||||||
|
[2] = 376131208
|
||||||
|
[3] = -1142882515
|
||||||
|
[4] = 1849467770
|
||||||
|
}
|
||||||
|
Color = -14790494
|
||||||
|
DisplayName = "???"
|
||||||
|
LineId = {
|
||||||
|
[1] = -1767394825
|
||||||
|
[2] = 1017529725
|
||||||
|
[3] = -1140094085
|
||||||
|
[4] = 280637596
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Status = {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
XSize = 1
|
||||||
|
YSize = 1
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
-- 1. Configuration & Constants
|
-- 1. Configuration & Constants
|
||||||
local APP_VERSION_TEXT = "v1.0.106"
|
local APP_VERSION_TEXT = "v1.0.107"
|
||||||
|
|
||||||
local CONFIG = {
|
local CONFIG = {
|
||||||
CLOUD_URL = "ws://stabadmin.fse-media.group",
|
CLOUD_URL = "ws://stabadmin.fse-media.group",
|
||||||
|
|||||||
Reference in New Issue
Block a user