1(function () { 2 var overlay = document.getElementById('searchOverlay'); 3 var input = document.getElementById('searchOverlayInput'); 4 var clearBtn = document.getElementById('searchOverlayClear'); 5 var closeBtn = document.getElementById('searchOverlayClose'); 6 var tabsContainer = document.getElementById('searchOverlayTabs'); 7 var emptyState = document.getElementById('searchOverlayEmpty'); 8 var loadingState = document.getElementById('searchOverlayLoading'); 9 var contentArea = document.getElementById('searchOverlayContent'); 10 var noResultsState = document.getElementById('searchOverlayNoResults'); 11 12 var debounceTimer = null; 13 var currentQuery = ''; 14 var activeTab = 'all'; 15 var cachedResults = null; 16 17 var categoryUrls = { 18 series: '/series/', 19 movies: '/peliculas/', 20 informatives: '/noticiero/', 21 podcast: '/podcast/', 22 channels: '/television/' 23 }; 24 25 var categoryLabels = { 26 series: 'Programas', 27 movies: 'PelÃculas', 28 informatives: 'Informativos', 29 podcast: 'Podcast', 30 chapters: 'CapÃtulos', 31 channels: 'Canales' 32 }; 33 34 function openSearch() { 35 overlay.classList.add('is-open'); 36 document.body.style.overflow = 'hidden'; 37 setTimeout(function () { input.focus(); }, 100); 38 } 39 40 function closeSearch() { 41 overlay.classList.remove('is-open'); 42 document.body.style.overflow = ''; 43 } 44 45 function showState(state) { 46 emptyState.style.display = 'none'; 47 loadingState.style.display = 'none'; 48 contentArea.style.display = 'none'; 49 noResultsState.style.display = 'none'; 50 if (state) state.style.display = ''; 51 } 52 53 function stripHtml(html) { 54 var tmp = document.createElement('div'); 55 tmp.innerHTML = html; 56 return tmp.textContent || tmp.innerText || ''; 57 } 58 59 function formatDuration(dur) { 60 if (!dur || dur === '00:00:00') return ''; 61 var parts = dur.split(':'); 62 var h = parseInt(parts[0], 10); 63 var m = parseInt(parts[1], 10); 64 if (h > 0) return h + 'h ' + m + 'min'; 65 return m + ' min'; 66 } 67 68 function formatDate(dateStr) { 69 if (!dateStr) return ''; 70 var d = new Date(dateStr); 71 if (isNaN(d)) return ''; 72 var day = d.getDate(); 73 var months = ['ene', 'feb', 'mar', 'abr', 'may', 'jun', 'jul', 'ago', 'sep', 'oct', 'nov', 'dic']; 74 return day + ' ' + months[d.getMonth()] + ' ' + d.getFullYear(); 75 } 76 77 function isNew(dateStr) { 78 if (!dateStr) return false; 79 var created = new Date(dateStr); 80 var now = new Date(); 81 var diffMs = now - created; 82 return diffMs <= 5 * 24 * 60 * 60 * 1000; 83 } 84 85 function buildCard(item, category) { 86 var href; 87 if (item.url) { 88 href = item.url; 89 } else if (category === 'chapters') { 90 href = '/series/' + (item.series_slug || '') + '/' + item.id; 91 } else { 92 var url = categoryUrls[category] || '/series/'; 93 href = url + (item.slug || ''); 94 } 95 var categoryText = (category === 'chapters' && item.season_name) 96 ? item.season_name 97 : (categoryLabels[category] || category); 98 var description = stripHtml(item.description || ''); 99 var durationHtml = ''; 100 if (category === 'chapters' && item.duration && item.duration !== '00:00:00') { 101 durationHtml = '<span class="search-overlay__card-duration">' + formatDuration(item.duration) + '</span>'; 102 } 103 var chipHtml = ''; 104 if (category === 'channels') { 105 chipHtml = '<span class="search-overlay__card-chip search-overlay__card-chip--live"><span class="pulse-dot"></span> En vivo</span>'; 106 } else if (category === 'chapters' && item.created_at) { 107 if (isNew(item.created_at)) { 108 chipHtml = '<span class="search-overlay__card-chip search-overlay__card-chip--new">Nuevo capÃtulo</span>'; 109 } else { 110 chipHtml = '<span class="search-overlay__card-chip">' + formatDate(item.created_at) + '</span>'; 111 } 112 } 113 114 if (category === 'channels' && item.logo) { 115 return '<a href="' + href + '" class="search-overlay__card search-overlay__card--channel">' + 116 '<div class="search-overlay__card-img-wrapper search-overlay__card-img-wrapper--channel">' + 117 '<img class="search-overlay__card-logo" src="' + item.logo + '" alt="' + (item.title || '').replace(/"/g, '"') + '" loading="lazy">' +
118 chipHtml + 119 '</div>' + 120 '<div class="search-overlay__card-info">' + 121 '<p class="search-overlay__card-title">' + (item.title || '') + '</p>' + 122 '</div>' + 123 '</a>'; 124 } 125 126 return '<a href="' + href + '" class="search-overlay__card">' + 127 '<div class="search-overlay__card-img-wrapper">' + 128 '<img class="search-overlay__card-img" src="' + (item.image || '/images/mini-78.png') + '" alt="' + (item.title || '').replace(/"/g, '"') + '" loading="lazy" onerror="this.src=\'/images/mini-78.png\'">' + 129 durationHtml + 130 chipHtml + 131 '<div class="search-overlay__card-play">' + 132 '<svg viewBox="0 0 24 24" fill="white"><polygon points="5 3 19 12 5 21 5 3"></polygon></svg>' + 133 '</div>' + 134 '</div>' + 135 '<div class="search-overlay__card-info">' + 136 '<p class="search-overlay__card-category">' + categoryText + '</p>' + 137 '<p class="search-overlay__card-title">' + (item.title || '') + '</p>' + 138 '</div>' + 139 '</a>'; 140 } 141 142 function renderResults(data) { 143 if (!data || !data.searches || data.searches.length === 0) { 144 showState(noResultsState); 145 tabsContainer.classList.remove('visible'); 146 return; 147 } 148 149 var sorted = data.searches.slice().sort(function (a, b) { 150 if (a.category === 'channels') return -1; 151 if (b.category === 'channels') return 1; 152 return 0; 153 }); 154 var filteredSearches = sorted; 155 if (activeTab !== 'all') { 156 filteredSearches = sorted.filter(function (s) { 157 return s.category === activeTab; 158 }); 159 } 160 161 if (filteredSearches.length === 0) { 162 showState(noResultsState); 163 return; 164 } 165 166 var html = ''; 167 filteredSearches.forEach(function (section) { 168 var items = section.data && section.data.data ? section.data.data : []; 169 if (items.length === 0) return; 170 171 var total = section.data.total || items.length; 172 var label = categoryLabels[section.category] || section.title; 173 174 html += '<div class="search-overlay__category">'; 175 html += '<div class="search-overlay__category-header">'; 176 html += '<h3 class="search-overlay__category-title">' + label + 177 '<span class="search-overlay__category-count">' + total + '</span></h3>'; 178 html += '</div>'; 179 html += '<div class="search-overlay__grid">'; 180 181 items.forEach(function (item) { 182 html += buildCard(item, section.category); 183 }); 184 185 html += '</div></div>'; 186 }); 187 188 if (!html) { 189 showState(noResultsState); 190 return; 191 } 192 193 contentArea.innerHTML = html; 194 showState(contentArea); 195 tabsContainer.classList.add('visible'); 196 } 197 198 function doSearch(query) { 199 if (query.length < 2) { 200 showState(emptyState); 201 tabsContainer.classList.remove('visible'); 202 cachedResults = null; 203 return; 204 } 205 206 showState(loadingState); 207 tabsContainer.classList.add('visible'); 208 currentQuery = query; 209 210 fetch('/apiv3/searcher/' + encodeURIComponent(query) + '?limit=12') 211 .then(function (res) { return res.json(); }) 212 .then(function (data) { 213 if (query !== currentQuery) return; 214 cachedResults = data; 215 renderResults(data); 216 }) 217 .catch(function () { 218 if (query !== currentQuery) return; 219 showState(noResultsState); 220 }); 221 } 222 223 // Events 224 input.addEventListener('input', function () { 225 var val = this.value.trim(); 226 clearBtn.style.display = val ? '' : 'none'; 227 228 clearTimeout(debounceTimer); 229 debounceTimer = setTimeout(function () { 230 activeTab = 'all';
231 setActiveTabUI('all'); 232 doSearch(val); 233 }, 400); 234 }); 235 236 clearBtn.addEventListener('click', function () { 237 input.value = ''; 238 clearBtn.style.display = 'none'; 239 showState(emptyState); 240 tabsContainer.classList.remove('visible'); 241 cachedResults = null; 242 input.focus(); 243 }); 244 245 closeBtn.addEventListener('click', closeSearch); 246 247 function setActiveTabUI(category) { 248 var tabs = tabsContainer.querySelectorAll('.search-overlay__tab'); 249 tabs.forEach(function (tab) { 250 tab.classList.toggle('is-active', tab.getAttribute('data-category') === category); 251 }); 252 } 253 254 tabsContainer.addEventListener('click', function (e) { 255 var tab = e.target.closest('.search-overlay__tab'); 256 if (!tab) return; 257 activeTab = tab.getAttribute('data-category'); 258 setActiveTabUI(activeTab); 259 if (cachedResults) { 260 renderResults(cachedResults); 261 } 262 }); 263 264 // Bind to all search triggers 265 document.querySelectorAll('.search-overlay-trigger').forEach(function (trigger) { 266 trigger.addEventListener('click', function (e) { 267 e.preventDefault(); 268 e.stopPropagation(); 269 openSearch(); 270 }); 271 }); 272})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.