feat: Search engine channel name + program name today
This commit is contained in:
@@ -0,0 +1,194 @@
|
||||
// ── SEARCH ────────────────────────────────────────────────────────────────────
|
||||
let searchActive = false;
|
||||
let searchQuery = '';
|
||||
|
||||
function toggleSearch() {
|
||||
const bar = document.getElementById('searchBar');
|
||||
const inp = document.getElementById('searchInput');
|
||||
searchActive = !searchActive;
|
||||
bar.classList.toggle('visible', searchActive);
|
||||
if (searchActive) {
|
||||
inp.focus();
|
||||
} else {
|
||||
clearSearch();
|
||||
}
|
||||
}
|
||||
|
||||
function clearSearch() {
|
||||
searchQuery = '';
|
||||
document.getElementById('searchInput').value = '';
|
||||
applySearch('');
|
||||
}
|
||||
|
||||
function onSearchInput(val) {
|
||||
searchQuery = val.trim().toLowerCase();
|
||||
applySearch(searchQuery);
|
||||
}
|
||||
|
||||
function applySearch(q) {
|
||||
if (!q) {
|
||||
// Remettre toutes les chaines
|
||||
renderAll();
|
||||
return;
|
||||
}
|
||||
|
||||
const today = new Date();
|
||||
today.setHours(0,0,0,0);
|
||||
const tomorrow = new Date(today.getTime() + 24*3600000);
|
||||
|
||||
// Filtrer les chaines : nom OU programme aujourd'hui correspond
|
||||
const filtered = channels.filter(ch => {
|
||||
// Match sur le nom de la chaine
|
||||
if (ch.name.toLowerCase().includes(q)) return true;
|
||||
// Match sur les programmes d'aujourd'hui (titre + desc)
|
||||
return (programs[ch.id]||[]).some(p => {
|
||||
if (p.stop < today || p.start > tomorrow) return false;
|
||||
if (p.title.toLowerCase().includes(q)) return true;
|
||||
if (p.desc && p.desc.toLowerCase().includes(q)) return true;
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
||||
renderFiltered(filtered, q);
|
||||
}
|
||||
|
||||
function renderFiltered(filteredChannels, q) {
|
||||
if (isMobile()) {
|
||||
renderMobileFiltered(filteredChannels, q);
|
||||
return;
|
||||
}
|
||||
|
||||
// ── GRID ──
|
||||
const list = document.getElementById('channelsList');
|
||||
const inner = document.getElementById('programsInner');
|
||||
const ROW_H = 80;
|
||||
|
||||
list.innerHTML = '';
|
||||
inner.innerHTML = '';
|
||||
inner.style.width = TOTAL_WIDTH_PX + 'px';
|
||||
inner.style.height = (filteredChannels.length * ROW_H) + 'px';
|
||||
|
||||
// Remettre la now-line
|
||||
const nowLine = document.createElement('div');
|
||||
nowLine.className = 'now-line'; nowLine.id = 'nowLine';
|
||||
nowLine.style.height = (filteredChannels.length * ROW_H) + 'px';
|
||||
inner.appendChild(nowLine);
|
||||
|
||||
const now = new Date();
|
||||
|
||||
filteredChannels.forEach((ch, i) => {
|
||||
// Colonne chaîne
|
||||
const cell = document.createElement('div');
|
||||
cell.className = 'channel-cell';
|
||||
if (ch.icon) {
|
||||
const img = document.createElement('img');
|
||||
img.className = 'channel-logo'; img.src = ch.icon;
|
||||
img.onerror = () => img.replaceWith(makePlaceholder(ch.name));
|
||||
cell.appendChild(img);
|
||||
} else { cell.appendChild(makePlaceholder(ch.name)); }
|
||||
const name = document.createElement('div');
|
||||
name.className = 'channel-name'; name.textContent = ch.name; name.title = ch.name;
|
||||
cell.appendChild(name);
|
||||
cell.addEventListener('click', () => openPip(ch.name, getNowPlaying(ch.id)));
|
||||
list.appendChild(cell);
|
||||
|
||||
// Programmes
|
||||
const row = document.createElement('div');
|
||||
row.className = 'program-row';
|
||||
row.style.cssText = `top:${i*ROW_H}px;position:absolute;left:0;right:0;`;
|
||||
|
||||
(programs[ch.id]||[]).filter(p => p.stop > GRID_START && p.start < GRID_END).forEach(p => {
|
||||
const x = Math.max(0, msToX(p.start.getTime()));
|
||||
const w = Math.min(TOTAL_WIDTH_PX, msToX(p.stop.getTime())) - x;
|
||||
if (w < 2) return;
|
||||
|
||||
const block = document.createElement('div');
|
||||
block.className = 'program-block';
|
||||
|
||||
// Highlight si le programme matche la recherche
|
||||
const matchesProg = p.title.toLowerCase().includes(q) || (p.desc && p.desc.toLowerCase().includes(q));
|
||||
if (matchesProg) block.classList.add('search-match');
|
||||
|
||||
if (p.stop < now) block.classList.add('is-past');
|
||||
if (p.start <= now && p.stop > now) {
|
||||
block.classList.add('is-live');
|
||||
block.style.setProperty('--progress', (((now-p.start)/(p.stop-p.start))*100).toFixed(1)+'%');
|
||||
}
|
||||
block.style.left = x + 'px'; block.style.width = w + 'px';
|
||||
if (w > 40) { const t = document.createElement('div'); t.className = 'program-title'; t.textContent = p.title; block.appendChild(t); }
|
||||
if (w > 80) {
|
||||
const ep = fmtEpisode(p.season, p.episode);
|
||||
const t = document.createElement('div'); t.className = 'program-time';
|
||||
t.textContent = (ep ? ep + ' ' : '') + `${fmtTime(p.start)} — ${fmtTime(p.stop)}`;
|
||||
block.appendChild(t);
|
||||
}
|
||||
block.addEventListener('mouseenter', e => showTooltip(e, p));
|
||||
block.addEventListener('mousemove', e => moveTooltip(e));
|
||||
block.addEventListener('mouseleave', hideTooltip);
|
||||
if (p.start <= now && p.stop > now) {
|
||||
block.addEventListener('click', () => openPip(ch.name, p.title));
|
||||
}
|
||||
row.appendChild(block);
|
||||
});
|
||||
inner.appendChild(row);
|
||||
});
|
||||
|
||||
positionNowLine();
|
||||
}
|
||||
|
||||
function renderMobileFiltered(filteredChannels, q) {
|
||||
const container = document.getElementById('mobileView');
|
||||
container.innerHTML = '';
|
||||
const now = new Date();
|
||||
const winStart = new Date(now.getTime() - 30*60000);
|
||||
const winEnd = new Date(now.getTime() + 4*3600000);
|
||||
|
||||
filteredChannels.forEach(ch => {
|
||||
const progs = (programs[ch.id]||[]).filter(p => p.stop > winStart && p.start < winEnd);
|
||||
if (!progs.length) return;
|
||||
|
||||
const section = document.createElement('div'); section.className = 'mobile-channel';
|
||||
const header = document.createElement('div'); header.className = 'mobile-channel-header';
|
||||
|
||||
if (ch.icon) {
|
||||
const img = document.createElement('img'); img.className = 'channel-logo'; img.src = ch.icon;
|
||||
img.onerror = () => img.replaceWith(makePlaceholder(ch.name));
|
||||
header.appendChild(img);
|
||||
} else { header.appendChild(makePlaceholder(ch.name)); }
|
||||
|
||||
const name = document.createElement('div'); name.className = 'channel-name'; name.textContent = ch.name;
|
||||
header.appendChild(name); section.appendChild(header);
|
||||
|
||||
const list = document.createElement('div'); list.className = 'mobile-programs';
|
||||
progs.forEach(p => {
|
||||
const isLive = p.start <= now && p.stop > now;
|
||||
const isPast = p.stop < now;
|
||||
const dur = Math.round((p.stop - p.start) / 60000);
|
||||
const matchesProg = p.title.toLowerCase().includes(q) || (p.desc && p.desc.toLowerCase().includes(q));
|
||||
const item = document.createElement('div');
|
||||
item.className = 'mobile-program' + (isLive ? ' is-live' : '') + (isPast ? ' is-past' : '') + (matchesProg ? ' search-match' : '');
|
||||
|
||||
if (isLive) {
|
||||
const bar = document.createElement('div'); bar.className = 'mobile-program-progress';
|
||||
bar.style.width = (((now-p.start)/(p.stop-p.start))*100).toFixed(1) + '%';
|
||||
item.appendChild(bar);
|
||||
}
|
||||
|
||||
const timeCol = document.createElement('div'); timeCol.className = 'mobile-program-time';
|
||||
timeCol.innerHTML = `<span>${fmtTime(p.start)}</span><span>${fmtTime(p.stop)}</span>`;
|
||||
item.appendChild(timeCol);
|
||||
|
||||
const info = document.createElement('div'); info.className = 'mobile-program-info';
|
||||
const ep = fmtEpisode(p.season, p.episode);
|
||||
info.innerHTML = `<div class="mobile-program-title">${p.title}</div><div class="mobile-program-dur">${ep ? ep + ' · ' : ''}${dur} min</div>`;
|
||||
item.appendChild(info);
|
||||
|
||||
if (isLive) {
|
||||
const badge = document.createElement('div'); badge.className = 'mobile-live-badge'; badge.textContent = 'LIVE';
|
||||
item.appendChild(badge);
|
||||
}
|
||||
list.appendChild(item);
|
||||
});
|
||||
section.appendChild(list); container.appendChild(section);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user