BlogRollUpdater

Para desestresarme de estudios, trabajo y otros «males», he creado BlogRollUpdater, un script que actualiza un «blog roll» (lista de enlaces a blogs o similares), ordenando sus elementos en función de la fecha de la última publicación. Podría haberlo hecho dentro de los scripts de la página, este en concreto en el que se encarga de cargar los enlaces, pero me pareció que en este caso no era algo tan «personal» y que le podría servir a cualquiera para su blog roll (sois libres de usarlo de ahí mismo, o copiarlo y modificarlo o hacer lo que os salga con ello). Podéis ver un ejemplo(puede que con el tiempo deje de estar ahí, en cuanto enrede con otra cosa), ademas de el propio blog roll de la pagina, si dais a actualizar veréis como los enlaces se cargan primero con la forma estándar que tenían (por orden del blog de enlaces) y luego se recolocan por fecha de actualización. A continuación el código, y alguna explicación de como hay que llamar a la función:

//Creado por lopez para cerocoma.blogspot.com
//puede ser usado por quien quiera para lo que quiera.
//Sería de agradecer que si lo usas, incluyeras en el blogroll el blog "0,",
//ya que das a entender que tiene algún contenido de interes. Sin embargo no tienes ninguna obligación de hacerlo

//BlogRollUpdater:
//Este objeto se encarga de actualizar el orden de los elementos li de un div en función de sus fechas de actualización
//espera que estos elementos tengan como primer enlace el de un blog
//y que el blog tenga algun feed. Si no tuviese feed (o no se encuentra) se considerará su fecha de actualización
//menor al de cualquier otra.
//
//English (soo bad :):
//This object update the li elements of a div element order using the date of the last entries.
//This object requires that the first link of the li elements is a blog link, and the blog has some feed.
//If a blog doesn't has feed or it is not in the google system the update date is less than others.

var elBlogRoll;
var nBlogs;
var nBlogsProcesados;
var blogs;

function BlogRollUpdater(divId,gFACargada,mostrarResumen,zIndex,tamResumen,styResumen){

 this.divId = divId;
 if(this.divId == undefined)
  this.divId = "BlogRoll";

 this.gFACargada = gFACargada;
 if(this.gFACargada == undefined)
  this.gFACargada = false;

 this.mostrarResumen = mostrarResumen;
 if(this.mostrarResumen == undefined)
  this.mostrarResumen = true;

 this.zIndex = zIndex;
 if(this.zIndex == undefined)
  this.zIndex = 1;

 this.tamResumen = tamResumen;
 if(this.tamResumen == undefined)
  this.tamResumen = 150;

 this.styResumen = styResumen;
 if(this.styResumen == undefined)
  this.styResumen = "background:#ccc;border:1px solid #c60;font-size:11px;text-indent:-15px;text-indent:0px;";

 this.actualizar = function (){
  elBlogRoll = this;
  nBlogs = 0;
  nBlogsProcesados = 0;
  blogs = new Array();

  if(this,gFACargada == false){
   this.gFACargada = true;
   google.load("feeds", "1");
   google.setOnLoadCallback(googleFeedApiInicializada(this));
   
  }else{
   googleFeedApiInicializada(this);
  }
 };
}

function googleFeedApiInicializada(){
 if(elBlogRoll.gFACargada == false){
  elBlogRoll.actualizar();
 }else{
  var container = document.getElementById(elBlogRoll.divId);
  sacarElementos(container);
 }
}

function sacarElementos(contenedor){
 var elems = contenedor.getElementsByTagName("li");
 nBlogs = elems.length;

 for each(var e in elems){
  var a = e.getElementsByTagName("a");
  var dir = a[0].getAttribute("href");
  
  //crear objeto Blog
  google.feeds.lookupFeed(dir,new Blog(e.innerHTML).setFeedUri);
 }
}

//Este objeto me demuestra la poca idea que tengo de javascript (persistencia de objetos, this, etc...)
//parece que en el array blogs no se cargan elementos de este tipo, si no elementos que tienen como padre a un Blog
//Seguro que hay alguna manera de hacerlo bien, pasando siempre el mismo objeto sin tener que crear uno cada vez...
//En concreto que los elementos almacenados en blogs son del tipo setEntry y del setFeedUri (donde se hacen lso push)
//seguramente hay alguna palabra reservada para indicar al padre de un objeto que en este caso si sería del tipo Blog

//Blog:
//Este objeto almacena los datos necesarios para el proceso de una entrada del blogroll
//
//This object has the necesary data to process an entry of the blogroll
function Blog(contenido){
 this.contenido = contenido;

 this.setEntry = function (result){
  this.contenido = contenido;
  if(result.feed != undefined)
   this.entry = result.feed.entries[0];
  nBlogsProcesados = nBlogsProcesados + 1;
  blogs.push(this);
  if(nBlogsProcesados == nBlogs){
   blogsProcesados();
  }
 }

 this.setFeedUri = function (result){
  if(result.url == null){
   nBlogsProcesados = nBlogsProcesados + 1;
   this.contenido = contenido;
   blogs.push(this);
  }else{
   var feed = new google.feeds.Feed(result.url);
   feed.setNumEntries(1);
   feed.load(new Blog(contenido).setEntry);
  }
 }

}

function blogsProcesados(){
 for(var i = 0; i < blogs.length; i++){
  for(var j = i+1; j < blogs.length; j++){
   if(blogs[i].entry == undefined){
    //no se ha encontrado la url del feed o no se ha cargado por algún error, se considera la fecha menor que las demás
    var b = blogs[i];
    blogs[i] = blogs[j];
    blogs[j] = b;
   }else{
    if(blogs[j].entry != undefined){
     //hay dos fechas para comparar
     if(0<((new Date(blogs[j].entry.publishedDate).getTime()) - (new Date(blogs[i].entry.publishedDate).getTime()))){
      //j es posterior a i
      var b = blogs[i];
      blogs[i] = blogs[j];
      blogs[j] = b;  
     }
    }//si no j no tiene fecha y se considera menor que i
   }
  }
 }

 var cadenaHTML = "<ul>";
 var indice = 0;

 for each(var b in blogs){

  if(elBlogRoll.mostrarResumen == true && b.entry != undefined){
   cadenaHTML = cadenaHTML + "\n<li><div  onMouseOver='mostrarResumenBlog(" + indice + ")' onMouseOut='ocultarResumenBlog(" + indice + ")'>";
  }else{
   cadenaHTML = cadenaHTML + "\n<li>";
  }

  cadenaHTML = cadenaHTML + b.contenido;

  if(elBlogRoll.mostrarResumen == true && b.entry != undefined){
   cadenaHTML = cadenaHTML + "</div><div id='resumenBlog" + indice + "' style='z-index:" + elBlogRoll.zIndex + ";position:absolute;visibility:hidden;" + elBlogRoll.styResumen + "'><span style='text-indent:15px;font-weight:bold;'>" + b.entry.title + "</span><br /><span style='font-style:italic;'>" + limpiar(b.entry.content, elBlogRoll.tamResumen) + "</span></div></li>";
  }else{
   cadenaHTML = cadenaHTML + "</li>";
  }

  indice = indice + 1;
 }
 cadenaHTML = cadenaHTML + "</ul>";

 var container = document.getElementById(elBlogRoll.divId); 
 container.innerHTML = cadenaHTML;

}


function mostrarResumenBlog(i){
 var container = document.getElementById("resumenBlog" + i);
 container.style.visibility = "visible";
}


function ocultarResumenBlog(i){
 var container = document.getElementById("resumenBlog" + i);
 container.style.visibility = "hidden";
}

function limpiar(cadena, l){
 var c = "";
 var i = 0;
 var f = 0;
 cadena.replace("\n"," ");
 cadena.replace("\t"," ");
 while(i > -1 && i < l){
  i = cadena.indexOf("<");
  f = cadena.indexOf(">");

  if(i > -1 && f > -1)
   cadena = cadena.replace(cadena.substring(i, f + 1), "");  
 }

 c = cadena.substring(0,l);
 if(cadena.length > l)
  c = c + "...";

 return c;
}

Para variar el código es bastante malucho (y mi ingles :P), me he dado cuenta de que se (aun) menos javascript de lo que yo creía.

Para llamar a la función hay que haber añadido la clave del Google Feeds API, la cual se pone en una etiqueta script como veis en el ejemplo que os dan, o como veis aquí. Además hay que incluir el script, y hacer la llamada. Todo junto quedaría así:

<script type="text/javascript" src="https://www.google.com/jsapi?key=VUESTRAAPIKEY"></script>

<script type="text/javascript" src="https://archivos.cerocoma.googlepages.com/BlogRollUpdater.js">
new BlogRollUpdater().actualizar();</script>

Se pueden pasar algunos parametros a la creación del objeto BlogRollUpdater, serían los siguientes en orden:

  1. divId: es el identificador de la etiqueta div que contiene el blog roll. Por defecto busca «BlogRoll».
  2. gFACargada: indica si la Ajax Feeds API ya ha sido cargada, por ejemplo en mi caso como ya la tenía cargada le indico el valor true. Si se indica el valor false (valor por defecto) hay que tener en cuenta que la llamada ha de realizarse durante la carga de la página, esto es debido al modo en que se cargan las apis de google.
  3. mostrarResumen: indica si hay que mostrar un resumen de la primera entrada del blog, al pasar el ratón por encima del enlace. Si se pone a false, los demás valores no importan. Por defecto a true.
  4. zIndex: es el valor del z-index que queremos en el div que se muestra el resumen. En mi caso con un 1 (valor por defecto) es suficiente para que se muestre por encima del sidebar, sin embargo podría ser necesario cambiarlo según el caso.
  5. tamResumen: es el tamaño del contenido de la entrada del resumen. Hay que tener en cuenta que se omiten las etiquetas html. El valor por defecto es 150.
  6. styResumen: es el stilo que se le pondrá al div en el que se muestra el resumen (salvo el z-index y el visibility que no son opcionales). El valor por defecto es de nuevo el que yo necesitaba: «background:#ccc;border:1px solid #c60;font-size:11px;text-indent:-15px;text-indent:0px;»

Con todo esto, mi llamada es (mas o menos) así:

<script type="text/javascript" src="https://www.google.com/jsapi?key=ABQIAAAAm8jjlSilwL_ngAOmGoVVJRTBirHsSigxVb89Gx4cAc1hSJZCexSRlLoIdwdR82Sjtdy_9-cXY0fKwA"></script>

<script type="text/javascript" src="https://archivos.cerocoma.googlepages.com/BlogRollUpdater.js"></script>

[...codigo de carga de los enlaces...]

new BlogRollUpdater('enlaces',true).actualizar();

Mientras lo hacía me he dado cuenta de que necesito algunas cosas, así que si teneis alguna sugerencia de estas (o de cualquier otra cosa) os lo agradecería.

  • Un editor de javascript que al menos compruebe la sintaxis, y si hace sugerencias sobre nombres de variables mejor, que la mayoría de los errores que he tenido me los quitaría.
  • Un texto completo y detallado sobre javascript y el dom, no tengo ni idea de los prototipos ni esas cosas del javascript y deben tener mucha potencia…
  • Algo sobre css, aunque de esto no estoy muy seguro, que no soy muy de mirar lo bonito de las cosas, mientras funcionen me valen, y eso que se que la mitad de lo que vale algo es lo que ves en el primer vistazo, pero es que me puede.

Pues lo dicho, cualquier sugerencia sobre estas cosas o cualquier otra será de agradecer.

PD: Se me olvidaba una apreciación, si el enlace no tiene un feed asociado, o no está en la BBDD de google asociado a esa dirección, se verá relegado al final, como le pasa en mi blog roll a fuckowski, supongo que por el reciente cambio del nombre de dominio.

PPD: Hay que tener en cuenta que la API trabaja con al cache que tiene de los feeds en sus servidores, por lo que feeds poco actualizados podrían no ver afectada su posición en el blog roll hasta unas horas despues de la última publicación. Es el caso del enlace Atom que es del feed de este sitio, y que mientras escribo esto sigue bastante abajo.

PPPD: Al cesar lo que es del cesar, he de decir que la idea la saque de feevy, pero pense que sería mas simple no tener que dar de alta los blogs…

EDITO (12-01-07): Me he dado cuenta de que en IE no rula, parece un problema de crear objetos desde otros scripts al contenedor del tipo. He probado a poner una función para crear el objeto, pero tampoco funciona…. Además en /. me han dicho que en konqueror tampoco rula, y me han aclarado algo que evitará que el código sea tan malo, así que tendré que hacer una nueva versión cuando pueda arreglar estas cosas (si es que soy capaz :P) cuando tenga tiempo, que llevo dos días sin empollar y el lunes se me acaban las vacaciones.

EDITO (13-01-07): El error de konqueror (no lo he probado expresamente, pero también se producía en firefox, solo que continuaba la ejecución) creo que esta solucionado. El problema era que el objeto devuelto por el getElementsByTagName al aplicarle el for each hacia una pasada mas despues de los elementos li con lapropiedad length, lo que provocaba el error, ya que length no tiene funciones…, hay que verlo para entenderlo, he guardado el script viejo (el que esta mal), por si alguien quiere verlo.

Lo de Internet Explorer tambien era por eso, parece que hacia una pasada previa, y como los for each no le gustaban, como que descartaba todo el script, o algo así, no se muy bien, porque me daba distintos errores cada vez. Unas veces «se esperaba ‘(‘» y otras «‘BlogRollUpdater’ no está definido’, la cosa es que quitando los for each se ha solucionado y ya funciona.

Con esto creo (supongo) que el script funciona con todos los navegadores, lo de arreglar el script para que sea mas legible, mas correcto lo dejo para mas adelante.