Nube de etiquetas con índice dinámico para Blogger

Hasta hace poco usaba un índice para las entradas basado en un script que las mostraba ordenadas por etiquetas de un tal Abu Farhan, pero lo eliminé al avisarme Google de que dicho script podía contener código malicioso. Me puse a buscar algo parecido y sólo encontré nubes de etiquetas por un lado o listas de entradas por el otro, ninguno que uniera ambas características, al menos en javascript, así que decidí hacérmelo yo mismo. El resultado puede verse en mi página de índice. No es muy espectacular visualmente pero es simple y eficaz, visualmente y a nivel de código.

El script permite:

  • Ver las etiquetas de nuestro blog en formato nube con diferentes medidas de fuente según su frecuencia
  • Ordenar la nube alfabéticamente
  • Mostrar los títulos de los artículos con la etiqueta seleccionada pudiendo mostrar sólo el título o el nombre del autor y/o la fecha de publicación

El código se divide en tres partes:

  • Se cargan todos los artículos y se guardan las etiquetas junto a las veces que éstas se repiten en una matriz bidimensional (peso) que posteriormente puede ordenarse o no (cargaEtiquetas() y contarEtiquetas())
  • Se genera la nube asignando a cada etiqueta un tamaño de fuente en porcentaje basado en su peso según el algoritmo ideado por Jeremy Martin y un enlace con evento onclick que llama a la función encargada de buscar los artículos correspondientes a la etiqueta elegida (generaCloud())
  • Se muestran los títulos de las entradas bajo la nube en orden inverso de publicación (loadPosts())

Para modificar la apariencia de la nube disponemos de las etiquetas:

  • #tagCloud div para contener la nube en sí, con atributos para modificar la visualización:
  • data-maxfont: porcentaje máximo para la fuente
  • data-minfont: porcentaje mínimo para la fuente
  • linkColor: color de la etiqueta normal
  • selectedLink: color de la etiqueta seleccionada
  • authorName: ="1" se muestra autor, cualquier otro valor no
  • date: ="1" muestra la fecha de publicación de los artículos
  • sort: ="1" ordena las etiquetas alfabéticamente
  • #listTitols div para contener el índice de artículos
  • #dataPost span para contener el nombre del autor y la fecha de publicación
  • #containerIdx div contenedor para los anteriores
    Veamos el código:

    var lastLink = null;
    var linkColor = '#000000', selectedLink = '#000000';
    var showAuthor = true;
    var showDate = true;
    
    function gestionaColorLink(alink)
    // Cambia el color de la etiqueta seleccionada y devuelve su color a la anterior
    {
     if (!(lastLink === null))
      lastLink.style.color = linkColor;
     lastLink = alink;
     alink.style.color = selectedLink;
    }
    
    var MonthNames = ['Enero', 'Febrero', 'Marzo', 'Abril' , 'Mayo', 'Juny', 'Julio' , 'Agosto', 'Septiembre', 'Octubre' , 'Noviembre' , 'Diciembre'];
    
    function loadPosts(label, alink)
    // Cargamos el feed de los posts del tema (label) seleccionado
    {
     gestionaColorLink(alink);
     iframe = document.getElementById('listTitols');
    
     if (window.XMLHttpRequest)
     { // code for IE7+, Firefox, Chrome, Opera, Safari
        xmlhttp=new XMLHttpRequest();
       }
     else
       { // code for IE6, IE5
        xmlhttp=new ActiveXObject('Microsoft.XMLHTTP');
       }
     xmlhttp.open('GET', 'http://' + window.location.host + '/feeds/posts/summary/-/' + label, false);
     xmlhttp.send();
     xmlDoc=xmlhttp.responseXML; 
    
     iframe.innerHTML = '';
    
     var x = xmlDoc.getElementsByTagName('entry');
     for(var i = 0; i <= x.length - 1; i++) // Obtenemos la URL
     {
      urls = x[i].getElementsByTagName('link');
      ThisPostURL = '';
         for(LinkNum = 0; LinkNum < urls.length; LinkNum++) 
         {
       if (urls[LinkNum].getAttribute('rel') == 'alternate') 
       {
           ThisPostURL = urls[LinkNum].getAttribute('href');
           break;
       }
         }
      nomautor = '';
      if (showAuthor) // Obtenemos el nombre del autor
      {
       autor = x[i].getElementsByTagName('author');
       nomautor = autor[0].getElementsByTagName('name')[0].childNodes[0].nodeValue + ', ';
      }
      public = '';
      if (showDate) // Obtenemos la fecha de publicación
      {
       data = x[i].getElementsByTagName('published')[0].childNodes[0].nodeValue;
       public = data.substring(8,10) + ' ' + MonthNames[parseInt(data.substring(5,7), 10) - 1] + ' ' + data.substring(0,4);
      }
      iframe.innerHTML += '' + x[i].getElementsByTagName('title')[0].childNodes[0].nodeValue + '' + ((showAuthor || showDate) ? '   (' + nomautor + public + ')' : '');
      iframe.innerHTML += '
    ';  
     }
    }
    
    var listaEtiquetas = new Array(); // Array bidimensional: [etiqueta, peso]
    
    function generaCloud()
    // Generación de la nube de etiquetas
    {
     var tc = document.getElementById('tagCloud');
        var minPercent = parseInt(tc.getAttribute('data-minfont'), 10);
        var maxPercent = parseInt(tc.getAttribute('data-maxfont'), 10);
     var ordena = (tc.getAttribute('sort') == '1');
     var vmax = 1, vmin = 999;
    
     linkColor = tc.getAttribute('linkColor');
     selectedLink = tc.getAttribute('selectedLink');
     showAuthor = (tc.getAttribute('authorName') == '1');
     showDate = (tc.getAttribute('date') == '1');
     if (ordena)
     {
      listaEtiquetas = listaEtiquetas.sort(function(a,b) {
       s1 = a[0].toLowerCase();
       s2 = b[0].toLowerCase();
       return ((s1 < s2) ? -1 : (s1 > s2) ? 1 : 0); });
     }
     for (idx = 0; idx < listaEtiquetas.length; idx++)
     {
      data = listaEtiquetas[idx][1];
      vmax = (data > vmax ? data : vmax);  
      vmin = (vmin > data ? data : vmin);
     }
     multiplier = (maxPercent - minPercent) / (vmax - vmin); 
     
     while (tc.hasChildNodes())
         tc.removeChild(tc.lastChild);
     for (idx = 0; idx < listaEtiquetas.length; idx++)
     {
      a = document.createElement('a');
      a.setAttribute('href', '#');
      a.setAttribute('onclick', 'loadPosts("' + listaEtiquetas[idx][0] + '", this)');
      a.innerHTML = listaEtiquetas[idx][0];
      a.style.fontSize = (minPercent + ((vmax - (vmax - (listaEtiquetas[idx][1] - vmin))) * multiplier)).toString() + '%';
      a.style.color = linkColor;
      tc.appendChild(a);
      espai = document.createTextNode('\u00A0'); // No usar ' ', al recargar página desaparece
      tc.appendChild(espai);
     }
    }
    
    function contarEtiquetas(label)
    // Añadimos la etiqueta (label) a la lista si no está o incrementamos su peso si ya existe
    {
     cnt = 0;
     ok = false;
     while ((cnt < listaEtiquetas.length) && (!ok))
     {
      ok = (listaEtiquetas[cnt][0] === label);
      if (!ok) cnt++;
     }
     if (ok)
      listaEtiquetas[cnt][1]++;
     else
      listaEtiquetas.push([label, 1]);
    }
    
    function cargaEtiquetas(TotalFeed) // <---- Entry point
    {
     if ('entry' in TotalFeed.feed) 
     {
      var PostEntries = TotalFeed.feed.entry.length;
      var PostNum = 0;
      for(; PostNum < PostEntries; PostNum++) 
      {
       ThisPost = TotalFeed.feed.entry[PostNum];
       label = ThisPost.category;
             for (i = 0; i < label.length; i++)
        contarEtiquetas(label[i].term);
      }
      if (PostNum > 0) generaCloud();
        }
    }
    

    Para usar el script añadimos el siguiente CSS a nuestra página:

    <style>
    #containerIdx
    {
    width: 100%;
    }
    #tagCloud
    {
    width: 90%;
    font-family:"Times New Roman",Georgia,Serif;
    text-align: center;
    margin-bottom: 20px;
    margin-left: auto;
    margin-right: auto;
    }
    #listTitols
    {
    width: 90%;
    text-align: center;
    margin-left: auto;
    margin-right: auto;
    }
    #dataPost
    {
    font-size: 12px;
    }
    </style>
    <div id="containerIdx">
    <div id="tagCloud" data-maxfont="250" data-minfont="100" linkColor="#000000" selectedLink="#666666" authorName="0" date="0" sort="1"></div>
    <div id="listTitols">&lt/div>
    </div>
    

    Y lo cargamos a continuación:

    <script type="text/javascript" src="http://localización_script_tagCloudIdx.js"></script>
    <script src="http://host_de_tu_blog/feeds/posts/default?max-results=1000&alt=json-in-script&callback=cargaEtiquetas"></script>
    

    Puede descargarse en GitHub.

    Reconocimientos: Efficient Tag Cloud AlgorithmJavaScript Tag CloudHow To Read Blogger XML Feed Using Javascript

    No hay comentarios:

    Publicar un comentario