iGoogle interface updated to lastest version of jQuery

5 09 2011

Many people have asked me to update my example of an iGoogle interface to the lastest version of jQuery. I have also repaired a problem with the database connection (I had moved my webpage to a different hosting company).

Here is the new version: http://www.jsabino.com/test2/

I have used jQuery 1.6.2 and jQuery UI 1.8.16.

You can also download the full source code here: http://www.jsabino.com/iNettutsDB3.zip

Thank you very much for all your comments.

Mario





Resolución del problema de cifras del programa «Cifras y letras» con PHP

21 05 2010

Resulta que hoy al llegar a comer a casa, haciendo zapping, me he enganchado un rato al programa «Cifras y letras», en Telemadrid. En el problema de las cifras, han salido varios muy complicados, donde los concursantes se han quedado muy lejos del exacto. Pero claro, la presentadora sí tenía el exacto, con unas operaciones que no se le habrían ocurrido ni de lejos. Por supuesto, pensé, la solución se la ha dado el ordenador.

Así que, según he terminado de comer, he dicho: vamos a hacer en PHP un programita que resuelva el problema. Es fácil, una función recursiva que examine todas las posibilidades y se quede con la mejor. En una hora aproximadamente, he obtenido esto:

$final=859;
$numbers=array(6,6,7,25,10,1);

$best=0; $solution="";

Calc($numbers,"");
echo "$best<br><br>$solution";

function Calc($numbers,$sol) {

  global $best,$final,$solution;

  if ($best==$final) return ;

  if (count($numbers)==1) {
    if (abs($numbers[0]-$final)<abs($best-$final)) {$best=$numbers[0]; $solution=$sol;}
  } else {
    foreach ($numbers as $number) {
      if (abs($number-$final)<abs($best-$final)) {$best=$number; $solution=$sol;}
      if ($best==$final) return ;
    }
    for ($i=0;$i<count($numbers)-1;$i++) {
      for ($j=$i+1;$j<count($numbers);$j++) {

        // Suma
        $new_numbers=$numbers;
        unset($new_numbers[$i]);
        unset($new_numbers[$j]);
        $new_numbers = array_values($new_numbers);
        array_push($new_numbers,$numbers[$i]+$numbers[$j]);
        Calc($new_numbers,$sol.$numbers[$i]."+".$numbers[$j]."=".($numbers[$i]+$numbers[$j])."<br>");

        // Multiplicación
        $new_numbers=$numbers;
        unset($new_numbers[$i]);
        unset($new_numbers[$j]);
        $new_numbers = array_values($new_numbers);
        array_push($new_numbers,$numbers[$i]*$numbers[$j]);
        Calc($new_numbers,$sol.$numbers[$i]."x".$numbers[$j]."=".($numbers[$i]*$numbers[$j])."<br>");

        // Resta 1
        if ($numbers[$i]-$numbers[$j]>0) {
          $new_numbers=$numbers;
          unset($new_numbers[$i]);
          unset($new_numbers[$j]);
          $new_numbers = array_values($new_numbers);
          array_push($new_numbers,$numbers[$i]-$numbers[$j]);
          Calc($new_numbers,$sol.$numbers[$i]."-".$numbers[$j]."=".($numbers[$i]-$numbers[$j])."<br>");
        }

        // Resta 2
        if ($numbers[$j]-$numbers[$i]>0) {
          $new_numbers=$numbers;
          unset($new_numbers[$i]);
          unset($new_numbers[$j]);
          $new_numbers = array_values($new_numbers);
          array_push($new_numbers,$numbers[$j]-$numbers[$i]);
          Calc($new_numbers,$sol.$numbers[$j]."-".$numbers[$i]."=".($numbers[$j]-$numbers[$i])."<br>");
        }

        // División 1
        if (($numbers[$j]!=0) && ($numbers[$i]%$numbers[$j]==0)) {
          $new_numbers=$numbers;
          unset($new_numbers[$i]);
          unset($new_numbers[$j]);
          $new_numbers = array_values($new_numbers);
          array_push($new_numbers,$numbers[$i]/$numbers[$j]);
          Calc($new_numbers,$sol.$numbers[$i]."/".$numbers[$j]."=".($numbers[$i]/$numbers[$j])."<br>");
        }

        // División 2
        if (($numbers[$i]!=0) && ($numbers[$j]%$numbers[$i]==0)) {
          $new_numbers=$numbers;
          unset($new_numbers[$i]);
          unset($new_numbers[$j]);
          $new_numbers = array_values($new_numbers);
          array_push($new_numbers,$numbers[$j]/$numbers[$i]);
          Calc($new_numbers,$sol.$numbers[$j]."/".$numbers[$i]."=".($numbers[$j]/$numbers[$i])."<br>");
        }
      }
    }
  }
}

Por supuesto, el código se puede mejorar, pero desde luego, funciona a las mil maravillas. En el array $numbers con las cifras, se introducen los números con los que jugaremos. Y la variable $final es el número buscado. Como resultado, la página muestra el número más cercano obtenido y las operaciones realizadas para encontrarlo.

Después he pensado: bueno, seguro que esto lo ha hecho antes alguien. Pues sí, he encontrado esta solución implementada en C por Pedro Reina, pero con 841 líneas de código (incluidos los comentarios). En PHP yo lo he solucionado con las míseras 83 líneas que podéis ver más arriba.

Otro día me pongo con el problema de las letras…





A complete iGoogle like interface example with jQuery

5 05 2010

After my previous post about how to mimic the iGoogle interface with database, I have been working in a new version. Now I have added all you need to build a simple page, with these features:

  • The user starts with an empty page, where he can add widgets. There is a link “Add widget at column …” that creates a new widget at the selected column.
  • Any widget can be configured in the same way that before.
  • Widget content comes from an ajax request to a page called “widgets_rpc.php”. This page receives the id of the widget and should respond with the HTML content of that widget.
  • Widget configuration is stored in the database when the user changes something and loaded at the beginning.

You can see a working example here: http://www.jsabino.com/test

And you can download the complete source code here: http://www.jsabino.com/iNettutsDB2.zip

Inside the ZIP file, you can find:

  • script.sql: the script to create the required table in the database. You must execute it.
  • iNettuts_rpc.php: the same file for saving and loading widget configuration. Change here the database connect parameters (server, database, username and password).
  • index.html: now it is a simple page with the columns layout but without any widget, because they are created dynamically by a function called “Add”. It looks for the last added widget id and creates a new one in the selected column.
function Add() {
  var i=1;
  while ($("#widget"+i).length>0) i++;
  iNettuts.addWidget("#"+$("#col").val(), {
    id:    "widget"+i,
    color: "color-blue",
    title: "widget "+i
  })
}
  • widgets_rpc.php: file that retrieves the content of each widget. It is a simple version showing only “This is the content for widget n” yet.
  • inettuts.js: the core file where the widget logic is located. It was first programmed by James Padolsey but I have changed many things. There are three new functions: initWidget (returns the default widget layout), loadWidget (gets widget content with an ajax call) and addWidget (creates a new widget). I have also modified makeSortable (now it is called several times) and sortWidgets (draws widgets in a different way).
initWidget : function (opt) {
  if (!opt.content) opt.content=iNettuts.settings.widgetDefault.content;
  return '<li class="new widget '+opt.color+'"><div class="widget-head"><h3>'+opt.title+'</h3></div><div class="widget-content">'+opt.content+'</div></li>';
},

loadWidget : function(id) {
  $.post("widgets_rpc.php", {"id":id},
    function(data){
      $("#"+id+" "+iNettuts.settings.contentSelector).html(data);
    }
  )
},

addWidget : function (where, opt) {
  $("li").removeClass("new");
  var selectorOld = iNettuts.settings.widgetSelector;
  iNettuts.settings.widgetSelector = '.new';
  $(where).append(iNettuts.initWidget(opt));
  iNettuts.addWidgetControls();
  iNettuts.settings.widgetSelector = selectorOld;
  iNettuts.makeSortable();
  iNettuts.savePreferences();
  iNettuts.loadWidget(opt.id);
},

I have tested this example both in Firefox and Internet Explorer. Some users have reported problems in the comments of my last article, but it works fine for me.





Experimentos con HDR y el plugin Before/After de jQuery

5 10 2009

BuenosAires_HDREstoy experimentando con la fotografía HDR, utilizando fotos de mis últimos viajes. HDR significa «High Dynamic Range» (Alto Rango Dinámico) y es una técnica fotográfica consistente en mezclar una misma foto tomada con distintas exposiciones para obtener un mayor rango dinámico. Para los que no conozcan esta técnica, recomiendo la lectura de Cómo hacer fotografías en HDR.

Cuando estoy de viaje no suelo llevar trípode lo que me habría permitido hacer bracketing, así que tomé las fotos en formato RAW y luego he utilizado Photomatix para obtener el HDR a partir de 3 fotos: la original, con +1 de exposición y con -1. Photomatix me está resultando mucho más fácil de usar que otros programas, como por ejemplo Artizen HDR, que es mucho más completo, pero se tarda más en lograr buenos resultados.

Por otro lado, quería enseñar las fotos en una página web pudiendo comparar fácilmente la foto original con la tratada. Buscando un poco, encontré el plugin de jQuery «Before/After». Este plugin permite poner una foto encima de otra y cambiar la visualización mediante un slider. Lo mejor es que compruebes el resultado tú mismo para comprender lo que se puede conseguir con este plugin:

http://www.jsabino.com/hdr

La primera foto está hecha en Buenos Aires. La segunda en El Calafate y la tercera en Ushuaia. Luego vienen tres de las cataratas de Iguazú y finalmente una realizada recientemente en la Ciudad Encantada de Cuenca.

Para hacer las fotos he utilizado mi cámara Panasonic Lumix LX-3. Esto es solo el principio: aun me queda mucho que aprender sobre HDR para que me queden bien las fotos. Al menos, el efecto que se consigue con el plugin Before/After es espectacular y permite comparar fotos en la web de una forma sencilla.





How to Mimic the iGoogle Interface with Database

19 09 2009

James Padolsey wrote a great tutorial about «How to mimic the iGoogle interface» with jQuery. Thank you very much, James. I have used your code intensively.

The next step was a new article explaining how to save widgets configuration into a cookie. Very useful too. But the final part was to store that data in a database.

That’s what a have done: an example of the same code, but using a database. The changes are very easy to implement. I have just had to modify savePreferences and sortWidgets functions in inettuts.js file, and to write a new PHP file for saving and loading data.

I call jQuery post function to send the data using AJAX for savePreferences:

$.post("iNettuts_rpc.php","value="+cookieString);

As you can see, cookieString is the same variable stored into a cookie. Then iNettuts_rpc.php manages with the database. It’s a very simple PHP showing how to store the value for a user called John Doe:

header("Cache-Control: no-cache");
header("Pragma: nocache");

// User_id -&gt; Should come from a session variable
$user_id="john doe";

// DB connect parameters
$server="localhost";
$user="root";
$password="";
$database="iNettuts";

$table="iNettuts";
$field="config";

// DB connect
mysql_connect($server,$user,$password);
@mysql_select_db($database);

if (isset($_REQUEST["value"])) {
 // SET value  

 $value=$_REQUEST["value"];

 $rs=mysql_query("SELECT * FROM $table WHERE user_id='$user_id'");
 if (mysql_numrows($rs)==0)
 mysql_query("INSERT INTO $table($field,user_id) VALUES('$value','$user_id')");
 else
 mysql_query("UPDATE $table SET $field='$value' WHERE user_id='$user_id'");
 echo "OK";

} else {
 // GET value 

 $rs=mysql_query("SELECT $field FROM $table WHERE user_id='$user_id'");
 if ($row=mysql_fetch_row($rs))
 echo $row[0];
 else
 echo "";
}

mysql_close();

And finally, the most difficult part: loading data to sort widgets:

sortWidgets : function () {
 var iNettuts = this,
 $ = this.jQuery,
 settings = this.settings;

 if(!settings.saveToCookie) {
 $('body').css({background:'#000'});
 $(settings.columns).css({visibility:'visible'});
 return;
 }

 $.post("iNettuts_rpc.php", "",
 function(data){

 var cookie=data;

 if (cookie=="") {
 $('body').css({background:'#000'});
 $(settings.columns).css({visibility:'visible'});
 iNettuts.addWidgetControls();
 iNettuts.makeSortable();
 return;
 }

 /* For each column */
 $(settings.columns).each(function(i){

 var thisColumn = $(this),
 widgetData = cookie.split('|')[i].split(';');

 $(widgetData).each(function(){
 if(!this.length) {return;}
 var thisWidgetData = this.split(','),
 clonedWidget = $('#' + thisWidgetData[0]),
 colorStylePattern = /\bcolor-[\w]{1,}\b/,
 thisWidgetColorClass = $(clonedWidget).attr('class').match(colorStylePattern);

 /* Add/Replace new colour class: */
 if (thisWidgetColorClass) {
 $(clonedWidget).removeClass(thisWidgetColorClass[0]).addClass(thisWidgetData[1]);
 }

 /* Add/replace new title (Bring back reserved characters): */
 $(clonedWidget).find('h3:eq(0)').html(thisWidgetData[2].replace(/\[-PIPE-\]/g,'|').replace(/\[-COMMA-\]/g,','));

 /* Modify collapsed state if needed: */
 if(thisWidgetData[3]==='collapsed') {
 /* Set CSS styles so widget is in COLLAPSED state */
 $(clonedWidget).addClass('collapsed');
 }

 $('#' + thisWidgetData[0]).remove();
 $(thisColumn).append(clonedWidget);
 });
 });

 /* All done, remove loading gif and show columns: */
 $('body').css({background:'#000'});
 $(settings.columns).css({visibility:'visible'});

 iNettuts.addWidgetControls();
 iNettuts.makeSortable();

 });
 }

I have used the same variable names to make it easier.

You can download the full example here.





No más alerts para depurar javascript

3 09 2009

Aun sigo programando el CMS, como ya comenté. Resulta que debido al uso intensivo de jQuery que hago y, por tanto, de javascript, me veía obligado a meter alerts en el código cuando no era capaz de encontrar los fallos con Firebug.

Ya harto de los alerts, me puse a buscar una alternativa y encontré esto:

Backbird

Se trata de una ventana flotante que se sitúa encima de la página y que sirve para mostrar mensajes, organizados en cuatro categorías: mensajes de debug, de información, avisos (warnings) y errores.

Una vez descargado el fichero ZIP de Blackbird, lo primero es incluir el js y el css en tu página:

<script type="text/javascript" src="blackbird.js"></script>
<link type="text/css" rel="Stylesheet" href="blackbird.css" />

De esta forma, pulsando F2 te aparecerá una ventanita con el siguiente aspecto:

Blackbird

Después, para hacer que aparezcan mensajes en ella, disponemos de las siguientes funciones javascript:

log.debug("Esto es un mensaje de depuración");
log.info("Esto es un mensaje de información");
log.warn("Esto es un aviso");
log.error("Y esto es un error");

Sencillo, pero muy útil y elegante… Y, por supuesto, gratuito…





Programando un CMS en verano

16 08 2009

Hace un calor tremendo en la calle y aquí estoy yo, programando un poco. Resulta que por cuestiones laborales, tengo que implementar una especie de gestor de contenidos, que ya va por la versión 2.0, pero que estoy rehaciendo casi por completo para la versión 3.0. La tecnología utilizada es PHP contra MySQL.

Todo empezó hace casi tres años, cuando tuve que implementar la primera versión. Apenas sabía PHP y tuve que buscarme la vida en tiempo record. Me basé en PHPNuke, cambiando mucho código para lograr cubrir los requisitos del cliente. Al menos, la aplicación lleva funcionando ya este tiempo en una gran empresa española, así que no debía estar tan mal.

Pero ahora he descubierto un nuevo mundo. Mis conocimientos de PHP ya no son lo que eran, así que decidí empezar de cero. Ahora el gestor de contenidos tiene un interfaz parecido a iGoogle y Netvibes, con widgets que se pueden configurar, mover, eliminar, etc. Esta parte la he desarrollado utilizando jQuery y copiando ejemplos que he sacado de varios sitios, sobre todo de esta página de NetTuts.

En la parte de servidor, PHP ya no está solo. Utilizo ADOdb para la gestión de la conexión con la base de datos, consiguiendo que lo que hoy funciona con MySQL, mañana lo pueda hacer con cualquier otra base de datos. Y lo que realmente ha supuesto un gran cambio, ahora utilizo el motor de plantillas Smarty para separar la parte de presentación de la lógica de negocio. Ahora que empiezo a sacar partido a Smarty, llego a pensar que se debería utilizar obligatoriamente en casi cualquier proyecto en PHP.

Para septiembre tengo que tener una beta operativa, así que tendré que seguir pasando calor y programando a la vez. En ota ocasión escribiré algo sobre todas estas tecnologías y unos cuantos trucos con las soluciones a los problemas que he ido encontrando.