timelineJS o cómo pasárselo bien durante horas.

Todo aquel que haya hecho, o haga, desarrollo web sabe una cosa: nunca pienses que la web será vista como la ves tú. A pesar de que en la fase de desarrollo, y test, se utilicen todos los navegadores del mercado algún detalle te saltará cuando menos te lo esperes.

Pues esto ha sido lo que nos ha llevado de cabeza dos dias. Para mostrar cierta información, en un reciente desarrollo, nos hemos decantado por el timelineJs de Verite.co. Tras echarle un ojo, ver cómo se integra en un desarrollo en marcha y sus posibilidades nos ponemos a ello y lo insertamos. Todo parece funcionar bien: Firefox ok, Chrome ok, Opera ok,.

Confiadamente pasamos al siguiente tema y, de inmediato, un compañero nos dice: “pues en Internet explorer no se ve”. ¡¡Venga hombre, qué dices!! Tendrás una versión vieja (mentira porque todos tenemos la misma), tendrás desactivada alguna opción o será problema de la cache de tu IE porque nosotros lo vemos bien … mira, ven, que en mi ordenador se ve ….mal!! al abrirlo con mi IE tampoco funciona!!! Dios, pero quien me manda a mi meterme en esto de la Web, si estaba mejor haciendo calceta.

Con la idea de solventarlo en dos minutos me pongo a ello. Abro la página en mi Firefox (tuneado con las extensiones webdeveloper y firebug)

https://www.example.com/timeline.html

y al mirar la consola no veo errores. Repito la operación en IE y nada. Sin verlo muy claro comienzo a buscar en Internet a ver si a alguno le ha pasado algo parecido. Nada, no hay nada. Activamos el modo debug en el timeline y vuelvo a examinar las consolas (la de Firebug y la de las Herramientas de Desarrollador de IE) y veo en la de Firebug salen un montón de mensajes (con un correcto funcionamiento del timeline) y que en IE se para enseguida con estos mensajes:

IE JSON
Google Docs ERROR: timeout
Google Docs ERROR: 

Bufff, toca remangarse y revisar el código. Modifico el timeline para que use timeline.js en lugar de su versión minimizada; busco el mensaje “IE JSON” y veo esto en su línea 536:

VMM.getJSON = function(url, data, callback) {
  if( typeof( jQuery ) != 'undefined' ){
    jQuery.ajaxSetup({
      timeout: 3000
    });
    /* CHECK FOR IE
    ================================================== */
    if ( VMM.Browser.browser == "Explorer" && parseInt(VMM.Browser.version, 10) >= 7 && window.XDomainRequest) {
      trace("IE JSON");
      var ie_url = url;
      if (ie_url.match('^http://')){
        return jQuery.getJSON(ie_url, data, callback);
      } else if (ie_url.match('^https://')) {
        ie_url = ie_url.replace("https://","http://");
        return jQuery.getJSON(ie_url, data, callback);
      } else {
      return jQuery.getJSON(url, data, callback);
      }

    }  else {
      return jQuery.getJSON(url, data, callback);
    }
  }
}

Busco Google Docs ERROR y veo:

  model: {

  googlespreadsheet: {

    getData: function(raw) {
      var getjsondata, key, url, timeout, tries = 0;

      key  = VMM.Util.getUrlVars(raw)["key"];
      url  = "https://spreadsheets.google.com/feeds/list/" + key + "/od6/public/values?alt=json";

      timeout = setTimeout(function() {
        trace("Google Docs timeout " + url);
        trace(url);
        if (tries < 3) {
          VMM.fireEvent(global, VMM.Timeline.Config.events.messege, "Still waiting on Google Docs, trying again " + tries);
          tries ++;
          getjsondata.abort()
          requestJsonData();
        } else {
          VMM.fireEvent(global, VMM.Timeline.Config.events.messege, "Google Docs is not responding");
        }
      }, 16000);

    function requestJsonData() {
      getjsondata = VMM.getJSON(url, function(d) {
        clearTimeout(timeout);
        VMM.Timeline.DataObj.model.googlespreadsheet.buildData(d);
      })
      .error(function(jqXHR, textStatus, errorThrown) {
        trace("Google Docs ERROR");
        trace("Google Docs ERROR: " + textStatus + " " + jqXHR.responseText);
        })
      .success(function(d) {
        clearTimeout(timeout);
      });
    }
    
    requestJsonData();
    },

Con estas pistas me pongo a examinar qué pasa con el documento de Google Docs que se usa como fuente de datos. Su url es del tipo:

https://docs.google.com/ruta_larga_al_documento&format=html

En la línea 14, del primer código, veo que se hace una cambio de protocolo: si el documento está en una ruta con protocolo https se modifica para que se solicite por http . ¿Por qué? no lo pensé en su momento. Solicité en el navegador el documento con protocolo http a ver qué pasaba y vi que Google hace una redirección permanente, un header 302, a la versión https del documento y añade dos headers más:

x-frame-options=SAMEORIGIN
x-xss-protection=1; mode=block

¿Serían estos dos headers quienes estaban causando problemas? Tras leer un rato sobre el tema

Me digo que no y me vuelvo a centrar en el código de timelineJS. Releo el código javascript con más calma:

else if (ie_url.match('^https://')) {
  ie_url = ie_url.replace("https://","http://");
  return jQuery.getJSON(ie_url, data, callback);
}

Si pedimos, con IE, un documento https, timelineJS, lo cambia a https y llama a jQuery.getJSON. Veamos qué dice la documentación sobre la función. Nada nuevo hasta que llego a las notas adicionales:

Due to browser security restrictions, most “Ajax” requests are subject to the same origin policy; the request can not successfully retrieve data from a different domain, subdomain, or protocol.

Por fín, la respuesta estaba ahí. Si nuestra página se ejecuta sobre https no se puede hacer una petición ajax vía http, y viceversa. Tras más de 5 horas teníamos la solución, para que luego digan que esto de hacer Web no es divertido 🙂