<div class=”canelaradio-player”>
<!– ==========================================
REPRODUCTOR
=========================================== –>
<div class=”cr-main”>
<div class=”cr-live”>
<span class=”cr-live-dot”></span>
AL AIRE AHORA
</div>
<div class=”cr-player-content”>
<button
class=”cr-play-button”
id=”crPlayButton”
type=”button”
aria-label=”Reproducir”
>
<span class=”cr-play-icon”>▶</span>
<span class=”cr-pause-icon”>❚❚</span>
</button>
<div class=”cr-info”>
<h2 id=”crStationName”>
Pichincha 106.5 FM
</h2>
<p id=”crProgram”>
Radiación Temprana
</p>
<p
class=”cr-frequency”
id=”crFrequency”
>
Pichincha 106.5 FM
</p>
</div>
<div class=”cr-volume”>
<span class=”cr-volume-icon”>
🔊
</span>
<input
type=”range”
id=”crVolume”
min=”0″
max=”1″
step=”0.01″
value=”1″
aria-label=”Volumen”
>
</div>
</div>
<div
class=”cr-status”
id=”crStatus”
>
Presiona ▶ para escuchar
</div>
<audio
id=”crAudio”
preload=”none”
></audio>
</div>
<!– ==========================================
EMISORAS
=========================================== –>
<div class=”cr-selector”>
<div class=”cr-selector-title”>
ELIGE TU EMISORA
<span>→</span>
</div>
<div class=”cr-carousel-wrapper”>
<button
class=”cr-arrow cr-arrow-left”
id=”crPrev”
type=”button”
aria-label=”Emisora anterior”
>
‹
</button>
<div
class=”cr-stations”
id=”crStations”
></div>
<button
class=”cr-arrow cr-arrow-right”
id=”crNext”
type=”button”
aria-label=”Siguiente emisora”
>
›
</button>
</div>
</div>
</div>
<style>
/* =========================================================
CANELA RADIO
ESTILOS GENERALES
========================================================= */
.canelaradio-player {
–cr-green: #073f2d;
–cr-green-light: #0b523b;
–cr-orange: #f5a623;
–cr-orange-light: #ffd89c;
width: 100vw;
max-width: 100vw;
/*
* ESTA ES LA CLAVE PARA SALIR
* DEL CONTENEDOR DE ELEMENTOR
*/
margin-left: calc(50% – 50vw);
margin-right: calc(50% – 50vw);
padding: 40px 5%;
background: #f5a623;
box-sizing: border-box;
font-family:
Arial,
Helvetica,
sans-serif;
overflow: hidden;
}
/* =========================================================
BOX SIZING
========================================================= */
.canelaradio-player *,
.canelaradio-player *::before,
.canelaradio-player *::after {
box-sizing: border-box;
}
/* =========================================================
REPRODUCTOR PRINCIPAL
========================================================= */
.cr-main {
width: 100%;
max-width: 1400px;
margin-left: auto;
margin-right: auto;
padding:
28px 32px 22px;
background:
var(–cr-green);
border-radius: 22px;
color: white;
box-shadow:
0 12px 30px rgba(0,0,0,.10);
}
/* =========================================================
AL AIRE
========================================================= */
.cr-live {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 22px;
color:
#e8eee9;
font-size: 12px;
font-weight: 700;
letter-spacing: 1.5px;
}
.cr-live-dot {
width: 7px;
height: 7px;
flex: 0 0 7px;
border-radius: 50%;
background:
#ff594f;
box-shadow:
0 0 0 4px
rgba(255,89,79,.12);
animation:
crPulse 1.7s infinite;
}
@keyframes crPulse {
0% {
box-shadow:
0 0 0 0
rgba(255,89,79,.45);
}
70% {
box-shadow:
0 0 0 7px
rgba(255,89,79,0);
}
100% {
box-shadow:
0 0 0 0
rgba(255,89,79,0);
}
}
/* =========================================================
CONTENIDO PRINCIPAL
========================================================= */
.cr-player-content {
width: 100%;
display: flex;
align-items: center;
gap: 22px;
min-width: 0;
}
/* =========================================================
PLAY
========================================================= */
.cr-play-button {
width: 64px;
height: 64px;
min-width: 64px;
flex:
0 0 64px;
border: none;
border-radius: 50%;
padding: 0;
background:
#f15b3a;
color: white;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
font-size: 23px;
transition:
transform .2s ease,
background .2s ease,
box-shadow .2s ease;
}
.cr-play-button:hover {
transform:
scale(1.06);
background:
#ff6845;
box-shadow:
0 7px 18px
rgba(0,0,0,.18);
}
.cr-play-icon {
display: block;
margin-left: 4px;
}
.cr-pause-icon {
display: none;
font-size: 17px;
}
.cr-play-button.playing
.cr-play-icon {
display: none;
}
.cr-play-button.playing
.cr-pause-icon {
display: block;
}
/* =========================================================
INFORMACIÓN
========================================================= */
.cr-info {
flex:
1 1 auto;
width: 0;
min-width: 0;
overflow: hidden;
}
.cr-info h2 {
margin:
0 0 6px;
padding: 0;
color: white;
font-size: 27px;
line-height: 1.15;
font-weight: 800;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.cr-info p {
margin: 0;
padding: 0;
}
#crProgram {
color:
rgba(255,255,255,.82);
font-size: 14px;
margin-bottom: 5px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.cr-frequency {
color:
rgba(255,255,255,.58);
font-size: 13px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* =========================================================
VOLUMEN
========================================================= */
.cr-volume {
flex:
0 0 auto;
display: flex;
align-items: center;
gap: 10px;
}
.cr-volume-icon {
font-size: 18px;
}
.cr-volume input {
width: 100px;
max-width: 100px;
accent-color:
var(–cr-orange);
cursor: pointer;
}
/* =========================================================
ESTADO
========================================================= */
.cr-status {
margin-top: 18px;
color:
rgba(255,255,255,.55);
font-size: 12px;
}
.cr-status.playing {
color:
#a8e4c5;
}
/* =========================================================
AUDIO
========================================================= */
#crAudio {
display: none;
}
/* =========================================================
SELECTOR
========================================================= */
.cr-selector {
width: 100%;
max-width: 1400px;
margin:
22px auto 0;
}
.cr-selector-title {
margin-bottom: 12px;
color:
#17352c;
font-size: 12px;
font-weight: 800;
letter-spacing: 1.2px;
}
.cr-selector-title span {
color:
var(–cr-orange);
margin-left: 3px;
}
/* =========================================================
CARRUSEL
========================================================= */
.cr-carousel-wrapper {
width: 100%;
min-width: 0;
display: flex;
align-items: center;
gap: 8px;
}
.cr-stations {
width: 0;
min-width: 0;
flex:
1 1 auto;
display: flex;
gap: 10px;
overflow-x: auto;
overflow-y: hidden;
padding:
3px 2px 8px;
scroll-behavior: smooth;
scrollbar-width: none;
-webkit-overflow-scrolling:
touch;
}
.cr-stations::-webkit-scrollbar {
display: none;
}
/* =========================================================
BOTONES EMISORAS
========================================================= */
.cr-station {
flex:
0 0 auto;
display: flex;
align-items: center;
gap: 6px;
padding:
12px 20px;
border: none;
border-radius: 30px;
background:
#ffe2b4;
color:
#49331d;
font-family:
Arial,
Helvetica,
sans-serif;
font-size: 13px;
white-space: nowrap;
cursor: pointer;
transition:
background .2s ease,
color .2s ease,
transform .2s ease;
}
.cr-station span {
font-weight: 600;
}
.cr-station strong {
font-weight: 800;
}
.cr-station:hover {
background:
#ffd397;
transform:
translateY(-2px);
}
.cr-station.active {
background:
var(–cr-green);
color: white;
}
/* =========================================================
FLECHAS
========================================================= */
.cr-arrow {
width: 36px;
height: 36px;
min-width: 36px;
flex:
0 0 36px;
border: none;
border-radius: 50%;
background:
var(–cr-green);
color: white;
display: flex;
align-items: center;
justify-content: center;
font-size: 23px;
line-height: 1;
cursor: pointer;
}
.cr-arrow:hover {
background:
var(–cr-green-light);
}
/* =========================================================
TABLET
========================================================= */
@media only screen and (max-width: 767px) {
/*
* FULL WIDTH REAL EN MÓVIL
*
* El reproductor sale del contenedor
* limitado de Elementor.
*/
.canelaradio-player {
width: 100vw !important;
max-width: 100vw !important;
margin-left:
calc(50% – 50vw) !important;
margin-right:
calc(50% – 50vw) !important;
padding:
24px 16px !important;
overflow:
hidden !important;
box-sizing:
border-box !important;
}
/*
* PLAYER INTERNO
*/
.cr-main {
width: 100% !important;
max-width: none !important;
margin:
0 !important;
padding:
24px 20px 18px;
border-radius:
18px;
}
/*
* CONTENIDO
*/
.cr-player-content {
width:
100%;
min-width:
0;
gap:
14px;
}
/*
* PLAY
*/
.cr-play-button {
width:
58px;
height:
58px;
min-width:
58px;
flex:
0 0 58px;
font-size:
19px;
}
/*
* INFORMACIÓN
*/
.cr-info {
width:
0;
min-width:
0;
flex:
1 1 auto;
overflow:
hidden;
}
.cr-info h2 {
font-size:
21px;
line-height:
1.15;
white-space:
nowrap;
overflow:
hidden;
text-overflow:
ellipsis;
}
#crProgram {
font-size:
13px;
white-space:
nowrap;
overflow:
hidden;
text-overflow:
ellipsis;
}
.cr-frequency {
font-size:
12px;
white-space:
nowrap;
overflow:
hidden;
text-overflow:
ellipsis;
}
/*
* OCULTAR VOLUMEN EN CELULAR
*/
.cr-volume {
display:
none;
}
/*
* ESTADO
*/
.cr-status {
margin-top:
15px;
font-size:
11px;
}
/*
* SELECTOR
*/
.cr-selector {
width:
100%;
max-width:
none;
margin:
20px 0 0;
}
/*
* CARRUSEL
*/
.cr-carousel-wrapper {
width:
100%;
min-width:
0;
}
.cr-stations {
width:
0;
min-width:
0;
flex:
1 1 auto;
gap:
8px;
overflow-x:
auto;
overflow-y:
hidden;
padding:
3px 2px 8px;
}
/*
* OCULTAR FLECHAS
* En móvil se usa swipe.
*/
.cr-arrow {
display:
none;
}
/*
* BOTONES
*/
.cr-station {
padding:
11px 16px;
font-size:
12px;
}
.cr-selector-title {
margin-bottom:
9px;
font-size:
10px;
}
}
/* =========================================================
CELULAR
========================================================= */
@media only screen and (max-width: 480px) {
.canelaradio-player {
width:
100vw !important;
max-width:
100vw !important;
margin-left:
calc(50% – 50vw) !important;
margin-right:
calc(50% – 50vw) !important;
padding:
20px 12px 24px !important;
}
.cr-main {
width:
100% !important;
padding:
21px 17px 17px;
border-radius:
17px;
}
.cr-live {
margin-bottom:
17px;
font-size:
10px;
letter-spacing:
1.1px;
}
.cr-player-content {
gap:
12px;
}
.cr-play-button {
width:
52px;
height:
52px;
min-width:
52px;
flex:
0 0 52px;
font-size:
18px;
}
.cr-info h2 {
font-size:
19px;
}
#crProgram {
font-size:
12px;
}
.cr-frequency {
font-size:
11px;
}
.cr-status {
margin-top:
13px;
font-size:
10px;
}
.cr-selector {
margin-top:
18px;
}
.cr-station {
padding:
10px 15px;
font-size:
11px;
}
}
/* =========================================================
CELULAR MUY PEQUEÑO
========================================================= */
@media only screen and (max-width: 360px) {
.canelaradio-player {
padding-left:
10px !important;
padding-right:
10px !important;
}
.cr-main {
padding:
19px 14px 15px;
}
.cr-play-button {
width:
48px;
height:
48px;
min-width:
48px;
flex:
0 0 48px;
}
.cr-info h2 {
font-size:
17px;
}
}
</style>
<script>
(function () {
/* =========================================================
EMISORAS
========================================================= */
const stations = [
{
name: “Pichincha 106.5 FM”,
frequency: “Pichincha 106.5 FM”,
program: “Radiación Temprana”,
stream: “https://canelaradio.makrodigital.com:9280/stream”
},
{
name: “Guayas 90.5 FM”,
frequency: “Guayas 90.5 FM”,
program: “Canela Radio Guayas”,
stream: “https://canelaradio.makrodigital.com:9290/stream”
},
{
name: “Azuay y Cañar 107.3 FM”,
frequency: “Azuay y Cañar 107.3 FM”,
program: “Canela Radio Azuay y Cañar”,
stream: “https://cuenca.canelaradio.makrodigital.com:9270/stream”
},
{
name: “Tungurahua 106.5 FM”,
frequency: “Tungurahua 106.5 FM”,
program: “Canela Radio Tungurahua”,
stream: “https://canelaradio.makrodigital.com:9260/stream”
},
{
name: “Imbabura 92.7 FM”,
frequency: “Imbabura 92.7 FM”,
program: “Canela Radio Imbabura”,
stream: “https://canelaradio.makrodigital.com:9250/stream”
},
{
name: “Carchi 92.9 FM”,
frequency: “Carchi 92.9 FM”,
program: “Canela Radio Carchi”,
stream: “http://canelaradio.makrodigital.com:9470/stream”
},
{
name: “Loja 96.9 / 98.9 FM”,
frequency: “Loja 96.9 FM y 98.9 FM”,
program: “Canela Radio Loja”,
stream: “https://canelaradio.makrodigital.com:9190/stream”
},
{
name: “Sucumbíos 94.5 FM”,
frequency: “Sucumbíos 94.5 FM”,
program: “Canela Radio Sucumbíos”,
stream: “https://canelaradio.makrodigital.com:9230/stream”
},
{
name: “Morona Santiago 91.7 FM”,
frequency: “Morona Santiago 91.7 FM”,
program: “Canela Radio Morona Santiago”,
stream: “https://canelaradio.makrodigital.com:9220/stream”
},
{
name: “El Oro 100.7 FM”,
frequency: “El Oro 100.7 FM”,
program: “Canela Radio El Oro”,
stream: “https://canelaradio.makrodigital.com:9240/stream”
},
{
name: “Manabí 102.5 FM”,
frequency: “Manabí 102.5 FM”,
program: “Canela Radio Manabí”,
stream: “https://manabi.canelaradio.makrodigital.com:9216/stream”
},
{
name: “Chimborazo 94.5 FM”,
frequency: “Chimborazo 94.5 FM”,
program: “Canela Radio Chimborazo”,
stream: “https://canelaradio.makrodigital.com:9212/stream”
},
{
name: “Napo 106.1 FM”,
frequency: “Napo 106.1 FM”,
program: “Canela Radio Napo”,
stream: “https://canelaradio.makrodigital.com:9202/stream”
},
{
name: “Bolívar 90.3 FM”,
frequency: “Bolívar 90.3 FM”,
program: “Canela Radio Bolívar”,
stream: “https://canelaradio.makrodigital.com:9208/stream”
},
{
name: “Los Ríos 90.7 FM”,
frequency: “Los Ríos 90.7 FM”,
program: “Canela Radio Los Ríos”,
stream: “https://canelaradio.makrodigital.com:9208/stream”
},
{
name: “Pastaza 101.9 FM”,
frequency: “Pastaza 101.9 FM”,
program: “Canela Radio Pastaza”,
stream: “https://canelaradio.makrodigital.com:9180/stream”
}
];
/* =========================================================
ELEMENTOS
========================================================= */
const audio =
document.getElementById(“crAudio”);
const playButton =
document.getElementById(“crPlayButton”);
const stationName =
document.getElementById(“crStationName”);
const program =
document.getElementById(“crProgram”);
const frequency =
document.getElementById(“crFrequency”);
const volume =
document.getElementById(“crVolume”);
const status =
document.getElementById(“crStatus”);
const stationsContainer =
document.getElementById(“crStations”);
const prevButton =
document.getElementById(“crPrev”);
const nextButton =
document.getElementById(“crNext”);
/* =========================================================
CREAR BOTONES
========================================================= */
stations.forEach(
function (station, index) {
const button =
document.createElement(“button”);
button.type =
“button”;
button.className =
“cr-station”;
button.dataset.index =
index;
/*
* Separar nombre y frecuencia
*/
let displayName =
station.name;
let displayFrequency =
“”;
const parts =
station.name.split(” “);
if (parts.length >= 3) {
const possibleFrequency =
parts.slice(-2).join(” “);
if (
possibleFrequency.includes(“FM”)
) {
displayFrequency =
possibleFrequency;
displayName =
parts
.slice(0, -2)
.join(” “);
}
}
button.innerHTML =
“<span>” +
displayName +
“</span>” +
(
displayFrequency
?
“<strong>” +
displayFrequency
.replace(” FM”, “”) +
“</strong>”
:
“”
);
button.addEventListener(
“click”,
function () {
loadStation(
index,
true
);
}
);
stationsContainer.appendChild(
button
);
}
);
const stationButtons =
document.querySelectorAll(
“.cr-station”
);
/* =========================================================
CARGAR EMISORA
========================================================= */
function loadStation(
index,
shouldPlay
) {
if (!stations[index]) {
return;
}
const station =
stations[index];
stationName.textContent =
station.name;
program.textContent =
station.program;
frequency.textContent =
station.frequency;
/*
* Activar botón
*/
stationButtons.forEach(
function (button, i) {
button.classList.toggle(
“active”,
i === index
);
}
);
/*
* Detener audio anterior
*/
audio.pause();
audio.removeAttribute(
“src”
);
audio.load();
/*
* Nuevo stream
*/
audio.src =
station.stream;
audio.load();
/*
* Estado
*/
playButton.classList.remove(
“playing”
);
status.classList.remove(
“playing”
);
status.textContent =
“Presiona ▶ para escuchar”;
/*
* Centrar botón seleccionado
*/
const activeButton =
stationButtons[index];
if (activeButton) {
activeButton.scrollIntoView({
behavior:
“smooth”,
block:
“nearest”,
inline:
“center”
});
}
/*
* Reproducir automáticamente
* cuando se selecciona una emisora
*/
if (shouldPlay) {
const promise =
audio.play();
if (
promise !== undefined
) {
promise.then(
function () {
playButton.classList.add(
“playing”
);
status.textContent =
“● Escuchando en vivo”;
status.classList.add(
“playing”
);
}
).catch(
function () {
status.textContent =
“Presiona ▶ para reproducir”;
}
);
}
}
}
/* =========================================================
PLAY / PAUSE
========================================================= */
playButton.addEventListener(
“click”,
function () {
if (audio.paused) {
const promise =
audio.play();
if (
promise !== undefined
) {
promise.then(
function () {
playButton.classList.add(
“playing”
);
status.textContent =
“● Escuchando en vivo”;
status.classList.add(
“playing”
);
}
).catch(
function () {
status.textContent =
“No se pudo iniciar el streaming”;
status.classList.remove(
“playing”
);
}
);
}
} else {
audio.pause();
playButton.classList.remove(
“playing”
);
status.textContent =
“Streaming pausado”;
status.classList.remove(
“playing”
);
}
}
);
/* =========================================================
EVENTO PLAYING
========================================================= */
audio.addEventListener(
“playing”,
function () {
playButton.classList.add(
“playing”
);
status.textContent =
“● Escuchando en vivo”;
status.classList.add(
“playing”
);
}
);
/* =========================================================
EVENTO PAUSE
========================================================= */
audio.addEventListener(
“pause”,
function () {
playButton.classList.remove(
“playing”
);
}
);
/* =========================================================
ESPERANDO STREAM
========================================================= */
audio.addEventListener(
“waiting”,
function () {
status.textContent =
“Conectando con la emisora…”;
status.classList.remove(
“playing”
);
}
);
/* =========================================================
ERROR STREAM
========================================================= */
audio.addEventListener(
“error”,
function () {
playButton.classList.remove(
“playing”
);
status.classList.remove(
“playing”
);
status.textContent =
“No se pudo conectar con esta emisora”;
}
);
/* =========================================================
VOLUMEN
========================================================= */
volume.addEventListener(
“input”,
function () {
audio.volume =
Number(volume.value);
}
);
/* =========================================================
FLECHA IZQUIERDA
========================================================= */
prevButton.addEventListener(
“click”,
function () {
stationsContainer.scrollBy({
left:
-300,
behavior:
“smooth”
});
}
);
/* =========================================================
FLECHA DERECHA
========================================================= */
nextButton.addEventListener(
“click”,
function () {
stationsContainer.scrollBy({
left:
300,
behavior:
“smooth”
});
}
);
/* =========================================================
INICIAR
========================================================= */
loadStation(
0,
false
);
})();
</script>
