JavaScript Agregados

person_checkAarón Abraham Mendoza Salazar history

Transparencia sobre el Código del Sitio

Para garantizar una total transparencia con nuestros visitantes, en esta página se detallan los diferentes scripts y fragmentos de código que se ejecutan en este blog. Cada uno tiene una función específica, desde mejorar la experiencia del usuario hasta realizar análisis de visitas y gestionar la monetización.

Codificador de HTML

Esta función JavaScript, encodeHtmlEntities(), toma el texto de un área de entrada (textarea) y lo procesa para convertir caracteres especiales de HTML (como <, >, &, comillas dobles y simples) en sus respectivas entidades HTML (como &lt;, &gt;, &amp;).


<script>
        function encodeHtmlEntities() {
            // Obtener el elemento del textarea
            const textArea = document.getElementById('html-input');
            let content = textArea.value;

            // El orden de reemplazo es crucial. 
            // 1. Reemplazar '&' PRIMERO para evitar el doble "escapado".
            content = content.replace(/&/g, '&amp;');
            // 2. Reemplazar los demás caracteres.
            content = content.replace(/</g, '&lt;');
            content = content.replace(/>/g, '&gt;');
            content = content.replace(/"/g, '&quot;');
            content = content.replace(/'/g, '&#039;');

            // Asignar el nuevo valor al textarea
            textArea.value = content;

            // Seleccionar el texto para que el usuario pueda copiarlo fácilmente
            textArea.focus();
            textArea.select();
        }
    </script>

Reproductor de Audio

Este script gestiona la funcionalidad del reproductor de audio personalizado utilizado en las publicaciones de pódcast. Permite controlar la reproducción, pausa, volumen, barra de progreso y modo de pantalla completa.


<script>
  const audio = document.getElementById('audio');
  const playPauseBtn = document.getElementById('playPauseBtn');
  const progressBar = document.getElementById('progressBar');
  const currentTimeSpan = document.getElementById('currentTime');
  const durationSpan = document.getElementById('duration');
  const fullscreenBtn = document.getElementById('fullscreenBtn');
  const audioPlayer = document.querySelector('.audio-player');
  const coverPlayBtn = document.getElementById('coverPlayBtn');

  const restartBtn = document.getElementById('restartBtn');
  const rewindBtn = document.getElementById('rewindBtn');
  const forwardBtn = document.getElementById('forwardBtn');
  const endBtn = document.getElementById('endBtn');

  const playPauseIcon = playPauseBtn.querySelector('.material-symbols-outlined');
  const fullscreenIcon = fullscreenBtn.querySelector('.material-symbols-outlined');
  const coverPlayIcon = coverPlayBtn.querySelector('.material-symbols-outlined');

  function formatTime(seconds) {
    const minutes = Math.floor(seconds / 60);
    const remainingSeconds = Math.floor(seconds % 60);
    return `${minutes}:${remainingSeconds < 10 ? '0' : ''}${remainingSeconds}`;
  }

  function updatePlayerUI() {
    playPauseIcon.textContent = audio.paused ? 'play_arrow' : 'pause';
    coverPlayIcon.textContent = audio.paused ? 'play_arrow' : 'pause';

    if (audio.duration) {
      progressBar.value = (audio.currentTime / audio.duration) * 100;
    } else {
      progressBar.value = 0;
    }
    currentTimeSpan.textContent = formatTime(audio.currentTime);
  }

  function togglePlayPause() {
    if (audio.paused) {
      audio.play();
    } else {
      audio.pause();
    }
    updatePlayerUI();
  }

  playPauseBtn.addEventListener('click', togglePlayPause);
  coverPlayBtn.addEventListener('click', togglePlayPause);

  audio.addEventListener('play', updatePlayerUI);
  audio.addEventListener('pause', updatePlayerUI);
  audio.addEventListener('ended', updatePlayerUI);
  audio.addEventListener('timeupdate', updatePlayerUI);

  audio.addEventListener('loadedmetadata', () => {
    durationSpan.textContent = formatTime(audio.duration);
    progressBar.max = 100;
    updatePlayerUI();
  });

  progressBar.addEventListener('click', (e) => {
    if (!audio.duration) return;
    const rect = progressBar.getBoundingClientRect();
    const clickX = e.clientX - rect.left;
    const progressBarWidth = rect.width;
    const seekTime = (clickX / progressBarWidth) * audio.duration;
    audio.currentTime = seekTime;
  });

  fullscreenBtn.addEventListener('click', () => {
    if (document.fullscreenElement) {
      document.exitFullscreen();
    } else {
      if (audioPlayer.requestFullscreen) {
        audioPlayer.requestFullscreen();
      } else if (audioPlayer.mozRequestFullScreen) { /* Firefox */
        audioPlayer.mozRequestFullScreen();
      } else if (audioPlayer.webkitRequestFullscreen) { /* Chrome, Safari and Opera */
        audioPlayer.webkitRequestFullscreen();
      } else if (audioPlayer.msRequestFullscreen) { /* IE/Edge */
        audioPlayer.msRequestFullscreen();
      }
    }
  });

  document.addEventListener('fullscreenchange', () => {
    const isFullscreen = !!document.fullscreenElement;
    audioPlayer.classList.toggle('fullscreen', isFullscreen);
    fullscreenIcon.textContent = isFullscreen ? 'fullscreen_exit' : 'fullscreen';
  });
  // Añadir listeners para prefijos de navegadores si es necesario.
  
  restartBtn.addEventListener('click', () => {
    audio.currentTime = 0;
    updatePlayerUI();
  });

  rewindBtn.addEventListener('click', () => {
    audio.currentTime = Math.max(0, audio.currentTime - 10);
    updatePlayerUI();
  });

  forwardBtn.addEventListener('click', () => {
    audio.currentTime = Math.min(audio.duration, audio.currentTime + 10);
    updatePlayerUI();
  });

  if (endBtn) {
    endBtn.addEventListener('click', () => {
      audio.currentTime = audio.duration;
      updatePlayerUI();
      audio.pause();
    });
  }

  updatePlayerUI();
</script>

Cursiva en Citas (Estilo APA)

Este breve script aplica una clase CSS específica al segundo elemento de cita en una página, probablemente para dar un formato de estilo APA a las referencias de los pódcasts de manera automática.


<script>
  document.addEventListener("DOMContentLoaded", function () {
    const citas = document.querySelectorAll(".cursivaCitaReferencia");
    if (citas.length >= 2) {
      citas[0].classList.add("solo-segunda");
    }
  });
</script>

Datos Estructurados (Schema.org)

Este código implementa datos estructurados en formato JSON-LD. Su función es describir el contenido de la página (título, autor, fecha, etc.) de una manera que los motores de búsqueda como Google puedan entender fácilmente, lo que puede mejorar la apariencia de las publicaciones en los resultados de búsqueda (Schema.org, n.d.).


<script type='application/ld+json'>
{
  "@context": "https://schema.org",
  "@graph": [
    {
      "@type": "BlogPosting",
      "headline": "<data:post.title.escaped/>",
      "description": "<data:post.snippet/>",
      "image": "<data:post.featuredImage/>",
      "author": {
        "@type": "Person",
        "name": "<data:post.author.name/>"
      },
      "publisher": {
        "@type": "Organization",
        "name": "Blog de Comunicación y Literatura Creativa",
        "logo": {
          "@type": "ImageObject",
          "url": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_VkbqPpJS1HPaKHGiXKNhKETikRFH5pNin41uT1mlqaxICvt9CrCVmADVUpuPQSgnBIFA2X16eXR2en9FnzTV7cRc01JV7drAd4It1sOf-IAlQIAQm1TKQ77DJALCdhZ_utj1wf5AZXKmZHVCZ23talo1BMgsC0qNTE6vM_EonMO_O8X9CKIKKQejPwQQ/s1024/Logotipo%20Mendoza%20Salazar%20%281%29.png"
        }
      },
      "keywords": [<b:loop values='data:post.labels' var='label'>"${label}"<b:if cond='data:label.isLast != "true"'>,</b:if></b:loop>],
      "datePublished": "<data:post.date.iso8601/>",
      "dateModified": "<data:post.lastUpdated.iso8601/>",
      "mainEntityOfPage": {
        "@type": "WebPage",
        "@id": "<data:post.url/>"
      }
    },
    {
      "@type": "BreadcrumbList",
      "itemListElement": [
        {
          "@type": "ListItem",
          "position": 1,
          "name": "Inicio",
          "item": "<data:blog.homepageUrl/>"
        },
        <b:if cond='data:post.labels'>
        {
          "@type": "ListItem",
          "position": 2,
          "name": "<data:post.labels.first/>",
          "item": "<data:blog.homepageUrl/>search/label/<data:post.labels.first/>"
        },
        </b:if>
        {
          "@type": "ListItem",
          "position": 3,
          "name": "<data:post.title.escaped/>",
          "item": "<data:post.url/>"
        }
      ]
    }
  ]
}
</script>

Cuenta Regresiva para Estrenos

Este código se utiliza para mostrar una cuenta regresiva hasta una fecha y hora específicas. Una vez que la fecha ha pasado, muestra un mensaje final. Es útil para generar expectación ante un nuevo lanzamiento.


<script>
  var end = new Date('05/15/2022 7:05 AM');
  var _second = 1000;
  var _minute = _second * 60;
  var _hour = _minute * 60;
  var _day = _hour * 24;
  var timer;

  function showRemaining() {
    var now = new Date();
    var distance = end - now;
    if (distance < 0) {
      clearInterval(timer);
      document.getElementById('countdown').innerHTML = 'Mensaje final';
      return;
    }
    var days = Math.floor(distance / _day);
    var hours = Math.floor((distance % _day) / _hour);
    var minutes = Math.floor((distance % _hour) / _minute);
    var seconds = Math.floor((distance % _minute) / _second);

    document.getElementById('countdown').innerHTML = days + ' días, ' + hours + ' horas, ' + minutes + ' minutos y ' + seconds + ' segundos';
  }
  timer = setInterval(showRemaining, 1000);
</script>

Contador de Visitas por Entrada

Este script, implementado el 10 de enero de 2022, utiliza jQuery y Firebase para registrar y mostrar el número de visitas para cada publicación de forma individual. La cuenta refleja las visitas a partir de dicha fecha.


<script src='http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js' type='text/javascript'></script>
<script src='https://cdn.firebase.com/v0/firebase.js' type='text/javascript'></script>
<script>
$.each($("a[name]"), function(i, e) {
  var elem = $(e).parent().find("#postviews");
  var blogStats = new Firebase("https://[TU-ID-DE-FIREBASE].firebaseio.com/pages/id/" + $(e).attr("name"));
  blogStats.once("value", function(snapshot) {
    var data = snapshot.val();
    var isnew = false;
    if (data == null) {
      data = {};
      data.value = 0;
      data.url = window.location.href;
      data.id = $(e).attr("name");
      isnew = true;
    }
    elem.text(data.value);
    data.value++;
    if (window.location.pathname != "/") {
      if (isnew) blogStats.set(data);
      else blogStats.child("value").set(data.value);
    }
  });
});
</script>

Botón Copiar

Utiliza la librería Clipboard.js para crear un botón que permite a los usuarios copiar fácilmente el contenido de un elemento predefinido (como un bloque de código o una cita) al portapapeles con un solo clic.


<script src="https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/2.0.11/clipboard.min.js"></script>
<script>
  var clipboard = new ClipboardJS('.btn');
  clipboard.on('success', function (e) {
    console.log('Copiado:', e.text);
    e.clearSelection();
  });
  clipboard.on('error', function (e) {
    console.error('Error al copiar');
  });
</script>

Nota: Se ha actualizado la URL de la librería Clipboard.js a una fuente más estándar y confiable (CDN) en lugar de Dropbox.

Mostrar Fecha Actual

Este script muestra la fecha actual (día, mes y año) al final de cada publicación. Se utiliza para sugerir a los lectores un formato de citación que incluya la fecha de consulta del artículo.


<script type="text/javascript">
  var meses = new Array ("enero","febrero","marzo","abril","mayo","junio","julio","agosto","septiembre","octubre","noviembre","diciembre");
  var f = new Date();
  document.write(f.getDate() + " de " + meses[f.getMonth()] + " de " + f.getFullYear());
</script>

Google Analytics

Este es el código de seguimiento estándar de Google Analytics (gtag.js). Nos permite recopilar datos anónimos sobre las visitas, como las páginas más populares o el origen del tráfico. Esta información es fundamental para entender qué contenido es de mayor interés y así mejorar el blog.


<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-*********"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

  gtag('config', 'G-*********');
</script>

Google AdSense

Este script de Google AdSense es necesario para la monetización del blog. Permite a Google verificar el sitio y mostrar anuncios relevantes para los visitantes. Los ingresos generados ayudan a mantener los costos operativos del blog.


<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-****************"
     crossorigin="anonymous"></script>

Publicaciones Relacionadas

Estos scripts trabajan en conjunto para mostrar una sección de publicaciones relacionadas al final de cada artículo. Analizan las etiquetas de la publicación actual para encontrar y mostrar otros artículos con temas similares, mejorando la navegación y la experiencia del usuario.


<script type='text/javascript'>
//<![CDATA[
var relatedTitles = new Array();
var relatedTitlesNum = 0;
var relatedUrls = new Array();
var thumburl = new Array();

function related_results_labels_thumbs(json) {
  for (var i = 0; i < json.feed.entry.length; i++) {
    var entry = json.feed.entry[i];
    relatedTitles[relatedTitlesNum] = entry.title.$t;
    try {
      thumburl[relatedTitlesNum] = entry.media$thumbnail.url;
    } catch (error) {
      s = entry.content.$t;
      a = s.indexOf("<img");
      b = s.indexOf("src=\"", a);
      c = s.indexOf("\"", b + 5);
      d = s.substr(b + 5, c - b - 5);
      if ((a != -1) && (b != -1) && (c != -1) && (d != "")) {
        thumburl[relatedTitlesNum] = d;
      } else {
        thumburl[relatedTitlesNum] = 'https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKDpj-B1pIYxRQcA-6_Hlsw0zyS1gEzZdEG-_6Bl_Nvu5cZCgPuK1wNBSfE13T6tDxlwLPno918OcvK0qA3_rEh-FNUKdzThlmPXZSzTZMnw9z1tKrLQ6H-5KKgT6xrCzKXZiaEHOiEXc/s400/noimage.png';
      }
    }
    if (relatedTitles[relatedTitlesNum].length > 150) relatedTitles[relatedTitlesNum] = relatedTitles[relatedTitlesNum].substring(0, 150) + "...";
    for (var k = 0; k < entry.link.length; k++) {
      if (entry.link[k].rel == 'alternate') {
        relatedUrls[relatedTitlesNum] = entry.link[k].href;
        relatedTitlesNum++;
      }
    }
  }
}

function removeRelatedDuplicates_thumbs() {
  var tmp = new Array(0);
  var tmp2 = new Array(0);
  var tmp3 = new Array(0);
  for (var i = 0; i < relatedUrls.length; i++) {
    if (!contains_thumbs(tmp, relatedUrls[i])) {
      tmp.length += 1;
      tmp[tmp.length - 1] = relatedUrls[i];
      tmp2.length += 1;
      tmp3.length += 1;
      tmp2[tmp2.length - 1] = relatedTitles[i];
      tmp3[tmp3.length - 1] = thumburl[i];
    }
  }
  relatedTitles = tmp2;
  relatedUrls = tmp;
  thumburl = tmp3;
}

function contains_thumbs(a, e) {
  for (var j = 0; j < a.length; j++) if (a[j] == e) return true;
  return false;
}

function printRelatedLabels_thumbs() {
  var output = '';
  for (var i = 0; i < relatedUrls.length; i++) {
    if ((relatedUrls[i] == currentposturl) || (!(relatedTitles[i]))) {
      relatedUrls.splice(i, 1);
      relatedTitles.splice(i, 1);
      thumburl.splice(i, 1);
    }
  }
  var r = Math.floor((relatedTitles.length - 1) * Math.random());
  var i = 0;

  if (relatedTitles.length > 0) {
    output += '<h4>' + relatedpoststitle + '</h4>';
    output += '<div class="related-link-container">';
    while (i < relatedTitles.length && i < 20 && i < maxresults) {
      output += '<a href="' + relatedUrls[r] + '">';
      output += '<img src="' + thumburl[r].replace('/s72-c/', '/s500/') + '"/>';
      output += '<div class="titulo">' + relatedTitles[r] + '</div>';
      output += '</a>';
      if (r < relatedTitles.length - 1) {
        r++;
      } else {
        r = 0;
      }
      i++;
    }
    output += '</div>';
    document.write(output);
  }
}
//]]>
</script>