(function (global) { var state = { categories: [], channels: [], categoryId: "__all__", selected: null, playList: [], playIndex: -1, mounted: false, focusInList: false, }; function $(id) { return document.getElementById(id); } function toast(msg) { if (global.McApp && McApp.toast) McApp.toast(msg); } function showLoading(on) { if (global.McApp && McApp.showLoading) McApp.showLoading(on); } function escapeHtml(s) { return String(s || "") .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """); } function safeUrl(u) { return String(u || "").replace(/'/g, "%27"); } function categoryList() { var all = [{ id: "__all__", name: "Alles", channelCount: 0 }].concat(state.categories || []); var favs = McApi.getFavs(); if (favs.length) { all.splice(1, 0, { id: "__fav__", name: "Favorieten", channelCount: favs.length }); } return all; } /** Bouw categoriechips één keer; update alleen active + behoud focus. */ function renderCategories(opts) { opts = opts || {}; var el = $("tv-cats"); if (!el) return; var all = categoryList(); var activeId = state.categoryId || "__all__"; var prevFocusId = null; var ae = document.activeElement; if (ae && ae.getAttribute && ae.getAttribute("data-cat-id")) { prevFocusId = ae.getAttribute("data-cat-id"); } el.innerHTML = ""; var focusTarget = null; all.forEach(function (c) { var b = document.createElement("button"); b.className = "cat-chip focusable" + (activeId === c.id ? " active" : ""); b.type = "button"; b.setAttribute("data-cat-id", c.id); b.innerHTML = escapeHtml(c.name) + (c.channelCount ? " (" + c.channelCount + ")" : "") + ''; b.addEventListener("click", function () { if (state.categoryId === c.id) { b.focus(); return; } state.categoryId = c.id; state.focusInList = false; setCategoryActive(c.id); loadChannels(c.id, { keepCategoryFocus: true, focusChipId: c.id }); }); b.addEventListener("focus", function () { state.focusInList = false; }); el.appendChild(b); if (c.id === (opts.focusChipId || prevFocusId || activeId)) { focusTarget = b; } }); if (opts.autoFocus || opts.keepCategoryFocus) { setTimeout(function () { if (focusTarget) focusTarget.focus(); }, 30); } } function setCategoryActive(id) { var chips = document.querySelectorAll("#tv-cats .cat-chip"); for (var i = 0; i < chips.length; i++) { var chip = chips[i]; if (chip.getAttribute("data-cat-id") === id) chip.classList.add("active"); else chip.classList.remove("active"); } } function renderChannels(channels, opts) { opts = opts || {}; state.channels = channels || []; var list = $("tv-list"); var detail = $("tv-detail"); if (!list) return; list.innerHTML = ""; if (!state.channels.length) { list.innerHTML = '
Geen zenders
'; } return; } state.channels.forEach(function (ch, idx) { var row = document.createElement("button"); row.className = "ch-row focusable"; row.type = "button"; row.setAttribute("data-idx", String(idx)); var now = ch.nowTitle || ""; var fav = McApi.isFav(ch.streamId); var logo = ch.logoUrl ? 'OK = kijken · Favoriet toevoegen met Favoriet
'; var playBtn = $("tv-play"); var favBtn = $("tv-fav"); if (playBtn) playBtn.onclick = function () { playChannel(ch, idx); }; if (favBtn) { favBtn.onclick = function () { McApi.toggleFav(ch.streamId); renderCategories({ keepCategoryFocus: true, focusChipId: state.categoryId }); loadChannels(state.categoryId || "__all__", { keepCategoryFocus: true, focusChipId: state.categoryId, }); }; } } function selectChannel(ch, idx, opts) { opts = opts || {}; state.selected = ch; state.playIndex = idx; var rows = document.querySelectorAll("#tv-list .ch-row"); for (var i = 0; i < rows.length; i++) { if (i === idx) rows[i].classList.add("active"); else rows[i].classList.remove("active"); } paintDetail(ch, idx); McApi.iptvEpg(ch.streamId).then(function (epg) { if (!state.selected || state.selected.streamId !== ch.streamId) return; ch.nowTitle = epg.now && epg.now.title; ch.nextTitle = epg.next && epg.next.title; var nowEl = $("tv-epg-now"); var nextEl = $("tv-epg-next"); if (nowEl) nowEl.textContent = ch.nowTitle || "Geen EPG beschikbaar"; if (nextEl) nextEl.textContent = ch.nextTitle || "—"; var row = document.querySelector('#tv-list .ch-row[data-idx="' + idx + '"] .ch-epg'); if (row) { row.textContent = ch.nowTitle ? "Nu · " + ch.nowTitle : "Geen programma-info"; } }).catch(function () {}); } function playChannel(ch, idx) { if (!ch) return; showLoading(true); McApi.iptvPlay(ch.streamId) .then(function (info) { showLoading(false); state.playList = state.channels.slice(); state.playIndex = idx; if (global.McApp && McApp.openLivePlayer) { McApp.openLivePlayer({ title: info.name || ch.name, url: info.streamUrl, fallbackUrl: info.fallbackStreamUrl, channels: state.playList, index: idx, }); } }) .catch(function (e) { showLoading(false); toast(e.message || "Live starten mislukt"); }); } function loadChannels(categoryId, opts) { opts = opts || {}; var id = categoryId || "__all__"; state.categoryId = id; setCategoryActive(id); showLoading(true); var req; if (id === "__fav__") { req = McApi.iptvChannels(null, true).then(function (res) { var all = (res && res.channels) || res || []; return (all || []).filter(function (c) { return McApi.isFav(c.streamId); }); }); } else if (id === "__all__") { req = McApi.iptvChannels(null, true).then(function (res) { return (res && res.channels) || res || []; }); } else { req = McApi.iptvChannels(id, true).then(function (res) { return (res && res.channels) || res || []; }); } req .then(function (channels) { showLoading(false); renderChannels(channels, { focusFirstChannel: !!opts.focusFirstChannel, }); if (opts.keepCategoryFocus) { var chip = document.querySelector('#tv-cats .cat-chip[data-cat-id="' + (opts.focusChipId || id) + '"]'); if (chip) { setTimeout(function () { chip.focus(); }, 40); } } }) .catch(function (e) { showLoading(false); toast(e.message || "Zenders laden mislukt"); if (opts.keepCategoryFocus) { var chip = document.querySelector('#tv-cats .cat-chip[data-cat-id="' + (opts.focusChipId || id) + '"]'); if (chip) chip.focus(); } }); } function mount(container) { state.mounted = true; state.focusInList = false; container.innerHTML = 'Kies een zender om te kijken
' + escapeHtml(e.message || "Live TV niet beschikbaar") + "
"; }); } function zap(delta) { if (!state.playList.length) return null; var next = state.playIndex + delta; if (next < 0) next = state.playList.length - 1; if (next >= state.playList.length) next = 0; var ch = state.playList[next]; state.playIndex = next; return ch; } global.McLive = { mount: mount, zap: zap, playChannel: playChannel, getPlayState: function () { return { list: state.playList, index: state.playIndex }; }, }; })(window);