1(function ($) { 2 3 window.RGPlayer = { 4 5 liveUrl: 'https://live.radiogrenouille.com:8443/live', 6 7 mode: 'live', 8 9 audio: null, 10 11 mobilePlayerOpen: true, 12 13 init() { 14 15 this.audio = document.getElementById('rg-audio'); 16 17 if (!this.audio) { 18 console.error('RGPlayer : audio introuvable'); 19 return; 20 } 21 22 this.bindEvents(); 23 24 this.initMarquee(); 25 26 this.updateLiveTitle(); 27 28 setInterval(() => { 29 this.updateLiveTitle(); 30 }, 30000); 31 32 console.log('RGPlayer initialisé'); 33 34 }, 35 36 bindEvents() { 37 38 const self = this; 39 40 this.updateProgressLayout(); 41 42 $(window).on('resize', function () { 43 self.updateProgressLayout(); 44 }); 45 46 47 // Bouton LIVE 48 $('.rg-live-button, .mobile-live').on('click', function () { 49 50 console.log('click'); 51 52 self.playLive(); 53 54 }); 55 56 // Lecture 57 this.audio.addEventListener('play', function () { 58 59 $('#rg-player').addClass('is-playing'); 60 61 self.showPlayer(); 62 63 if (self.mode === 'live') { 64 $('.rg-live-status').addClass('is-live'); 65 $('.rg-live-button').html('<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20.64" height="25" x="0px" y="0px" viewBox="0 0 17.5 21.2" xml:space="preserve" fill="#272727"><rect width="6" height="21.2"></rect><rect x="11.5" width="6" height="21.2"></rect></svg> Pause'); 66 $('.mobile-live') 67 .html('<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20.64" height="25" x="0px" y="0px" viewBox="0 0 17.5 21.2" xml:space="preserve" fill="#272727"><rect width="6" height="21.2"></rect><rect x="11.5" width="6" height="21.2"></rect></svg> Pause') 68 .addClass('is-playing'); 69 } 70 71 $('.rg-play').html('<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20.64" height="25" x="0px" y="0px" viewBox="0 0 17.5 21.2" xml:space="preserve" fill="#bbbcbf"><rect width="6" height="21.2"></rect><rect x="11.5" width="6" height="21.2"></rect></svg>'); 72 73 self.updateCardsState(); 74 75 if (self.mode === "podcast" && self.currentPodcast) { 76 77 document.dispatchEvent( 78 new CustomEvent("podcastPlay", { 79 detail: self.currentPodcast 80 }) 81 ); 82 83 } 84 85 86 }); 87 88 89 // Pause 90 this.audio.addEventListener('pause', function () { 91 92 $('#rg-player').removeClass('is-playing'); 93 94 $('.rg-live-status').removeClass('is-live'); 95 96 $('.rg-live-button').html('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><path fill="#272727" d="M187.2 100.9C174.8 94.1 159.8 94.4 147.6 101.6C135.4 108.8 128 121.9 128 136L128 504C128 518.1 135.5 531.2 147.6 538.4C159.7 545.6 174.8 545.9 187.2 539.1L523.2 355.1C536 348.1 544 334.6 544 320C544 305.4 536 291.9 523.2 284.9L187.2 100.9z"></path></svg> Ãcouter') 97 $('.rg-play').html('<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20.64" height="25" x="0px" y="0px" viewBox="0 0 17.5 21.2" xml:space="preserve" fill="#bbbcbf"><path d="M0,0l17.5,10.9L0,21.2V0z"></path></svg>'); 98 $('.mobile-live') 99 .html('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><path fill="#272727" d="M187.2 100.9C174.8 94.1 159.8 94.4 147.6 101.6C135.4 108.8 128 121.9 128 136L128 504C128 518.1 135.5 531.2 147.6 538.4C159.7 545.6 174.8 545.9 187.2 539.1L523.2 355.1C536 348.1 544 334.6 544 320C544 305.4 536 291.9 523.2 284.9L187.2 100.9z"></path></svg> Ãcouter') 100 .removeClass('is-playing'); 101 102 $('.play-button-mask').addClass('pause'); 103 104 $('.audio').removeClass('pause'); 105 106 if (self.mode === "podcast" && self.currentPodcast) { 107 108 document.dispatchEvent( 109 new CustomEvent("podcastPause", { 110 detail: self.currentPodcast 111 }) 112 ); 113 114 } 115 116 117 }); 118 119 $('.rg-progress-hitbox').on('click', function (e) { 120 121 if (!self.audio.duration) { 122 return; 123 } 124 125 const rect = this.getBoundingClientRect(); 126 127 const x = e.clientX - rect.left; 128 129 const percent = Math.max( 130 0, 131 Math.min(1, x / rect.width) 132 ); 133 134 self.audio.currentTime = percent * self.audio.duration; 135 136 self.updateProgress(); 137 138 }); 139 140 $('.rg-volume-hitbox').on('click', function (e) { 141 142 const rect = this.getBoundingClientRect(); 143 144 const y = rect.bottom - e.clientY; 145 146 const volume = Math.max( 147 0,
148 Math.min(1, y / rect.height) 149 ); 150 151 RGPlayer.audio.volume = volume; 152 153 RGPlayer.updateVolume(); 154 155 }); 156 157 158 159 160 161 162 // Fin 163 this.audio.addEventListener('ended', function () { 164 165 $('#rg-player').removeClass('is-playing'); 166 167 $('.rg-live-status').removeClass('is-live'); 168 169 $('.rg-play').text('â¶'); 170 171 }); 172 173 $('.rg-play').on('click', function () { 174 175 self.toggle(); 176 177 }); 178 179 180 $('.rg-back15').on('click', function () { 181 182 self.audio.currentTime -= 15; 183 184 }); 185 186 187 $('.rg-forward15').on('click', function () { 188 189 self.audio.currentTime += 15; 190 191 }); 192 193 this.audio.addEventListener('timeupdate', function () { 194 195 $('.rg-current').text( 196 self.formatTime(self.audio.currentTime) 197 ); 198 199 self.updateProgress(); 200 201 if (self.mode === "podcast" && self.currentPodcast) { 202 203 document.dispatchEvent( 204 new CustomEvent("podcastProgress", { 205 detail: { 206 source: self.currentPodcast.source, 207 percent: 208 (self.audio.currentTime / self.audio.duration) * 100 209 } 210 }) 211 ); 212 213 } 214 215 216 }); 217 218 219 220 this.audio.addEventListener('loadedmetadata', function () { 221 222 if (self.audio.duration != 'Infinity') { 223 $('.rg-duration').text( 224 self.formatTime(self.audio.duration) 225 ); 226 } 227 228 }); 229 230 let draggingProgress = false; 231 232 $('.rg-progress-hitbox, .rg-progress-dot').on('pointerdown', function (e) { 233 234 if (!self.audio.duration) return; 235 236 draggingProgress = true; 237 238 updateProgressFromPointer(e); 239 240 }); 241 242 $(document).on('pointermove', function (e) { 243 244 if (!draggingProgress) return; 245 246 updateProgressFromPointer(e); 247 248 }); 249 250 $(document).on('pointerup pointercancel', function () { 251 252 draggingProgress = false; 253 254 }); 255 256 function updateProgressFromPointer(e) { 257 258 const rect = $('.rg-progress-hitbox')[0].getBoundingClientRect(); 259 260 const x = e.clientX - rect.left; 261 262 const percent = Math.max( 263 0, 264 Math.min(1, x / rect.width) 265 ); 266 267 self.audio.currentTime = percent * self.audio.duration; 268 269 self.updateProgress(); 270 271 } 272 273 let draggingVolume = false; 274 275 $('.rg-volume-hitbox, .rg-volume-thumb').on('pointerdown', function (e) { 276 277 draggingVolume = true; 278 279 updateVolumeFromPointer(e); 280 281 }); 282 283 $(document).on('pointermove', function (e) { 284 285 if (!draggingVolume) return; 286 287 updateVolumeFromPointer(e); 288 289 }); 290 291 $(document).on('pointerup pointercancel', function () { 292 293 draggingVolume = false; 294 295 }); 296 297 function updateVolumeFromPointer(e) { 298 299 const rect = $('.rg-volume-hitbox')[0].getBoundingClientRect(); 300 301 const y = rect.bottom - e.clientY; 302 303 const volume = Math.max( 304 0, 305 Math.min(1, y / rect.height) 306 ); 307 308 self.audio.volume = volume; 309 310 self.updateVolume(); 311 312 } 313 314 $('.rg-progress-dot, .rg-volume-thumb').on('pointerdown', function (e) { 315 this.setPointerCapture(e.pointerId); 316 }); 317 318 $('.toggle-mobile').on('click', function () { 319 320 if ($('#header-player').hasClass('mobile-open')) { 321 self.hidePlayer(); 322 } else { 323 self.showPlayer(); 324 } 325 326 }); 327 328 document.addEventListener("openPodcastPlayer", (e) => { 329 330 const data = e.detail; 331 332 RGPlayer.playPodcast(data); 333 334 }); 335 336 337 338 339 340 }, 341 342 playLive() { 343 344 this.mode = 'live'; 345 346 // Si le live est déjà chargé 347 if (this.audio.src === this.liveUrl) { 348 349 this.toggle(); 350 351 return; 352 353 } 354 355 this.audio.src = this.liveUrl; 356 357 this.audio.load(); 358 359 this.audio.play(); 360 361 $('#rg-player') 362 .removeClass('mode-podcast') 363 .addClass('mode-live'); 364 365 }, 366 367 playPodcast(data) { 368 369 this.mode = 'podcast'; 370 371 // Même podcast déjà chargé 372 if ( 373 this.currentPodcast && 374 this.currentPodcast.source === data.source && 375 this.audio.src === data.audio 376 ) { 377 378 this.audio.play(); 379 380 return; 381 382 } 383 384 385 // Nouveau podcast 386 this.currentPodcast = data; 387 388 this.audio.src = data.audio; 389 this.audio.load(); 390 391 this.audio.play(); 392 393 console.log('avant mode'); 394 395 $('.mobile-live').removeClass('is-playing'); 396 397 $('#rg-player') 398 .removeClass('mode-live') 399 .addClass('mode-podcast'); 400 401 console.log('apres mode'); 402 403 $('.rg-title').text(data.title); 404 $('.rg-category').text(data.category); 405 $('.rg-duration').text(data.duration); 406 407 this.initMarquee(); 408 409 $('.rg-live-status') 410 .removeClass('is-live'); 411 412 413 document.dispatchEvent( 414 new CustomEvent("podcastPlay", { 415 detail: data 416 }) 417 ); 418 419 }, 420 421 422 423 424 425 toggle() { 426 427 if (this.audio.paused) { 428 429 this.audio.play(); 430 431 } else { 432 433 this.audio.pause(); 434 435 } 436 437 }, 438 439 initMarquee() { 440 441 $('.rg-title, .rg-live-title').each(function () { 442 443 const $el = $(this); 444 445 $el.off('.marquee'); 446 447 $el.on('mouseenter.marquee', function () { 448 449 // déjà en cours 450 if ($el.hasClass('is-marquee')) { 451 return; 452 } 453 454 // overflow ? 455 if (this.scrollWidth <= this.clientWidth) { 456 return; 457 } 458 459 $el.addClass('is-marquee'); 460 461 const text = $el.text(); 462 463 $el.html(` 464 <div class="rg-title-marquee animate">
465 <span>${text}</span> 466 <span>${text}</span> 467 </div> 468 `); 469 470 $el.find('.rg-title-marquee') 471 .one('animationend', function () { 472 473 $el.removeClass('is-marquee'); 474 $el.text(text); 475 476 }); 477 478 }); 479 480 }); 481 482 }, 483 484 485 formatTime(seconds) { 486 487 if (!seconds || isNaN(seconds)) { 488 return '00:00'; 489 } 490 491 let minutes = Math.floor(seconds / 60); 492 let secs = Math.floor(seconds % 60); 493 494 return ( 495 String(minutes).padStart(2, '0') + 496 ':' + 497 String(secs).padStart(2, '0') 498 ); 499 500 }, 501 502 updateLiveTitle() { 503 504 const self = this; 505 506 $.ajax({ 507 url: 'https://radiogrenouille.com/log/onAir.xml', 508 dataType: 'xml', 509 cache: false, 510 success(xml) { 511 512 const artiste = $(xml).find('artiste').text().trim(); 513 const titre = $(xml).find('titre').text().trim(); 514 515 let text = titre; 516 517 if (artiste) { 518 text = artiste + ' â ' + titre; 519 } 520 521 if (text === self.liveTitle) { 522 return; 523 } 524 525 self.liveTitle = text; 526 527 $('.rg-live-title').text(text); 528 529 self.initMarquee(); 530 531 } 532 }); 533 534 }, 535 536 537 538 updateProgressLayout() { 539 540 const $header = $('#header-player'); 541 542 if (!$header.length) { 543 return; 544 } 545 546 const left = $header.offset().left; 547 const width = window.innerWidth - left; 548 549 $('.rg-progress').css({ 550 width: width + 'px' 551 }); 552 553 }, 554 555 updateVolume() { 556 557 const volume = this.audio.volume; 558 const percent = volume * 100; 559 560 $('.rg-volume-fill').css('height', percent + '%'); 561 $('.rg-volume-thumb').css('bottom', percent + '%'); 562 563 }, 564 565 566 567 updateProgress() { 568 569 if (!this.audio.duration) { 570 return; 571 } 572 573 const percent = (this.audio.currentTime / this.audio.duration) * 100; 574 575 $('.rg-progress-bar').css('width', percent + '%'); 576 $('.rg-progress-dot').css('left', percent + '%'); 577 578 }, 579 580 581 582 updateCardsState() { 583 584 let currentAudio = this.audio.currentSrc; 585 586 587 // Reset slider 588 $('.play-button-mask').addClass('pause'); 589 590 591 // Reset cartes podcasts 592 $('.audio').removeClass('pause'); 593 594 595 $('.carousel-item, .audio').each(function () { 596 597 let audio = $(this).data('audio'); 598 599 600 if (audio && currentAudio.includes(audio)) { 601 602 603 // Slider 604 $(this) 605 .closest('.carousel-item') 606 .find('.play-button-mask') 607 .removeClass('pause'); 608 609 610 // Grilles podcasts 611 $(this) 612 .addClass('pause'); 613 614 615 } 616 617 }); 618 619 }, 620 621 showPlayer() { 622 623 if (window.innerWidth > 768) { 624 return; 625 } 626 627 $('#header-player') 628 .css({ 629 top: 'calc(100dvh - ' + $('#header-player').outerHeight() + 'px', 630 }) 631 .addClass('mobile-open'); 632 633 $('.toggle-mobile').html(` 634 <svg width="16" height="16" viewBox="0 0 16 16"> 635 <polyline 636 points="2,5 8,11 14,5" 637 fill="none" 638 stroke="#000" 639 stroke-width="2" 640 /> 641 </svg> 642 `); 643 644 }, 645 646 hidePlayer() { 647 648 if (window.innerWidth > 768) { 649 return; 650 } 651 652 $('#header-player') 653 .css({ 654 top: '100dvh', 655 }) 656 .removeClass('mobile-open'); 657 658 $('.toggle-mobile').html(` 659 <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" x="0px" y="0px" viewBox="0 0 17 17" enable-background="new 0 0 17 17" xml:space="preserve" class="audioBar"> 660 <rect x="0" width="2" height="16" transform="translate(0)"> 661 <animate attributeName="height" attributeType="XML" dur="1s" values="2;16;
6612" repeatCount="indefinite"></animate> 662 </rect> 663 <rect x="5" width="2" height="16" transform="translate(0)"> 664 <animate attributeName="height" attributeType="XML" dur="1s" values="2;16;2" repeatCount="indefinite" begin="0.3s"></animate> 665 </rect> 666 <rect x="10" width="2" height="16" transform="translate(0)"> 667 <animate attributeName="height" attributeType="XML" dur="1s" values="2;16;2" repeatCount="indefinite" begin="0.5s"></animate> 668 </rect> 669 <rect x="15" width="2" height="16" transform="translate(0)"> 670 <animate attributeName="height" attributeType="XML" dur="1s" values="2;16;2" repeatCount="indefinite" begin="0.3s"></animate> 671 </rect> 672 </svg> 673 `); 674 675 }, 676 677 678 679 680 }; 681 682 683 $(function () { 684 685 RGPlayer.init(); 686 687 }); 688 689})(jQuery);
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.