Siempre viene bien hacernos un control para direcciones IP, que permita pegar toda la dirección ip y que se rellenen las cajas correctamente y que al pulsar el punto se mueva de caja, y la verdad es que tampoco es muy difÃcil de hacer.
Para crearnos el control, vamos a crearnos cuatro cajas de texto (<input type=”text” .. />) que tengan el mismo id, y vamos a controlar que solo se puedan pulsar números, el punto y teclas de control (CTRL+C, por ejemplo), que si escribimos 3 caracteres nos salte de caja, lo mismo que si pulsamos un punto.
Tendremos varias funciones, una de ellas que se ejecuta en el onload del body se encargará de añadir los eventos para pulsar la tecla y levantarla (onmousedown y onmouseup) para evitar que se introduzcan teclas no deseadas y para gestionar el control.
// Obtiene los elementos de la caja de texto y modifica los eventos de pulsacion de teclas
function ini() {
var objs = getElementsById("input", "ip");
for (var i=0; i<objs.length; i++) {
if (window.event) {
objs[i].onkeydown = function() {filtroTeclado(event)};
objs[i].onkeyup = function() {entradaIP(event)};
} else {
objs[i].onkeydown = filtroTeclado;
objs[i].onkeyup = entradaIP;
}
}
}
Tambien son necesarias dos funciones: una para que nos devuelva los elementos del mismo tipo (tag) que tiene el mismo id, y otra que nos devuelve el siguiente elemento en orden de la lista obtenida anteriormente.
// Devuelve los elementos de cierto tipo (tag) que tienen el mismo id
function getElementsById(tag, id) {
var objs = document.getElementsByTagName(tag);
var res = new Array();
for (var i=0; i<objs.length; i++) {
if (objs[i].id == id) {
objs[i].rel = res.length;
res[res.length] = objs[i];
}
}
return res;
}
// Devuelve el siguiente objeto en el arrays de objetos con el mismo id
function dameSiguiente(obj) {
var objs = getElementsById("input", "ip");
var res = null;
for (var i=1; i<objs.length; i++) {
if (obj == objs[i-1]) {
res = objs[i];
}
}
return res;
}
Dentro de las funciones que se encargan de la gestión del control, son necesarias dos: una que evita la pulsación de teclas no deseadas
// Solo permito teclear letras, el punto y teclas de control
function filtroTeclado(evt) {
if (evt.keyCode != 190 && // .
evt.keyCode != 9 && // tab
evt.keyCode != 8 && // borrar <-
!((evt.keyCode >= 96 && evt.keyCode <=105) || evt.keyCode == 110) && // teclado numerico
!evt.ctrlKey && !evt.altKey && !evt.shiftKey && // CTRL, ALT, SHIFT
!String.fromCharCode(evt.keyCode).match(/\d/)) { // num
evt.returnValue = false;
return false;
}
}
y otra que se encarga de la gestión de las cajas: si se pulsa un punto que pase a la siguiente caja y si ocupa mas de tres digitos reparta el contenido en las otras cajas
// Gestiona el control de direcciones IP
function entradaIP(evt) {
// Obtengo el objeto que recibe el evento del teclado
var obj = null;
if (evt.srcElement) {
obj = evt.srcElement;
} else {
obj = evt.target;
}
var txt = obj.value;
// Si pulso el punto paso a la siguiente caja de texto
// El punto se permite pulsar pero no se debe mostrar
if (txt.substring(txt.length-1) == '.') { // Pulso el punto
obj.value = txt.substring(0, txt.length-1);
var sig = dameSiguiente(obj);
if (sig != null && txt != ".") sig.focus();
evt.returnValue = false;
return false;
// Si la longitud que tiene es mayor que 3 (por ejemplo
// debido a un copy/paste, se reparte el contenido por el
// resto de las cajas
} else if (txt.length > 3) {
var nums = new Array();
// Si tiene puntos se divide por los puntos
if (txt.indexOf(".") > -1) {
nums = txt.split(".");
// Si no tiene puntos, se divide en numeros de 3 digitos
} else {
for (var i=0; i<txt.length/3; i++) {
nums[nums.length] = txt.substring(i*3, i*3+3);
}
}
var act = obj;
var cont = 0;
// Se modifican los elementos posteriores
while (act != null && cont<nums.length) {
act.value = nums[cont++];
act = dameSiguiente(act);
}
}
}
Se le podrÃa añadir la funcionalidad de controlar si es una dirección IP válida, no permitiendo números mayores de 255 o el número 0, pero eso lo dejo para otro dÃa.
ExtJS es una librerÃa Javascript que hace de puente a las librerÃas de Yahoo!, jQuery y Prototype+Scriptaculous para ofrecernos de forma sencilla componentes GUI en nuestras aplicaciones cliente.
Entre los componentes que nos ofrece encontramos diálogos, menús, tablas, layouts, paneles, pestañas y mucho más. ExtJS
VÃa / dzone
Algo a lo que suelo estar acostumbrado es a usar el teclado para realizar ciertas acciones (shortcuts), cuando estás 8 horas diarias delante de un ordenador, si no haces uso de los shortcuts, perderás mucho tiempo moviendo el ratón de un lado para otro.
Antes el uso estaba más restringido a las aplicaciones de escritorio (CTRL + S para guardar el documento, ALT + F4 para cerrar la ventana, …), pero ahora que cada vez se usa con más frecuencia aplicaciones web, se echan en falta estas facilidades. Aunque hay que decir que últimamente se ven con más frecuencia: TAB + INTRO para enviar un correo en GMail (claro que esta no se programa), CONTROL + S para guardar una entrada en Blogger, …
Completísimo tutorial que nos explica cómo realizar un lector de feeds usando jQuery Mobile (que actualmente está en versión alpha). Cuyas características son las siguientes:
Mostrar lista de feeds con sus logos
Mostrar el feed cuando se hace click
Crear los estilos para los artículos
Crear un icono Apple-touch para que el usuario pueda añadir la aplicación a su teléfono
Hoy toca realizar conexiones con el servidor usando llamadas autenticadas. Para eso usaremos JWT (JSON Web Token), que viene a ser el envío de un token en cada llamada que necesita autenticación. El token está formado por tres cadenas codificadas en base64 y separadas por un punto (header.payload.signature). En el payload se envía toda la info que queramos, pero sin pasarse porque el token no debería ser muy largo y sobre todo sin enviar información sensible, porque el token no está encriptado, es por ello que la comunicación navegador/servidor debe ser mediante HTTPS. Si quieres una explicación más detallada, aquí te lo explican mejor.
En el payload vamos a guardar varios claims, entre ellos el username y un uuid que usaremos para validar que el usuario es correcto. JWT asegura que el token es válido, pero no viene mal añadir cierta seguridad. Cuando el usuario se loguea un nuevo uuid es generado, por lo que se comprobará si coinciden para el username enviado.
Ya tenemos la explicación teórica, ahora vamos con la parte de programación, primero la parte servidor y luego el frontend.
Lo primero que tenemos que hacer es añadir un nuevo plugin a hapi.js que se encargue de la autenticación, registrando un nuevo strategy a server.auth que use JWT. El plugin hapi-auth-jwt2 se encargará de toda la autenticación JWT y añadiremos una capa extra de validación comprobando que el username y el uuid coinciden.
Y por último añadimos una nueva ruta para autenticar (login) el usuario, comprobamos que el usuario y la contraseña coinciden, y si es así, creamos un uuid que guardamos en la bd y generamos el JWT y lo enviamos en la cabecera Authorization:
Vale, ya tenemos la parte del servidor, ahora la parte del frontend. Primero es añadir las rutas para /login, /account y /logout. He añadido un meta que indica si la ruta tiene que ser obligatoriamente autenticada, obligatoriamente no autenticada o como sea. Para ello, para cada ruta comprobará si el JWT token está almacenado o no y según el meta redirigirá a home o continuará:
Para gestionar el almacenamiento en el navegador en vez de cookies vamos a usar sessionStorage y localStorage para guardar el token JWT. Como el formulario de login permite recordar la sesión, vamos a usar ambos storages. Si no se recuerda usaremos sessionStorage, que se borrará cuando se cierra el navegador, en caso contrario usaremos localStorage.
import Config from'@/js/config';
/**
* API methods for sesion/local storage.
* Depending on `this.session` it saves only on `sessionStorage` or also in `localStorage`
*
* @since v0.8.0
*/classstorage{
/**
* Constructor
*
* @param {boolean} session If stored only in session
*/constructor( session = false ) {
this.session = session;
}
/**
* It saves the token in the session (and local storage is this.session === false),
*
* @param {strig} token JWT token
*/
setJWTToken( token ) {
sessionStorage.setItem( Config.jwt.storageKey, token );
if ( ! this.session ) {
localStorage.setItem( Config.jwt.storageKey, token );
}
}
/**
* It gets a value from session storage or in local if session = false
*
* @returns {string}
*/
getJWTToken() {
const sessionValue = sessionStorage.getItem( Config.jwt.storageKey );
if ( sessionValue ) {
return sessionValue;
}
if ( ! this.session ) {
const storedValue = localStorage.getItem( Config.jwt.storageKey );
return storedValue;
}
returnnull;
}
/**
* Removes JWT token from session and local storage
*/
removeJWTToken() {
sessionStorage.removeItem( Config.jwt.storageKey );
localStorage.removeItem( Config.jwt.storageKey );
}
}
exportdefault storage;
También hemos creado una librería para tratar las llamadas a la API. Hay dos métodos, uno para autenticar el usuario (login) que mirará la cabecera Authorization, y otro método que obtiene los datos del usuario actual realizando una llamada autenticada enviando el JWT token en la cabecera Authorization:
import Config from'@/js/config';
import Storage from'@/js/utils/storage';
/**
* API backend methods
*
* @since 0.8.0
*/classapiFetch{
/**
* Authenticate an user, if ok, JWT token is sent by the server in Authorization header
*
* @param {string} username User name
* @param {string} password Password
*
* @returns {Promise}
*/
auth( username, password ) {
return fetch( Config.api.user.auth, {
method: 'POST',
body: JSON.stringify(
{ username, password }
),
mode: 'cors',
} ).then( response => {
const auth = response.headers.get( 'Authorization' );
if ( auth ) {
return {
response: true,
token: auth,
};
}
return {
response: false,
message: 'Username or password not valid',
};
} );
}
getUser( username ) {
return fetch( `${ Config.api.user.get }${ username }`, {
method: 'GET',
headers: {
Authorization: new Storage().getJWTToken(),
},
} ).then( response => response.json() );
}
}
exportdefault apiFetch;
Ahora solo faltan los controladores para login, account y logout. Login realizar la llamada al servidor y si se obtiene el JWT se guarda:
<template><sectionclass="section"><divclass="container"><h1class="title">
Login
</h1><divv-if="error"class="columns is-centered has-margin-bottom-2"
><b-notificationclass="column is-7-tablet is-6-desktop is-5-widescreen"type="is-danger"has-iconaria-close-label="Close notification"role="alert"size="is-small "
>
{{ error }}
</b-notification></div></div><divclass="container"><divclass="columns is-centered"><divclass="column is-5-tablet is-4-desktop is-3-widescreen has-background-light login-form"><b-fieldlabel="Username"><b-inputv-model="username"value=""maxlength="30"icon="account-circle-outline"
/></b-field><b-fieldlabel="Password"><b-inputv-model="password"value=""type="password"icon="lock-outline"
/></b-field><divclass="field"><b-checkboxv-model="remember">
Remember me
</b-checkbox></div><divclass="has-text-right"><b-buttontype="is-primary"
@click="submit"
>
Log in
</b-button></div></div></div></div></section></template><script>import ApiFectch from'@/js/utils/api';
import Storage from'@/js/utils/storage';
exportdefault {
name: 'Login',
// Form data and error messages if login fails
data() {
return {
username: '',
password: '',
remember: false,
error: '',
};
},
methods: {
// It logs in, using the backend API for authenticate the user data.// If user logs in, it saves the JWT token in the browser. If not, shows error message.
submit: function() {
const api = new ApiFectch();
api.auth( this.username, this.password )
.then( response => {
const storage = new Storage( ! this.remember );
if ( !! response.response && !! response.token ) {
storage.setJWTToken( response.token );
this.error = false;
// `go` refreshes the page, so user data is updatedthis.$router.go( '/' );
} else {
storage.removeJWTToken();
this.error = response.message;
}
} );
},
},
};
</script><stylelang="scss"scoped>.login-form {
border-radius: 4px;
}
</style>
Logout borra los datos JWT del navegador y redirige a home:
<template><sectionclass="hero is-medium is-primary is-bold"><divclass="hero-body"><divclass="container has-text-centered"><h1class="title">
{{ message }}
</h1><h2class="subtitle">
Miss you 💛
</h2></div></div></section></template><script>import User from'@/js/utils/user';
exportdefault {
name: 'Logout',
// Dummy data
data() {
return {
message: 'Bye',
};
},
// After being created it logs out and go to home
created() {
new User().logout();
// `go` instead of `push` because refreshing the page updates the user data// Maybe using vuex is a better way to do it, or not...this.$router.go( '/' );
},
};
</script>
Y Account recupera los datos del usuario una vez que ha creado el controlador:
<template><divid="account"><sectionclass="hero is-primary is-bold"><divclass="hero-body"><divclass="has-text-centered"><h1class="title">
{{ message }}
</h1><h2class="subtitle">
Your data
</h2></div></div></section><sectionclass="section"><divclass="container"><divclass="tile is-ancestor"><divclass="tile is-parent"><pclass="tile is-child notification">
Some content
</p></div><divclass="tile is-8 is-parent"><divclass="tile is-child notification is-info"><ulid="data"><liv-for="( value, key ) in user":key="key"
>
{{ key }} : {{ value }}
</li></ul></div></div></div></div></section></div></template><script>// Dummy componentimport ApiFectch from'@/js/utils/api';
import User from'@/js/utils/user';
exportdefault {
name: 'Account',
data() {
return {
message: 'Account',
user: {},
};
},
created() {
const user = new User().getCurrentUser();
new ApiFectch().getUser( user.username )
.then( response =>this.user = response );
},
};
</script>
Interesante script que realiza el efecto de escala de grises en cualquier imagen o elementos HTML. Funciona para navegadores que permiten canvas, IE ya tiene un filtro que lo realiza.
Su uso es muy sencillo, tan sólo importar la librería y ejecutar el siguiente código: