2011/06/30

Pasos de wizard

Hoy toca una entrada original 100% hecha por mí.

Se me ha encaprichado el usar un wizard en cierto proyecto y además quería que los pasos a seguir en éste se vieran gráficamente de la mejor manera posible, además de que fuera configurable y lo que me ha salido es ésto.



Para el código completo, podéis pasaros por mi JSFiffle


Os pongo el CSS completo, que está autocomentado para que os lo podáis llevar a la cama como lectura de dormitorio.
/**
 *  Steps UL/LI styles.
 *  ================================================
 *  (C) 2011 José Ramón Díaz - jrdiazweb@gmail.com
 *
 *  Useful wizard step indications
 *
 *  Sample:
 *   *
 *  Classes
 *   N/A              - Uncompleted step. Greyed by default
 *   end              - Last uncompleted step. Greyed and no "arrow". Modern browsers just need "last" class instead of end.
 *   last             - Boundary class. Marks last completed step and last step. Used only by modern browsers (the ones with the ability of interpret multiple classes for one element)
 *   current          - Current step
 *   completed        - Completed step. Lighter color
 *   completedLast    - Last completed step. Older browsers compatibility. Modern browsers just need "last completed" classes
 *   completedLastEnd - Last step when all steps are completed. Older browsers compatibility. Modern browsers just need "last completed" classes
 *
 *  If you plan to use only actual browsers just need the classes "", "current" and "completedLast OR "last completed"
 *  If you need full browsers compatibiliy, you need "", "end", "current", "completed", "completedLast" and "completedLastEnd"
 *
 * Extending steps with jQuery
 *     You can also use the steps jQuey plugin. You can find it at
 *     http://3nibbles.blogspot.com/2011/06/pasos-de-wizard.html
 *     
 *  Legal stuff
 *      You are free to use this CSS, but you must give credit or keep header intact.
 *      Please, tell me if you find it useful. An email will be enough.
 *      If you enhance this code or correct a bug, please, tell me.
 */
/* Display styles */
.steps { clear: both; font-family: Arial; font-size: 12px; text-align: left; }
  .steps ul { display: table; table-layout: fixed; width: 100%; }
    .steps ul li { display: table-cell; list-style:none; vertical-align: middle;
                   _display: inline; *width: 120px; /* IE6/7 Hacks */ }

    /* CSS2.x browsers and IE fill width hacks - To fill 100% width they need to set the number the steps in the UL */
    .steps ul.1step li,  .steps ul.2steps li, .steps ul.3steps li, .steps ul.4steps li, .steps ul.5steps li,
    .steps ul.6steps li, .steps ul.7steps li, .steps ul.8steps li, .steps ul.9steps li, .steps ul.10steps li { display: inline; float:left; }
    .steps ul.1step  li  { width: 100%; }
    .steps ul.2steps li  { width: 50%;  }
    .steps ul.3steps li  { width: 33%;  }
    .steps ul.4steps li  { width: 25%;  }
    .steps ul.5steps li  { width: 20%;  }
    .steps ul.6steps li  { width: 16%;  }
    .steps ul.7steps li  { width: 14%;  }
    .steps ul.8steps li  { width: 12%;  }
    .steps ul.9steps li  { width: 11%;  }
    .steps ul.10steps li { width: 10%;  }

      .steps ul li.current {}
      .steps ul li.last a { }
      .steps ul li.completed {}
        .steps ul li.completed.last, .steps ul li.completedLast {}

      .steps ul li a { display:block; text-decoration:none; font-weight: normal; }
        .steps ul li.completed a:hover, .steps ul li.completed.last a:hover, .steps ul li.completedLast a:hover { cursor:pointer; }

        .steps ul li a em, .steps ul li a strong, .steps ul li a span, .steps ul li em, .steps ul li strong, .steps ul li span { display:block; font-style:normal; }
        .steps ul li a em, .steps ul li a strong, .steps ul li em, .steps ul li strong { font-weight:bold; }
        .steps ul li a span, .steps ul li a div, .steps ul li span, .steps ul li div  { height: 45px; /* For even heights */ }

/* Default colors */
.steps { text-shadow: 1px 1px #777; color:#CCCCCC; }
        .steps ul li a:link, .steps ul li a:visited, .steps ul li a:hover, .steps ul li a:active { color:#CCCCCC; }
        .steps ul li.completed.last a, .steps ul li.completed.last a:link, .steps ul li.completed.last a:visited, .steps ul li.completed.last a:hover, .steps ul li.completed.last a:active, .steps ul li.current a,
        .steps ul li.completedLast a, .steps ul li.completedLast a:link, .steps ul li.completedLast a:visited, .steps ul li.completedLast a:hover, .steps ul li.completedLast a:active,
        .steps ul li.completedLastEnd a, .steps ul li.completedLastEnd a:link, .steps ul li.completedLastEnd a:visited, .steps ul li.completedLastEnd a:hover, .steps ul li.completedLastEnd a:active,
        .steps ul li.current a, .steps ul li.current a:link, .steps ul li.current a:visited, .steps ul li.current a:hover,  .steps ul li.current a:active,
        .steps ul li.completed a, .steps ul li.completed a:link, .steps ul li.completed a:visited, .steps ul li.completed a:hover, .steps ul li.completed a:active
         { color:#FFFFFF; }
        .steps ul li.completed a:hover, .steps ul li.completed.last a:hover, .steps ul li.completedLast a:hover { color:#FFFF99; }

/* Color themes - No images */
      .steps ul li { padding: 0 5px 0 5px; background-color: #EBEBEB; }
        .steps ul li.current { background-color:#6699CC; }
        .steps ul li.last {  }
        .steps ul li.completed { background-color:#3366CC; }
        .steps ul li.completed.last, .steps ul li.completedLast { }

/* Color themes - Blue images theme. Sprites size: 23px x 71px */
     .steps ul.blue li { height:71px; padding: 0 20px 0 5px;
                          background: #EBEBEB url("data:image/gif;base64, R0lGODlhFwBjAcQfAP39/Yuv29fh7PLy8mWRxqDB5eXl5Xij0zdytVaJw8vc8Pr6+uDp8/j4+O7u7vX19Ud+vfb29rzN4Ojo6PH0+Wuc0erw9vj6/PX4+77R5/v7+3adxmaZzDNmzOvr6wAAACH5BAEAAB8ALAAAAAAXAGMBAAX/4CeOZGmeaKqubOu+cCzPdG3feK7vfO//wKBwSCwaj8ikcslsOp/QqHRKrVqv2OIAAPB4v+Bw2MHtis/hCdmMbnvWbPd5LW/D6+j1BC+elB18Y2WBgnGEd4RfeoleflyMXmsDkI4AgIyImI+QmYmLjJWQb4Ocm5qmnqiHpKd7oH+lqoF0sYazsnyhsa6Juqe2eGXAcmsPr1yTqcNyvquWkKzOvLe4ddHUyYll2djHy25rl4TN1NN8nYFl5niS0NXEXMa926figeTn73LXfOqn3Om4rKtTzB0AgOyQeYtlL+A3N8gG1hGwAIC8RBUKLGgAiUOAig8MJOKQscECRhw8/4KUiCYlhwIRFiB04/KAAossxbh8+cBioJ0VFGz8uTNAz5B4dpKE+ayOUg4HtjzI6eUpT5lOn9rcSNWqx6Mi23gteZKmV6M4xY6FibWlV6g3p7p9W+CBhpke3qbMyPWM3pRokYb5u7SBBr+Eo6YFQzglW4CN4fb9EvnrXcaRyfKq/NWigc+cM0boApqz4qkGODt+IDO1aps4VTu2OEB2UIsOZAd2XdmmBo68I9fVEDJ4YqmuODPN1hs55cYlNdiLHHga9AImGzauPvhv0N+I9db1rFMvbLnl6ca82N3raYlvRTcN/5T7XKVkG7ZXav++y4zgyWHVbgIqdUABARa40/9w7KlVk3NZucSUfve9x8d/2ElHVGfoJVXYAhSqReBPAHI00niCBXLgYoQ8BgmE2hyUQAc01mjjjTZKwIUAOPaIIwIBAIDBBj4WSeMBAFAgAAJG+ghBAA0AcECTPlaggZIQUPljABpoIIGWOCbAAAMWzAimjVwqeaaNT2Iw5Jo1HnDlknB2AKQGAHxZZwIRUGBBlnDemWedHSSQgQUUEEConEoyCeeTXOgJZwV9/lmnoJKuaaifZsIZQAQX8FhnmxgoWucBoNL5KJSD1klApYCuCWSUmZ5pqAAXdLomqqES+iQDXhJKqZ+EAjlarWDySeuln1pEqKEVIavlRweNmgH/AA1I2ySqzsJpKAALaFuksa1qmqqpZ5IrbpGvhqqrlpjWSWmosYIJwbXlnsmtBPVqeS+4RHp6rKO2ZhAqumDeGay3qb5LZbyTNvwovuv6uG+/VP4LQMC74ikBwckOQAEFHCfMZb7JnhtoBh67eqzDTSLAMgAByOsxxk1CsAXNp3oMsr87lwzmyR97K7IFCA/dcp0ONNAAzFSCW4BsKVnAxdSyISAAAAJQ3YGcAGDNWQcQCNClbDRaiYECqtEoaNs0JiDA2mPXyOUFYhPGpsEWVHYjt3nrdaPMeEYWZgR06z04lxgcoPiNhl7Q918Wjxb4U07OTHmVoDIguI93Nv6W/5GGauC5V002e3lKOfON+radW5XxybJT2e7pO8H7qQaO565l5JO7pO9obAtvbwYMhG08mPPiLmuzxXvbUwNTB9rsAxzsmUFFBRAaJLijJh92zxZlr+lNC1Sfbpqna7qA5Ms/fLLYZxJwJe7mP6zA2b5TaSUF+MufkSCwPw1gjkqMCqAAnYS+2hkpSIlTSpPEBAAFsm5c+xOdA3uUAA1cwIIX7BEC9geAz/XISh804Y0gACwDqtBGSFLeC2nEQnA9zkYso8DqJAi5BfipMYMDlgw3V6MEJGmHB3SbEP1WIwIckYl2amHdOiCBKyGxdhBYAP+YKMQryg4C8UBbF2VjRA4MeNErY5RNA3RINQ6EAAA7") no-repeat right -71px;
                          _background: #EBEBEB url("../images/steps/blue_step_sprite.gif") no-repeat right -71px; /* IE6 needs phisical images */ }
        .steps ul.blue li.current { background-color:#6699CC; }
        .steps ul.blue li.completed, .steps ul.blue li.completedLast, .steps ul.blue li.completedLastEnd { background-color:#3366CC; }

/* Color themes - Green images theme. Sprites size: 23px x 71px */
      .steps ul.green li { height:71px; padding: 0 20px 0 5px;
                           background: #EBEBEB url("data:image/gif;base64,R0lGODlhFwBjAcQfAJ3MktXk0eTk5Pb29vDw8K/0kJnrdnSxZWepVsPZvu3t7Yf2U8Hyrfj5+O7y7enp6Yu/fl2iS/Pz87z1oeHs3ujv5+/07vT2873atvf391SdQoP+R////zajAObm5gAAACH5BAEAAB8ALAAAAAAXAGMBAAX/4CeOZGmeaKqubOu+cCzPdG3feK7vfO//wKBwSCwaj8ikcslsOp/QqHRKrVqv2OKAwxkovuCwWLztjs/jshfN/qrb7DccLZ+TuWv73azf5/sKdYCBeINuhYaEfIOCgI19j3aRkoiMlZaLmH+Ql46depNzoW2jpJ+gp6KpqpmcZgKGarCaiYqbfVwciaVnvL2rccDBrZTExbesyMmxwnTGy7TMz6bT1MrWu81p2tvX2Mzew+HO47/Vzufo5X7Z6+zg2em/7mAPvt3x9GDcYwX8YRsY/PuyYcFABRsCDkxoUB7AhAIdEky4gQI/igolYsy4bmPDjhsjltuYkEAzkhzn/6CsKGzlhJYrRbZZWdDkNJop0eBk+WynzDM7c4oJ+tJYUKFgjk5Q0OkoUoROLWZyuqHoH6pCsU6wuQZrTq9LC3lNOXZDADxlBXope3bAg7EvBxCASnVC24lU1SatyzQPVr17lfZ9eBRw4J1bWzk1fJhm4ltHLSIL+nPoTsk6cVa2TNPkNc2raC7wPDMmMJekS5PcDJTkY5WrT4ZMDYckVzshuW28jZsia9WjL0L8x5C3nuESCRowfvz3nOUHmSYH06CBggMdsmvfzn07huoBuovvrgFAAwkQxqvPDqGBgwAR1o9HAEBCg/TyxUMY8B5BfvIADJBBAv91dwAFFFSAXf+B2wX4HoPb0WcBehBqt9978VUYQYANEFhhBwcQ4EAF/lVYnoAeVnhAAhU4sGCFF8L3IX0ZdPhhBxCISOKHG6J444oWKHgjAARcEN6MAEz4IoQ5GpkhhPTZlyKEIY5YIoTlSfljAgFcsCSDTR5ZIX0UDHhjjiPeuCEBNn4YopYmEtkAAVv6+CGHCsyYwHlTFpjjnG7uKUGf+ZXHJqH5hWjkl4XKiah8igbAqHw9tslkkQFcWSACCdT46Hp/JqDpf5yehx+Ejj5Z4IqLxmknlZhOul6ln6oXpqzqcerph6GOmp+uGZwKJoqq/neAAg44IOx/J1rKYKS4jqdBAgM4u+r/odGKFwG1DQDAK4q+yoeAAjV6C2O1CRT7K7nB3omuupAqEGS24nFYq3oPSCABveI1MIABZW1QQQMZADxWBAE0UEBZOFZrMFYdIBDAABOMxd4AFywMcQfNeqXdAV1q7FSDGD8cVIQJZEwVdzkWPPJ221b78naKiowTgCWfbGDKNq80XssmozQftzvZWmTPG6nX4wVBY7TeigNQQJN8RLos9HqcXlCBz/I1ifQGv3J4NaRFSp10owT8e3aiKW/ttJ9s2swgp2U+fOkFZoPN4JoKJ6QimxIAHCebFQcqoAFDEizBjBQQjPi5DRROZQAZBK53gSdaoDGVGLt9eaMOU/TsmgAW5P35rBMPsHbXpPdMauok/bef5rGLS7kEY6tnnsq1q3dgA6a/Le3EFyzA9XgHYPy13+MhLODNPysPfXcIUDCA6tOz7HjR1JeJO/fcUetA071/LAHtOmungfVWg5/dAQ1YQH7u6z8/M4juzT92BNZjPzMG/NHf1RAgAYp5rAPsE6DQEFCjinksgWM5QAbkVxYIjkUC4wtYCAAAOw==") no-repeat right -71px;
                           _background: #EBEBEB url("../images/steps/green_step_sprite.gif") no-repeat right -71px; /* IE6 needs phisical images */ }
        .steps ul.green li.current { background-color:#83FE47; }
        .steps ul.green li.completed, .steps ul.green li.completedLast, .steps ul.green li.completedLastEnd { background-color:#36A300; }

/* Color themes - Sprite positions & default colors - MUST BE AT THE END! */
      .steps ul li { background-position: right -71px; }
        .steps ul li.current { background-position: right -142px; }
        .steps ul li.end, .steps ul li.last, .steps ul li.completedLastEnd { background-position: right top; }
        .steps ul li.completed { background-position: right -213px; }
        .steps ul li.completed:last-child, .steps ul li.completedLast, .steps ul li.completed.last { background-position: right -284px; } /* class "last completed" only supported by modern browsers */
        .steps ul li:last-child { background-position: right top; } /* CSS3 browsers */

La versión comprimida para los que quieran exprimir el máximo del ancho de banda y que se queda en unos míseros 7kb incluyendo las imágenes de los temas azul y verde, listo para cachear y reduciendo el número de peticiones de 13 a solamente 1 por usar sprites y convertir las imágenes a base64... no se puede pedir más.
/** Steps UL/LI styles. (C) 2011 José Ramón Díaz - jrdiazweb@gmail.com */
.steps{clear:both;font-family:Arial;font-size:12px;text-align:left;text-shadow:1px 1px #777;color:#CCC}
.steps ul{display:table;table-layout:fixed;width:100%}
.steps ul li{display:table-cell;list-style:none;vertical-align:middle;_display:inline;width:120px;background-color:#EBEBEB;background-position:right -71px;padding:0 5px}
.steps ul.1step li,.steps ul.2steps li,.steps ul.3steps li,.steps ul.4steps li,.steps ul.5steps li,.steps ul.6steps li,.steps ul.7steps li,.steps ul.8steps li,.steps ul.9steps li,.steps ul.10steps li{display:inline;float:left}
.steps ul.1step li{width:100%}
.steps ul.2steps li{width:50%}
.steps ul.3steps li{width:33%}
.steps ul.4steps li{width:25%}
.steps ul.5steps li{width:20%}
.steps ul.6steps li{width:16%}
.steps ul.7steps li{width:14%}
.steps ul.8steps li{width:12%}
.steps ul.9steps li{width:11%}
.steps ul.10steps li{width:10%}
.steps ul li a{display:block;text-decoration:none;font-weight:400}
.steps ul li.completed a:hover,.steps ul li.completed.last a:hover,.steps ul li.completedLast a:hover{cursor:pointer;color:#FF9}
.steps ul li a em,.steps ul li a strong,.steps ul li a span,.steps ul li em,.steps ul li strong,.steps ul li span{display:block;font-style:normal}
.steps ul li a em,.steps ul li a strong,.steps ul li em,.steps ul li strong{font-weight:700}
.steps ul li a span,.steps ul li a div,.steps ul li span,.steps ul li div{height:45px}
.steps ul li a:link,.steps ul li a:visited,.steps ul li a:hover,.steps ul li a:active{color:#CCC}
.steps ul li.completed.last a,.steps ul li.completed.last a:link,.steps ul li.completed.last a:visited,.steps ul li.completed.last a:hover,.steps ul li.completed.last a:active,.steps ul li.current a,.steps ul li.completedLast a,.steps ul li.completedLast a:link,.steps ul li.completedLast a:visited,.steps ul li.completedLast a:hover,.steps ul li.completedLast a:active,.steps ul li.completedLastEnd a,.steps ul li.completedLastEnd a:link,.steps ul li.completedLastEnd a:visited,.steps ul li.completedLastEnd a:hover,.steps ul li.completedLastEnd a:active,.steps ul li.current a,.steps ul li.current a:link,.steps ul li.current a:visited,.steps ul li.current a:hover,.steps ul li.current a:active,.steps ul li.completed a,.steps ul li.completed a:link,.steps ul li.completed a:visited,.steps ul li.completed a:hover,.steps ul li.completed a:active{color:#FFF}
.steps ul li.current{background-color:#69C;background-position:right -142px}
.steps ul li.completed{background-color:#36C;background-position:right -213px}
.steps ul.blue li{height:71px;background:#EBEBEB url("data:image/gif;base64, R0lGODlhFwBjAcQfAP39/Yuv29fh7PLy8mWRxqDB5eXl5Xij0zdytVaJw8vc8Pr6+uDp8/j4+O7u7vX19Ud+vfb29rzN4Ojo6PH0+Wuc0erw9vj6/PX4+77R5/v7+3adxmaZzDNmzOvr6wAAACH5BAEAAB8ALAAAAAAXAGMBAAX/4CeOZGmeaKqubOu+cCzPdG3feK7vfO//wKBwSCwaj8ikcslsOp/QqHRKrVqv2OIAAPB4v+Bw2MHtis/hCdmMbnvWbPd5LW/D6+j1BC+elB18Y2WBgnGEd4RfeoleflyMXmsDkI4AgIyImI+QmYmLjJWQb4Ocm5qmnqiHpKd7oH+lqoF0sYazsnyhsa6Juqe2eGXAcmsPr1yTqcNyvquWkKzOvLe4ddHUyYll2djHy25rl4TN1NN8nYFl5niS0NXEXMa926figeTn73LXfOqn3Om4rKtTzB0AgOyQeYtlL+A3N8gG1hGwAIC8RBUKLGgAiUOAig8MJOKQscECRhw8/4KUiCYlhwIRFiB04/KAAossxbh8+cBioJ0VFGz8uTNAz5B4dpKE+ayOUg4HtjzI6eUpT5lOn9rcSNWqx6Mi23gteZKmV6M4xY6FibWlV6g3p7p9W+CBhpke3qbMyPWM3pRokYb5u7SBBr+Eo6YFQzglW4CN4fb9EvnrXcaRyfKq/NWigc+cM0boApqz4qkGODt+IDO1aps4VTu2OEB2UIsOZAd2XdmmBo68I9fVEDJ4YqmuODPN1hs55cYlNdiLHHga9AImGzauPvhv0N+I9db1rFMvbLnl6ca82N3raYlvRTcN/5T7XKVkG7ZXav++y4zgyWHVbgIqdUABARa40/9w7KlVk3NZucSUfve9x8d/2ElHVGfoJVXYAhSqReBPAHI00niCBXLgYoQ8BgmE2hyUQAc01mjjjTZKwIUAOPaIIwIBAIDBBj4WSeMBAFAgAAJG+ghBAA0AcECTPlaggZIQUPljABpoIIGWOCbAAAMWzAimjVwqeaaNT2Iw5Jo1HnDlknB2AKQGAHxZZwIRUGBBlnDemWedHSSQgQUUEEConEoyCeeTXOgJZwV9/lmnoJKuaaifZsIZQAQX8FhnmxgoWucBoNL5KJSD1klApYCuCWSUmZ5pqAAXdLomqqES+iQDXhJKqZ+EAjlarWDySeuln1pEqKEVIavlRweNmgH/AA1I2ySqzsJpKAALaFuksa1qmqqpZ5IrbpGvhqqrlpjWSWmosYIJwbXlnsmtBPVqeS+4RHp6rKO2ZhAqumDeGay3qb5LZbyTNvwovuv6uG+/VP4LQMC74ikBwckOQAEFHCfMZb7JnhtoBh67eqzDTSLAMgAByOsxxk1CsAXNp3oMsr87lwzmyR97K7IFCA/dcp0ONNAAzFSCW4BsKVnAxdSyISAAAAJQ3YGcAGDNWQcQCNClbDRaiYECqtEoaNs0JiDA2mPXyOUFYhPGpsEWVHYjt3nrdaPMeEYWZgR06z04lxgcoPiNhl7Q918Wjxb4U07OTHmVoDIguI93Nv6W/5GGauC5V002e3lKOfON+radW5XxybJT2e7pO8H7qQaO565l5JO7pO9obAtvbwYMhG08mPPiLmuzxXvbUwNTB9rsAxzsmUFFBRAaJLijJh92zxZlr+lNC1Sfbpqna7qA5Ms/fLLYZxJwJe7mP6zA2b5TaSUF+MufkSCwPw1gjkqMCqAAnYS+2hkpSIlTSpPEBAAFsm5c+xOdA3uUAA1cwIIX7BEC9geAz/XISh804Y0gACwDqtBGSFLeC2nEQnA9zkYso8DqJAi5BfipMYMDlgw3V6MEJGmHB3SbEP1WIwIckYl2amHdOiCBKyGxdhBYAP+YKMQryg4C8UBbF2VjRA4MeNErY5RNA3RINQ6EAAA7") no-repeat right -71px;_background:#EBEBEB url(../images/steps/blue_step_sprite.gif) no-repeat right -71px;padding:0 20px 0 5px}
.steps ul.blue li.current{background-color:#69C}
.steps ul.blue li.completed,.steps ul.blue li.completedLast,.steps ul.blue li.completedLastEnd{background-color:#36C}
.steps ul.green li{height:71px;background:#EBEBEB url(data:image/gif;base64,R0lGODlhFwBjAcQfAJ3MktXk0eTk5Pb29vDw8K/0kJnrdnSxZWepVsPZvu3t7Yf2U8Hyrfj5+O7y7enp6Yu/fl2iS/Pz87z1oeHs3ujv5+/07vT2873atvf391SdQoP+R////zajAObm5gAAACH5BAEAAB8ALAAAAAAXAGMBAAX/4CeOZGmeaKqubOu+cCzPdG3feK7vfO//wKBwSCwaj8ikcslsOp/QqHRKrVqv2OKAwxkovuCwWLztjs/jshfN/qrb7DccLZ+TuWv73azf5/sKdYCBeINuhYaEfIOCgI19j3aRkoiMlZaLmH+Ql46depNzoW2jpJ+gp6KpqpmcZgKGarCaiYqbfVwciaVnvL2rccDBrZTExbesyMmxwnTGy7TMz6bT1MrWu81p2tvX2Mzew+HO47/Vzufo5X7Z6+zg2em/7mAPvt3x9GDcYwX8YRsY/PuyYcFABRsCDkxoUB7AhAIdEky4gQI/igolYsy4bmPDjhsjltuYkEAzkhzn/6CsKGzlhJYrRbZZWdDkNJop0eBk+WynzDM7c4oJ+tJYUKFgjk5Q0OkoUoROLWZyuqHoH6pCsU6wuQZrTq9LC3lNOXZDADxlBXope3bAg7EvBxCASnVC24lU1SatyzQPVr17lfZ9eBRw4J1bWzk1fJhm4ltHLSIL+nPoTsk6cVa2TNPkNc2raC7wPDMmMJekS5PcDJTkY5WrT4ZMDYckVzshuW28jZsia9WjL0L8x5C3nuESCRowfvz3nOUHmSYH06CBggMdsmvfzn07huoBuovvrgFAAwkQxqvPDqGBgwAR1o9HAEBCg/TyxUMY8B5BfvIADJBBAv91dwAFFFSAXf+B2wX4HoPb0WcBehBqt9978VUYQYANEFhhBwcQ4EAF/lVYnoAeVnhAAhU4sGCFF8L3IX0ZdPhhBxCISOKHG6J444oWKHgjAARcEN6MAEz4IoQ5GpkhhPTZlyKEIY5YIoTlSfljAgFcsCSDTR5ZIX0UDHhjjiPeuCEBNn4YopYmEtkAAVv6+CGHCsyYwHlTFpjjnG7uKUGf+ZXHJqH5hWjkl4XKiah8igbAqHw9tslkkQFcWSACCdT46Hp/JqDpf5yehx+Ejj5Z4IqLxmknlZhOul6ln6oXpqzqcerph6GOmp+uGZwKJoqq/neAAg44IOx/J1rKYKS4jqdBAgM4u+r/odGKFwG1DQDAK4q+yoeAAjV6C2O1CRT7K7nB3omuupAqEGS24nFYq3oPSCABveI1MIABZW1QQQMZADxWBAE0UEBZOFZrMFYdIBDAABOMxd4AFywMcQfNeqXdAV1q7FSDGD8cVIQJZEwVdzkWPPJ221b78naKiowTgCWfbGDKNq80XssmozQftzvZWmTPG6nX4wVBY7TeigNQQJN8RLos9HqcXlCBz/I1ifQGv3J4NaRFSp10owT8e3aiKW/ttJ9s2swgp2U+fOkFZoPN4JoKJ6QimxIAHCebFQcqoAFDEizBjBQQjPi5DRROZQAZBK53gSdaoDGVGLt9eaMOU/TsmgAW5P35rBMPsHbXpPdMauok/bef5rGLS7kEY6tnnsq1q3dgA6a/Le3EFyzA9XgHYPy13+MhLODNPysPfXcIUDCA6tOz7HjR1JeJO/fcUetA071/LAHtOmungfVWg5/dAQ1YQH7u6z8/M4juzT92BNZjPzMG/NHf1RAgAYp5rAPsE6DQEFCjinksgWM5QAbkVxYIjkUC4wtYCAAAOw==) no-repeat right -71px;_background:#EBEBEB url(../images/steps/green_step_sprite.gif) no-repeat right -71px;padding:0 20px 0 5px}
.steps ul.green li.current{background-color:#83FE47}
.steps ul.green li.completed,.steps ul.green li.completedLast,.steps ul.green li.completedLastEnd{background-color:#36A300}
.steps ul li.completed:last-child,.steps ul li.completedLast,.steps ul li.completed.last{background-position:right -284px}
.steps ul li.end,.steps ul li.last,.steps ul li.completedLastEnd,.steps ul li:last-child{background-position:right top}

2011/06/28

Lazy load de vídeos

Seguimos exprimiendo milisegundos a la carga de páginas. Si nuestra página tiene vídeos, veremos como la instanciación del objeto del vídeo puede matar nuestro rendimiento, sobre todo si se tienen varios vídeos en una misma página. Para agilizarlo, la mejor manera es realizar la carga del objeto de reproducción bajo demanda de la misma manera que se hace por ejemplo en el navegador de Android. Vamos a ver dos aproximaciones, una con jQuery y otra con javascript, pero ambas se basan en "ocultar" el código real del objeto del reproductor con un comentario HTML poniendo delante una fachada de cartón piedra para ocupar el espacio del vídeo. Una vez se hace ckick en el hueco del vídeo, se elimina el comentario y se carga el objeto real de modo que comience la reproducción de forma automática.

Para el usuario el proceso es transparente, pero la velocidad de la página y su uso de memoria nos lo agradecerán. Esta técnica se usa en Facebook por ejemplo.

Utilizando jQuery
La imagen que se usará como fachada, se coloca de fondo de un hipervínculo que apunta a la dirección real del vídeo (por accesibilidad) y a éste, se le asocia un evento de click que se dispare una única vez. Una vez se hace click, se elimina el href y el comentario del objeto del vídeo.



En el evento de document ready, se procesarán los hipervínculos de la clase "video-in-link" para asignar el evento de click que se ejecutará una única vez mediante el método "one".


Usando javascript a pelo
En esta versión, se usará el evento onClick para llamar a una función que recibirá el hipervínculo pulsado mediante "this".



Una vez se pulsa el hipervínculo y se llama a la función, se elemina el manejador de evento de click del hipervínculo que hemos pulsado para evitar que se llame de nuevo. También se elimina el comentario y el href para obtener el mismo funcionamiento que antes.


Notas

El vídeo debe estar en modo de "autoplay" porque si no, el usuario tendrá que hacer click 2 veces. En youtube habrá que poner el parámetro "autoplay=1" al final.

Para evitar los flashes blancos al instanciar el objeto flash del vídeo, hay que poner el parámetro "wmode" con valor "transparent".

2011/06/07

Raphaël - Haciendo más sencillo el uso de SVG y VML

Si alguno ha estado en este mundillo en los últimos 9-10 años, sabrá que uno de los grandes desconocidos del desarrollo web son los gráficos vectoriales. Siempre ha sido muy sencillo añadir un gif a una página, pero a la hora de por ejemplo, dibujar líneas arbitrariamente, por ejemplo encima de la imagen o generación de gráficas dinámicamente, las elecciones tecnológicas eran pocas y además no estándar en general.

A la hora de hacer estas virguerías, siempre le he tenido un ojo echado primero al VML y luego al SVG, pero quien haya tocado el tema aunque minimamente sabrá que es muy potente, pero muy engorroso.

En este aspecto nos echa una mano Raphäel, una librería javascript que permite el uso de SVG y VML de forma (muy) sencilla y además es multiplataforma soportando incluso IE6 (quizás hasta IE5.5 SP2 que es donde yo empecé a hacer pinitos con VML).

Un ejemplo sencillo de Raphaël para dibujar un círculo sacado directamente de su web
// Creates canvas 320 × 200 at 10, 50
var paper = Raphael(10, 50, 320, 200);

// Creates circle at x = 50, y = 40, with radius 10
var circle = paper.circle(50, 40, 10);
// Sets the fill attribute of the circle to red (#f00)
circle.attr("fill", "#f00");

// Sets the stroke attribute of the circle to white
circle.attr("stroke", "#fff");

Ahora un ejemplo más complejo




Bastante sorprendente verdad? pues os invito a pasaros por la web de Raphaël para ver éste y otros ejemplos interactivos.

2011/06/06

Clase para realizar captchas en PHP

En algún momento siempre tendremos que realizar la verificación de que el sujeto al otro lado de la pantalla es un humano y no un robot. Por esa razón aparecieron los temido captchas que nos obligan a realizar un proceso de validación de usuario que las máquinas no son capaces de resolver.

La base de funcionamiento es muy sencilla. Se crea una sesión y en ella se guarda el valor que se corresponde con el captcha gráfico de modo que en el proceso de validación de la información se compara el valor de captcha introducido con el almacenado en la sesión. Si no son iguales, no es un humano (o es uno muy torpe).

En este caso, para dibujar el captcha se usa un TTF almacenado en una ruta del servidor especificada en la variable $font_path.

Por supuesto, que esta versión es una muy simplificada y se puede mejorar en todos los aspectos, pero sirve como plantilla para entender cómo funciona todo el proceso.

interface CaptchaInterface{ 
    function setString($string); 
    function getString(); 
    function getImage(); 
    function auth($input); 
} 

// Clase usando Imagick 
class Captcha implements CaptchaInterface{ 
    private $captchaString; 

    public function setString($string){ 
        $this->captchaString = $string; 
    } 

    public function getString(){ 
        return $this->captchaString; 
    } 

    public function getImage(){ 
        $image = new Imagick(); 
        $image->newImage(80,40,'none'); 
        $image->setImageBackgroundColor('#ffffff'); 

        $string = new ImagickDraw(); 
        $string->setFont("fontPath"); 
        $string->setFontSize(14); 
        $string->annotation(0,20,$this->captchaString); 

        $image->drawImage($string); 
        return $image; 

    } 

    public function renderImage($image){ 
        header("Content-type: image/png"); 
        header("Cache-control: no-cache"); 
        $image->setImageFormat("png"); 
        echo $image; 
        exit; 
    } 

    public function auth($input){ 
        return ($this->captchaString == $input)?true:false; 
    } 
} 

//For GD 
class CaptchaGd implements CaptchaInterface{ 
    private $captchaString; 

    public function setString($string){ 
        $this->captchaString = $string; 
    } 

    public function getString(){ 
        return $this->captchaString; 
    } 

    public function getImage(){ 
        $image = imagecreate(80,40); 
        $bcolor = ImageColorAllocate($image, 255, 255, 255); 
        $textcolor = ImageColorAllocate($image, 0,0,0); 
        $font_path="fontPath"; 
        ImageTTFText ($image, 14, 0, 0, 20, $textcolor, 
                $font_path,$this->captchaString); 
        return $image; 

    } 

    public function renderImage($image){ 
        header("Content-type: image/png"); 
        header("Cache-control: no-cache"); 
        $textcolor = ImageColorAllocate($image, 255, 255, 255); 
        ImagePNG($image); 
        ImageColorAllocate($image,$textcolor); 
        imagedestroy($image); 
        exit; 
    } 

    public function auth($input){ 
        return ($this->captchaString == $input)?true:false; 
    } 
}
Para usarlo, lo primero es usar como si fuera una imagen un PHP que usa esta clase dentro del formulario donde se quiera usar el captcha. El PHP de la "imagen" sería:
//CreateCaptchaImage 
session_start(); 
$obj = new Captcha(); 
$_SESSION['captcha']=substr((md5(date("YmdD His"))), 0, 5); 
$obj->setString($_SESSION['captcha']); 
$obj->renderImage($obj->getImage());
Una vez enviados los datos del formulario, el chequeo de la validación del captcha se haría con el siguiente trozo de PHP
//AuthExample(POST Object) 
session_start(); 
$obj = new Captcha(); 
$obj->setString($_SESSION['captcha']); 
if($obj->auth($_POST['captcha']){ 
//True Action 

}else{ 
//False Action 

}