Truc: una transició entre imatges amb jQuery

Un amic de la casa, Scmute, que és l’impulsor del netlabel i l’associació Tecnonucleo i webmaster dels respectius llocs web, aprofitant que està remodelant les pàgines, em passa aquest truc per a fer transicions entre dues imatges.

És un exemple del localitzador  de jquery  i de com es poden tocar les propietats css dels elements directament. Com a detall, indicar que la propietat d’opacitat es declara dos cops, una per a navegadors IE i l’altre per a la resta. Però després es referencien de forma única des del javascript . El jQuery n’encapsula l’accés.

Vet aquí el codi:

<html>
    <head>
        <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
        <script src="jquery-1.8.2.js"></script>
        <style>
            .p{
                height:198px;
                width:198px;
                cursor:pointer;
                opacity: 1;
            }
            .c{
                height:198px;
                width:198px;
                cursor:pointer;
                filter: alpha(opacity=0);
                opacity: 0;
            }
        </style>
    </head>
    <body>
        <div class="p" style="background-image: url('imatge1.jpg')"/>
        <div class="c" style="background-image: url('imatge2.jpg')"/>
        <script>
            $(".c").hover(
                function(){
                    $(this).stop().animate({"opacity":0.9});
                },
                function(){
                    $(this).stop().animate({"opacity":0});
            });
        </script>
    </body>
</html>

Si voleu veure el truc en acció, mireu aquest enllaç.

Anuncis

HTML5. WebSockets amb Jetty

Continuant amb l’exploració de l’HTML5, avui parlo dels WebSockets. I per a il·lustrar-lo, faré un petit exercici amb websockets fent servir el servidor java Jetty.

La idea de l’exemple l’he tret del projecte http://code.google.com/p/phpwebsocket/. El client html és pràcticament idèntic. La part del servidor, en el meu exemple, l’he implementada amb Java; estant en PHP en l’original.

Els WebSockets permeten realitzar la comunicació bidireccional i full-duplex sobre un canal TCP, entre la pàgina web i el servidor de forma molt més eficient que amb l’esquema de petició-resposta de l’HTML, o que la comunicació amb AJAX o, més explí­citament, mitjançant Comet.

El protocol WebSocket es descriu a la RFC6455.

A dia d’avui, aquest protocol es troba implementat per les darreres versions dels principals navegadors:  Chrome, Internet Explorer, Firefox i Safari.

A més, el servidor ha de soportar també els WebSockets de servidor.

En si mateix el protocol recorda una crida HTTP, però no ho és:

Exemple de petició des del client:

GET /mychat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat
Sec-WebSocket-Version: 13
Origin: http://example.com

Exemple de resposta del servidor:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat

Des del punt de vista del desenvolupament parlem, doncs, de clients i servidors de WebSockets.
HTML5 ofereix una API (normalitzada pel W3C) per a desenvolupar clients d’aplicacions basades en WebSocket.

Per la banda del servidor, els llenguatges més utlitzats, com Java o php aporten classes, mètodes i funcions per a crear i utilitzar websockets de servidor.

Sobre servidors Java: jWebSocket, Apache Tomcat i Jetty també tenen implementacions de sockets de servidor.
Sobre servidors PHP: phpWebSockets

Podem trobar implementacions de servidor WebSocket amb altres llenguatges. Per exemple amb Python una cerca ens proporciona: pywebsocket, o WebSocket for Python (ws4py).

Per a fer un experiment senzill utilitzaré el servidor de java servlets Jetty (versió 8) que proporciona una implementació java de WebSockets de servidor. Podeu revisar aquest article: Jetty WebSocket Server, i aquest altre: Jetty/Feature/WebSockets.
Per descomptat, el javadoc: http://download.eclipse.org/jetty/stable-8/apidocs/

Per a provar els WebSockets he creat un petit projecte amb Eclipse, amb la següent estructura de carpetes:

A la carpeta src hi creo el package java amb la següent classe: com.stsoftlliure.proves.html5.websockets.ProvaWebSocket

Fitxer ProvaWebSocket.java:

package com.stsoftlliure.proves.html5.websockets;

import java.io.IOException;
import java.util.Date;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.eclipse.jetty.websocket.WebSocket;
import org.eclipse.jetty.websocket.WebSocketServlet;
import org.eclipse.jetty.util.log.Log;
public class ProvaWebSocket extends WebSocketServlet  {

   private static final long serialVersionUID = 8442112605629206192L;


    protected void doGet(HttpServletRequest request,
                         HttpServletResponse response)
    throws ServletException ,IOException  {
      response.getOutputStream().println("aquesta és la resposta");
      Log.info("[doGet] No fa res");
    }

    public WebSocket doWebSocketConnect(HttpServletRequest request,
                                        String protocol) {
      Log.info("[doWebSocketConnect] entra a doWebSocketConnect");
      return new ProvaServerWebSocket();
    }

    // inner class que implementa la interface WebSocket
    class ProvaServerWebSocket implements WebSocket.OnTextMessage {
      // la connexió
      Connection wsConn;
      Date dataInici;

      public ProvaServerWebSocket() {
         dataInici = new Date();
      }

      @Override
      public void onClose(int arg0, String arg1) {
         // TODO Auto-generated method stub
         Log.info("[onClose] arg0:" + arg0 + "; arg1: " + arg1);
         wsConn.close(arg0, arg1);
      }

      @Override
      public void onOpen(Connection wsConn) {
         this.wsConn = wsConn;
         try {
            Log.info("[onOpen] wsConn:" + wsConn);
            wsConn.sendMessage("Estic connectat!");
         } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            Log.debug("Error: " + e.toString());
         }
      }

      @Override
      public void onMessage(String sMessage) {
         // TODO Auto-generated method stub
         Log.info("[onMessage] Missatge rebut: " + sMessage);
            // fa el tractament:
         // to do
         // help, hola, nom, edat, data, adeu
 
         try {
            if (sMessage.trim().equalsIgnoreCase("help")) {
               wsConn.sendMessage("ordres acceptades: help, hola, nom, edat, data, adeu");
            }

            if (sMessage.trim().equalsIgnoreCase("hola")) {
               wsConn.sendMessage("Hola! benvingut al servidor WebSocket!");
            }

            if (sMessage.trim().equalsIgnoreCase("nom")) {
               wsConn.sendMessage("Jo em dic ProvaWebSocket.class");
            }

            if (sMessage.trim().equalsIgnoreCase("edat")) {
               wsConn.sendMessage("En funcionament des de: " + dataInici);
            }

            if (sMessage.trim().equalsIgnoreCase("data")) {
               wsConn.sendMessage("ara són les: " + (new Date()));
            }

            if (sMessage.trim().equalsIgnoreCase("adeu")) {
               wsConn.sendMessage("Adéu siau!");
               wsConn.close();
            }
         } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            Log.debug("Error: " + e.toString());
         }
      }
   }
}

Algunes remarques a la classe anterior:

– La classe és un servlet del tipus nou WebSocketServlet.
– Els WebSocketServlet implementen el doGet. El puc invocar directament i obtinc una resposta HTML. Però la funcionalitat apareix amb el nou mètode doWebSocketConnect. A doWebSocketConnect és on treballa el socket de servidor.
-Quan es rep una nova connexió, el mètode doWebSocketConnect el que fa és instanciar la inner class ProvaServerWebSocket, que implementa la interface WebSocket.OnTextMessage.
– La interface WebSocket té altres subinterfaces, a més de la OnTextMessage, per a tractar altres tipus de connexions. Les subinterfaces de WebSocket són: WebSocket.OnBinaryMessage, WebSocket.OnControl, WebSocket.OnFrame, WebSocket.OnTextMessage.
– en la connexió onOpen, obtinc l’objecte Connection wsConn. Que serà propiament el canal full duplex entre servidor i client.
– El tractament d’un missatge rebut el faig en implementar el mètode public void onMessage(String sMessage). sMessage és el missatge rebut des del client. Les respostes al client s’envien mitjançant l’objecte Connection wsConn obtingut en l’event onOpen. En aquest exemple, simplement analitzo quin missatge rebo i, depenent del missatge, envio una o altre resposta al client.
– A la sortida onClose, tanco la connexió

Senzill i net, oi?

L’altre part és la pàgina del client. Una pàgina HTML5 que fa servir websockets ha d’implementar els mètodes que responen als esdeveniments de connexió, alliberament, recepció i enviament de missatges al servidor. Vet aquí la pàgina HTML que dialoga amb el servidor:

Fitxer prova.html

<html>
<head>
<title>WebSocket</title>

<style>
 html,body{font:normal 0.9em arial,helvetica;}
 #log {width:440px; height:200px; border:1px solid #7F9DB9; overflow:auto;}
 #msg {width:330px;}
</style>

<script>
var socket;

function init(){
  var host = "ws://localhost:8080/websockets2/servlet/WebSocket";
  try{
    socket = new WebSocket(host);
    log('WebSocket - status '+socket.readyState);
    socket.onopen    = function(msg){ log("Welcome - status "+this.readyState); };
    socket.onmessage = function(msg){ log("Received: "+msg.data); };
    socket.onclose   = function(msg){ log("Disconnected - status "+this.readyState); };
  }
  catch(ex){ log(ex); }
  $("msg").focus();
}

function send(){
  var txt,msg;
  txt = $("msg");
  msg = txt.value;
  if(!msg){ alert("El missatge no pot ser nul"); return; }
  txt.value="";
  txt.focus();
  try{ socket.send(msg); log('Enviat: '+msg); } catch(ex){ log(ex); }
}

function quit(){
  log("Adéu!");
  socket.close();
  socket=null;
}

// Utilities
function $(id){ return document.getElementById(id); }
function log(msg){ $("log").innerHTML+="<br>"+msg; }
function onkey(event){ if(event.keyCode==13){ send(); } }
</script>

</head>
<body onload="init()">
 <h3>WebSocket</h3>
 <div id="log"></div>
 <input id="msg" type="textbox" onkeypress="onkey(event)"/>
 <button onclick="send()">Send</button>
 <button onclick="quit()">Quit</button>
 <div>Commands: help, hola, nom, edat, data, adeu</div>
</body>
</html>

Alguns comentaris: com es veu, la pàgina és senzilla, la clau del funcionament està al mètode init

function init(){
  var host = "ws://localhost:8080/websockets2/servlet/WebSocket";
  try{
    socket = new WebSocket(host);
    log('WebSocket - status '+socket.readyState);
    socket.onopen    = function(msg){ log("Welcome - status "+this.readyState); };
    socket.onmessage = function(msg){ log("Received: "+msg.data); };
    socket.onclose   = function(msg){ log("Disconnected - status "+this.readyState); };
  }
  catch(ex){ log(ex); }
  $("msg").focus();
}

El qual crea l’objecte socket, el connecta al host, adonem-nos del protocol “ws”; i enllaça els esdeveniments del socket: onopen, onmessage, onclose amb les funcions que els tracten.

L’enviament de missatges al servidor es realitza amb la funció send, que invoca al mètode send de l’objecte socket.

function send(){
  var txt,msg;
  txt = $("msg");
  msg = txt.value;
  if(!msg){ alert("El missatge no pot ser nul"); return; }
  txt.value="";
  txt.focus();
  try{ socket.send(msg); log('Enviat: '+msg); } catch(ex){ log(ex); }
}

Amb l’anterior ja tinc tot el codi. Poso la pàgina prova.html a la carpeta webcontent del projecte Eclipse. A més, en la carpeta webcontent també hi posaré la carpeta WEB-INF, amb una subcarpeta classes buida, la carpeta lib amb les llibreries que em calen i el web.xml.

En resum, a webcontent hi trobo:

webcontent/
    prova.html
    WEB-INF/
        classes/
        lib/
            jetty-util-8.1.2.v20120308.jar
            jetty-websocket-8.1.2.v20120308.jar
        web.xml

Les llibreries a la carpeta lib corresponen a la versió 8 del Jetty i es poden trobar a la seva carpeta lib. He vist que cal incloure-les al war. Em pensava que es referenciarien automàticament al fer deploy al Jetty, però no ha estat així.

El web.xml és força estàndar:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
	http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
	<display-name>tailor</display-name>
	<servlet>
		<servlet-name>WebSocket</servlet-name>
		<servlet-class>com.stsoftlliure.proves.html5.websockets.ProvaWebSocket</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>WebSocket</servlet-name>
		<url-pattern>/servlet/*</url-pattern>
	</servlet-mapping>

	<welcome-file-list>
		<welcome-file>prova.html</welcome-file>
	</welcome-file-list>
</web-app>

Si de cas, remarcar el mapeig de /servlet/* a WebSocket. Aquesta és l’explicació de
var host = “ws://localhost:8080/websockets2/servlet/WebSocket”;
a la funció init.

Amb Jetty, a més, m’ha calgut establir el “context.” Al projecte, el fitxer de context l’he guardat a la carpeta homònima. És el següent fitxer.

websockets2.xml:

<?xml version="1.0"  encoding="ISO-8859-1"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">

<Configure class="org.eclipse.jetty.webapp.WebAppContext">
  <Set name="contextPath">/websockets2</Set>
  <Set name="war"><SystemProperty name="jetty.home" default="."/>/webapps/websockets2.war</Set>
</Configure>

Compte amb els salts de línia. El fitxer XML de context potser no queda “bonic” tal com està, però us podeu trobar amb problemes estranys si afegiu salts de línia.

Amb el fitxer anterior li estem dient quin és el war que ha de desplegar i quin nom tindrà el context. Amb aquest fitxer de context acabem de completar l’explicació del

var host = “ws://localhost:8080/websockets2/servlet/WebSocket”;

Ara és evident:

ws:// – Protocol websocket. Amb websockets segurs, serà wss://
localhost:8080 – Host i port del servidor websocket
websockets2 – El context, o l’aplicació web.
servlet/WebSocket – el Servlet servidor de WebSocket

Finalment, per automatitzar el muntatge i ddesplegament al servidor jetty local de l’apliació, faig servir el següent build.xml d’ant:

<project name="websocket" default="deploy" basedir=".">
  <description>
  proves amb websockets 2
  </description>

  <!-- estableix propietats globals -->
  <property name="src" location="src"/>
  <property name="bin" location="bin"/>
  <property name="war" location="war"/>
  <property name="webcontent" location="webcontent" />
  <property name="context" location="context" />
  <property name="build" location="build"/>
  <property name="lib" location="/home/albert/jetty/lib" />
  <property name="deploy" location="/home/albert/jetty/webapps" />
  <property name="deploy-context" location="/home/albert/jetty/contexts" />


  <target name="init">
    <!-- time stap -->
    <tstamp/>
    <!-- Crea el directory bin utilitzat en la compilació -->
    <mkdir dir="${bin}"/>
  </target>


  <target name="compile" depends="init" description="compila els fitxers font" >
    <!-- Compila les fonts java de ${src} en ${build} -->
    <javac srcdir="${src}" destdir="${bin}">
      <classpath>
       <pathelement location="${lib}/jetty-websocket-8.1.2.v20120308.jar"/>
       <pathelement location="${lib}/servlet-api-3.0.jar"/>
       <pathelement location="${lib}/jetty-util-8.1.2.v20120308.jar"/>
      </classpath>
   </javac>
  </target>

  <target name="build" depends="compile" description="genera el build">
    <!-- Crea el directori de distribució -->
    <delete dir="${build}" />
    <mkdir dir="${build}"/>

    <!--  copia webcontent a build -->
    <copy todir="${build}" >
        <fileset dir="${webcontent}" />
    </copy>
    
    <!--  copia bin a classes -->
    <copy todir="${build}/WEB-INF/classes">
        <fileset dir="${bin}" />
    </copy>
  </target>
     
  <target name="war" depends="build" description="genera el war">   
    <delete file="${war}/websockets2.war" />
    <jar jarfile="${war}/websockets2.war" basedir="${build}"> 
      <include name="**/*"/>
    </jar>
  </target>
  	
  <target name="deploy" depends="war" description="deploy del war i del context">
    <delete file="${deploy}/websockets2.war" />
    <delete file="${deploy-context}/websockets2.xml" />
  	<copy file="${war}/websockets2.war" todir="${deploy}" />
  	<copy file="${context}/websockets2.xml" todir="${deploy-context}" /> 
  </target>

</project>

Aleshores, ses d’Eclipse puc executar el build.xml que compila la classe, construeix el war i el copia a la carpeta webapps del meu Jetty local.

Per provar l’aplicació només en cal engegar el Jetty i obrir el navegador amb suport de websockets (en el meu cas, el Chrome) i apuntar-lo a l’adreça http://localhost:8080/websockets2

I el resultat és el següent.

I, efectivament, puc mantindre el diàleg amb el servidor:

Apunts d’HTML5

HTML5 és més que un llenguatge d’etiquetes. És el resultat de la convergència dels esforços del WWW Consortium i el WHATWG per a fer evolucionar l’HTML. Amb HTML5 podem parlar pròpiament de “programació”. A la vegada, es manté la compatibilitat cap enrere, Tot HTML previ a HTML5 “és també” HTML5. Ara bé, si fem servir les noves etiquetes, fulls d’estil i les noves APIs de javascript podrem arribar molt lluny.

Una demostració del que es pot arribar a fer amb HTML5 és aquest Mario Bros. El codi es pot descarregar. Només cal registrar-se gratuitament a codeproject.com.

Què ens proporciona HTML5? Sense ser exhaustius:

– Noves etiquetes amb nova semàntica per a organitzar millor la presentació de la informació
– Noves etiquetes per a afegir àudio i vídeo a les pàgines sense haver de dependre de plugins de terceres parts.
– Canvas, que ens permet dibuixar en 2D i 3D generant imatges tipus bitmap.
– SVG Scalable Vector Graphics. Per a generar imatges vectorials.
– Un mecanisme nou i consistent entre els diferents browsers per a emmagatzemar dades locals (Local Storage, més enllà de les cookies).
– Nous tipus de camps als formularis HTML amb, això és important, validació pel propi navegador.
– Geolocalització.
– Microdata (una eina per al posicionament a Internet).
– aplicacions offline.
– Base de dades SQL local
– …

Compatibilitat
HTML5 no està especificat del tot. No tots els navegadors dels diferents dispositius suporten HTML5, només els més moderns. No tots els navegadors moderns suporten “tot” l’HTML5. O el suporten exactament igual. En definitiva, res de nou.

Tanmateix, és una bona idea avançar poc a poc en la migració a HTML5. Tot l’HTML anterior a HTML5 és, també HTML5, per tant, la transició a HTML5 “heavy” es pot encetar gradualment.

Abans d’utilitzar una característica d’HTML5 caldrà determinar si està disponible al navegador de l’usuari. En entorns controlats, com intranets, on el navegador de l’usuari pot estar especificat, aquesta pot ser una tasca senzilla. En entorns més oberts una aproximació útil és fer servir llibreries javascript per a determinar les prestacions del navegador. Una de recomanada sovint als diferents manuals és la llibreria opensource Modernizr.

En general, fent servir Modernizr, seguirem el següent esquema:

if (Modernizr.CaracterísticaHTML5) {
    // puc fer servir HTML5
} else {
    // no puc fer servir HTML5
}

Anem a repassar amb més detall algunes opcions:

Noves etiquetes (algunes)
Noves etiquetes per a organitzar la informació, section, article… N’hi han moltes més!

<section>
una secció
<aside> 
informació addicional
</aside>
<header>la capcelera</header>
<article>un article</article>
<footer>el peu</footer>
</section>

Aquestes etiquetes permeten organitzar la informació de forma més propera a com s’està presentant habitualment. Un dels criteri en el desenvolupament d’HTML5 és “estandaritzar” allò que s’està fent. O si es vol, de regular les bones pràctiques. És dir, no s’inventa res si no cal, més aviat, s’afina. Les etiquetes section, article… anteriors es podien trobar abans d’HTML5 com els noms més habituals en DIVs, com va descobrir Google en analitzar milers de pàgines, fent les funcions d’organització que justament ara assumeixen.

CSS
Aquestes etiquetes no tenen, a priori, una representació gràfica predeterminada i això ens porta a l’altre element fonamental de’HTML5: el CSS3 (CSS level 3, acrtualment en desenvolupament).

A HTML5 es considera una mala pràctica (en realitat, des d’abans d’HTML5 que es considera una mala pràctica) l’ús dels atributs modificadors de la presentació a les etiquetes. Totes les característiques de presentació haurien d’anar a fulls d’estil CSS.

Etiquetes per a audio i video
Finalment, l’audio i el video es podran reproduir de forma nativa sense que calguin plugins (és la fi de Flash?). En realitat, la reproducció d’àudio i vídeo nativa dependrà dels còdecs disponibles. A més, no tots els navegadors suporten les etiquetes audio i video. Tot plegat, la següent estructura es pot utilitzar com a punt de partida:

Audio:

<audio controls>
    <source src="audio.ogg" type="audio/ogg">
    <source src="audio.mp3" type="audio/mpeg">
    <a href="http://player.swf?soundFile=audio.mp3">
    http://player.swf?soundFile=audio.mp3</a>
</audio>

L’explicació és la següent: la llista de sources ens permet proporcionar l’audio en diferents formats, amb l’esperança que algun d’ells serà nadiu al navegador dels usuaris (ogg és nadiu a firefox i chrome). El navegador reproduirà el primer de la llista per al que disposi de còdecs. Atenció, doncs, a l’ordre! En cas que el navegador no suporti l’etiqueta audio, aleshores, encara es podrà fer servir el plugin de flash. Si tampoc això funciona, es podrà descarregar el fitxer d’àudio amb un enllaç.

Vídeo:
És simètric a l’anterior:

<video controls 
width="360" height="240"
       poster="poster.jpg">
    <source src="video.ogv" type="video/ogg">
    <source src="video.mp4" type="video/mp4">
    <a href="http://player.swf?file=video.mp4">
    http://player.swf?file=video.mp4</a>
</video>

Com a comentari general, vàlid per a audio i video, indicar que cal especificar correctament els Content-type dels àudios i els vídeos. No A més, cal que els Content-types estiguin disponibles a la llista de Content-types del servidor web que serveis les pàgines.

Canvas
Canvas ens permet dibuixar. Això que sembla tan poca cosa, senzillament no es podia fer abans de forma fàcil. Ara es pot. l’etiqueta canvas determina una regió rectangular en la que es podrà, mitjançant javascript, dibuixar línies, rectangles, corbes… pintar de colors, amb diferents tipus de pinzells, aplicar transformacions…
Canvas és, en ell mateix, tot un món. Vegem-ne un petit exemple:

<!DOCTYPE html>
<html>
<head><title>Canvas</title>
<script>
function Pinta() {
    alert(Math.PI);
    var canvas = document.getElementById('idCanvas');
    var ctx = canvas.getContext('2d');
    ctx.beginPath();
    ctx.moveTo(10,120);

    for(var x=10; x<=310; ++x) {
        y = 100*Math.sin(2*Math.PI*(x - 10)/300);
        ctx.lineTo(x, 120 - y);
    }
    ctx.stroke();
}
</script>
</head>
<body onload="Pinta()">

<canvas id="idCanvas" width="320" height="240">
</canvas>
</body>
</html>

Ens dibuixa la bonica corba sinusoïdal.

 

SVG
No només es poden crear programàticament imatges bitmap o raster. Tambés es poden crear imatges vectorials amb SVG. SVG ja és un vell conegut de diferents browser, com el Firefox, però ara passa a formar part de “l’especificació” d’HTML5. Per a il·lustrar, aquí mostro el que podria ser l’esquelet d’un petit joc:  proveu a fer click al cercle roig que batega i es mou ràpidament per la regió rectangular. Vet aquí una combinació de SVG i JavaScript.

Aquesta animació ha estat del tipus programàtic basada en setInterval de JavaScript, però és possible animar els objecte SVG de forma declarativa, amb l’element “animate”.

<!DOCTYPE html>
<html>
<head>
<meta charset="latin-1" />
<title>svg</title>
<script>
var xx=240;
var yy=160;
var rr=10;
var ix=1; var iy=1; var ir=1;
var obj=null;
var objClicks = null;
var totalClicks = 0;
function init() {
  obj = document.getElementById("idCercle");
  objClicks = document.getElementById("idScoring");
  setInterval("anima()",10);
}

function anima() {
  obj.setAttribute("cx", xx);
  obj.setAttribute("cy", yy);
  obj.setAttribute("r",rr);

  xx += ix; yy += iy; rr += ir;

  if (((xx + rr) > 480) || ((xx - rr) < 0)) {ix = -ix};
  if (((yy + rr) > 320) || ((yy - rr) < 0)) {iy = -iy};
  if ((rr > 20) || (rr < 5)) {ir = -ir};

  return true;
}

function ImA(whatiam) {
      ++totalClicks;
	  objClicks.innerHTML="jo soc un " + whatiam + "; total de clicks: " + totalClicks;
	  return true;
}
</script>
</head>
<body onload="init()">
<svg height="320" width="480" id="idSvgCanvas">
    <circle fill="#ff0000" onclick="ImA('cercle');" id="idCercle">
	</circle>
</svg>
<div id="idScoring">Click me!</div>
</body>
</html>

 

Nous tipus de camps de formulari

HTML5 afegeix nous camps de formulari. Pel que fa a la compatibilitat amb navegadors no HTML5, aquests representaran als nous camps com camps de text. De fet, aquest serà el comportament dels navegadors que suporten HTML5 però que no tenen un comportament específics per a aquests nous tipus.

Dit d’una altra forma, si preveiem que un camp d’un formulari s’adapta a algun dels nous tipus, és interessant fer servir el nou tipus, encara que el navegador no suporti HTML5 perquè, de totes formes, els seguirà presentant com una caixa de text.
En un futur aquests nous camps estaran.

Actualment ja hi han alguns navegadors que fan un tractament especial i validació “nadiua” per a determinats camps.

En el cas de Google Chrome, de moment hi ha representació gràfica especial per als type=”number”, type=”range” i s’ha afegit l’atribut “placeholder” en els input type de tipus text.

<!DOCTYPE html>
<html>
<head><title>Form</title>
</head>
<body ">
<form id="idForm">
data: <input type="date" name="data" placeholder="data"/><br />
hora: <input type="time" name="hora" placeholder="hora"/><br />
placeholder: <input type="text" placeholder="prova placeholder" name="texts" /><br />
email: <input type="email" name="email" placeholder="email" /><br />
url: <input type="url" name="url" placeholder="url"/><br />
Nombre: <input type="number" min="0" max="100" step="5" value="50" /><br />
Rang: <input type="range" min="0" max="100" step="5" value="50" /><br />
cerca: <input type="search" name="cerca" placeholder="cerca"><br />
color: <input type="color" name="color" placeholder="color"><br />
</form>
</body>
</html>

El codi anterior es presenta de la següent forma amb Google Chrome:

Geolocalització
Si està disponible en el navegador del dispositiu mòbil de l’usuari la geolocalització, geolocation, permet, si l’usuari dona el seu permís, que els llocs puguin conèixer l’ubicació del dispositiu del client. Depenent del dispositiu aquesta ubicació es determina de forma molt precisa, via gps, o no tant, per triangulació entre repetidors de telefonia, o de forma més burda, per rang d’IP del proveïdor. A priori, doncs hi ha indeterminació pel que fa a la precisió en la geolocalització, i en el temps de resposta. És més costosa, però molt més precisa, una localització per GPS que una localització per IP, que és immediata, però que pot tenir una “precisió” d’un radi de centenars de quilòmetres.

Un exemple de localització el tenim en el següent codi:

<!DOCTYPE html>
<html>
<head>
<meta charset="latin-1" />
<title>geoloc</title>
<script type="text/javascript">
function geoloc() {
	if (navigator.geolocation) {
	  var timeoutVal = 10 * 1000 * 1000;
	  navigator.geolocation.getCurrentPosition(
	    displayPosition, 
	    displayError,
	    { enableHighAccuracy: true, timeout: timeoutVal, maximumAge: 0 }
	  );
	}
	else {
	  alert("Aquest navegador no suporta la geolocalització");
	}
}

function displayPosition(position) {
  var lLat = position.coords.latitude; 
  var lLong = position.coords.longitude
  var sText= "<b>Latitud:</b> " + lLat + 
             ", <b>Longitud:</b> " + lLong;
  document.getElementById("idPos").innerHTML = sText;
  var sImgSrc = "http://maps.google.com/maps/api/staticmap?" +
               "center=" + lLat + "," + lLong + "&zoom=14&size=512x512&maptype=roadmap&" + 
               "markers=color:blue%7Clabel:S%7C" + lLat + "," + lLong + "&sensor=false";
  document.getElementById("idImg").src = sImgSrc;
}

function displayError(error) {
  var errors = { 
    1: 'Permission denied',
    2: 'Position unavailable',
    3: 'Request timeout'
  };
  document.getElementById("idPos").innerHTML = "Error: " + errors[error.code];
}
</script>
</head>
<body onload="geoloc()" onunload="GUnload()">
Geolocalització.<br />
<div id="idPos">Calculant posició...</div>
<img id="idImg" src="" height="512" width="512" "alt="la meva ubicació"/>
</body>
</html>

Al provar la pàgina anterior al Mozilla Firefox, primer de tot observo que em demana permís:

Li en dono, i em mostra la meva posició.

Val a dir que no ha estat gens precís! Estic fent aquest experiment amb l’ordinador de sobretaula i, evidentment, la localització va per rang d’IP. Encara bo que ha trobat que és Barcelona! Evidentment, la potència de la geolocalització apareix quan la fem servir sobre dispositius mòbils que permeten una ubicació precisa.

Microdata
Les microdata permet afegir semàntica a les pàgines html. Això que sona molt críptic, i que segurament ho és X)), es pot traduir a “un sistema per afegir informació útil a la nostra pàgina web per a que cercadors i indexadors, o sigui Google, la trobin més interessant i la facin sortir més amunt o més destacada en les cerques”.

Microdata afegeix la semàntica mitjançant uns atributs (itemscope, itemtype, itemid, itemprop, itemref) en les etiquetes de l’HTML.

Aquests atributs permeten identificar peces del contingut de la pàgina marcat per etiquetes HTML com valors de propietats definides en un vocabulari.

Els vocabularis són petits i estan relacionats amb un camp semàntic.

Les Microdata utilitzen vocabularis per a definir la semàntica que es volo afegir. Tothom pot crear els vocabularis que vulgui per al camp semàntic que li interessi, però ja n’hi han de fets. I els podeu examinar a http://www.data-vocabulary.org/

Per exemple, per al camp semàntic “persona” podem trobar el següent vocabulari a http://www.data-vocabulary.org/Person/

Property          Description
--------          ----------- 
name (fn)         Name
nickname          Nickname
photo             An image link
title             The person's title (for example, Financial Manager)
role              The person's role (for example, Accountant)
url               Link to a web page, such as the person's home page
                  affiliation (org) The name of an organization with
                  which the person is associated (for example, an
                  employer). If fn and org have the exact same value,
                  Google will interpret the information as referring
                  to a business or organization, not a person.
friend	          Identifies a social relationship between the person
                  described and another person.
contact	          Identifies a social relationship between the person
                  described and another person.
acquaintance	  Identifies a social relationship between the person
                  described and another person.
address (adr)	  The location of the person. Can have the
                  subproperties 
                       street-address, 
                       locality, 
                       region, 
                       postal-code, 
                       and country-name.

Com funciona? el següent exemple esta extret de la Wikipedia

HTML original

<section> Hello, my name is John Doe, 
I am a graduate research assistant at 
the University of Dreams. 

My friends call me Johnny. 
You can visit my homepage at 
<a href="http://www.JohnnyD.com">www.JohnnyD.com</a>. 
I live at 1234 Peach Drive Warner Robins, Georgia.
</section>

Aquest HTML se li pot afegir semàntica, és dir, significat per als cercadors, de la següent forma:

<section itemscope itemtype="http://data-vocabulary.org/Person"> 
        Hello, my name is 
        <span itemprop="name">John Doe</span>, 
        I am a 
        <span itemprop="title">graduate research assistant</span> 
        at the 
        <span itemprop="affiliation">University of Dreams</span>. 
        My friends call me 
        <span itemprop="nickname">Johnny</span>. 
        You can visit my homepage at 
        <a href="http://www.JohnnyD.com" itemprop="url">www.JohnnyD.com</a>. 
        <section itemprop="address" itemscope itemtype="http://data-vocabulary.org/Address">
                I live at 
                <span itemprop="street-address">1234 Peach Drive</span> 
                <span itemprop="locality">Warner Robins</span>
                , 
                <span itemprop="region">Georgia</span>.
        </section>
</section>

Aquest tros de codi HTML5 (noteu l’us de l’etiqueta section) és interpretat per Google de la següent forma (es pot provar el resultats d’afegir Microdata a les pàgines amb el Google’s Rich Snippet Testing Tool.)

I una advertència: que Google pugui interpretar la Microdata NO vol dir, repeteixo, NO vol dir, que forçosament en millori el posicionament.

Database Storage
L’especificació del “Database storage” permet afegir una base de dades SQL local al navegador de l’usuari, per a utilitzar-la localment i com a magatzem persistent.
L’especificació no diu quina BD ha de ser, però almenys Chrome (també Chromium i Android) i Opera estan implementant SQLite i són les úniques implementacions a dia d’avui.

La tria d’SQLite no és casual. Aquesta eficient i potent BD opensource forma part de projectes com FireFox i Chrome des de fa molt de temps. La meva opinió és que disposar d’aquesta BD formant part del nucli del navegador i no posar-la a disposició del usuari era una provocació.

Que la BD triada a les implementacions disponibles sigui SQLlite permet entre d’altres coses, que la puguem examinar (i operar amb ella) fent us d’eines com SQLIteman. Això sí, cal saber on buscar: En el cas del Linux Ubuntu 12.04 que tinc instal·lat i amb el que faig els meus experiments, resulta que, per a Chrome, les bases de dades SQLite es troben, ben amagades, a /home/usuari/.config/google-chrome/Default/databases/file__0 i es van numerant. Amb una inspecció de la carpeta no es poden identificar per nom.

Cal repetir, però, que Database Storage no està present a tots els navegadors que suporten HTML5.

Tanmateix, és temptador disposar d’una BD SQL local.

El següent és un exemple d’agenda de telefons que vaig trobar a Internet (lamentablement, no em vaig guardar l’enllaç) i que he adaptat.

<!DOCTYPE html>
<html>
    <head>
        <meta charset="latin-1" />
        <title>SQL Storage</title>
            
        <script>        
        var sCreate = "CREATE TABLE IF NOT EXISTS Agenda (id INTEGER PRIMARY KEY AUTOINCREMENT, Nom TEXT, cognom TEXT, telefon TEXT)";
        var sSelectAll = "SELECT * FROM Agenda";
        var sInsert = "INSERT INTO Agenda (Nom, cognom, telefon) VALUES (?, ?, ?)";
        var sUpdate = "UPDATE Agenda SET Nom = ?, cognom = ?, telefon = ? WHERE id = ?";
        var sDelete = "DELETE FROM Agenda WHERE id=?";
        var sDrop = "DROP TABLE Agenda";
        var db, dataset;        
        var resultats, id, Nom, cognom, telefon;
        
        function inicialitza() {
            resultats = document.getElementById('resultats');
            id = document.getElementById('id');
            Nom = document.getElementById('Nom');     
            cognom = document.getElementById('cognom'); 
            telefon = document.getElementById('telefon');
            db = openDatabase("AgendaTelefons.db", "1.0", "Agenda de Telefons", 200000);
            crearTaula();
        }
        
        function onError(tx, error) {
            alert(error.message);
        }
        
        function mostrarFiles() {
            resultats.innerHTML = '';
            db.transaction(function(tx) {
                tx.executeSql(sSelectAll, [], function(tx, result) {
                    dataset = result.rows;
                    for (var i = 0, item = null; i < dataset.length; i++) {
                        item = dataset.item(i);
                        resultats.innerHTML +=
                        '<li>' + item['cognom'] + ' , ' + item['Nom'] + 
                        ' <a href="#" onclick="carregarFila('+i+')">modificar</a>  ' + 
                        '<a href="#" onclick="esborrarFila('+item['id']+')">esborrar</a></li>';
                    }
                });
            });
        }
        
        function crearTaula() {
            db.transaction(function(tx) {
                tx.executeSql(sCreate, [], mostrarFiles, onError);
            });
        }
        
        function afegirFila() {
            db.transaction(function(tx) {
                tx.executeSql(sInsert, [Nom.value, cognom.value, telefon.value], carregar_i_reset, onError);
            });
        }
        
        function carregarFila(i) {
            var item = dataset.item(i);
            Nom.value = item['Nom'];
            cognom.value = item['cognom'];
            telefon.value = item['telefon'];
            id.value = item['id'];
        }
        
        function actualitzarFila() {
            db.transaction(function(tx) {
                tx.executeSql(sUpdate, 
                          [Nom.value, cognom.value, telefon.value, id.value], 
                          carregar_i_reset, 
                          onError);
            });
        }
        
        function esborrarFila(id) {
            db.transaction(function(tx) {
                tx.executeSql(sDelete, [id], mostrarFiles, onError);
            });
            netejaFormulari();
        }
        
        function esborrarTaula() {
            db.transaction(function(tx) {
                tx.executeSql(sDrop, [], mostrarFiles, onError);
            });
            netejaFormulari();
        }
        
        function carregar_i_reset() {
            netejaFormulari();
            mostrarFiles();
        }
        
        function netejaFormulari() {
            Nom.value = '';
            cognom.value = '';
            telefon.value = '';
            id.value = '';
        }
        </script>
    </head>
    
    <body onload="inicialitza();">   
        <br/><br/>
        <div align="center">
            <input type="hidden" id="id"/>
            Nom:<input type="text" id="Nom"/><br/>
            Cognom:<input type="text" id="cognom"/><br/>
            Telefon: <input type="text" id="telefon"/><br/>
            <button onClick="netejaFormulari()">Neteja Formulari</button>
            <button onClick="actualitzarFila()">Actualitzar</button>
            <button onClick="afegirFila()">Afegir</button>
            <button onClick="esborrarTaula()">Esborrar taula</button>
            <div id="resultats"> </div>
        </div>
    </body>
</html>

Local Storage
Local Storage és una alternativa als Cookies. És dir, és un mecanisme senzill i consistent entre els navegadors per a emmagatzemar parelles nom-valor de forma persistent (o sigui que entre sessions mantenim la informació).

Aquesta característica l’implementen tots els navegadors que suporten HTML5, des de Internet Explorer fins a Safari, passant pels Chrome, Android, Opera o FireFox. Com a curiositat, en el cas de FireFox resulta que la tecnologia amb que s’implementa Local Storage és, ves quina cosa, SQLite.

LocalStorage proporciona una API senzilla:
getItem(clau),
setItem(clau, valor),
removeItem(clau),
lenght: número de clausdeter
key(index): clau i-èssima

Els dos últims ens permeten implementar un iterador. Anem a repetir l’agenda de telefons fent servir LocalStorage:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="latin-1" />
        <title>Local Storage</title>
            
        <script>              
        var resultats, NomCognom, telefon;
        
        function inicialitza() {
            resultats = document.getElementById('resultats');
            NomCognom = document.getElementById('NomCognom');     
            telefon = document.getElementById('telefon');
            mostrarFiles();
        }
        
        
        function mostrarFiles() {
            resultats.innerHTML = '';
            for (var i = 0; i < localStorage.length; i++) {
            	item = localStorage.key(i);
               resultats.innerHTML +=
               '<li>' + item + "-" + localStorage[item] + 
               ' <a href="#" onclick="carregarFila(\''+ item +'\')">modificar</a>  ' + 
               '<a href="#" onclick="esborrarFila(\''+ item + '\')">esborrar</a></li>';
            }
        }
        
        
        function afegirFila() {
            localStorage[NomCognom.value] = telefon.value;
            mostrarFiles();            
        }
        
        
        function carregarFila(item) {
            NomCognom.value = item;
            telefon.value = localStorage[item];
        }
        
        
        function esborrarFila(item) {
            localStorage.removeItem(item);
            netejaFormulari();
        }
        
        
        function netejaFormulari() {
            NomCognom.value = '';
            telefon.value = '';
            mostrarFiles();
        }
        </script>
    </head>
    
    <body onload="inicialitza();">   
        <br/><br/>
        <div align="center">
            Nom i cognom: <input type="text" id="NomCognom"/><br/>
            Telefon: <input type="text" id="telefon"/><br/>
            <button onClick="netejaFormulari()">Neteja Formulari</button>
            <button onClick="afegirFila()">Afegir - Actualitzar</button>
            <div id="resultats"> </div>
        </div>
    </body>
</html>

I recordem que amb Chrome, podem repassar el contingut del localStorage (amb F12, eines de desenvolupador, a la pestanya resources).

Aplicacions Offline
Amb HTML5 podem fer aplicacions. Les aplicacions amb HTML5 són aplicacions web, és dir, connectades. Aplicacions que viuen a Internet. Tanmateix, característiques com Local Storage o Database Storage ens obren la possibilitat a desenvolupar aplicacions que puguin treballar desconnectades.

Les aplicacions desconnectades es descarregaran total o parcialment al nostre dispositiu, i seran funcionals quan el dispositiu no estigui connectat a Internet, és dir, físicament estaran al nostre dispositiu.

Fer una aplicació offline és tan senzill com afegir-li un fitxer de manifest de caché a una aplicació HTML5 “normal”, i que les pàgines de l’aplicació el declarin a l’etiqueta HTML

<html manifest="/cache.manifest">

El manifest té un content-type específic: “text/cache-manifest”. Cal assegurar que el servidor web serveix el content type correctament.

Què hi posarem en aquest manifest? ni més ni menys que la llista de pàgines i recursos que formaran l’aplicació offline. Un cop el navegador llegeixi el manifest procedirà a descarregar els diferents components. Per exemple:

CACHE MANIFEST
/index.html
/estils.css
/scripts.js
/imatge01.jpg
/imatge02.jpg
/imatge03.jpg

La primera línia ha de ser “CACHE MANIFEST”.

Tanmateix, també podem voler que hi hagin pàgines o recursos que explícitament no es descarreguin mai. Aleshores podem distingir entre recursos a la xarxa, i recursos a la caché

   
CACHE MANIFEST
NETWORK:
/comptador.php
CACHE:
/index.html
/estils.css
/scripts.js
/imatge01.jpg
/imatge02.jpg
/imatge03.jpg

Els recursos a la secció NETWORK no seran descarregats.

A més, por passar l’aplicació web hi hagin recursos que no estiguin definits ni a NETWORK, ni a CACHE. En aquest cas podem tenir una secció FALLBACK per a tractar aquests casos especials.

Per a saber-ne molt més
Hi han molts i molt bons manuals d’HTML5. I alguns d’ells es poden trobar lliures a Internet. En particular, jo m’he llegit, o m’estic llegint els següents:

http://diveintohtml5.info/. Aquesta web és la versió online i lliure del llibre “HTML5. Up and Running” de Mark Pilgrim, publicat per O’Reilly.

Un altre llibre interessant: Pro HTML5 Programming, publicat per Apress

Aquest és molt fàcil de llegir, és curtet i va al gra: HTML5 for Web Designers, publicat per “A Book Apart”.

Una cerca a la web us proporcionarà molts resultats. Els llibres que he citat són, simplement, alguns que he llegit i que, per tant, amb veig amb cor de recomanar.

A més, crec que és molt útil disposar des “xuletaris” o cheat sheets. Aquests estan molt bé:

HTML 5: http://webdesign.about.com/b/2012/05/01/html5-cheat-sheet.htm

Encara un altre d’HTML5: http://www.smashingmagazine.com/2009/07/06/html-5-cheat-sheet-pdf/

Canvas: http://www.nihilogic.dk/labs/canvas_sheet/

Un bon xuletari de CSS també ajuda
CSS: http://coding.smashingmagazine.com/2009/07/13/css-3-cheat-sheet-pdf/

En aquest enllaç, fins i tot fan una recopilació de cheat sheets de CSS.

Finalment, si voleu veure exemples d’HTML5 en acció,aquesta web en proporciona molts: http://html5demos.com/

Com evitar la redirecció a "blogger.com.es"

Aquest matí m’he trobat que Google ja havia aplicat la redirecció automàtica dels blogs a Blogger.com a blogger.com.es.

“.com” és el domini internacional per excel·lència. Posats a triar un domini local, en el cas del bloc d'”Apunts de tecnologia” que des del principi ha fet servir el català i que és militant en l’ús de la nostra llengua, no hi ha cap dubte que el més escaient seria el domini “.cat”.

Tanmateix, i mentre no tingui el .cat, com puc recuperar el domini .com?

Doncs bé, aquest canvi de Google ha provocat la reacció immediata de la comunitat blocaire catalana que ja han publicat diferents mètodes per a evitar aquesta redirecció. Des de l’opció més radical, que seria migrar a WordPress, fins a d’altres que mantenen el bloc a Blogspot.

Apunts de Tecnologia” ha aplicat aquest, que amaga el “.es”, publicat per  Xavier Caballé al seu bloc “L’Home Dibuixat”.

Enganxat directament des del bloc “L’Home Dibuixat”:
  • Aneu a l’edició de la plantilla del bloc:

Edició de la plantilla HTMLEdició de la plantilla HTML
i escolliu l’opció “Modifica l’HTML”. Es mostrarà un avís:
Avís de la modificació de la plantillaAvís de la modificació de la plantilla
Feu clic a “Continua” i a l’editor HTML afegiu el codi que s’indica més avall:
Inserir el codi HTML per evitar la redireccióInserir el codi HTML per evitar la redirecció

  • Enganxeu aquest codi immediatament per sota de la marca <head>

<script type=“text/javascript”>if ((window.location.href.toString().indexOf(‘.com/’))==’-1′){window.location.href = window.location.href.toString().replace(‘.blogspot.com.es/’,’.blogspot.com/ncr/’);}</script>

Moltes gràcies, Xavi!

Un iframe que es redimensiona amb el contingut

Un truc amb javascript: un iframe que es dimensiona per adaptar-se al seu contingut. L’iframe “resizable”.

Important: aquest truc només funciona si el contingut de l’iframe pertany al mateix domini que la pàgina contenidora, l’explicació és que el javascript de la pàgina contenidora ha d’accedir al document de l’iframe, i això només és possible entre pàgines del mateix domini.

per a veure com funciona, en un servidor web de proves crearé dues pàgines:

1. Una página amb un text llarg, d’unes quantes línies (el lorem ipsum és un gran candidat…) que serà la que posaré dins de l’iframe.

2. Una pàgina contenidora com aquesta:

<html>
    <head>
    <meta
        content=”text/html; charset=ISO-8859-1″     
        http-equiv=”content-type”>
    <title>prova1.html</title>
    <script language=”javascript”>
    function MostraMissatge() {
        var v_ifr = document.getElementById(“IDIframe”);
        v_ifr.style.height = (v_ifr.contentWindow.document.body.scrollHeight + 25) + “px”;
        return false;
    }
    </script>
    </head>
    <body>
    <iframe 
        src=”pagina.html” 
        id=”IDIframe” 
        height=”300″ width=”300″ 
        onload=”MostraMissatge()”>

    </iframe>
</body>
</html>

I, com podreu comprovar, funciona. 😉

com obrir una finestra de navegador passant-li paràmetres amb submit i post

Un truc amb javascript: com obrir una finestra de navegador passant-li paràmetres amb method=”post”.

Per exemple, suposem la següent pàgina PHP que recupera els paràmetres amb $_POST. Com ho fem per que  s’executi i rebi els paràmetres dins d’una finestra oberta amb open.window?

<html>
<head>
<meta content=”text/html; charset=ISO-8859-1″
http-equiv=”content-type”>
<title>pagina2</title>
</head>
<body
style=”margin-left: 222px; width: 351px; margin-top: 58px; height: 213px;”>
<big style=”font-weight: bold;”><span
style=”font-family: Helvetica,Arial,sans-serif;”>Mostra els resultats</span></big><br>
<br>
<span style=”font-family: Helvetica,Arial,sans-serif;”>Text 1:</span>&nbsp;
<?php echo $_POST[“v_Text1”] ?><br>
<br>
<span style=”font-family: Helvetica,Arial,sans-serif;”>Text 2:</span>&nbsp;
<?php echo $_POST[“v_Text2”] ?><br>
<br>
<br>
<a style=”font-family: Helvetica,Arial,sans-serif;”
href=”javascript:window.close()”>Tanca</a><br>
</body>
</html>

Podem invocar la pàgina anterior dins d’una finestra oberta amb open.window i passant-li arguments amb POST amb la següent tècnica:
1 – creant la finestra de destinació en l’event OnSubmit del formulari
2 – indicant en l’Action del formulari la pàgina a la que volem accedir
3 – indicant en el Target del formulari que la destinació és la finestra creada a l’OnSubmit

Per exemple, així:

<html>
<head>
<script language=”javascript”>
function ObreFinestra() {
var v_Config=”top=50px,left=50px,width=640px,height=480px,resizable=yes”;
var v_FinestraProva = window.open(“”, “wndFinestraProva”, v_Config);
}

</script>
</head>
<body>
<form
style=”margin-left: 172px; width: 508px; margin-top: 76px; height: 161px;”
onsubmit=”javascript:ObreFinestra();” target=”wndFinestraProva”
method=”post” action=”pagina2.php”
name=”Prova”> <big><span
style=”font-family: Helvetica,Arial,sans-serif;”></span></big><span
style=”font-family: Helvetica,Arial,sans-serif;”><big
style=”font-weight: bold;”>Obrir formulari amb
post en una finestra separada</big><br>
<br>
Text 1: <input name=”v_Text1″><br>
Text 2: <input name=”v_Text2″><br>
<br>
<input name=”v_Submit” value=”Enviar” type=”submit”> <input
name=”v_Reset” value=”Esborrar” type=”reset”><br>
</span></form>
</body>
</html>

Un applet invisible / 11 setembre 2008

Un applet invisible

Un applet java invisible pot ser quelcom útil. En aquest apunt poso un exemple molt senzill d’applet invisible amb l’HTML que l’utilitza.

import java.applet.*;

public class AppletGeneric extends Applet {
  String sNom;
  String sCognom1;
  String sCognom2;
  String sNumTelefon;

  //Construct the applet

  public AppletGeneric() {
  }

  //Initialize the applet
  public void init() {
  }
  //Start the applet

  public void start() {
  }
  //Stop the applet

  public void stop() {
  }
  //Destroy the applet

  public void destroy() {
  }

  //Get Applet information
  public String getAppletInfo()
  {
    return “Applet Information”;
  }

  public void setNom(String sNom) {this.sNom = sNom;}
  public void setCognom1(String sCognom1) {this.sCognom1 = sCognom1;}
  public void set
Cognom2(String sCognom2) {this.sCognom2 = sCognom2;}
  public void set
NumTelefon(String sNumTelefon) {this.sNumTelefon = sNumTelefon;}
  public String get
Nom() {return sNom;}
  public String get
Cognom1() {return sCognom1;}
  public String get
Cognom2() {return sCognom2;}
  public String get
NumTelefon() {return sNumTelefon;}
}

I la pàgina HTML que l’invoca:

<HTML>
<HEAD>
<META HTTP-EQUIV=”Content-Type” CONTENT=”text/html; charset=iso-8859-1″>
<TITLE>
HTML Test Page
</TITLE>
<script language=”javascript”>
function Canviar() {
  document.TestApplet.setNom(document.ProvaApplet.sNom_1.value);
  document.TestApplet.setCognom1(document.ProvaApplet.sCognom1_1.value);
  document.TestApplet.setCognom2(document.ProvaApplet.sCognom2_1.value);
  document.TestApplet.setNumTelefon(document.ProvaApplet.sNumTelefon_1.value);

  ProvaApplet.sNom_2.value=document.TestApplet.getNom();
  ProvaApplet.sCognom1_2.value=document.TestApplet.getCognom1();
  ProvaApplet.sCognom2_2.value=document.TestApplet.getCognom2();
  ProvaApplet.sNumTelefon_2.value=document.TestApplet.getNumTelefon();

}
</script>

</HEAD>
<BODY>
Applet invisible.<BR>
<APPLET
  CODEBASE = “.”
  CODE     = “AppletGeneric.class”
  NAME     = “TestApplet”
  WIDTH    = 0
  HEIGHT   = 0
  HSPACE   = 0
  VSPACE   = 0
  ALIGN    = middle
>
</APPLET>

<applet code=”” width=”” height=””>
</applet>

<FORM NAME=”ProvaApplet” METHOD=”” ACTION=””>
  Nom  <input type=”text” name=”sNom_1″><br>
  Cognom1 <input type=”text” name=”sCognom1_1″><br>
  Cognom2 <input type=”text” name=”sCognom2_1″><br>
  Número telefono <input type=”text” name=”sNumTelefon_1″><br>
<hr>
  Nom  <input type=”text” name=”sNom_2″><br>
  Cognom1 <input type=”text” name=”sCognom1_2″><br>
  Cognom2 <input type=”text” name=”sCognom2_2″><br>
  Número telefon <input type=”text” name=”sNumTelefon_2″><br>
<hr>
<input type=”button” value=”Canviar” onclick=”Canviar()”>
</FORM>
</BODY>
</HTML>

A l’Internet Explorer, depenent del nivell de seguretat, demanarà permís per a executar el codi de l’applet.

L’applet java és descarrega com un .class. Una alternativa seria comprimir-lo i descarregar-lo com un .jar.

Com inicialitzar Dates amb Javascript / 03 setembre 2008

03 setembre 2008

Com inicialitzar Dates amb Javascript

Exemple de com inicialitzar una data amb Javascript, o sigui, com obtenir un objecte Date que es refereixi a la data que vulguem.
// La data que vull obtenir dd/mm/aaaa
var vStrAvui = “02/09/2008”;    

// Separo dia, mes i any
var vTokenAvui = vStrAvui.split(“/”);
var vAvuiDia = vTokenAvui[0];
var vAvuiMes = vTokenAvui[1];
var vAvuiAny = vTokenAvui[2];

// Obtinc un objecte Date
var vAvui = new Date();  

// I l’inicialitzo
vAvui.setFullYear(parseFloat(vAvuiAny), parseFloat(vAvuiMes) – 1, parseFloat(vAvuiDia));

Comentaris:
Per què millor així que no pas inicialitzant amb vAvui = new Date( “02/09/2008”); ?
perquè amb new Date(string_data)  i depenent de la versió de navegador, la data es converteix segons el format “dd/mm/aaaa” o segons el format “mm/dd/aaaa”. Per entendre’ns, de l’IE6 a l’IE7 no funciona igual.

Més coses: per què convertir amb parseFloat en comptes de fer-ho amb parseInt? perquè parseInt te un bug que fa que la conversió falli. Increïble però cert.

L’última: per què li resto 1 al mes? perquè els mesos en Javascript són un array indexat que comença amb zero.

Un applet invisible

Un applet java invisible pot ser quelcom útil. En aquest apunt poso un exemple molt senzill d’applet invisible amb l’HTML que l’utilitza.

import java.applet.*;

public class AppletGeneric extends Applet {
  String sNom;
  String sCognom1;
  String sCognom2;
  String sNumTelefon;

  //Construct the applet

  public AppletGeneric() {
  }

  //Initialize the applet
  public void init() {
  }
  //Start the applet

  public void start() {
  }
  //Stop the applet

  public void stop() {
  }
  //Destroy the applet

  public void destroy() {
  }

  //Get Applet information
  public String getAppletInfo()
  {
    return "Applet Information";
  }

  public void setNom(String sNom) {this.sNom = sNom;}
  public void setCognom1(String s
Cognom1) {this.sCognom1 = sCognom1;}
  public void set
Cognom2(String sCognom2) {this.sCognom2 = sCognom2;}
  public void set
NumTelefon(String sNumTelefon) {this.sNumTelefon = sNumTelefon;}
  public String get
Nom() {return sNom;}
  public String get
Cognom1() {return sCognom1;}
  public String get
Cognom2() {return sCognom2;}
  public String get
NumTelefon() {return sNumTelefon;}
}

I la pàgina HTML que l’invoca:

<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
<TITLE>
HTML Test Page
</TITLE>
<script language="javascript">
function Canviar() {
  document.TestApplet.setNom(document.ProvaApplet.sNom_1.value);
  document.TestApplet.setCognom1(document.ProvaApplet.sCognom1_1.value);
  document.TestApplet.setCognom2(document.ProvaApplet.sCognom2_1.value);
  document.TestApplet.setNumTelefon(document.ProvaApplet.sNumTelefon_1.value);

  ProvaApplet.sNom_2.value=document.TestApplet.getNom();
  ProvaApplet.sCognom1_2.value=document.TestApplet.getCognom1();
  ProvaApplet.sCognom2_2.value=document.TestApplet.getCognom2();
  ProvaApplet.sNumTelefon_2.value=document.TestApplet.getNumTelefon();

}
</script>

</HEAD>
<BODY>
Applet invisible.<BR>
<APPLET
  CODEBASE = "."
  CODE     = "AppletGeneric.class"
  NAME     = "TestApplet"
  WIDTH    = 0
  HEIGHT   = 0
  HSPACE   = 0
  VSPACE   = 0
  ALIGN    = middle
>
</APPLET>

<applet code="" width="" height="">
</applet>

<FORM NAME="ProvaApplet" METHOD="" ACTION="">
  Nom  <input type="text" name="sNom_1"><br>
  Cognom1 <input type="text" name="sCognom1_1"><br>
  Cognom2 <input type="text" name="sCognom2_1"><br>
  Número telefono <input type="text" name="sNumTelefon_1"><br>
<hr>
  Nom  <input type="text" name="sNom_2"><br>
  Cognom1 <input type="text" name="sCognom1_2"><br>
  Cognom2 <input type="text" name="sCognom2_2"><br>
  Número telefon <input type="text" name="sNumTelefon_2"><br>
<hr>
<input type="button" value="Canviar" onclick="Canviar()">
</FORM>
</BODY>
</HTML>

A l’Internet Explorer, depenent del nivell de seguretat, demanarà permís per a executar el codi de l’applet.

L’applet java és descarrega com un .class. Una alternativa seria comprimir-lo i descarregar-lo com un .jar.

Com inicialitzar Dates amb Javascript

Exemple de com inicialitzar una data amb Javascript, o sigui, com obtenir un objecte Date que es refereixi a la data que vulguem.

// La data que vull obtenir dd/mm/aaaa
var vStrAvui = "02/09/2008";    

// Separo dia, mes i any
var vTokenAvui = vStrAvui.split("/");
var vAvuiDia = vTokenAvui[0];
var vAvuiMes = vTokenAvui[1];
var vAvuiAny = vTokenAvui[2];

// Obtinc un objecte Date
var vAvui = new Date();  

// I l’inicialitzo
vAvui.setFullYear(parseFloat(vAvuiAny), parseFloat(vAvuiMes) – 1, parseFloat(vAvuiDia));

Comentaris:
Per què millor així que no pas inicialitzant amb vAvui = new Date(
"02/09/2008"); ?
perquè amb new Date(string_data)  i depenent de la versió de navegador, la data es converteix segons el format "dd/mm/aaaa" o segons el format "mm/dd/aaaa". Per entendre’ns, de l’IE6 a l’IE7 no funciona igual.

Més coses: per què convertir amb parseFloat en comptes de fer-ho amb parseInt? perquè parseInt te un bug que fa que la conversió falli. Increïble però cert.

L’última: per què li resto 1 al mes? perquè els mesos en Javascript són un array indexat que comença amb zero.