PageSourceSearch

https://www.radiosvh.info/radiosantvicenc/contact

html radiosvh.info collected 2026-09-25 01:03:59 UTC 90,601 bytes, 2,279 lines download raw bytes

1
2
3
4
5<!DOCTYPE html>
6<html lang="ca">
7<head>
8  
9
10  
11    <base hx-preserve="true" href="/"/>
12  
13
14
15  <meta hx-preserve="true" charset="UTF-8">
16  <meta hx-preserve="true" http-equiv="X-UA-Compatible" content="IE=edge">
17  <meta hx-preserve="true" content="width=device-width, initial-scale=1.0" name="viewport">
18
19
20
21  <meta hx-preserve="true" name="author" content="EnaCast">
22  <meta hx-preserve="true" name="fragment" content="!">
23
24  <meta name="apple-mobile-web-app-capable" content="yes">
25  <meta name="mobile-web-app-capable" content="yes">
26  <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
27  <meta name="apple-mobile-web-app-title" content="Ràdio Sant Vicenç">
28  <meta name="application-name" content="Ràdio Sant Vicenç">
29
30  
31    <title>Ràdio Sant Vicenç</title>
32    <meta property="og:title" content="Ràdio Sant Vicenç"/>
33    <meta property="og:description" content="Ràdio Sant Vicenç"/>
34  
35
36  <meta hx-preserve="true" name="theme-color" content="None">
37
38  <link hx-preserve="true" rel="manifest" href="manifest.webmanifest" />
39
40  <link hx-preserve="true" rel="apple-touch-icon" href="/radiosantvicenc/apple-touch-icon.png">
41
42
43  
44  <link hx-preserve="true" rel="icon" href="/radiosantvicenc/favicon-32.png" sizes="32x32">
45  <link hx-preserve="true" rel="icon" href="/radiosantvicenc/favicon-76.png" sizes="76x76">
46  <link hx-preserve="true" rel="icon" href="/radiosantvicenc/favicon-57.png" sizes="57x57">
47  <link hx-preserve="true" rel="icon" href="/radiosantvicenc/favicon-96.png" sizes="96x96">
48  <link hx-preserve="true" rel="icon" href="/radiosantvicenc/favicon-128.png" sizes="128x128">
49  <link hx-preserve="true" rel="icon" href="/radiosantvicenc/favicon-192.png" sizes="192x192">
50  <link hx-preserve="true" rel="icon" href="/radiosantvicenc/favicon-228.png" sizes="228x228">
51
52
53  <link hx-preserve="true" rel="shortcut icon" sizes="196x196" href="/radiosantvicenc/favicon-196.png">
54
55
56  <link hx-preserve="true" rel="apple-touch-icon" href="/radiosantvicenc/favicon-120.png" sizes="120x120">
57  <link hx-preserve="true" rel="apple-touch-icon" href="/radiosantvicenc/favicon-152.png" sizes="152x152">
58  <link hx-preserve="true" rel="apple-touch-icon" href="/radiosantvicenc/favicon-180.png" sizes="180x180">
59
60  <link hx-preserve="true" href="https://fonts.googleapis.com/css?family=Montserrat&display=swap" rel="stylesheet">
61
62  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/leaflet.min.css" crossorigin="">
63
64  <link hx-preserve="true" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.css"
65        integrity="sha256-rByPlHULObEjJ6XQxW/flG2r+22R5dKiAoef+aXWfik=" crossorigin="anonymous"/>
66  <link hx-preserve="true" rel="stylesheet"
67        href="https://cdnjs.cloudflare.com/ajax/libs/jQuery-ui-Slider-Pips/1.11.4/jquery-ui-slider-pips.min.css"
68        integrity="sha256-sKlemxcbJhCtL3BoS3JA7MIORA4bizjl1VpSgpBI4r8=" crossorigin="anonymous"/>
69
70  <link hx-preserve="true" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/photoswipe/4.1.3/photoswipe.min.css"
71        integrity="sha512-yxWNfGm+7EK+hqP2CMJ13hsUNCQfHmOuCuLmOq2+uv/AVQtFAjlAJO8bHzpYGQnBghULqnPuY8NEr7f5exR3Qw=="
72        crossorigin="anonymous"/>
73  <link hx-preserve="true" rel="stylesheet"
74        href="https://cdnjs.cloudflare.com/ajax/libs/photoswipe/4.1.3/default-skin/default-skin.min.css"
75        integrity="sha512-Rck8F2HFBjAQpszOB9Qy+NVLeIy4vUOMB7xrp46edxB3KXs2RxXRguHfrJqNK+vJ+CkfvcGqAKMJTyWYBiBsGA=="
76        crossorigin="anonymous"/>
77
78  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/nprogress.min.css">
79
80  
81  
82
83  <link hx-preserve="true" rel="stylesheet"
84        href="/generated_css/3/radiosantvicenc.css">
85  
86
87
88  <link hx-preserve="true" rel="stylesheet"
89        href="https://enacast.com/static/themes/enacast-nx-ui/fontawesome.min.css">
90  <link hx-preserve="true" rel="stylesheet" href="https://enacast.com/static/frontend_v2/third-party/climacons/climacons-font.css">
91  <link hx-preserve="true" rel="stylesheet" href="https://enacast.com/static/frontend_v2/weather.css">
92
93  <link hx-preserve="true" rel="stylesheet" href="https://enacast.com/static/frontend_v2/player/player-nx-ui.css">
94
95  
96  
97
98</head>
99
100<body hx-ext="head-support">
101
102
103
104  <!--&lt;!&ndash; Global site tag (gtag.js) - Google Analytics &ndash;&gt;-->
105<!--
vendor: 63 bytes, line 105
105<script async src="https://www.googletagmanager.com/gtag/js?id=
105G-TE405X0472
vendor: 11 bytes, line 105
105"></script>
105-->
106<!--
106<script>-->
107<!--  window.dataLayer = window.dataLayer || [];-->
108<!--  function gtag(){dataLayer.push(arguments);}-->
109<!--  gtag('js', new Date());-->
110
111<!--  gtag('config', 'G-TE405X0472');-->
112<!--</script>
112-->
113
114<script defer data-domain="enacast.com" src="https://plausible.io/js/plausible.js"></script>
114
115
116<!-- Matomo EnaCast -->
117<script>
118  var _paq = window._paq = window._paq || [];
119  /* tracker methods like "setCustomDimension" should be called before "trackPageView" */
120  
121  _paq.push(["disableCookies"]);
122  _paq.push(['trackPageView']);
123  _paq.push(['enableLinkTracking']);
124  (function() {
125    var u="//web-analytics.enacast.com/";
126    _paq.push(['setTrackerUrl', u+'matomo.php']);
127    _paq.push(['setSiteId', '10']);
128    var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
129    g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
130  })();
131</script>
131
132<!-- End Matomo Code -->
133
134<!-- Global site tag (gtag.js) - Google Analytics -->
vendor: 64 bytes, lines 134-135
134
135<script async src="https://www.googletagmanager.com/gtag/js?id=
135UA-68940270-23
vendor: 12 bytes, line 135
135"></script>
136<script>
137  
vendor: 134 bytes, lines 137-141
137window.dataLayer = window.dataLayer || [];
138  function gtag(){dataLayer.push(arguments);}
139  gtag('js', new Date());
140
141  gtag('config', '
141UA-31833407-5
vendor: 11 bytes, lines 141-144
141');
142
143  
144  
145</script>
145
146
147
148
149
150
151
152
153    
154
155
156<div class="wrapper">
157  
158    
159    <div class="toast" data-delay="4000" style="position: absolute; top: 25px; right: 25px;">
160      <div class="toast-header">
161        <img src="/radiosantvicenc/favicon-32.png" class="rounded mr-2" alt="">
162        <strong class="mr-auto">Resuming podcast</strong>
163        
164        <button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
165          <span aria-hidden="true">&times;</span>
166        </button>
167      </div>
168    </div>
169    
170  
171
172  
173    
174    
175      
176
177
178
179<div id="header_with_cover"
180     hx-boost="true"  
181     hx-target="#hx-content-div"
182     hx-push-url="true"
183     hx-swap="innerHTML show:window:top"
184     class="nav_header back-img-directive"
185     style="background-image: url(''); background-position: center; transition: background-image 400ms ease-in-out; background-size: cover;">
186  <nav x-data="{ isNavCollapsed: true }" class="navbar navbar-expand-lg navbar-light bg-white">
187    <a class="navbar-brand" href=".">
188        <img id="header_radio_logo"
189                src="https://ik.imagekit.io/7ftrkrun31/enacast/logos/2015/10/13/logo_santvi_W6x5uzw.png?tr=w-512,h-None,fo-auto" style="height: 50px" class="d-inline-block align-top pl-sm-2 pl-md-5">
190    </a>
191    <button x-on:click="isNavCollapsed = !isNavCollapsed" class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav"
192            aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
193      <span class="navbar-toggler-icon"></span>
194    </button>
195
196    <div preload="mouseover" :class="{ 'collapse': isNavCollapsed }" class="collapse navbar-collapse flex-grow-1 text-right" id="navbarNav">
197      <ul class="navbar-nav ml-auto flex-nowrap">
198        <!-- Global Search Button -->
199        <li class="enacast-nav-item pr-4" id="global_search_button">
200          <a x-on:click="$dispatch('open-search-modal')" class="nav-link" href="javascript:void(0);">
201            <i class="fas fa-search"></i> <span class="d-none d-md-inline">Cercar</span>
202          </a>
203        </li>
204        
205        <li class="enacast-nav-item active pr-4" id="menu_item_home">
206          <a x-on:click='$("#navbarNav").collapse("hide");' class="nav-link" href=".">Inici</a>
207        </li>
208
209        
210        <li class="enacast-nav-item pr-4" id="menu_item_programs">
211          <a x-on:click='$("#navbarNav").collapse("hide");' class="nav-link" href="programs"><span>Programes</span></a>
212        </li>
213        
214        
215      
216          <li class="enacast-nav-item pr-4" id="menu_item_albums">
217            <a x-on:click='$("#navbarNav").collapse("hide");' class="nav-link" href="albums"><span>Fotografies</span></a>
218          </li>
219        
220        
221        
222          <li class="enacast-nav-item pr-4" id="menu_item_schedules">
223            <a x-on:click='$("#navbarNav").collapse("hide");' class="nav-link" href="schedules" ><span>Programació</span></a>
224          </li>
225        
226
227        
228
229        
230        
231          <li id="custom_menu_423"
232              class="enacast-nav-item dropdown show pr-4 ">
233            <a class="dropdown-toggle nav-link" style="cursor: pointer;" role="button" id="dropdownMenuLink423"
234               data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
235              Món educatiu
236            </a>
237
238            <div class="dropdown-menu" aria-labelledby="dropdownMenuLink423">
239              
240                
241                  <a x-on:click='$("#navbarNav").collapse("hide");'class="dropdown-item "
242                      href="moneducatiu">Aula Dial
243                  </a>
244                
245              
246            </div>
247          </li>
248        
249          <li id="custom_menu_451"
250              class="enacast-nav-item dropdown show pr-4 ">
251            <a class="dropdown-toggle nav-link" style="cursor: pointer;" role="button" id="dropdownMenuLink451"
252               data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
253              Qui som
254            </a>
255
256            <div class="dropdown-menu" aria-labelledby="dropdownMenuLink451">
257              
258                
259                  <a x-on:click='$("#navbarNav").collapse("hide");'class="dropdown-item "
260                     href="page/qui-som">Història
261                  </a>
262                
263              
264            </div>
265          </li>
266        
267        
268      </ul>
269    </div>
270  </nav>
271</div>
272
273    
274    
275  
276
277  <div id="hx-content-div"
278       hx-boost="true"  
279       hx-target="#hx-content-div"
280       hx-push-url="true"
281       hx-swap="innerHTML show:window:top"  
282       class="container pt-4 px-2 mb-5 pb-5" class="content">
283
284  
285  
286
287<div class="row" style="margin-top: -60px;">
288  <div class="col-md-8 col-12 bg-neutral_100 mx-auto p-5">
289
290    <div class="px-5">
291    
292
293      
294        <div class="row d-flex justify-content-between mt-5">
295          <div>
296            <h2>On trobar-nos</h2>
297
298            <p class="text-black">
299              Ca l'Aragall<br> c/ Barcelona 16-18 primer pis 08620<br> Sant Vicenç dels Horts
300            </p>
301          </div>
302          <div id="conctact_mapid" style="width: 240px; height: 200px;"></div>
303        </div>
304      
305
306    
307      <div class="row mt-5">
308        <div>
309          <h2>Contacta amb nosaltres</h2>
310
311          <p class="text-black">
312            
313              <a href="tel:936769377"
314                 class="text-black">Telf. 936769377</a><br>
315            
316            
317              <a href="mailto:" class="text-black" target="_blank"></a><br>
318            
319          </p>
320        </div>
321      </div>
322    
323
324      <div class="row mt-5">
325        
326          <a class="mr-3"
327             target="_blank"
328             href="https://www.facebook.com/radiosantvicenc">
329            <i class="icon-bg-grey pull-right fab fa-facebook facebook-icon-bg rounded-x icon-bg-grey fa-3x"
330               title="Contacte a Facebook"></i>
331          </a>
332        
333        
334          <a class="mr-3"
335             target="_blank"
336             href="https://twitter.com/radiosvh902">
337            <i class="icon-bg-grey pull-right fab fa-twitter twitter-icon-bg rounded-x icon-bg-grey fa-3x"
338               title="Contacte a Twitter"></i>
339          </a>
340        
341        
342      </div>
343
344
345    </div>
346  </div>
347</div>
348
349<script>
350  window.addEventListener('load', function() {
351    let success = window.location.search.includes("success=1");
352    console.log(success);
353    console.log(window.location.search);
354    if (success) {
355      Swal.fire("Missatge enviat correctament");
356    }
357  });
358
359  const lat = radio.latitude;
360  const lon = radio.longitude;
361
362  let mymap = L.map('conctact_mapid', {
363    zoomControl: false,
364    doubleClickZoom: false,
365  }).setView([lat, lon], 15);
366
367  L.tileLayer('https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token=' + mapbox_public_key, {
368    attribution: 'Map data &copy; <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, <a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery © <a href="https://www.mapbox.com/">Mapbox</a>',
369    maxZoom: 17,
370    id: 'mapbox/streets-v11',
371    tileSize: 512,
372    zoomOffset: -1,
373    accessToken: mapbox_public_key
374  }).addTo(mymap);
375
376  let marker = L.marker([lat, lon]).addTo(mymap);
377
378  function onMapClick(e) {
379    window.open("https://www.google.com/maps/search/?api=1&query=" + lat.toString() + "," + lon.toString(), "_blank")
380  }
381
382  mymap.on('click', onMapClick);
383
384  
385  
386  
387  
388</script>
388
389
390
391
392  </div>
393
394  
395    
396    <footer class="footeroom fixed-bottom">
397      
398
399<div >
400  
401
402
403
404
405
406
407<div x-data class="p-0 px-md-2 container">
408  
409  <div class="enacast-player bg-primary d-flex rounded" style="height: 85px; align-items: center;">
410    <div class="mx-2 mx-md-4">
411      <img class="rounded" style="width: 45px; height: 45px;"
412           alt="Now playing logo"
413           :src="$store.player.now_playing_image">
414    </div>
415
416    <div @click="$store.player.playBack(10)"
417         style="background: none;"
418         class="player-button mr-2"
419         x-show="$store.player.showFwdBackBtn">
420      <i class="fas fa-undo" title="-10s"></i>
421    </div>
422    <div @click="$store.player.play()"
423         class="player-button mr-2"
424         x-show="$store.player.showPlayBtn">
425      <i class="fas fa-play"></i>
426    </div>
427    <div @click="$store.player.pause()"
428         class="player-button mr-2" x-show="$store.player.showPauseBtn">
429      <i class="fas fa-pause"></i>
430    </div>
431    <div @click="$store.player.playForward(30)"
432         style="background: none;"
433         class="player-button mr-2"
434         x-show="$store.player.showFwdBackBtn">
435      <i class="fas fa-undo" style="transform: scaleX(-1); filter: flipH(); float: none;" title="+30s"></i>
436    </div>
437
438    <div @click="$store.player.stop()"
439         class="player-button mr-2"
440         x-show="$store.player.showStopBtn">
441      <i class="fas fa-stop"></i>
442    </div>
443
444    <div id="enacast_now_playing_info" class="ml-1 ml-md-3 col-md-4 mw-0">
445      <div class="d-flex flex-column">
446        <span x-text="$store.player.now_playing_title" class="one-line-text"
447              style="font-size: 0.8125rem; font-weight: bold; color: white; display: inline-block; margin-left: -10px;"></span>
448
449        <span class="" x-show="$store.player.player_status == 'playing_live'" style="margin-left: -10px;">
450        Directe <i class="fas fa-stream icon-sm rounded-x color-default"
451                                     style="transform: rotate(-90deg)"></i><i
452          class="fas fa-stream icon-sm rounded-x color-default" style="transform: rotate(90deg)"></i>
453      </span>
454
455      </div>
456      
457      
458      <div class="pb-1"
459           x-show="$store.player.player_status == 'playing_podcast' || $store.player.player_status == 'paused' || $store.player.current_schedule != undefined">
460        <div id="slider_nx" class=""></div>
461        <div class="d-flex justify-content-between">
462          <span
463            style="font-size: 0.8125rem; font-weight: bold; color: white; display: inline-block; margin-left: -10px;"
464            x-text="$store.player.elapsed_time"></span>
465          <span
466            style="font-size: 0.8125rem; font-weight: bold; color: white; display: inline-block; margin-left: -10px;"
467            x-text="$store.player.remaining_time"></span>
468        </div>
469      </div>
470    </div>
471
472    <div class="mx-3 d-none d-md-block" x-show="$store.player.now_playing_podcast_id">
473      
474      <social-share-buttons podcast="now_playing_podcast_id" inverted="true"></social-share-buttons>
475    </div>
476
477    
478    <div class="ml-auto p-2 d-none d-lg-block">
479      <span style="font-size: 0.5rem; opacity: 0.75;">Powered by</span>
480      <a href="https://enacast.com">
481        <img src="https://enacast.com/static/enacast_logos/enacast_negative.png" style="height: 15px; margin-bottom: 4px;"></a>
482    </div>
483
484    
485    <div class="d-none">
486      <audio id="enacast-player">
487        <source src="" type="audio/mpeg">
488      </audio>
489    </div>
490  </div>
491</div>
492
493</div>
494
495    </footer>
496    
497  
498
499</div>
500
501<!-- Root element of PhotoSwipe. Must have class pswp. -->
502<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">
503
504    <!-- Background of PhotoSwipe.
505         It's a separate element as animating opacity is faster than rgba(). -->
506    <div class="pswp__bg"></div>
507
508    <!-- Slides wrapper with overflow:hidden. -->
509    <div class="pswp__scroll-wrap">
510
511        <!-- Container that holds slides.
512            PhotoSwipe keeps only 3 of them in the DOM to save memory.
513            Don't modify these 3 pswp__item elements, data is added later on. -->
514        <div class="pswp__container">
515            <div class="pswp__item"></div>
516            <div class="pswp__item"></div>
517            <div class="pswp__item"></div>
518        </div>
519
520        <!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->
521        <div class="pswp__ui pswp__ui--hidden">
522
523            <div class="pswp__top-bar">
524
525                <!--  Controls are self-explanatory. Order can be changed. -->
526
527                <div class="pswp__counter"></div>
528
529                <button class="pswp__button pswp__button--close" title="Close (Esc)"></button>
530
531                <button class="pswp__button pswp__button--share" title="Share"></button>
532
533                <button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>
534
535                <button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>
536
537                <!-- Preloader demo https://codepen.io/dimsemenov/pen/yyBWoR -->
538                <!-- element will get class pswp__preloader--active when preloader is running -->
539                <div class="pswp__preloader">
540                    <div class="pswp__preloader__icn">
541                      <div class="pswp__preloader__cut">
542                        <div class="pswp__preloader__donut"></div>
543                      </div>
544                    </div>
545                </div>
546            </div>
547
548            <div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
549                <div class="pswp__share-tooltip"></div>
550            </div>
551
552            <button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
553            </button>
554
555            <button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
556            </button>
557
558            <div class="pswp__caption">
559                <div class="pswp__caption__center"></div>
560            </div>
561
562        </div>
563
564    </div>
565
566</div>
567
568
569
570<div id="global-search-modal" x-data="globalSearchComponent" @open-search-modal.window="openModal" class="modal fade" tabindex="-1" aria-hidden="true">
571  <div class="modal-dialog modal-lg modal-dialog-scrollable" style="max-width: 70%;">
572    <div class="modal-content ">
573      <div class="modal-body " style="max-height: 80vh; overflow-y: auto;">
574        <div class="form-group">
575          <div class="input-group mb-3">
576            <input x-model="search_input" @input.debounce.500ms="fetchSearchResults" type="text" class="form-control form-control-lg"
577                  placeholder="Search..." autofocus x-ref="searchInput">
578          </div>
579        </div>
580
581        <!-- Tabs -->
582        <ul class="nav nav-tabs " id="searchTabs" role="tablist">
583          <li class="nav-item" role="presentation">
584            <a class="nav-link  active" id="all-tab" data-toggle="tab" href="#all" role="tab"
585               aria-controls="all" aria-selected="true" @click="activeTab = 'all'">
586              Tots <span class="badge badge-light" x-text="total_results"></span>
587            </a>
588          </li>
589          <li class="nav-item" role="presentation">
590            <a class="nav-link " id="podcasts-tab" data-toggle="tab" href="#podcasts" role="tab"
591               aria-controls="podcasts" aria-selected="false" @click="activeTab = 'podcasts'">
592              Podcasts <span class="badge badge-light" x-text="podcasts_count"></span>
593            </a>
594          </li>
595          <li class="nav-item" role="presentation">
596            <a class="nav-link " id="moments-tab" data-toggle="tab" href="#moments" role="tab"
597               aria-controls="moments" aria-selected="false" @click="activeTab = 'moments'">
598              Moments <span class="badge badge-light" x-text="moments_count"></span>
599            </a>
600          </li>
601          <li class="nav-item" role="presentation">
602            <a class="nav-link " id="news-tab" data-toggle="tab" href="#news" role="tab"
603               aria-controls="news" aria-selected="false" @click="activeTab = 'news'">
604              Notícies <span class="badge badge-light" x-text="news_count"></span>
605            </a>
606          </li>
607        </ul>
608
609        <!-- Tab contents -->
610        <div class="tab-content pt-3" id="searchTabsContent">
611          <!-- Loading indicator -->
612          <div x-show="isLoading" class="text-center py-5">
613            <div class="spinner-border text-primary" role="status">
614              <span class="sr-only">Loading...</span>
615            </div>
616          </div>
617
618          <!-- No results message -->
619          <div x-show="!isLoading && search_input.length >= 3 && total_results === 0" class="text-center py-5">
620            <p class="text-muted">No results found for "<span x-text="search_input"></span>"</p>
621          </div>
622
623          <!-- All Results Tab -->
624          <div class="tab-pane fade show active" id="all" role="tabpanel" aria-labelledby="all-tab"
625               x-show="!isLoading && total_results > 0">
626            <div class="search-results">
627
628              <!-- Podcasts -->
629              <div x-show="podcasts_results.length > 0" class="result-section mb-4">
630                <h4>Podcasts</h4>
631                <div class="row">
632                  <template x-for="podcast in podcasts_results.slice(0, 6)">
633                    <div class="col-12 col-md-6 col-xl-4 mb-3">
634                      <div class="p-2 h-100 border rounded">
635                        <div class="d-flex overflow-hidden">
636                          <div class="flex-shrink-0" style="width: 80px; height: 80px;">
637                            <img class="img-fluid rounded"
638                                 style="margin-right: 1rem; width: 80px; height: 80px; object-fit: cover;"
639                                 x-bind:src="podcast.document.thumbnail || 'https://ik.imagekit.io/7ftrkrun31/enacast/logos/2022/02/11/icon_8YNoPw6.png?tr=w-256,h-None,fo-auto'"
640                                 :onerror="'this.src=\\'https://ik.imagekit.io/7ftrkrun31/enacast/logos/2022/02/11/icon_8YNoPw6.png?tr=w-256,h-None,fo-auto\\''"
641                                 alt="">
642                          </div>
643                          <div class="flex-grow-1 overflow-hidden ml-3 ms-3 d-flex flex-column">
644                            <h5 class="mt-0 mb-1 text-truncate" style="max-width: 100%;">
645                              <a :href="window.radioPrefix + 'programs/' + podcast.document.programCodename + '/' + podcast.document.podcastPublicID"
646                                 @click.prevent="$('#global-search-modal').modal('hide'); htmx.ajax('GET', window.radioPrefix + 'programs/' + podcast.document.programCodename + '/' + podcast.document.podcastPublicID, {target: '#hx-content-div', pushURL: true})"
647                                 class="text-decoration-none text-dark">
648                                <span x-text="podcast.document.podcastTitle"></span>
649                              </a>
650                            </h5>
651                            <p class="small mb-1 text-truncate" style="max-width: 100%;">
652                              <span x-html="podcast.highlights[0]?.snippet || podcast.document.transcription?.substring(0, 100) + '...'" class="d-block" style="display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; text-overflow: ellipsis;"></span>
653                            </p>
654                            <p class="small text-muted mb-1">
655                              <i class="far fa-calendar-alt"></i>
656                              <span x-text="formatDate(podcast.document.recorded_ts)"></span>
657                            </p>
658                            <div class="mt-auto">
659                              <a @click="$store.player.play_podcast(podcast.document)" href="javascript:void(0);" class="btn btn-sm btn-primary me-1">
660                                <i class="fas fa-play text-white"></i> Play
661                              </a>
662                              <a :href="window.radioPrefix + 'programs/' + podcast.document.programCodename + '/' + podcast.document.podcastPublicID"
663                                 @click.prevent="$('#global-search-modal').modal('hide'); htmx.ajax('GET', window.radioPrefix + 'programs/' + podcast.document.programCodename + '/' + podcast.document.podcastPublicID, {target: '#hx-content-div', pushURL: true})"
664                                 class="btn btn-sm">
665                                <i class="fas fa-info-circle"></i> Details
666                              </a>
667                            </div>
668                          </div>
669                        </div>
670                      </div>
671                    </div>
672                  </template>
673                </div>
674                <div x-show="podcasts_results.length > 6" class="text-right mt-2">
675                  <a href="#podcasts" data-toggle="tab" @click="activeTab = 'podcasts'" class="">View all podcasts &raquo;</a>
676                </div>
677              </div>
678
679              <!-- Moments -->
680              <div x-show="moments_results.length > 0" class="result-section mb-4">
681                <h4>Moments</h4>
682                <div class="row">
683                  <template x-for="moment in moments_results.slice(0, 6)">
684                    <div class="col-12 col-md-6 col-xl-4 mb-3">
685                      <div class="p-2 h-100 border rounded">
686                        <div class="d-flex overflow-hidden">
687                          <div class="flex-shrink-0" style="width: 80px; height: 80px;">
688                            <img class="img-fluid rounded"
689                                 style="margin-right: 1rem; width: 80px; height: 80px; object-fit: cover;"
690                                 x-bind:src="moment.document.thumbnail || 'https://ik.imagekit.io/7ftrkrun31/enacast/logos/2022/02/11/icon_8YNoPw6.png?tr=w-256,h-None,fo-auto'"
691                                 :onerror="'this.src=\\'https://ik.imagekit.io/7ftrkrun31/enacast/logos/2022/02/11/icon_8YNoPw6.png?tr=w-256,h-None,fo-auto\\''"
692                                 alt="">
693                          </div>
694                          <div class="flex-grow-1 overflow-hidden ml-3 ms-3 d-flex flex-column">
695                            <h5 class="mt-0 mb-1 text-truncate" style="max-width: 100%;">
696                              <a :href="window.radioPrefix + 'programs/' + moment.document.programCodename + '/' + moment.document.podcastPublicID"
697                                 @click.prevent="$('#global-search-modal').modal('hide'); htmx.ajax('GET', window.radioPrefix + 'programs/' + moment.document.programCodename + '/' + moment.document.podcastPublicID, {target: '#hx-content-div', pushURL: true})"
698                                 class="text-decoration-none text-dark">
699                                <span x-text="moment.document.podcastTitle"></span>
700                              </a>
701                            </h5>
702                            <p class="small mb-1 text-truncate" style="max-width: 100%;">
703                              <span x-html="moment.highlights[0]?.snippet || moment.document.transcription?.substring(0, 100) + '...'" class="d-block" style="display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; text-overflow: ellipsis;"></span>
704                            </p>
705                            <p class="small text-muted mb-1">
706                              <i class="far fa-calendar-alt"></i>
707                              <span x-text="formatDate(moment.document.recorded_ts)"></span>
708                            </p>
709                            <div class="mt-auto">
710                              <a @click="$store.player.play_podcast_at_time(moment.document, moment.document.start)" href="javascript:void(0);" class="btn btn-sm btn-primary me-1">
711                                <i class="fas fa-play text-white"></i> Play
712                              </a>
713                              <a :href="window.radioPrefix + 'programs/' + moment.document.programCodename + '/' + moment.document.podcastPublicID"
714                                 @click.prevent="$('#global-search-modal').modal('hide'); htmx.ajax('GET', window.radioPrefix + 'programs/' + moment.document.programCodename + '/' + moment.document.podcastPublicID, {target: '#hx-content-div', pushURL: true})"
715                                 class="btn btn-sm">
716                                <i class="fas fa-info-circle"></i> Details
717                              </a>
718                            </div>
719                          </div>
720                        </div>
721                      </div>
722                    </div>
723                  </template>
724                </div>
725                <div x-show="moments_results.length > 6" class="text-right mt-2">
726                  <a href="#moments" data-toggle="tab" @click="activeTab = 'moments'" class="">View all moments &raquo;</a>
727                </div>
728              </div>
729
730              <!-- News -->
731              <div x-show="news_results.length > 0" class="result-section mb-4">
732                <h4>Notícies</h4>
733                <div class="row">
734                  <template x-for="news in news_results.slice(0, 6)">
735                    <div class="col-12 col-md-6 col-xl-4 mb-3">
736                      <div class="p-2 h-100 border rounded">
737                        <div class="d-flex overflow-hidden">
738                          <div class="flex-shrink-0" style="width: 80px; height: 80px;">
739                            <img class="img-fluid rounded"
740                                 style="margin-right: 1rem; width: 80px; height: 80px; object-fit: cover;"
741                                 x-bind:src="news.document.main_image || 'https://ik.imagekit.io/7ftrkrun31/enacast/logos/2022/02/11/icon_8YNoPw6.png?tr=w-256,h-None,fo-auto'"
742                                 :onerror="'this.src=\\'https://ik.imagekit.io/7ftrkrun31/enacast/logos/2022/02/11/icon_8YNoPw6.png?tr=w-256,h-None,fo-auto\\''"
743                                 alt="">
744                          </div>
745                          <div class="flex-grow-1 overflow-hidden ml-3 ms-3 d-flex flex-column">
746                            <h5 class="mt-0 mb-1" x-text="news.document.headline"></h5>
747                            <div class="small mb-2 text-truncate" style="display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; text-overflow: ellipsis;">
748                              <span x-html="news.highlights[0]?.snippet || news.document.summary"></span>
749                            </div>
750                            <p class="small text-muted mb-1">
751                              <i class="far fa-calendar-alt"></i>
752                              <span x-text="formatDate(news.document.datetime_to_publish_ts)"></span>
753                            </p>
754                            <div class="mt-auto">
755                              <a :href="window.radioPrefix + 'news/' + news.document.slug"
756                                 @click.prevent="$('#global-search-modal').modal('hide'); htmx.ajax('GET', window.radioPrefix + 'news/' + news.document.slug, {target: '#hx-content-div', pushURL: true})"
757                                 class="btn btn-sm btn-primary">Llegir més</a>
758                            </div>
759                          </div>
760                        </div>
761                      </div>
762                    </div>
763                  </template>
764                </div>
765                <div x-show="news_results.length > 6" class="text-right mt-2">
766                  <a href="#news" data-toggle="tab" @click="activeTab = 'news'" class="">View all news &raquo;</a>
767                </div>
768              </div>
769            </div>
770          </div>
771
772
773          <!-- Podcasts Tab -->
774          <div class="tab-pane fade" id="podcasts" role="tabpanel" aria-labelledby="podcasts-tab"
775               x-show="!isLoading && podcasts_results.length > 0">
776            <div class="row">
777              <template x-for="podcast in podcasts_results">
778                <div class="col-12 col-md-6 col-xl-4 mb-3">
779                  <div class="p-2 h-100 border rounded">
780                    <div class="d-flex overflow-hidden">
781                      <div class="flex-shrink-0" style="width: 80px; height: 80px;">
782                        <img class="img-fluid rounded"
783                             style="margin-right: 1rem; width: 80px; height: 80px; object-fit: cover;"
784                             x-bind:src="podcast.document.thumbnail || 'https://ik.imagekit.io/7ftrkrun31/enacast/logos/2022/02/11/icon_8YNoPw6.png?tr=w-256,h-None,fo-auto'"
785                             :onerror="'this.src=\\'https://ik.imagekit.io/7ftrkrun31/enacast/logos/2022/02/11/icon_8YNoPw6.png?tr=w-256,h-None,fo-auto\\''"
786                             alt="">
787                      </div>
788                      <div class="flex-grow-1 overflow-hidden ml-3 ms-3 d-flex flex-column">
789                        <h5 class="mt-0 mb-1 text-truncate" style="max-width: 100%;">
790                          <a :href="window.radioPrefix + 'programs/' + podcast.document.programCodename + '/' + podcast.document.podcastPublicID"
791                             @click.prevent="$('#global-search-modal').modal('hide'); htmx.ajax('GET', window.radioPrefix + 'programs/' + podcast.document.programCodename + '/' + podcast.document.podcastPublicID, {target: '#hx-content-div', pushURL: true})"
792                             class="text-decoration-none text-dark">
793                            <span x-text="podcast.document.podcastTitle"></span>
794                          </a>
795                        </h5>
796                        <p class="small mb-1 text-truncate" style="max-width: 100%;">
797                          <span x-html="podcast.highlights[0]?.snippet || podcast.document.transcription?.substring(0, 100) + '...'" class="d-block" style="display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; text-overflow: ellipsis;"></span>
798                        </p>
799                        <p class="small text-muted mb-1">
800                          <i class="far fa-calendar-alt"></i>
801                          <span x-text="formatDate(podcast.document.recorded_ts)"></span>
802                        </p>
803                        <div class="mt-auto">
804                          <a @click="$store.player.play_podcast(podcast.document)" href="javascript:void(0);" class="btn btn-sm btn-primary me-1">
805                            <i class="fas fa-play text-white"></i> Play
806                          </a>
807                          <a :href="window.radioPrefix + 'programs/' + podcast.document.programCodename + '/' + podcast.document.podcastPublicID"
808                             @click.prevent="$('#global-search-modal').modal('hide'); htmx.ajax('GET', window.radioPrefix + 'programs/' + podcast.document.programCodename + '/' + podcast.document.podcastPublicID, {target: '#hx-content-div', pushURL: true})"
809                             class="btn btn-sm">
810                            <i class="fas fa-info-circle"></i> Details
811                          </a>
812                        </div>
813                      </div>
814                    </div>
815                  </div>
816                </div>
817              </template>
818            </div>
819          </div>
820
821          <!-- Moments Tab -->
822          <div class="tab-pane fade" id="moments" role="tabpanel" aria-labelledby="moments-tab"
823               x-show="!isLoading && moments_results.length > 0">
824            <div class="row">
825              <template x-for="moment in moments_results">
826                <div class="col-12 col-md-6 col-xl-4 mb-3">
827                  <div class="p-2 h-100 border rounded">
828                    <div class="d-flex overflow-hidden">
829                      <div class="flex-shrink-0" style="width: 80px; height: 80px;">
830                        <img class="img-fluid rounded"
831                             style="margin-right: 1rem; width: 80px; height: 80px; object-fit: cover;"
832                             x-bind:src="moment.document.thumbnail || 'https://ik.imagekit.io/7ftrkrun31/enacast/logos/2022/02/11/icon_8YNoPw6.png?tr=w-256,h-None,fo-auto'"
833                             :onerror="'this.src=\\'https://ik.imagekit.io/7ftrkrun31/enacast/logos/2022/02/11/icon_8YNoPw6.png?tr=w-256,h-None,fo-auto\\''"
834                             alt="">
835                      </div>
836                      <div class="flex-grow-1 overflow-hidden ml-3 ms-3 d-flex flex-column">
837                        <h5 class="mt-0 mb-1 text-truncate" style="max-width: 100%;">
838                          <a :href="window.radioPrefix + 'programs/' + moment.document.programCodename + '/' + moment.document.podcastPublicID"
839                             @click.prevent="$('#global-search-modal').modal('hide'); htmx.ajax('GET', window.radioPrefix + 'programs/' + moment.document.programCodename + '/' + moment.document.podcastPublicID, {target: '#hx-content-div', pushURL: true})"
840                             class="text-decoration-none text-dark">
841                            <span x-text="moment.document.podcastTitle"></span>
842                          </a>
843                        </h5>
844                        <p class="small mb-1 text-truncate" style="max-width: 100%;">
845                          <span x-html="moment.highlights[0]?.snippet || moment.document.transcription?.substring(0, 100) + '...'" class="d-block" style="display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; text-overflow: ellipsis;"></span>
846                        </p>
847                        <p class="small text-muted mb-1">
848                          <i class="far fa-calendar-alt"></i>
849                          <span x-text="formatDate(moment.document.recorded_ts)"></span>
850                        </p>
851                        <div class="mt-auto">
852                          <a @click="$store.player.play_podcast_at_time(moment.document, moment.document.start)" href="javascript:void(0);" class="btn btn-sm btn-primary me-1">
853                            <i class="fas fa-play text-white"></i> Play
854                          </a>
855                          <a :href="window.radioPrefix + 'programs/' + moment.document.programCodename + '/' + moment.document.podcastPublicID"
856                             @click.prevent="$('#global-search-modal').modal('hide'); htmx.ajax('GET', window.radioPrefix + 'programs/' + moment.document.programCodename + '/' + moment.document.podcastPublicID, {target: '#hx-content-div', pushURL: true})"
857                             class="btn btn-sm">
858                            <i class="fas fa-info-circle"></i> Details
859                          </a>
860                        </div>
861                      </div>
862                    </div>
863                  </div>
864                </div>
865              </template>
866            </div>
867          </div>
868
869          <!-- News Tab -->
870          <div class="tab-pane fade" id="news" role="tabpanel" aria-labelledby="news-tab"
871               x-show="!isLoading && news_results.length > 0">
872            <div class="row">
873              <template x-for="news in news_results">
874                <div class="col-12 col-md-6 col-xl-4 mb-3">
875                  <div class="p-2 h-100 border rounded">
876                    <div class="d-flex overflow-hidden">
877                      <div class="flex-shrink-0" style="width: 80px; height: 80px;">
878                        <img class="img-fluid rounded"
879                             style="margin-right: 1rem; width: 80px; height: 80px; object-fit: cover;"
880                             x-bind:src="news.document.main_image || 'https://ik.imagekit.io/7ftrkrun31/enacast/logos/2022/02/11/icon_8YNoPw6.png?tr=w-256,h-None,fo-auto'"
881                             :onerror="'this.src=\\'https://ik.imagekit.io/7ftrkrun31/enacast/logos/2022/02/11/icon_8YNoPw6.png?tr=w-256,h-None,fo-auto\\''"
882                             alt="">
883                      </div>
884                      <div class="flex-grow-1 overflow-hidden ml-3 ms-3 d-flex flex-column">
885                        <h5 class="mt-0 mb-1 text-truncate" style="max-width: 100%;" x-text="news.document.headline"></h5>
886                        <div class="small mb-2 text-truncate" style="display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; text-overflow: ellipsis;">
887                          <span x-html="news.highlights[0]?.snippet || news.document.summary"></span>
888                        </div>
889                        <p class="small text-muted mb-1">
890                          <i class="far fa-calendar-alt"></i>
891                          <span x-text="formatDate(news.document.datetime_to_publish_ts)"></span>
892                        </p>
893                        <div class="mt-auto">
894                          <a :href="window.radioPrefix + 'news/' + news.document.slug"
895                             @click.prevent="$('#global-search-modal').modal('hide'); htmx.ajax('GET', window.radioPrefix + 'news/' + news.document.slug, {target: '#hx-content-div', pushURL: true})"
896                             class="btn btn-sm btn-primary">Llegir més</a>
897                        </div>
898                      </div>
899                    </div>
900                  </div>
901                </div>
902              </template>
903            </div>
904          </div>
905
906        </div>
907
908        <div class="search-meta text-muted small mt-3" x-show="search_input.length >= 3 && !isLoading">
909          <span x-text="total_results"></span> results found in <span x-text="search_time_ms"></span>ms
910        </div>
911      </div>
912    </div>
913  </div>
914</div>
915
916<script>
917  document.addEventListener('alpine:init', () => {
918    Alpine.data('globalSearchComponent', () => ({
919      search_input: '',
920      isLoading: false,
921      activeTab: 'all',
922
923      // Results containers
924      podcasts_results: [],
925      moments_results: [],
926      news_results: [],
927
928      // Counts
929      podcasts_count: 0,
930      moments_count: 0,
931      news_count: 0,
932
933      search_time_ms: 0,
934
935      openModal() {
936        $('#global-search-modal').modal('show');
937        this.$nextTick(() => {
938          this.$refs.searchInput.focus();
939        });
940      },
941
942      clearSearch() {
943        this.search_input = '';
944        this.podcasts_results = [];
945        this.moments_results = [];
946        this.news_results = [];
947        this.podcasts_count = 0;
948        this.moments_count = 0;
949        this.news_count = 0;
950      },
951
952      get total_results() {
953        return this.podcasts_count + this.moments_count + this.news_count;
954      },
955
956      formatDate(ts) {
957        if (!ts) return '';
958        // If it's a string, try to parse as ISO, else treat as timestamp (seconds)
959        let d = typeof ts === 'string' ? new Date(ts) : new Date(ts * 1000);
960        if (isNaN(d.getTime())) return '';
961        return d.toLocaleDateString();
962      },
963
964      async fetchSearchResults() {
965        if (this.search_input.length < 3) {
966          this.clearSearch();
967          return;
968        }
969
970        this.isLoading = true;
971
972        try {
973          // Initialize Typesense client if not already done
974          initTypesense();
975
976          // Search parameters
977          const baseParams = {
978            'q': this.search_input,
979            'filter_by': 'radioCodename:radiosantvicenc',
980            'per_page': 30,
981            'page': 1
982          };
983
984          // Run searches in parallel
985          const [podcastsResults, momentsResults, newsResults] = await Promise.all([
986            // Podcasts search (full podcasts)
987            typesense_client.collections('enacast___podcasts').documents().search({
988              ...baseParams,
989              'query_by': 'podcastTitle,podcastDescription',
990              'sort_by': 'recorded_ts:desc'
991            }),
992
993            // Moments search (podcast chunks/segments)
994            typesense_client.collections('enacast___podcasts-chunks').documents().search({
995              ...baseParams,
996              'query_by': 'transcription',
997              'sort_by': 'recorded_ts:desc'
998            }),
999
1000            // News search
1001            typesense_client.collections('enacast___news').documents().search({
1002              ...baseParams,
1003              'query_by': 'headline,body',
1004              'sort_by': 'datetime_to_publish_ts:desc'
1005            })
1006          ]);
1007
1008          // Set results from the API response
1009          this.podcasts_results = podcastsResults.hits || [];
1010          this.podcasts_count = podcastsResults.found || 0;
1011
1012          this.moments_results = momentsResults.hits || [];
1013          this.moments_count = momentsResults.found || 0;
1014
1015          this.news_results = newsResults.hits || [];
1016          this.news_count = newsResults.found || 0;
1017
1018          // Use the highest search time for display
1019          this.search_time_ms = Math.max(
1020            podcastsResults.search_time_ms || 0,
1021            momentsResults.search_time_ms || 0,
1022            newsResults.search_time_ms || 0
1023          );
1024        } catch (error) {
1025          console.error('Search error:', error);
1026        } finally {
1027          this.isLoading = false;
1028        }
1029      }
1030    }));
1031
1032    // Wait for Alpine to be ready so $store is available
1033    $('#global-search-modal').on('shown.bs.modal', function () {
1034      // Use Alpine's $data to get the component and focus the input
1035      let alpineComponent = Alpine.$data(document.getElementById('global-search-modal'));
1036      if (alpineComponent && alpineComponent.$refs && alpineComponent.$refs.searchInput) {
1037        alpineComponent.$refs.searchInput.focus();
1038      }
1039    });
1040  });
1041
1042  document.addEventListener('keydown', function(e) {
1043    // Check for Ctrl+K (Windows/Linux) or Cmd+K (Mac)
1044    if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'k') {
1045      e.preventDefault();
1046      window.dispatchEvent(new CustomEvent('open-search-modal'));
1047    }
1048  });
1049</script>
1049
1050
1051<script>
1052  $('#global-search-modal').on('shown.bs.modal', function () {
1053    $(this).find('input[type="text"]').trigger('focus');
1054  });
1055</script>
1055
1056
1057
1058<script>window.radioPrefix = '';</script>
1058
1059
1060
1061
1062<script
1063  src="https://code.jquery.com/jquery-3.4.1.min.js"
1064  integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo="
1065  crossorigin="anonymous"></script>
vendor: 1 bytes, line 1065
1065
1066<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/umd/popper.min.js"
1067        integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo"
1068        crossorigin="anonymous"></script>
1068
1069
1070<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"
1071        integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6"
1072        crossorigin="anonymous"></script>
1072
1073
1074
1075
1076<script>
1077  const typesense_host = "typesense-hgs884w80gc8gc80ssk84coo";
1078  const typesense_port = "8108";
1079  const typesense_protocol = "http";
1080  const typesense_search_api_key = "azX92P4d73j0FsXmiytDe3IcFYzsiOLv";
1081  let typesense_client = null;
1082
1083  function initTypesense() {
1084    if (typesense_client) {
1085      return;
1086    }
1087    typesense_client = new Typesense.Client({
1088      'nodes': [{
1089        'host': typesense_host,
1090        'port': typesense_port,
1091        'protocol': typesense_protocol,
1092      }],
1093      'apiKey': typesense_search_api_key,
1094      'connectionTimeoutSeconds': 2
1095    });
1096  }
1097</script>
1097
1098
1099<script>
1100  document.addEventListener('alpine:init', () => {
1101      const programs_names_list = [];
1102  
1103    programs_names_list.push('El Sacacorchos');
1104  
1105    programs_names_list.push('Caña activa');
1106  
1107    programs_names_list.push('Informatiu - Sant Vicenç al dia');
1108  
1109    programs_names_list.push('Més que estrenes.');
1110  
1111    programs_names_list.push('Sal y pimienta');
1112  
1113    programs_names_list.push('Batibull');
1114  
1115    programs_names_list.push('La pirámide');
1116  
1117    programs_names_list.push('Hell Awaits Radio  ');
1118  
1119    programs_names_list.push('Años luz');
1120  
1121    programs_names_list.push('El Conte');
1122  
1123    programs_names_list.push('Moments clàssics');
1124  
1125    programs_names_list.push('Fem esport');
1126  
1127    programs_names_list.push('Radio Games');
1128  
1129    programs_names_list.push('Jazz club de nit');
1130  
1131    programs_names_list.push('Collita pròpia');
1132  
1133    programs_names_list.push('Voltes al dial');
1134  
1135    programs_names_list.push('Vicentinades');
1136  
1137    programs_names_list.push('Barris');
1138  
1139    programs_names_list.push('Tomàquets i Pebrots');
1140  
1141    programs_names_list.push('A dues bandes');
1142  
1143    programs_names_list.push('Recerques');
1144  
1145    programs_names_list.push('Rock Parking Lot');
1146  
1147    programs_names_list.push('Heavy MPL. ');
1148  
1149    programs_names_list.push('10 contra 1');
1150  
1151    programs_names_list.push('El jardí de la memòria');
1152  
1153    programs_names_list.push('Pasarela Shopper');
1154  
1155    programs_names_list.push('MKC (Més que cinema)');
1156  
1157    programs_names_list.push('Encuentros');
1158  
1159    programs_names_list.push('De bon rotllo');
1160  
1161    programs_names_list.push('A quatre bandes');
1162  
1163    programs_names_list.push('Mix de Trébol');
1164  
1165    programs_names_list.push('Ple municipal');
1166  
1167    programs_names_list.push('The Psychedelic Years');
1168  
1169    programs_names_list.push('2gether');
1170  
1171    programs_names_list.push('Rebel·lió a la granja');
1172  
1173    programs_names_list.push('Campanya de joguines (10 - 12 h)');
1174  
1175    programs_names_list.push('Campanya de joguines (12 - 14.30 h)');
1176  
1177    programs_names_list.push('Campanya de joguines (16 - 18 h)');
1178  
1179    programs_names_list.push('Campanya de joguines (18 - 20 h)');
1180  
1181    programs_names_list.push('Festa Major d&#x27;Hivern 2013 (part 1)');
1182  
1183    programs_names_list.push('Festa Major d&#x27;Hivern 2013 (part 2)');
1184  
1185    programs_names_list.push('Festa Major d&#x27;Hivern 2013 (part 3)');
1186  
1187    programs_names_list.push('Festa Major d&#x27;Hivern 2013 (part 4)');
1188  
1189    programs_names_list.push('Sábado sabadete ');
1190  
1191    programs_names_list.push('Sábado sabadete (2ª part)');
1192  
1193    programs_names_list.push('Psicologia al dia');
1194  
1195    programs_names_list.push('Aula dial');
1196  
1197    programs_names_list.push('Festa Major d&#x27;Estiu 2013 - Pregó');
1198  
1199    programs_names_list.push('Tocat del bolet');
1200  
1201    programs_names_list.push('Klaatu Barada Nikto');
1202  
1203    programs_names_list.push('Un poble, una rotllana');
1204  
1205    programs_names_list.push('Lunáticos');
1206  
1207    programs_names_list.push('Teatre a la ràdio - s&#x27;aixeca el teló');
1208  
1209    programs_names_list.push('La nit del coaching');
1210  
1211    programs_names_list.push('Festa Major d&#x27;Hivern i Mostra');
1212  
1213    programs_names_list.push('Famílies i escola');
1214  
1215    programs_names_list.push('Fly&#x27;s rock');
1216  
1217    programs_names_list.push('Sal y Pimienta The Luxe');
1218  
1219    programs_names_list.push('Tours i trucs');
1220  
1221    programs_names_list.push('Aquellos maravillosos 80');
1222  
1223    programs_names_list.push('Madinati');
1224  
1225    programs_names_list.push('A la Vieja Escuela');
1226  
1227    programs_names_list.push('Cap infant sense joguina');
1228  
1229    programs_names_list.push('Eleccions municipals');
1230  
1231    programs_names_list.push('MixTeen');
1232  
1233    programs_names_list.push('Fitness mental');
1234  
1235    programs_names_list.push('Las Noches en Vela');
1236  
1237    programs_names_list.push('Tutifruti');
1238  
1239    programs_names_list.push('Desfem');
1240  
1241    programs_names_list.push('Acción adolescente');
1242  
1243    programs_names_list.push('Audiència pública');
1244  
1245    programs_names_list.push('La República');
1246  
1247    programs_names_list.push('La coctelera');
1248  
1249    programs_names_list.push('Detrás del arte');
1250  
1251    programs_names_list.push('L&#x27;envelat');
1252  
1253    programs_names_list.push('Area 52');
1254  
1255    programs_names_list.push('Llargs i curts');
1256  
1257    programs_names_list.push('Torna-la a tocar');
1258  
1259    programs_names_list.push('Bona tarda poesia');
1260  
1261    programs_names_list.push('Dando la nota');
1262  
1263    programs_names_list.push('Som terra');
1264  
1265    programs_names_list.push('Sendes');
1266  
1267    programs_names_list.push('Moments d&#x27;òpera');
1268  
1269    programs_names_list.push('The Block');
1270  
1271    programs_names_list.push('Ciutat en Vers');
1272  
1273    programs_names_list.push('Gerónimo');
1274  
1275    programs_names_list.push('L&#x27;alcalde respon');
1276  
1277    programs_names_list.push('Espai Públic 2030');
1278  
1279    programs_names_list.push('Eureka!');
1280  
1281    programs_names_list.push('Col·lectius en xarxa');
1282  
1283    programs_names_list.push('Tinc una idea');
1284  
1285    programs_names_list.push('La meva playlist');
1286  
1287    programs_names_list.push('Moments d&#x27;opera');
1288  
1289    programs_names_list.push('Aula dial Curs');
1290  
1291    programs_names_list.push('momentsopera');
1292  
1293    programs_names_list.push('L&#x27;any que ens va canviar la vida');
1294  
1295    programs_names_list.push('Audiència Publica');
1296  
1297    programs_names_list.push('locucio_ajuntament');
1298  
1299    programs_names_list.push('El grup');
1300  
1301    programs_names_list.push('bellesa al aire');
1302  
1303    programs_names_list.push('Manifest');
1304  
1305    programs_names_list.push('Coblejant');
1306  
1307    programs_names_list.push('esplai-public');
1308  
1309    programs_names_list.push('Coses del directe');
1310  
1311    programs_names_list.push('Festival XS');
1312  
1313    programs_names_list.push('Arrels i ales');
1314  
1315    programs_names_list.push('Valors a l&#x27;alça');
1316  
1317    programs_names_list.push('Peluts');
1318  
1319    programs_names_list.push('Una de cada');
1320  
1321    programs_names_list.push('L&#x27;Eriçó i la Guineu');
1322  
1323    programs_names_list.push('Notícies en Xarxa');
1324  
1325    programs_names_list.push('En joc');
1326  
1327    programs_names_list.push('Dia Mundial de la Ràdio');
1328  
1329    programs_names_list.push('Eighty');
1330  
1331    programs_names_list.push('Joves en acció');
1332  
1333    programs_names_list.push('Onda ODS');
1334  
1335    programs_names_list.push('El dia abans del col·lapse');
1336  
1337    programs_names_list.push('¡No me digas!');
1338  
1339    programs_names_list.push('Tertulia deportiva');
1340  
1341    programs_names_list.push('Vicentines: les nostres referents');
1342  
1343    programs_names_list.push('Alerta alimentària');
1344  
1345    programs_names_list.push('Revolución Sexitosa');
1346  
1347    programs_names_list.push('Parlem de salut');
1348  
1349    programs_names_list.push('Dejando huella');
1350  
1351    programs_names_list.push('Rauxa');
1352  
1353    programs_names_list.push('Sintetitzades');
1354  
1355    programs_names_list.push('El Dia R');
1356  
1357    programs_names_list.push('La Timidez');
1358  
1359    programs_names_list.push('A les portes de Troia');
1360  
1361    programs_names_list.push('Veus i arrels');
1362  
1363    programs_names_list.push('Destruir la democràcia');
1364  
1365    programs_names_list.push('Fes Ta Festa');
1366  
1367    programs_names_list.push('Obert x vacances');
1368  
1369    programs_names_list.push('El calaix');
1370  
1371    programs_names_list.push('L&#x27;hora del conte');
1372  
1373    programs_names_list.push('La caixa tonta');
1374  
1375    programs_names_list.push('Rambla, 54');
1376  
1377    programs_names_list.push('Això va en sèrie');
1378  
1379    programs_names_list.push('El món és digital');
1380  
1381    programs_names_list.push('Mosaic sonor');
1382  
1383    programs_names_list.push('La terra no és plana');
1384  
1385    programs_names_list.push('Gaudeix la festa');
1386  
1387    programs_names_list.push('Més enllà del prospecte');
1388  
1389
1390  const options = {
1391    includeScore: true,
1392    findAllMatches: true,
1393  };
1394
1395let fuse;
1396Alpine.data('searchProgramsComponent', () => ({
1397  search_input: '',
1398  results: [],
1399  selectOnAir: '1',
1400
1401  filterProgramsResults() {
1402    // console.log("filterProgramsResults");
1403    // console.log(this.search_input);
1404    if (this.search_input.length < 3) {
1405      this.results = [];
1406      return;
1407    }
1408    if (!fuse) {
1409      // console.log("new Fuse");
1410      // console.log(programs_names_list);
1411      fuse = new Fuse(programs_names_list, options);
1412    }
1413    this.results = fuse.search(this.search_input);
1414    // console.log(this.results);
1415  },
1416
1417  show(program_name, onair) {
1418    let showOnAir = this.selectOnAir === '3';
1419    if (this.selectOnAir === '1') {
1420        showOnAir = onair == 'True';
1421    }
1422    if (this.selectOnAir === '2') {
1423        showOnAir = onair == 'False';
1424    }
1425
1426    if (this.search_input.length < 3) {
1427      return showOnAir;
1428    }
1429
1430    const found = this.results.find(element => element.item === program_name && element.score < 0.5);
1431
1432    return found !== undefined && showOnAir;
1433  },
1434}));
1435
1436      let mediaelementjs;
1437  let mediaelement;
1438
1439
1440Alpine.store('player', {
1441  now_playing_title: '',
1442  now_playing_image: '',
1443  now_playing_summary: '',
1444  player_status: 'stopped',
1445  now_playing_podcast_id: '',
1446  now_playing_marker: null,
1447  now_playing_markers: null,
1448  current_schedule: null,  // current live schedule, NOT podcast
1449  timer: null,
1450  interval: null,
1451  elapsed_time: 0,
1452  remaining_time: 0,
1453  showPlayBtn: true,
1454  showPauseBtn: false,
1455  showStopBtn: false,
1456  showFwdBackBtn: false,
1457  interval_update_slider_non_playing_live: null,
1458
1459  clickedMarkerId: null,
1460  clickedMarkerAtMs: null,
1461
1462  slider_options_v4: null,
1463  slider_pips_options_v4: null,
1464
1465  init() {
1466    // 
1467    this.init_slider();
1468
1469    this.init_mp();
1470
1471    if (!isEmbed || !podcast_embed) {
1472        this.update_now_playing();
1473    }
1474
1475    if (isEmbed) {
1476        // console.log("isEmbed");
1477        if (podcast_embed)
1478            console.log(podcast_embed);
1479        if (podcast_embed && !marker_embed) {
1480            this.set_podcast(podcast_embed);
1481        }
1482        if (marker_embed) {
1483            // this.playMarker(podcast_embed, marker_embed);
1484            this.set_podcast(podcast_embed, null, marker_embed['id']);
1485        }
1486    }
1487  },
1488  init_slider() {
1489    this.slider_options_v4 = {
1490      min: 0,
1491      max: 100,
1492      value: 0,
1493      step: 1,
1494      range: "min",
1495      animate: "fast",  // or "true" or "fast"
1496      stop: function (event, ui) {
1497        const per_one = ui.value / 100;
1498        const second = Alpine.store('player').getDuration() * per_one;
1499        Alpine.store('player').setCurrentTime(second);
1500      }
1501    };
1502
1503    this.slider_pips_options_v4 = {
1504      first: "false",
1505      last: "false",
1506      rest: "false",
1507      labels: [],
1508      step: 1
1509    };
1510
1511    $("#slider_nx").slider(this.slider_options_v4).slider("pips", this.slider_pips_options_v4);
1512    // clean "pips"
1513    $("#slider_nx span.ui-slider-label").each(function () {
1514      $(this).parent().remove();
1515    });
1516  },
1517
1518  init_mp() {
1519    if (mediaelementjs) {
1520      return;
1521    }
1522    mediaelementjs = new MediaElementPlayer('enacast-player', {
1523      success: function (mediaElement, originalNode, instance) {
1524        // 
1525        mediaelement = mediaElement;
1526        // 
1527        // 
1528
1529        mediaElement.addEventListener('timeupdate', function (e) {
1530          // 
1531          // 
1532
1533          // 
1534          Alpine.store('player').update_podcast_current_percent_position_percent()
1535
1536          if (Alpine.store('player').interval_update_slider_non_playing_live === undefined) {
1537            clearInterval(this.interval_update_slider_non_playing_live);
1538            Alpine.store('player').interval_update_slider_non_playing_live = undefined;
1539          }
1540
1541          Alpine.store('player').updateSlider();
1542
1543          // 
1544
1545
1546          // TODO: save outside so we don't query all the time
1547          let slider_nx = $("#slider_nx");
1548
1549          let podcast_id = this.now_playing_podcast_id;
1550
1551          // 
1552          // 
1553
1554          if (Alpine.store('player').now_playing_marker === null || Alpine.store('player').now_playing_marker === undefined) {
1555            // 
1556            // 
1557            // 
1558            // 
1559            if (Alpine.store('player').slider_pips_options_v4.labels.length === 0 && Alpine.store('player').now_playing_markers && Alpine.store('player').now_playing_markers.length > 0) {
1560              let labels = [];
1561              // 
1562              for (let c in Alpine.store('player').now_playing_markers) {
1563                const m_percent = Math.round((Alpine.store('player').now_playing_markers[c].startsecond / Alpine.store('player').getDuration()) * 100);
1564                labels[m_percent] = Alpine.store('player').now_playing_markers[c].name;
1565              }
1566              Alpine.store('player').slider_pips_options_v4.labels = labels;
1567              // 
1568              // console.log(Alpine.store('player').slider_pips_options_v4.labels);
1569              slider_nx.slider("pips", Alpine.store('player').slider_pips_options_v4);
1570
1571              // remove non-markers pips
1572              $("#slider_nx span.ui-slider-label").each(function () {
1573                if ($(this).text().length === 0) {
1574                  $(this).parent().remove();
1575                } else {
1576                  // this is a marker, set alt, ant title, for tooltips
1577                  const marker_title = $(this).text();
1578                  $(this).parent().prop("alt", marker_title);
1579                  $(this).parent().prop("title", marker_title);
1580                  // jQuery UI tooltips, TODO: we should run this on document also on every new page loaded?
1581                  $(this).parent().tooltip();
1582                }
1583              });
1584
1585            }
1586          }
1587
1588        }, false);
1589      }
1590    });
1591  },
1592
1593  getCurrentSchedule: function () {
1594    return this.current_schedule;
1595  },
1596
1597  updateSlider() {
1598    // 
1599    let percent = this.getPercent();
1600    if (percent === undefined) {
1601      return;
1602    }
1603
1604    // 
1605    let slider_nx = $("#slider_nx");
1606
1607    // 
1608    // 
1609
1610    // 
1611    slider_nx.slider("value", percent);
1612
1613    if (this.getCurrentSchedule() !== undefined) {
1614      // Live
1615      slider_nx.slider("disable");  // this produces a more muted color than I like, but prevents "seeking"
1616      // remove handle
1617      $("#slider_nx span.ui-slider-handle").each(function () {
1618        $(this).addClass('d-none');
1619      });
1620    } else {
1621      // Podcast
1622      slider_nx.slider("enable");  // this produces a more muted color than I like, but prevents "seeking"
1623      $("#slider_nx span.ui-slider-handle").each(function () {
1624        $(this).removeClass('d-none');
1625      });
1626    }
1627  },
1628
1629  getPercent() {
1630    let duration = this.getDuration();
1631    // 
1632    let currTime = this.getCurrentTime();
1633    // 
1634    if (this.now_playing_marker) {
1635      // A section without an end (docs/sections.md) runs to the next section,
1636      // else to the end of the episode: see markerEnd().
1637      const end = this.markerEnd(this.now_playing_marker, this.now_playing_markers, duration);
1638      if (end !== null && this.getCurrentTime() > end) {
1639        this.stop();
1640      }
1641      currTime = this.getCurrentTime() - this.now_playing_marker.startsecond;
1642      duration = (end !== null ? end : duration) - this.now_playing_marker.startsecond;
1643
1644    }
1645    return Math.round((currTime / duration) * 100);
1646  },
1647
1648  setSrc(src_url) {
1649    // 
1650    mediaelementjs.setSrc(src_url);
1651  },
1652
1653  toggle_play_pause_podcast(podcast, pause_if_playing = true) {
1654    console.log("toggle_play_pause_podcast");
1655    const urlParams = new URLSearchParams(window.location.search);
1656    const second = urlParams.get('s') || 0;
1657    if (pause_if_playing && this.is_playing() && podcast.id === this.getCurrentPodcastId()) {
1658      this.pause();
1659    } else {
1660      this.play_podcast(podcast, second);
1661    }
1662  },
1663
1664  // playPodcastWithConfirmation(podcast, second) {
1665  //     console.log("playWithConfirmation");
1666  //     Swal.fire({
1667  //           title: 'Play podcast?',
1668  //           // text: podcast.title,
1669  //           icon: 'question',
1670  //           showCancelButton: true,
1671  //           confirmButtonText: 'Play',
1672  //           cancelButtonText: 'Cancel',
1673  //           confirmButtonColor: '#3085d6',
1674  //           cancelButtonColor: '#d33'
1675  //     }).then((result) => {
1676  //         if (result.isConfirmed) {
1677  //             console.log("playWithConfirmation confirmed", podcast, second);
1678  //             this._play_podcast(podcast, second);
1679  //         }
1680  //     });
1681  // },
1682
1683  stop_timer() {
1684    if (this.timer) {
1685      clearInterval(this.timer);
1686      this.timer = null;
1687    }
1688  },
1689
1690  pause() {
1691    // this.showPlay = true;
1692    this.showPlayBtn = true;
1693    this.showPauseBtn = false;
1694    mediaelement.pause();
1695    try {
1696      this.stop_timer();
1697    } catch (e) {
1698
1699    }
1700    this.player_status = "paused";
1701  },
1702
1703  is_playing() {
1704    return this.player_status === "playing_podcast" || this.player_status === "playing_live";
1705  },
1706
1707  getCurrentPodcastId() {
1708    return this.now_playing_podcast_id;
1709  },
1710
1711  isPodcastPlaying(podcast_id) {
1712    if (this.player_status !== "playing_podcast")
1713      return false;
1714    return this.is_playing() && podcast_id === this.getCurrentPodcastId();
1715  },
1716
1717  playMarker(podcast, marker) {
1718    if (this.markerIsPlaying(marker)) {
1719      this.pause();
1720      return;
1721    }
1722
1723    this.clickedMarkerId = marker['id'];
1724    // console.log("marker: ", marker['id']);
1725    this.clickedMarkerAtMs = new Date().getTime();
1726
1727    if (!this.isPodcastPlaying(podcast['id']))
1728      this.play_podcast(podcast, marker['startsecond']);
1729    else
1730      this.setCurrentTime(marker['startsecond']);
1731
1732    $("div.collapse").collapse('hide');
1733    $("div#collapse_marker_" + marker['id']).collapse('show');
1734  },
1735
1736  playSearchTranscriptMoment(podcast, startsecond) {
1737      if (!this.isPodcastPlaying(podcast['id']))
1738          this.play_podcast(podcast, startsecond);
1739      else
1740          this.setCurrentTime(startsecond);
1741  },
1742
1743  play_podcast_at_time(podcast, secondsToPlay) {
1744      if (!this.isPodcastPlaying(podcast['id']))
1745          this.play_podcast(podcast, secondsToPlay);
1746      else
1747          this.setCurrentTime(secondsToPlay);
1748  },
1749
1750  markerIsPlaying(marker) {
1751    const marker_id = marker['id'];
1752      // console.log("marker_id: ", marker_id);
1753    // TODO: take into account end second on the future
1754    if (!this.isPodcastPlaying(this.now_playing_podcast_id))
1755      return false;
1756
1757    // Reduce glitches (as getcurrent time has some lag):
1758    if (this.clickedMarkerId !== undefined) {
1759      // a marker have just been clicked, but it's not me
1760      if (this.clickedMarkerId !== marker_id && (new Date().getTime() - this.clickedMarkerAtMs) < 500)
1761        return false;
1762      // the marker has been clicked less than 500ms ago, so I'm playing
1763      if (this.clickedMarkerId === marker_id && (new Date().getTime() - this.clickedMarkerAtMs) < 500)
1764        return true;
1765    }
1766
1767    // This finds the first marker that matches the current time
1768    let start_seconds = [];
1769    let curr_time = this.getCurrentTime();
1770    if (start_seconds.length === 0) {
1771      for (let i = 0; i < this.now_playing_markers.length; i++) {
1772        start_seconds.push(this.now_playing_markers[i].startsecond);
1773      }
1774    }
1775
1776    let largest = 0;
1777
1778    for (let i = 0; i <= start_seconds.length; i++) {
1779      if (start_seconds[i] > largest) {
1780        if (curr_time > start_seconds[i])
1781          largest = start_seconds[i];
1782      }
1783     }
1784    return largest === marker.startsecond;
1785  },
1786
1787  toggle_play_pause() {
1788    if (this.player_status === "playing_podcast") {
1789      this.pause();
1790    } else if (this.player_status === "paused") {
1791      this.play();
1792    } else if (this.player_status === "playing_live") {
1793      this.stop();
1794    } else if (this.player_status === "stopped") {
1795      this.play();
1796    }
1797  },
1798
1799  setTitle: function (title) {
1800      this.now_playing_title = title;
1801  },
1802
1803  setSummary: function (summary) {
1804    this.now_playing_summary = summary;
1805  },
1806
1807  setMarkers: function (markers) {
1808    this.slider_pips_options_v4.labels.length = 0;
1809    this.now_playing_markers = markers;
1810    // We don't have the podcast duration until we start playing, so we can't put the
1811    //  "markers"/"pips" on the timeline until we have the duration
1812  },
1813
1814  // Where a section stops, in seconds into the served audio. Same rule as the
1815  // backend's section_range() and the Astro site's markerPlaybackRange()
1816  // (docs/sections.md): the marker's own end when it is after its start, else
1817  // the start of the next section, else the episode's end (null while the
1818  // duration is unknown, so callers fall back to the audio's duration).
1819  markerEnd(marker, markers, duration) {
1820    const start = marker.startsecond || 0;
1821    if (typeof marker.endsecond === "number" && marker.endsecond > start)
1822      return marker.endsecond;
1823    let end = null;
1824    for (const m of markers || []) {
1825      const s = m.startsecond || 0;
1826      if (s > start && (end === null || s < end))
1827        end = s;
1828    }
1829    if (end === null && duration > 0)
1830      end = duration;
1831    return end;
1832  },
1833
1834  set_podcast: function (podcast, second = null, marker_id = null) {
1835    this.setSrc(podcast.audio);
1836    if (marker_id !== null) {
1837      const marker = podcast.markers.filter(obj => obj.id === marker_id)[0];
1838      this.setTitle(marker.name + " (" + podcast.name + ")");
1839      // TODO: limit play from start second to end second of the marker
1840      //          (and also shown timers, that we don't have, yet)
1841      // TODO:
1842      // this.setDescription(marker.descr);
1843      second = marker.startsecond;
1844
1845      console.log("marker", marker);
1846      this.now_playing_image = marker.image;
1847      this.now_playing_marker = marker;
1848      this.now_playing_markers = podcast.markers;
1849    } else {
1850      this.setTitle(podcast.title);
1851      this.setSummary(podcast.summary);
1852      this.setMarkers(podcast.markers);
1853    }
1854
1855    // TODO: htmx
1856    // if (second === null) {
1857    //   second = EnaCastLocalDataBase.get_podcast_current_position_secs(podcast.id);
1858    //   if (second !== 0) {
1859    //     $('.toast').toast('show');
1860    //   }
1861    // }
1862
1863    if (second) {
1864      // this is needed for safari, if we don't wait until it start playing it doesn't jump to the second
1865      let tmp_listener = mediaelement.addEventListener("playing", function tmp_func() {
1866          mediaelement.removeEventListener("playing", tmp_func);
1867        // this.setCurrentTime(second); // this is null here
1868          mediaelement.setCurrentTime(second);
1869      });
1870
1871    }
1872    if (marker_id == null) {
1873        this.now_playing_image = podcast.program_logo_400x400;
1874    }
1875    this.now_playing_podcast = podcast;
1876    this.now_playing_podcast_id = podcast.id;
1877
1878    this.current_schedule = undefined;
1879    this.next_playing_program = undefined;
1880    this.next_playing_title = undefined;
1881    this.next_playing_summary = undefined;
1882    this.next_playing_image = undefined;
1883  },
1884
1885  play_podcast(podcast, second = 0) {
1886      this._play_podcast(podcast, second);
1887      // canAutoplay.audio().then(({result}) => {
1888      //     if (result === true) {
1889      //         this._play_podcast(podcast, second);
1890      //     } else {
1891      //         this.playPodcastWithConfirmation(podcast, second);
1892      //     }
1893      // });
1894  },
1895
1896  _play_podcast(podcast, second = 0) {
1897    // 
1898    // 
1899    // 
1900    this.set_podcast(podcast, second);
1901
1902    // 
1903    this.player_status = "playing_podcast";
1904    // start interval to update time and remaining time
1905
1906
1907    // 
1908    if (second !== undefined) {
1909      // this is needed for safari, if we don't wait until it start playing it doesn't jump to the second
1910      mediaelement.addEventListener("playing", function tmp_func() {
1911        mediaelement.removeEventListener("playing", tmp_func);
1912        mediaelement.setCurrentTime(second);
1913      });
1914    }
1915    this.play();
1916  },
1917
1918  play() {
1919    // 
1920    // 
1921    // 
1922    // 
1923    // 
1924    // 
1925    // 
1926    // 
1927    // 
1928
1929    // 
1930    if (!this.now_playing_podcast_id) {
1931      this.setSrc("https://enacast.com/radiosantvicenc/streams/SD");
1932      this.setLiveInfo();
1933    } else {
1934      this.player_status = "playing_podcast";
1935      this.timer = setInterval(() => {
1936          this.elapsed_time = this.getCurrentTimeHuman();
1937          this.remaining_time = this.getRemainingTimeHuman();
1938      }, 1000);
1939    }
1940
1941    this.showPlayBtn = false;
1942    this.showPauseBtn = this.player_status === "playing_podcast";
1943    this.showStopBtn = true;
1944    this.showFwdBackBtn = this.player_status === "paused" || this.player_status === "playing_podcast";
1945
1946    mediaelementjs.play();
1947
1948    // 
1949
1950    // 
1951    // 
1952    // 
1953    // 
1954    // 
1955    // 
1956    // 
1957    // 
1958    // 
1959  },
1960
1961  playBack: function (seconds) {
1962    mediaelementjs.setCurrentTime(mediaelementjs.currentTime - seconds);
1963  },
1964
1965  playForward: function (seconds) {
1966    mediaelementjs.setCurrentTime(mediaelementjs.currentTime + seconds);
1967  },
1968
1969  stop() {
1970    this.showPlayBtn = true;
1971    this.showPauseBtn = false;
1972    this.showStopBtn = false;
1973    this.showFwdBackBtn = false;
1974
1975    this.player_status = "stopped";
1976
1977    try {
1978      this.stop_timer();
1979    } catch (e) {
1980    }
1981    mediaelementjs.pause();
1982    if (this.now_playing_marker)
1983      this.setCurrentTime(this.now_playing_marker.startsecond);
1984    else
1985      this.setCurrentTime(0);
1986
1987    if (!isEmbed) {
1988      mediaelementjs.setSrc("https://");
1989
1990      this.setLiveInfo();
1991    }
1992  },
1993
1994  setCurrentTime(second) {
1995    mediaelement.setCurrentTime(second);
1996  },
1997
1998  setLiveInfo: function () {
1999    this.player_status = "playing_live";
2000    this.now_playing_podcast_id = "";
2001    this.now_playing_podcast = undefined;
2002    // this.now_playing_title = radio.name;
2003    this.now_playing_summary = "";
2004    this.now_playing_markers = "";
2005    this.now_playing_marker = undefined;
2006
2007    // this.now_playing_image = radio.logo_square_256;
2008
2009    this.current_schedule = undefined;
2010
2011    this.next_playing_program = undefined;
2012    this.next_playing_title = undefined;
2013    this.next_playing_summary = undefined;
2014    this.next_playing_image = undefined;
2015
2016    this.update_now_playing();
2017  },
2018
2019  getDuration() {
2020    // 
2021    // 
2022    // 
2023    // 
2024    // 
2025    // 
2026      if (isNaN(mediaelement.duration)) {
2027          return 0;
2028      }
2029      return mediaelement.duration;
2030  },
2031
2032  getCurrentTime: function () {
2033    if (this.current_schedule) {
2034      // Live
2035      let d = new Date();
2036      let total_seconds = 0;
2037      total_seconds += (d.getHours() - this.current_schedule.start_time_local.split(":")[0]) * 3600;
2038      total_seconds += (d.getMinutes() - this.current_schedule.start_time_local.split(":")[1]) * 60;
2039      total_seconds += (d.getSeconds() - this.current_schedule.start_time_local.split(":")[2]);
2040      return total_seconds;
2041    }
2042
2043    if (!mediaelement) return;
2044    // Podcast
2045    return mediaelement.currentTime;
2046  },
2047
2048  getCurrentTimeHuman: function () {
2049    const s = this.getCurrentTime();
2050    if (s < 3600)
2051      return new Date(s * 1000).toISOString().substring(14, 19);
2052    else
2053      return new Date(s * 1000).toISOString().substring(11, 19);
2054  },
2055
2056  getRemainingTimeHuman: function () {
2057    const s = this.getDuration() - this.getCurrentTime();
2058    if (s < 3600)
2059      return "-" + new Date(s * 1000).toISOString().substring(14, 19);
2060    else
2061      return "-" + new Date(s * 1000).toISOString().substring(11, 19);
2062  },
2063
2064  get_podcast_current_percent_position_percent(podcast_id) {
2065    if (typeof (Storage) !== "undefined") {
2066      const pos_list_dict = localStorage.getItem("current_percent_position_podcasts");
2067      if (pos_list_dict) {
2068        const pos = JSON.parse(pos_list_dict)[podcast_id];
2069        if (pos) {
2070          return pos;
2071        }
2072      }
2073      return 0;
2074    } else {
2075      // Sorry! No Web Storage support...
2076    }
2077  },
2078
2079  update_podcast_current_percent_position_percent() {
2080    if (typeof (Storage) !== "undefined") {
2081      let pos_list_dict = localStorage.getItem("current_percent_position_podcasts");
2082      if (pos_list_dict) {
2083        pos_list_dict = JSON.parse(pos_list_dict);
2084      } else {
2085        pos_list_dict = {};
2086      }
2087      pos_list_dict[this.now_playing_podcast_id] = this.getPercent();
2088      localStorage.setItem("current_percent_position_podcasts", JSON.stringify(pos_list_dict));
2089    } else {
2090      // Sorry! No Web Storage support...
2091    }
2092  },
2093
2094  update_now_playing() {
2095    let url = `/api/v3/now_playing/?page_size=100&radio=radiosantvicenc`;
2096    fetch(url)
2097      .then(response => response.json())
2098      .then(data => {
2099        this.now_playing_title = data.current_program.name;
2100        this.now_playing_image = data.current_program.logo_square_400;
2101
2102        if (this.now_playing_title === undefined || this.now_playing_title === null || this.now_playing_title === "") {
2103          this.setTitle(radio.name);
2104        }
2105
2106        if (this.now_playing_image === undefined || this.now_playing_image === null || this.now_playing_image === "") {
2107          this.now_playing_image = radio.logo_square_256;
2108        }
2109      });
2110  }
2111
2112});
2113
2114  });
2115</script>
2115
2116
2117
2118
2119<script src="//cdnjs.cloudflare.com/ajax/libs/moment.js/2.10.6/moment-with-locales.min.js"></script>
vendor: 1 bytes, line 2119
2119
2120<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
vendor: 1 bytes, line 2120
2120
2121<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/can-autoplay.min.js" integrity="sha256-h/0BsGoRtodhD+FDU7WeqveJ0PM9gPDX++eE+M2eaZ4=" crossorigin="anonymous"></script>
2121
2122
2123<script type="text/javascript">
2124  
2125
2126  const enacast_template = "enacast-nx-htmx";
2127  const radio = {"codename": "radiosantvicenc", "name": "R\u00e0dio Sant Vicen\u00e7", "language": "ca", "custom_domain": "www.radiosvh.info", "streams": [{"name": "56k standard stream", "bitrate": 64, "host": "streaming.enacast.com", "port": 8000, "mount_point": "stvicenc56.mp3", "url": "https://streaming.enacast.com:8000/stvicenc56.mp3", "ssl": false, "enacast_managed_stream": true}, {"name": "128k standard stream", "bitrate": 128, "host": "streaming.enacast.com", "port": 8000, "mount_point": "stvicenc128.mp3", "url": "https://streaming.enacast.com:8000/stvicenc128.mp3", "ssl": false, "enacast_managed_stream": true}], "contact_mail": "", "phone_number_whatsapp": "", "twitter": "https://twitter.com/radiosvh902", "facebook": "https://www.facebook.com/radiosantvicenc", "youtube": "http://www.youtube.com/user/radiosvh902/", "instagram": "https://www.instagram.com/radiosvh902/", "tiktok": "", "linkedin": "", "default_stream": "https://enacast.com/radiosantvicenc/streams/HD", "podcast_service": true, "has_news": false, "direccio": "Ca l'Aragall, c/ Barcelona 16-18 primer pis 08620, Sant Vicen\u00e7 dels Horts", "active": true, "latitude": 41.398456, "primary_color": "#FF8601", "dark_theme": false, "longitude": 2.007947, "send_stream_alert_enacast_admins": false, "send_stream_alert_radio_contacts": true, "logo_radio": "https://enacast.com/media/logos/2015/10/13/logo_santvi_W6x5uzw.png", "logo_radio_square": "https://enacast.com/media/logos/2022/02/11/icon_8YNoPw6.png", "logo_square_256": "https://ik.imagekit.io/7ftrkrun31/enacast/logos/2022/02/11/icon_8YNoPw6.png?tr=w-256,h-None,fo-auto", "logo_square_128": "https://ik.imagekit.io/7ftrkrun31/enacast/logos/2022/02/11/icon_8YNoPw6.png?tr=w-128,h-None,fo-auto", "time_zone": "Europe/Madrid", "android_app_id": "", "ios_app_id": "", "cover_big_landscape": "https://ik.imagekit.io/7ftrkrun31/enacast/radio_covers/2022/02/11/COVER.jpg?tr=w-1300,h-250,fo-auto", "cover_orig": "https://enacast.com/media/radio_covers/2022/02/11/COVER.jpg", "cover": "https://enacast.com/media/cache/a5/07/a50741e4c75069d377a24b2c0a4136e7.jpg", "now_playing_program_codename": "", "now_playing_program_id": null, "analytics_web_tracking_id": null, "matomo_internal_web_tracking_id": 10, "glossary": "R\u00e0dio Sant Vicen\u00e7: name of the radio\nR\u00e0dio Sant Vicen\u00e7: emissora municipal de r\u00e0dio p\u00fablica de Sant Vicen\u00e7 dels Horts\nSant Vicen\u00e7 dels Horts: municipi del Baix Llobregat on opera l'emissora\nAula dial: projecte educatiu de r\u00e0dio adre\u00e7at a escolars del municipi\nCa l'Aragall: edifici c\u00e8ntric que allotja la seu actual de la r\u00e0dio\nBaix Llobregat: comarca catalana a la qual pertany el municipi\nServei Educatiu del Baix Llobregat-6: servei de la Generalitat col\u00b7laborador del projecte Aula dial\nPremi el CAC a l'Escola: guard\u00f3 del Consell Audiovisual atorgat al projecte Aula dial\nPremis de Comunicaci\u00f3 Local: distinci\u00f3 de la Diputaci\u00f3 de Barcelona guanyada per la r\u00e0dio\nFesta Major d'Hivern: festivitat local en qu\u00e8 es va inaugurar oficialment l'emissora\nMostra Comercial i Agr\u00edcola: fira anual vicentina des d'on s'emet en directe\nLa Pepada: primer programa em\u00e8s per la r\u00e0dio l'any 1982\nPep Ramoneda: locutor hist\u00f2ric i un dels creadors del programa La Pepada\nPep Romero: co-presentador de l'espai fundacional d'entreteniment La Pepada\nPepi Meca: locutora hist\u00f2rica i co-conductora del programa inaugural La Pepada\nGolfes de l'Ajuntament: primer espai municipal on es van instal\u00b7lar els estudis\n90.2 FM: freq\u00fc\u00e8ncia del dial des d'on emet actualment l'emissora\n101.2 FM: primer dial de freq\u00fc\u00e8ncia utilitzat per R\u00e0dio Sant Vicen\u00e7\n103.9 FM: dial utilitzat per l'emissora municipal a partir de 1985\n90.9 FM: freq\u00fc\u00e8ncia utilitzada per la r\u00e0dio vicentina des de 1993\nSom un poble: programa cultural pioner condu\u00eft per les primeres col\u00b7laboradores locals\nPaquita Pahissa: pionera de l'emissora i conductora del programa Som un poble\nImmaculada Caralt: comunicadora local i co-conductora hist\u00f2rica de Som un poble\nRosa Maria Caralt: col\u00b7laboradora fundacional del programa cultural Som un poble\nLos Petetes de la radio: espai infantil pioner em\u00e8s en els primers anys\nEncarni Vela: locutora i directora del programa infantil Los Petetes de la radio\nPilar Capar\u00e0: col\u00b7laboradora hist\u00f2rica encarregada de la conducci\u00f3 de programes musicals\nArcadi Tuset: locutor de la primera \u00e8poca especialitzat en espais musicals\nJosep Anton Ramoneda: conductor de programes musicals en la fase inicial de la r\u00e0dio\nEmissores Municipals de Catalunya: associaci\u00f3 de r\u00e0dios a la qual es va incorporar el 1985\nAntics Salesians: edifici municipal que va allotjar els estudis entre 1990 i 2016\nInformatiu Comarcal: programa col\u00b7laboratiu entre emissores municipals del Baix Llobregat\
2127nPla Educatiu d'Entorn: marc comunitari de cooperaci\u00f3 educativa que inclou Aula dial\nRecerques: programa radiof\u00f2nic premiat sobre treballs de recerca de secund\u00e0ria\nPremis R\u00e0dio Associaci\u00f3: reconeixement d'\u00e0mbit catal\u00e0 atorgat al programa local Recerques\nCap infant sense joguina: campanya solid\u00e0ria anual de Nadal amb marat\u00f3 radiof\u00f2nica\nEureka!: programa de COM R\u00e0dio que va transmetre des de la vila\nCOM R\u00e0dio: xarxa radiof\u00f2nica que va participar en el 30\u00e8 aniversari local\nPla\u00e7a de la Vila: espai p\u00fablic c\u00e8ntric utilitzat per a transmissions radiof\u00f2niques exteriors\nCarrer Barcelona: carrer c\u00e8ntric on se situa la seu de Ca l'Aragall\nPrograma de Barris: espai informatiu especial dedicat a les associacions de ve\u00efns\nLa Xarxa de Comunicaci\u00f3 Local: plataforma col\u00b7laboradora en cobertures electorals i informatives\nBlocs electorals: quotes pol\u00edtiques de temps rebutjades per defensar criteris period\u00edstics professionals\nLOREG: llei org\u00e0nica reguladora del r\u00e8gim electoral a l'Estat espanyol\nTN de TV3: telenot\u00edcies que va destacar la cobertura electoral independent de la r\u00e0dio\nRegidoria d'Educaci\u00f3: \u00e0rea de l'Ajuntament impulsora de projectes escolars com Aula dial\nVicent\u00ed: gentilici referit a la ciutadania o entitats de Sant Vicen\u00e7 dels Horts\nControl de so: espai t\u00e8cnic equipat per a la mescla i emissi\u00f3 sonora\nGraella: planificaci\u00f3 peri\u00f2dica dels programes emesos per l'estaci\u00f3 de r\u00e0dio\nEstudi de gravaci\u00f3: sala insonoritzada i equipada per a la producci\u00f3 de continguts\nPle municipal: sessi\u00f3 plen\u00e0ria de l'Ajuntament que la r\u00e0dio transmet en directe", "podcast_summary_preference": "long", "default_title_template": "ai_or_default", "in_trial_period": false, "trial_start_date": null, "holidays": false, "on_holidays_today": false, "next_holiday_period": null, "pre_roll_intro": null, "show_chat": true, "allow_downloads": true, "allow_rss": true, "sound_archive_enabled": true, "sound_archive_default_view": "chronology", "module_videos_youtube": false, "module_ads": false, "titol_fora_programacio": "R\u00e0dio Sant Vicen\u00e7 90.2 fm", "descripcio_fora_programacio": "Esteu escoltant en directe R\u00e0dio Sant Vicen\u00e7 90.2 fm", "has_insights_pro": false, "custom_llm_context": "", "terminology": {}, "needs_onboarding": false, "web_url": "http://radio.santvidigital.cat/", "created_via": "admin"};
2128  const default_cover = "https://enacast.com/media/frontend_config_covers/radiosantvicenc/2022/7/7/2022-7-7_1657194224..jpg";
2129
2130  // const isDebug = true;
2131  const isDebug = false;
2132
2133  const isProd = true;
2134  const isChrome = /Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor);
2135
2136  const mapbox_public_key = "pk.eyJ1Ijoib3Jpb2xlbmFjYXN0IiwiYSI6ImNrZmNnOTRodjFmaDYyc3FzdGUxbmVocXkifQ.6BPyoMjBEfn-v48e_Mc3HA";
2137
2138  let base_url = "https://enacast.com/" + radio.codename;
2139  if (radio.custom_domain != undefined && radio.custom_domain.length > 4)
2140    base_url = "https://" + radio.custom_domain;
2141
2142  const window_width = $(window).width();
2143  const standalone = window.navigator.standalone,
2144    userAgent = window.navigator.userAgent.toLowerCase(),
2145    safari = /safari/.test(userAgent) && !isChrome,
2146    android = /android/.test(userAgent),
2147    ios = /iphone|ipod|ipad/.test(userAgent),
2148    mobile = android | ios,
2149    isEmbed = 0 === 1;
2150  moment.locale(radio.language);
2151
2152  // redirect to custom domain if radio has one
2153  if (radio.custom_domain !== undefined && window.location.hostname.indexOf(radio.custom_domain) === -1 && isProd) {
2154    let target_url = window.location.href;
2155    target_url = target_url.replace("www.", "");
2156    const base_custom_domain = radio.custom_domain.replace("www.", "");
2157
2158    // multiple possible urls to rewrite (order important to avoid issues):
2159    target_url = target_url.replace(base_custom_domain + "/" + radio.codename, radio.custom_domain);
2160    target_url = target_url.replace(base_custom_domain, radio.custom_domain);
2161    target_url = target_url.replace("enacast.com/" + radio.codename, radio.custom_domain);
2162
2163    // console.log("radio codename 0 to: " + radio.codename);
2164    // console.log("radio custom domain 0 to: " + radio.custom_domain);
2165    // console.log("redirecting 0 to: " + target_url);
2166    window.location = target_url;
2167  }
2168
2169
2170</script>
2170
2171
2172
2173<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.3/TweenMax.min.js"></script>
2173
2174
2175<script src="https://cdnjs.cloudflare.com/ajax/libs/mediaelement/4.2.16/mediaelement-and-player.min.js"
2176        integrity="sha256-RDugr3p+2CciPH+zwAjAK5/x1lG2SS6cJwN4sH2fYAg=" crossorigin="anonymous" defer></script>
2176
2177
2178<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"
2179        integrity="sha256-VazP97ZCwtekAsvgPBSUwPFKdrwD3unUfSGVYrahUqU="
2180        crossorigin="anonymous"></script>
vendor: 1 bytes, line 2180
2180
2181<script src="https://cdnjs.cloudflare.com/ajax/libs/jQuery-ui-Slider-Pips/1.11.4/jquery-ui-slider-pips.min.js"
2182        integrity="sha256-OL8tV6ONTtyNVwcLNx0HfV0RVvreW1rvBCu/Nv2zIxo=" crossorigin="anonymous"></script>
2182
2183
2184<script src="https://cdnjs.cloudflare.com/ajax/libs/headroom/0.11.0/headroom.min.js"
2185        integrity="sha256-AsUX4SJE1+yuDu5+mAVzJbuYNPHj/WroHuZ8Ir/CkE0=" crossorigin="anonymous"></script>
2185
2186
2187<script src="https://cdnjs.cloudflare.com/ajax/libs/photoswipe/4.1.3/photoswipe.min.js"
2188        integrity="sha512-2R4VJGamBudpzC1NTaSkusXP7QkiUYvEKhpJAxeVCqLDsgW4OqtzorZGpulE3eEA7p++U0ZYmqBwO3m+R2hRjA=="
2189        crossorigin="anonymous" defer ></script>
vendor: 1 bytes, line 2189
2189
2190<script src="https://cdnjs.cloudflare.com/ajax/libs/photoswipe/4.1.3/photoswipe-ui-default.min.js"
2191        integrity="sha512-SxO0cwfxj/QhgX1SgpmUr0U2l5304ezGVhc0AO2YwOQ/C8O67ynyTorMKGjVv1fJnPQgjdxRz6x70MY9r0sKtQ=="
2192        crossorigin="anonymous" defer></script>
2192
2193
2194<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/leaflet.min.js" defer></script>
2194
2195
2196
2197
2198<script
2199  src="https://browser.sentry-cdn.com/7.34.0/bundle.tracing.min.js"
2200  integrity="sha384-3w0Bmtwehbv8MH8akckH78UJlX4muadjYSzwTQYYgOp4T96F/25itcN5r0oijRU+"
2201  crossorigin="anonymous"></script>
2201
2202
2203<script>
2204  Sentry.init({
2205    dsn: "https://[email protected]/4504595370606592",
2206    
2207    release: "EnaCastFrontend_PROD@63fdb072457bb2c9ce440be42c1f69b0f2d01e1b",
2208    integrations: [new Sentry.BrowserTracing()],
2209    tracesSampleRate: 0.0001,
2210  });
2211</script>
2211
2212
2213<script>
2214
2215  navigator.serviceWorker.getRegistrations().then(function(registrations) {
2216    for(let registration of registrations) {
2217      registration.unregister();
2218    }
2219  });
2220
2221
2222  for (let key in localStorage){
2223   if (key.startsWith("ngStorage-")){
2224     localStorage.removeItem(key);
2225   }
2226  }
2227</script>
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242<script src="https://cdn.jsdelivr.net/npm/[email protected]"></script>
vendor: 1 bytes, line 2242
2242
2243<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/ext/debug.js"></script>
vendor: 1 bytes, line 2243
2243
2244<script src="https://cdn.jsdelivr.net/npm/[email protected]/preload.js"></script>
vendor: 1 bytes, line 2244
2244
2245<script src="https://cdn.jsdelivr.net/npm/[email protected]/head-support.js"></script>
2245
2246
2247<script defer src="https://unpkg.com/[email protected]"></script>
2247
2248
2249
2250
2251<script defer src="https://cdn.jsdelivr.net/npm/typesense@1/dist/typesense.min.js"></script>
vendor: 1 bytes, line 2251
2251
2252<script defer src="https://cdn.jsdelivr.net/npm/fuse.js/dist/fuse.basic.min.js"></script>
2252
2253
2254
2255
2256<script src="https://cdn.jsdelivr.net/npm/masonry-layout@4/dist/masonry.pkgd.min.js"></script>
2256
2257
2258
2259
2260
2261<script src="https://cdn.jsdelivr.net/npm/[email protected]/nprogress.js"></script>
vendor: 1 bytes, line 2261
2261
2262<script>
2263  document.addEventListener('htmx:beforeRequest', function (event) {
2264    NProgress.start();
2265  });
2266  document.addEventListener('htmx:afterRequest', function (event) {
2267    NProgress.done();
2268  });
2269
2270</script>
2270
2271
2272<script defer src="https://enacast.com/static/social_shares.js"></script>
vendor: 1 bytes, line 2272
2272
2273<script defer src="https://enacast.com/static/third_party/clipboard/clipboard.js"></script>
2273
2274
2275
2276
2277
2278</body>
2279</html>

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.