PageSourceSearch

https://mxplus.tv/assets/js/search-overlay.js?v=1787856984

js mxplus.tv collected 2026-10-01 14:18:09 UTC 10,004 bytes, 272 lines download raw bytes

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, '&quot;') + '" 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, '&quot;') + '" 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.