Añadir preferencias a una extensión


Este artículo es una vuelta de tuerca más al ejemplo mostrado en Crear una extensión dinámica en la barra de estado que le añade un menú emergente que te permite alternar entre múltiples stocks para visualizar. También añade un diálogo de preferencias que te permite cambiar a un stock diferente a aquellos incluidos en el menú emergente.

Como ya ocurrió antes, los conceptos cubiertos en los anteriores artículos de la serie no se volverán a tocar por lo que si todavía no los has visto, puedes hacerlo ahora:

También, por motivos de referencia, puede que te interese echarle un vistazo al Sistema de preferencias.

Descargar el ejemplo

Puedes descargar una copia de este ejemplo para echarle un vistazo o para usarlo como base para tu propia extensión.

Descargar el ejemplo

Actualizar los manifiestos

El manifiesto de instalación y el manifiesto chrome necesitan ser actualizados. A grandes rasgos, simplemente hay que cambiar el ID de la extensión. Sin embargo, necesitamos añadir una nueva línea al fichero install.rdf.

 <em:optionsURL>chrome://stockwatcher2/content/options.xul</em:optionsURL>

Esta línea establece la URL del fichero XUL que describe el diálogo de opciones.

Establecer los valores predeterminados

Para establecer una preferencia predeterminada para el stock a monitorizar, necesitamos añadir un nuevo directorio al paquete de nuestra extensión llamado "defaults", el cual contendrá otro directorio llamado "preferencias". Dentro de él, crearemos un fichero defaults.js que describirá los valores predeterminados de nuestras preferencias.

 pref("stockwatcher2.symbol", "GOOG");

Para aprender más sobre el sistemas de preferencias, lee la API de preferencias.

El código JavaScript

Para poder monitorizar los cambios en nuestras preferencias necesitamos instalar un observador utilizando la interfaz nsIPrefBranch2. Para hacer eso, necesitamos reimplementar nuestro código como un objeto.

Eso implica convertir cada función en un miembro de la clase StockWatcher. Echemos un vistazo a cada función de la clase.

startup()

La función StockWatcher.startup() es llamada cuando nuestra extensión se carga por vez primera. Su función es iniciar el observador para controlar los cambios en nuestras preferencias, instanciando un objeto para usarlo para que administre nuestras preferencias e instalando una rutina de intervalo para actualizar la información del stock periódicamente.

 var StockWatcher = {
   prefs: null,
   tickerSymbol: "",
   
   // Initialize the extension
   
   startup: function()
   {
     // Register to receive notifications of preference changes
     
     this.prefs = Components.classes["@mozilla.org/preferences-service;1"]
         .getService(Components.interfaces.nsIPrefService)
         .getBranch("stockwatcher2.");
     this.prefs.QueryInterface(Components.interfaces.nsIPrefBranch2);
     this.prefs.addObserver("", this, false);
     
     this.tickerSymbol = this.prefs.getCharPref("symbol").toUpperCase();
 
     this.refreshInformation();    
     window.setInterval(this.refreshInformation, 10*60*1000);
   },

Nuestro objeto posee dos variables miembro. prefs está configurada por startup() para que referencie las preferencias de nuestra extensión mientras que tickerSymbol indica el símbolo de stock a monitorizar.

La primera cosa que hace la función startup() es obtener una referencia de las preferencias para nuestra extensión. Esto se hace en dos pasos:

  • Primero, obtenemos el servicio de preferencias. Este componente maneja la administración de preferencias para Firefox y cualquier extensión.
  • Segundo, llamamos a nsIPrefService.getBranch(). Esto nos deja indicar una bifurcación del árbol de preferencias específica al que acceder. De modo predeterminado, tendríamos acceso a todas las preferencias pero sólo queremos acceder a aquellas relativas a nuestra extensión por lo que especificamos que queremos acceder a la rama "stockwatcher2".

Tras obtener la rama de preferencias para nuestra extensión, llamaremos al método QueryInterface() sobre ellas para poder usar los métodos de la interfaz nsIPrefBranch2.

El siguiente paso es registrar un observador de preferencias llamando al método addObserver() para establecer que en el momento en el que cualquier evento se produzca sobre las preferencias, nuestro objeto (this) recibirá una notificación. Cuando ocurra un evento, tal como una preferencia que ha sido alterada, nuestro método observe() será llamado automáticamente.

Ahora que estamos monitorizando las preferencias, podemos configurarlas para ver la información del stock y mostrarla en el panel de la barra de estado.

Lo primero que necesitamos hacer es obtener el símbolo de stock configurado actualmente para ver desde las preferencias. Para hacerlo, llamamos al método nsIPrefBranch.getCharPref(), especificando que queremos la preferencia llamada "symbol", que es donde guardamos la elección del usuario para el stock a visualizar. Convertiremos por la fuerza el símbolo a mayúsculas ya que es el modo en el que los símbolos de stock son normalmente mostrados.

Después, llamamos a nuestro método refreshInformation() para obtener y mostrar inmediatamente la información actual sobre el stock para el cual la extensión está configurada para monitorizar. Miraremos los detalles de cómo funciona este método más adelante.

Lo último que hace el método startup() es llamar al método del DOM window.setInterval() para configurar un callback que automáticamente ejecute nuestro método refreshInformation() cada 10 minutos. La duración del intervalo está especificada en milisegundos.

shutdown()

El método StockWatcher.shutdown() desactiva el observador sobre nuestras preferencias. Aquí es también donde añadiríamos cualquier otra tarea de apagado que necesitásemos realizar.

  shutdown: function()
  {
    this.prefs.removeObserver("", this);
  },

observe()

La función StockWatcher.observe() es llamada siempre que ocurre un evento en la rama de preferencias que estamos siguiendo. Para más detalles sobre cómo funcionan los observadores, revisa la interfaz nsIObserver.

   observe: function(subject, topic, data)
   {
     if (topic != "nsPref:changed")
     {
       return;
     }
 
     switch(data)
     {
       case "symbol":
         this.tickerSymbol = this.prefs.getCharPref("symbol").toUpperCase();
         this.refreshInformation();
         break;
     }
   },

El parámetro topic indica el tipo de evento ocurrido. Si no es nsPref:changed, simplemente ignoraremos el evento ya que lo único que nos interesa son los cambios de los valores de nuestras preferencias.

Una vez hemos comprobado que el evento es de hecho un cambio en las preferencias, miramos el parámetro data el cual contiene el nombre de la preferencia que ha cambiado. En nuestro ejemplo sólo hay una preferencia pero puedes monitorizar tantas como quieras.

Si la preferencia cambiada es "symbol", recuperamos el valor actualizado de la preferencia llamando al método nsIPrefBranch.getCharPref() y la copiamos en nuestra variable tickerSymbol.

Una vez hemos obtenido la preferencia actualizada, llamamos a refreshInformation() para actualizar de inmediato el visor con la nueva información del stock.

watchStock()

Ahora vamos a añadir un método que establece el stock que queremos monitorizar, cambiando la preferencia e inmediatamente pidiendo una actualización del visor. Este método será usado cuando el usuario utilice el menú emergente que añadiremos para cambiar el stock que se está controlando.

   watchStock: function(newSymbol)
   {
     this.prefs.setCharPref("symbol", newSymbol);
   },

La única nueva información para nosotros aquí es la llamada a la función setCharPref() del objeto de la preferencia, el cual establece el valor de la preferencia "symbol".

Date cuenta de que el hacer esta llamada provocará que el método StockWatcher.observe() sea llamado y la información del stock sea actualizada.

refreshInformation()

Este método difiere ligeramente de las versiones anteriores en la forma en la que recupera la preferencia del stock a seguir y en la manera en que construye la URL a monitorizar, además de cómo construye la cadena que es mostrada en el panel de la barra de estado.

   refreshInformation: function()
   {
     // Because we may be called as a callback, we can't rely on
     // "this" referring to the right object, so we need to reference 
     // it by its full name
     
     var symbol = StockWatcher.tickerSymbol;
     
     var fullUrl = "http://quote.yahoo.com/d/quotes.csv?f=sl1d1t1c1ohgv&e=.csv&s="
         + symbol;
     
     function infoReceived()
     {
       var samplePanel = document.getElementById('stockwatcher2');
       var output = httpRequest.responseText;
         
       if (output.length)
       {
         // Remove any whitespace from the end of the string
         output = output.replace(/\W*$/, "");
       
         // Build the tooltip string
         var fieldArray = output.split(",");
         samplePanel.label = symbol + ": " + fieldArray[1];
         samplePanel.tooltipText = "Chg: " + fieldArray[4] + " | " +
             "Open: " + fieldArray[5] + " | " +
             "Low: " + fieldArray[6] + " | " +
             "High: " + fieldArray[7] + " | " +
             "Vol: " + fieldArray[8];
       }
     }
     
     var httpRequest = new XMLHttpRequest();
     
     httpRequest.open("GET", fullUrl, true);
     httpRequest.onload = infoReceived;
     httpRequest.send(null);
   }
 }

Date cuenta de que aquí usamos StockWatcher.tickerSymbol en vez de this.tickerSymbol para conseguir el símbolo del stock a visualizar. Hacemos esto debido a que refreshInformation() es por lo general llamado como un callback desde setInterval. En tales casos, this no se refiere al objeto correcto. Lee Method binding para una información más detallada.

Una vez tenemos el símbolo en la variable local symbol, la usaremos para construir la URL y la cadena a mostrar en el panel de la barra de estado.

Instalando los escuchadores de eventos

La única cosa que nos queda por hacer es instalar los escuchadores de eventos necesarios para ejecutar las rutinas startup() y shutdown() automáticamente cuando la ventana del navegadore es cargada o descargada.

window.addEventListener("load", function(e) { StockWatcher.startup(); }, false);
window.addEventListener("unload", function(e) { StockWatcher.shutdown(); }, false);

Diseñar el diálogo de preferencias

Ahora que hemos escrito todo el código, necesitamos construir el fichero XUL para el diálogo de opciones.

<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
 
<prefwindow id="stockwatcher2-prefs"
     title="StockWatcher 2 Options"
     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
<prefpane id="sw2-stock-pane" label="Stock Settings">
  <preferences>
    <preference id="pref_symbol" name="stockwatcher2.symbol" type="string"/>
  </preferences>
 
  <hbox align="center">
    <label control="symbol" value="Stock to watch: "/>
    <textbox preference="pref_symbol" id="symbol" maxlength="4"/>
  </hbox>
</prefpane>
 
</prefwindow>

El bloque <preferences> establece todas las opciones que hemos implementado así como sus tipos. En nuestro caso, sólo tenemos una única preferencia, el símbolo de stock a monitorizar. Las preferencias se identifican a través del nombre (en este caso, el nombre es "stockwatcher2.symbol").

La interfaz de usuario actual es descrita en el bloque <prefpane>. El elemento <hbox> indica que los controles contenidos dentro de él han de disponerse horizontalmente, uno al lado del otro en la ventana.

Nuestro diálogo posee dos controles en él. El primero es una etiqueta que describe el cuadro de texto. El segundo es el propio cuadro de texto en el cual el usuario introduce el símbolo. La propiedad preference relacionada con el cuadro de texto hace referencia al elemento <preference> "pref_symbol" y a la preferencia "stockwatcher2.symbol". Esto hace que el valor de la preferencia se actualice automáticamente para reflejar el contenido del cuadro de texto.

Añadir el menú contextual

Añadir el menú contextual es fácil. Todo lo que hay que hacer se hace en el fichero <tt>stockwatcher2.xul</tt>. El primer paso es añadir el atributo context al panel de la barra de estado:

 <statusbar id="status-bar">
   <statusbarpanel id="stockwatcher2"
     label="Loading..."
     context="stockmenu"
     onclick="StockWatcher.refreshInformation()"
   />
 </statusbar>

Ahora cuando el usuario pulse sobre el panel de la barra de estado se actualizará la información del stock pero cuando lo haga con el botón derecho aparecerá un menú emergente.

Definir el menú es también sencillo. Todo lo que necesitamos hacer es añadir un popupset que describa el menú en statusbar, así:

 <popupset>
   <menupopup id="stockmenu">
     <menuitem label="Refresh Now" default="true"
               oncommand="StockWatcher.refreshInformation()"/>
     <menuseparator/>
     <menuitem label="Apple (AAPL)" oncommand="StockWatcher.watchStock('AAPL')"/>
     <menuitem label="Google (GOOG)" oncommand="StockWatcher.watchStock('GOOG')"/>
     <menuitem label="Microsoft (MSFT)" oncommand="StockWatcher.watchStock('MSFT')"/>
     <menuitem label="Yahoo! (YHOO)" oncommand="StockWatcher.watchStock('YHOO')"/>
   </menupopup>
 </popupset>

Cada elemento en el menú posee una propiedad label que especifica el texto mostrado en el menú, además de una propiedad oncommand que indica el código JavaScript a ejecutar cuando el usuario elija dicho elemento.

La opción Actualizar ahora llama a la función StockWatcher.refreshInformation() para actualizar el visor. El resto de las opciones llaman a la función StockWatcher.watchStock() para empezar a observar un stock diferente.

Para un tutorial más detallado sobre la creación de menús emergentes, lee Tutorial XUL:Menús emergentes.

Etiquetas y colaboradores del documento

Última actualización por: Mgjbot,