Se refiere a la técnica de petición de datos desde el cliente a un servidor web, en segundo plano, de forma asíncrona, y obtener una respuesta formateada en XML.\n\nEsta técnica se ha empleado con éxito en multitud de aplicaciones web modernas como: ~GMail, Microsoft Live! , Yahoo Messenger, etc... Permite la actualización de una web sin la carga completa de la misma, dando la sensación al usuario de mayor velocidad, o de un funcionamiento más parecido a una aplicación de escritorio.\n\n//Ver// [[Bibliografía]]
El acceso al Modelo de Objetos del Documento HTML (DOM) nos permite manejar desde programación en la parte del cliente, cualquier aspecto de nuestra página. Como habíamos dicho en la introducción, debido a las diferencias entre los distintos navegadores (mismo objeto, distintos métodos y propiedades), se hace necesario una librería de abstracción que nos permita modificar las propiedades y llamar a los métodos de una misma forma (Ver ejemplo de la [[problemática con window.onload]]). \n\nHay muchas soluciones en forma de librerías de manejo de DOM, como [[Prototype]], [[Mootools]], [[jQuery]], entre las más conocidas y usadas. En concreto, nosotros nos referiremos, en adelante, a jQuery como nuestra librería de manejo de DOM común.\n\n
!!Creditos\n* Documentación ~NormaWeb de //Juan Luis Serradilla Amarilla//\n* Diseño formulario //Inma Bermejo Salar//\n* Documentación ~JavaScript inicial y ejemplos de código creada por //José Francisco Rives Lirola//
!!JavaScript\n* [[ECMAScript|http://www.ecma-international.org/publications/standards/Ecma-262.htm]]. Especificación del estándar de ~JavaScript.\n* [[Web oficial de Prototype|http://www.prototypejs.org/]]\n* [[Web oficial de Mootools|http://mootools.net/]]\n* [[Web oficial de jQuery|http://jquery.com]] . Documentación visual [[Visual jQuery|http://www.visualjquery.com/]]\n* Artículo original en inglés [[Objectifying JavaScript|http://www.digital-web.com/articles/objectifying_javascript/]]. Describe cómo crear objetos en ~JavaScript y ocultar sus propiedades y métodos privados.\n* [[JavaScript closures for dummies|http://blog.morrisjohns.com/javascript_closures_for_dummies]]. Muestra los conceptos básicos de clausura.\n* [[JavaScript no intrusivo|http://adactio.com/atmedia2005/]]\n\n!!DOM\n* Información sobre [[Document Object Model|http://es.wikipedia.org/wiki/Document_Object_Model]] en la Wikipedia\n* [[Técnica de detección de objetos|http://www.quirksmode.org/js/support.html]]\n* [[Detectando el navegador usando detección de objetos1|http://www.javascriptkit.com/javatutors/objdetect.shtml]]\n\n!!Navegadores Modernos\n* [[Lista de características de InternetExplorer 6|http://www.microsoft.com/spain/windowsxp/evaluation/features/internetexplorer.mspx]]\n* [[Lista de características de Opera|http://es.wikipedia.org/wiki/Opera_(navegador)]]\n* [[Lista de características de Netscape|http://es.wikipedia.org/wiki/Netscape_Navigator]]\n* [[Lista de características de Firefox|http://es.wikipedia.org/wiki/Mozilla_Firefox]]\n* [[Lista de características de Safari|http://es.wikipedia.org/wiki/Safari_(navegador)]]\n* [[Lista de características de Konqueror|http://es.wikipedia.org/wiki/Konqueror]]\n* Información sobre [[XMLHttpRequest|http://es.wikipedia.org/wiki/XmlHttpRequest]] en la Wikipedia\n* Información sobre [[AJAX|http://es.wikipedia.org/wiki/AJAX]] en la Wikipedia
!!El problema del menú de las 1000 funciones\nA menudo nos habrá ocurrido o hemos visto un código ~JavaScript compuesto de multitud de funciones que manejan un típico menú desplegable, o algo parecido, vemos varias funciones interrelacionadas para manejar o realizar una determinada acción y otras funciones que se dedican a otra cosa.\nej:\n{{{\n function despliegamenu(){\n ...\n }\n\n function recogemenu(){\n ...\n }\n\n function validaform(){\n ...\n }\n}}}\n\nEl problema llega cuando llega una persona nueva y ve nuestro código. ¿qué funciones manejan el menú y qué funciones no sirven para nada del menú?. Cuando el número de funciones aumenta, esto puede llevar tiempo y resultar una pesadilla de código. \n\nEn programación orientada a objetos, vemos que un objeto se refiere a una //cosa// y que sus propiedades definen qué es esa cosa y sus métodos, describen las acciones que podemos hacer con ella. Es recomendable agrupar todas las funciones relacionadas, en un solo objeto, así podríamos tener el objeto menú con sus funciones para el manejo del mismo. Vamos a ver las formas de crear un objeto y la mejor manera para ocultar sus propiedades privadas.\n\n!! Los objetos literales\nEn ~JavaScript podemos crear un objeto fácilmente declarando un objeto de forma literal, usando llaves y pares de valores, nombre:valor , donde nombre, es el nombre de la variable y valor, el valor que tome, pudiendo ser cualquier tipo soportado por ~JavaScript, incluido, una función. Una variable cuyo valor es una función, será un método de nuestro objeto.\n\n{{{\nvar coche = {\n ruedas: 4,\n puertas: 3,\n motor: "diesel",\n arrancar: function(){ /* código de la función */ }\n}\n}}}\n\nTambién podremos crear un nuevo objeto a través de la palabra clave new\n{{{\nvar coche = new Object();\ncoche.ruedas = 4;\ncoche.puertas = 3;\ncoche.motor = "diesel";\ncoche.arrancar = function(){ /* código de la función */ }\n}}}\n\nPodemos acceder a las propiedades de nuestro objeto usando dos tipos de notación, la notación punto "." y la notación como array "[]" . En ocasiones usar la segunda notación tipo array asociativo es útil para funciones que admitan parámetros de distinto tipo.\n{{{\nalert(coche.puertas);\nalert(coche["puertas"]);\n\nfunction da_valor(propiedad, valor){\n coche[propiedad] = valor;\n}\n\nda_valor("puertas",5);\nda_valor("motor","gasolina");\n}}}\n\n!!Las funciones objeto\nEn ~JavaScript, una función es un objeto. Podemos usar esto para crear un objeto plantilla, de manera que podamos crear diferentes instancias de un mismo objeto plantilla.\n\n{{{\nfunction coche(){\n //Propiedades por defecto\n this.ruedas = 4;\n this.puertas = 3;\n this.motor = "diesel";\n this.arrancar = function(){ /* código de la función */ }\n}\n\nvar micoche = new coche();\nalert(micoche.ruedas); //mostrará el valor 3\n}}}\n\nTenemos el problema con esta forma de declarar nuestro objeto plantilla, que si expandimos la declaración del objeto, esto no se aplicará a las instancias ya creadas. Esto es porque al crear el nuevo objeto, tiene una copia nueva de coche, pero no una referencia a este objeto, con el consiguiente consumo de memoria. En particular si creamos varios cientos de estos objetos, el consumo de memoria se verá aumentado sensiblemente.\n{{{\n coche.velocidad = "160";\n alert(micoche.velocidad); // muestra undefined , la nueva propiedad no es aplicada en la instancia micoche creada anteriormente\n}}}\n\nPara poder solucionar este problema tenemos la cláusula {{{ prototype }}} con la que podremos crear referencias al objeto en vez de una copia.\n{{{\n function coche(){\n this.ruedas = 4;\n this.puertas = 3;\n }\n \n coche.prototype.arrancar = function(){ /* Código de la función */ }\n\n var micoche = new coche();\n\n coche.prototype.velocidad = "160";\n alert(micoche.velocidad); // muestra 160\n}}}\n\nLa ventaja como vemos es que, podemos extender el objeto plantilla con nuevas propiedades y métodos, y estos también existirán en las instancias creadas anteriormente.\n\n!!Protegiendo las propiedades privadas, la clausura\nHasta ahora, todos los métodos que hemos usado no nos prohibían el acceso a las propiedades de un objeto, pudiendo acceder desde afuera directamente a cualquier método o propiedad fuera de la declaración del objeto, por ejemplo: {{{ coche.ruedas = "sin ruedas"; }}} \n\nLas funciones en ~JavaScript también pueden devolver un objeto, podemos usar esto para ocultar las variables privadas (o funciones/métodos ). Veamos esto con un ejemplo:\n{{{\n function coche(){\n this.ruedas = 4;\n this.puertas = 3;\n this.velocidad = "160";\n return { obtenvelocidad : this.velocidad }\n }\n\n var micoche = new coche();\n alert(micoche.ruedas); // muestra undefined\n alert(micoche.obtenvelocidad); // muestra 160\n}}}\n\nPara proteger nuestras variables privadas, lo que haremos es devolver un objeto que será el constructor del mismo, y que proveerá del acceso a los métodos públicos pero no a los privados.\n{{{\n function coche(){\n ruedas = 4;\n puertas = 3;\n velocidad = "160"\n controlaTemperatura = function(){ /* código de la función privada */ }\n\n function constructor(){\n this.obtenNumRuedas = function(){ return ruedas; }\n this.ejecutaControlTemperatura = function(){ controlaTemperatura(); }\n this.cambiaVelocidad = function(){\n velocidad = "140";\n }\n }\n\n return new constructor();\n }\n\n var micoche = new coche();\n alert(micoche.ruedas); // muestra undefined\n alert(micoche.obtenNumRuedas); //muestra 4\n}}}\n\n\n~~//Ver más sobre clausuras con ~JavaScript en// [[JavaScript closures for dummies|http://blog.morrisjohns.com/javascript_closures_for_dummies]]~~\n\n!! Creando un objeto copia a partir de la plantilla\nPodríamos querer realmente que una instancia de un objeto, sea una copia, de manera que la modificación del objeto plantilla no afecte a nuestro objeto copia, y mantengamos aún las propiedades de clausura visto anteriormente. La forma de hacerlo es aplicando de nuevo {{{ new }}} sobre el objeto plantilla\n{{{\n var coche = new function(){ //Esta es la línea que afecta para que creemos una copia y no una referencia\n ruedas = 4;\n puertas = 3;\n velocidad = "160"\n controlaTemperatura = function(){ /* código de la función privada */ }\n\n function constructor(){\n this.obtenNumRuedas = function(){ return ruedas; }\n this.ejecutaControlTemperatura = function(){ controlaTemperatura(); }\n this.cambiaVelocidad = function(){\n velocidad = "140";\n }\n }\n\n return new constructor();\n }\n\n var micoche = new coche();\n}}}\n\n\n~~//Ver artículo original en inglés //[[Objectifying JavaScript|http://www.digital-web.com/articles/objectifying_javascript/]]~~
Modelo de objetos de un documento HTML. Es la estructura de objetos que tenemos en el navegador cuando parseamos un documento HTML. //Ver// [[Bibliografía]]
[[Programación JavaScript]]
!!!Frontend (HTML y CSS)\nEs el mismo código de [[NormaWeb|http://www.um.es/atica/mncs/normaweb]] salvo en el cambio de los campos de opción para el tipo de relación con la Universidad por un select, para dar más variedad al ejemplo. Dentro de la parte visual (HTML y CSS) hay que tener cuidado en no dejar referencias a eventos DOM como los clásicos ''onsubmit'' u ''onclick'' que se suelen usar dentro del HTML. De esta manera podremos separar completamente el diseño, de la implementación.\n\nLo único que tendremos que modificar es la llamada a las librerías ~JavaScript que vamos a usar.\n{{{\n<!-- Librería de validación de formularios en JavaScript -->\n<script type="text/javascript" src="js/yav.js"></script>\n<! -- Archivo que contiene la traducción de los mensajes de error en castellano -->\n<script type="text/javascript" src="js/yav-config.js"></script>\n\n<!-- Librería para el manejo de DOM -->\n<script type="text/javascript" src="js/jquery.js"></script>\n\n<!-- Código de nuestra página -->\n<script type="text/javascript" src="js/formulario3_code.js"></script>\n}}}\n\n!!!Código ~JavaScript\nEl código de nuestra página se ocupará de cargar las reglas de validación que vamos a aplicar y usar uno de los métodos de YAV para mostrar los mensajes, el paso por variable, en caso de haber un error YAV creará un array en jsErrors con las cadenas de error a mostrar. Debido a la cantidad de campos del formulario y las dependencias entre ellos, el número de reglas es bastante grande. Por lo general no tendremos tantas reglas:\n\n{{{\n//declaramos las reglas de validación\nvar reglas = new Array();\nreglas[0]= 'sugerencia_queja|required|{id:"sugerencia",msg:"Debe indicar una de las dos opciones, sugerencia o queja."}';\nreglas[1]= 'relacion_universidad|required|{id:"relacion_universidad",msg:"Debe indicar el tipo de relación que tiene con la Universidad."}';\nreglas[2]= 'dependencia|required|{id:"dependencia",msg:"Debe indicar el asunto de la sugerencia o queja obligatoriamente."}';\nreglas[3]= 'texto|required|{id:"texto",msg:"Debe indicar el texto de la sugerencia o queja obligatoriamente."}';\nreglas[4]= 'apellidos|required|pre-condition';\nreglas[5]= 'nombre|required|pre-condition';\nreglas[6]= '4|and|5|pre-condition';\nreglas[7]= 'apellidos|empty|pre-condition';\nreglas[8]= 'nombre|empty|pre-condition';\nreglas[9]= '7|and|8|pre-condition';\nreglas[10]= '6|or|9|{id:"apellidos",msg:"Escriba su Nombre completo con Apellidos."}';\nreglas[11]= 'respuesta|notequal|ninguno|pre-condition';\nreglas[12]= '4|and|5|post-condition';\nreglas[13]= '11|implies|12|{id:"apellidos",msg:"Debe escribir su Nombre y Apellidos en caso de requerir respuesta."}';\nreglas[14]= 'respuesta|equal|email|pre-condition';\nreglas[15]= 'diremail|required|pre-condition';\nreglas[16]= 'diremail|email|pre-condition';\nreglas[17]= '15|and|16|post-condition';\nreglas[18]= '14|implies|17|{id:"diremail",msg:"Debe de introducir una dirección de email válida."}';\nreglas[19]= 'respuesta|equal|telefono|pre-condition';\nreglas[20]= 'numtlf|required|pre-condition';\nreglas[21]= 'numtlf|regexp|^([\s\s(]{0,1}([0-9]){3}[\s\s)]{0,1}[ ]?)?(([0-9]){2,4}[ ]?[-]?[ ]?)+$|pre-condition';\nreglas[22]= '20|and|21|post-condition';\nreglas[23]= '19|implies|22|{id:"numtlf",msg:"Debe de introducir número de teléfono válido, ej: (968) 12 43 54, 646234987, 968 234 234."}';\nreglas[24]= 'respuesta|equal|fax|pre-condition';\nreglas[25]= 'numfax|required|pre-condition';\nreglas[26]= 'numfax|regexp|^([\s\s(]{0,1}([0-9]){3}[\s\s)]{0,1}[ ]?)?(([0-9]){2,4}[ ]?[-]?[ ]?)+$|pre-condition';\nreglas[27]= '25|and|26|post-condition';\nreglas[28]= '24|implies|27|{id:"numfax",msg:"Debe de introducir número de fax válido, ej: (968) 12 43 54, 646234987, 968 234 234."}';\nreglas[29]= 'respuesta|equal|correo|pre-condition';\nreglas[30]= 'domicilio|required|pre-condition';\nreglas[31]= 'numero|required|pre-condition';\nreglas[32]= 'escalera|required|pre-condition';\nreglas[33]= 'piso|required|pre-condition';\nreglas[34]= 'municipio|required|pre-condition';\nreglas[35]= 'cpostal|required|pre-condition';\nreglas[36]= 'provincia|required|pre-condition';\nreglas[37]= '30|and|31-32-33-34-35-36|post-condition';\nreglas[38]= '29|implies|37|{id:"domicilio",msg:"Debe introducir los datos de su dirección postal."}';\nreglas[39]= 'numero|integer|{id:"domicilio",msg:"El número de su domicilio debe de ser un valor numérico."}';\nreglas[40]= 'cpostal|regexp|^([1-4[0-9]){2}[0-9]{3}$|post-condition';\nreglas[41]= '35|implies|40|{id:"domicilio",msg:"El código postal debe de ser válido, ej: 30293, 35093,.."}';\n\n}}}\n\nYAV crea el array de forma desordenada, es decir, la posición [i] en el array jsErrors no mantiene relación con la posición [i] del array de reglas, pero nosotros añadiremos la información del campo relacionado con el error para mostrar el mensaje de error encima del campo dentro del mensaje de error configurado dentro de las reglas que hemos indicado a YAV. Para esto declararemos el mensaje de error en formato [[JSON|http://www.json.org/]], que básicamente consiste en definir los datos como si definieramos un objeto en ~JavaScript ^^(ver [[Creación de objetos en JavaScript]])^^ para luego parsearlo dentro de una variable de forma rápida con la función ''eval''.\n\nLa sintaxis para escribir las reglas de YAV podemos encontrarla en: [[http://yav.sourceforge.net/en/validationrules.html|http://yav.sourceforge.net/en/validationrules.html]].\n\nEn muchas ocasiones validaremos el valor de un campo de texto usando expresiones regulares, por ejemplo para DNI, nº de teléfono, código postal, etc... Internet es una gran fuente de datos de expresiones regulares ya creadas para usar para validad nuestros campos, en concreto [[Regular Expresion Library|http://regexlib.com/default.aspx]] es un sitio web que se ocupa de recopilarlas, permite subir nuevas expresiones o realizar búsquedas de expresiones regulares dependiendo de la función que realicen o usar un programa como [[The Regulator|http://regex.osherove.com/]] que ayuda a confeccionar y testear la expresión regular que buscamos.\n\nComentaremos seguidamente el código que mostrará los errores en caso de haberlos:\n{{{\n$(document).ready(function(){\n $("#formsugerencias").submit(function(){\n //Borramos si existe, los errores mostrados\n $(".error").remove();\n $("#errorsDiv").remove();\n //Volvemos a generar la capa vacía para indicar los posibles errores\n $("body").append("<div id='errorsDiv'></div>");\n if (performCheck(this.id, reglas, 'jsVar')){\n //dejamos que el formulario se envíe normalmente\n return true;\n }else{\n //Indicamos que hay errores\n $("div.info").append("<p class='error'>ERROR: Se han encontrado errores, revise los errores encontrados en el formulario.</p>");\n //Marcamos cada error con la clase error debajo de cada objeto con id\n for(i=0;i<jsErrors.length;i++){\n error_encontrado = eval("("+jsErrors[i]+")");\n $("label[@for="+error_encontrado.id+"]").before("<p class='error'>"+error_encontrado.msg+"</p>");\n }\n //Movemos el scroll de la página para mostrar el texto de error\n $("div.info").get(0).scrollIntoView(true);\n\n //cancelamos la propagación del evento y no ejecutamos el envío del formulario\n return false;\n }\n });\n});\n}}}\n\nHemos usado jQuery como librería para el manejo del DOM. Por lo que la sintaxis sigue siendo ~JavaScript pero escrito de la forma acostumbrada en jQuery.\n\nLo primero que hace es añadir el manejador de eventos para el formulario. Esto debemos hacerlo una vez cargada la estructura DOM de la página por lo que lo haremos dentro de un manejador de eventos ''$(document).ready();'' que nos asegurará esto.\n\nSi el manejador del evento submit del formulario devuelve //false//, se cancelará el envío del formulario, si devolviese //true// el formulario es enviado de la forma acostumbrada, esto nos servirá para cancelar el envío del formulario en caso de que haya errores para mostrar.\n\nComo el código de ejecución del evento submit puede haberse realizado anteriormente (mostrando errores) es necesario quitar todos los mensajes de error que hemos mostrado, además de la capa con id "errorsDiv" que necesita YAV para mostrar los errores (si usamos el método del ejemplo 2) o para insertar la declaración de la variable jsErrors con los mensajes de error producidos (usando el método 3 de YAV), por lo tanto debemos borrarla y generarla siempre antes de usar YAV, aunque, en la web de YAV indica que se debe insertar en el HTML, nosotros lo haremos desde código para tener total independencia de la interfaz y el código. Esto lo hacemos con las líneas:\n{{{\n$(".error").remove();\n$("#errorsDiv").remove();\n}}}\n\nUsando selectores CSS, el objeto $ de jQuery permite seleccionar un conjunto de elementos. En caso de que no exista ningún objeto que corresponda al selector indicado, nos devolverá un conjunto vacío sobre el que ejecutaremos los métodos indicados y en este caso no hará nada. En las anteriores líneas, ejecutamos el método remove() que elimina los objetos DOM que hayamos seleccionado.\n\nLuego volvemos a crear la capa errorDiv añadiéndola al final de nuestra página:\n{{{\n$("body").append("<div id='errorsDiv'></div>");\n}}}\n\nEjecutamos la función de YAV ''performCheck()'' indicando el id del formulario, el conjunto de reglas y el tipo de error que vamos a ejecutar, que en este ejemplo es a través de variable array ''jsVar''.\n{{{\nif (performCheck(this.id, reglas, 'jsVar')){\n return true;\n}else{\n ....\n}\n}}}\n\nComo estamos dentro del evento submit del formulario, el objeto ''this'' hace referencia al formulario que ha generado el evento y podemos acceder directamente a su ''id'' usando su propiedad en el objeto form, ''this.id''. En caso de que la función de validación sea positiva, dejamos que el formulario se procese y en otro caso mostraremos los errores.\n\nTodos los mensajes de error que añadiremos serán de la clase ''error'' existente en la hoja de estilo de ~NormaWeb. Primero mostraremos el mensaje indicando que se han generado errores y que el usuario debe proceder a rectificar los datos, este mensaje lo mostraremos dentro de la capa de la clase info existente en el formulario.\n{{{\n$("div.info").append("<p class='error'>ERROR: Se han encontrado errores, revise los errores encontrados en el formulario.</p>");\n}}}\n\nNOTA: El código HTML se añadiría en todas las capas de la clase info, en nuestro caso sólamente en una pues sólamente hay una capa con esta clase. En caso de desear que sea un único sitio, tendríamos que poner un ''id'' a la capa en cuestión para identificarla.\n\nPara cada mensaje dentro del array jsErrors creado por YAV al realizar la validación parsearemos el mensaje que definimos en sintaxis JSON. Lo que haremos será mostrar el mensaje de error encima del campo ''label'' asociado al control del formulario cuyo ''id'' pasamos en el mensaje.\n\n{{{\nfor(i=0;i<jsErrors.length;i++){\n error_encontrado = eval("("+jsErrors[i]+")");\n $("label[@for="+error_encontrado.id+"]").before("<p class='error'>"+error_encontrado.msg+"</p>");\n}\n}}}\n\nEl declarar la cadena de error en JSON nos permite acceder a los distintos campos de forma rápida. Podemos ver un ejemplo sencillo del mecanismo usado:\n{{{\nvar micoche = { "puertas": 2, "ruedas": 2, "motor": "diesel" };\nalert(micoche.motor);\n//es similar a obtener la declaración del objeto dentro de una cadena de texto\nvar datoscoche = '{ "puertas": 2, "ruedas": 2, "motor": "diesel" }';\nvar micoche2 = eval("("+datoscoche+")");\nalert(micoche2.motor);\n}}}\n\nEl método ''before'' de jQuery nos permite insertar código //delante// de un objeto DOM. Métodos relacionados //after//, //preppend//, //append//.\n\nPor último ejecutamos el método DOM nativo ''scrollIntoView'' para mover el scroll de la página al comienzo de la capa de la clase info. El método ''get'' de jQuery nos permite obtener el objeto DOM número X dentro del conjunto de elementos jQuery obtenidos a través del selector.\n\n{{{\n//Movemos el scroll de la página para mostrar el texto de error\n$("div.info").get(0).scrollIntoView(true);\n\n//cancelamos la propagación del evento y no ejecutamos el envío del formulario\nreturn false;\n}}}\n\nNo podemos ejecutar el método scrollIntoView sobre un objeto jQuery ya que no existe el método, así que tenemos que obtener el elemento DOM asociado.\n\nPor último devolvemos en la función un valor ''false'' para cancelar el envío del formulario.
[[Programación JavaScript]]\n[[Validación de formularios]]\n[[Acceso a DOM]]\n[[Seminario]]\n[[Bibliografía]]\n[[Autores]]
Librería de manejo de DOM y efectos ~JavaScript. Es modular y ocupa poco. Con licencia MIT.\n\n//Ir a sitio [[web|http://mootools.net/]]//
<!--{{{-->\n\n<div id='topMenu' refresh='content' tiddler='MainMenu'></div>\n<div id='sidebar'>\n<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>\n<!-- <div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div> -->\n</div>\n<div id='displayArea'>\n<div id='messageArea'></div>\n<div id='tiddlerDisplay'></div>\n</div>\n<!--}}}-->
//~JavaScript// como la mayoría de lenguajes script, otorga mucha flexibilidad a la hora de programar. Es por esto, por lo que hasta ahora, no se ha tenido excesivo cuidado a la hora de programar en la parte cliente, infringiendo todas las reglas que sí manteníamos cuando programábamos en un entorno servidor.\n\nEn la actualidad, los navegadores modernos (léase [[navegadores modernos]]) gracias a su soporte ~JavaScript cada vez más acorde con el estándar que define el [[ECMA|http://www.ecma-international.org/publications/standards/Ecma-262.htm]] permiten realizar aplicaciones web más intuitivas. En los últimos años ha crecido el uso de ~JavaScript como lenguaje cliente, para realizar validaciones o realizar acciones en segundo plano, dando una sensación de velocidad de manejo al usuario. \n\nEl mayor problema cuando usamos intensívamente ~JavaScript, es que, al ser un lenguaje de la parte del cliente, y debido a la pluralidad de clientes (navegadores web) que además mantienen divergencias con el estándar, nos encontramos con la necesidad de usar librerías para obtener una abstracción suficiente para que, el trabajo de contemplar las diferencias entre los navegadores sea de la librería y no del programador final.\n\n!!Librerías para manejo de DOM\nLas librerías para el manejo de [[DOM]] nos permiten un control total de una web por parte del cliente, pues podemos trabajar con cualquier objeto de una página web (enlaces, párrafos, ventanas, capas,....) con ~JavaScript. Por defecto, ~JavaScript define una serie de métodos con los que obtener un objeto del DOM y trabajar con él (//document.getElementById()//, //document.getElementsByTagName()//,...), pero a menudo el programador ve la necesidad de nuevos métodos con los que poder trabajar más fácilmente. Es importante usar una librería de manejo de DOM que sea sencilla (pocos métodos y propiedades) con una curva de aprendizaje no demasiado alta.\n\n//Leer más en// [[Acceso a DOM]]\n\n!!Validación de formularios\nLa validación desde la parte cliente es muy importante ya que el usuario no tiene que esperar a cargar una nueva página para obtener una validación desde el servidor web. De esta manera se pasa un filtro previo al envío de datos de un formulario al servidor, y de esta forma, evitamos hacer un envío de datos innecesario en caso de que los datos no estén formateados correctamente. Esta validación siempre se hace en ~JavaScript puesto que es el lenguaje de programación de la mayoría de los clientes (navegadores web).\n\nExisten librerías ~JavaScript que podemos usar para realizar la validación de formularios con las reglas más comunes (alfanumérico, numérico, no_vacío, email, igual a, longitud máxima o mínima, ....), nosotros veremos una de ellas.\n\nLeer más en [[Validación de formularios]]\n\n!!Creación de nuevas librerías o clases ~JavaScript\nAl igual que Java, ~JavaScript admite la creación de objetos, similares a una clase Java, donde podremos, instanciar un objeto, crear propiedades y métodos, ... Es recomendable cuando creamos una librería de utilidades, que lo declaremos como objeto, más que como una simple función, y así mejorar la abstracción a la hora de usarla.\n\nLeer más sobre [[Creación de objetos en JavaScript]]\n\n!!Comprimiendo el código\nUna de las problemáticas a la hora de usar librerías en entorno cliente, es que el cliente, debe de bajarlas para poder usarlas en nuestro código. Esto repercute en la necesidad de descargar un mayor volumen de datos. Una librería puede ocupar un par de decenas de kB, como jQuery, o YAV, a ocupas cientos de kilobytes como ocurre en las librerías visuales como Yahoo UI!, Dojo, qooxdoo,... Existe una regla básica a la hora de crear una web que es:\n>A menos kB la página se descarga antes y por tanto se ejecuta antes.\nCuando desarrollamos, lo normal es usar la versión de la librería para desarrollo, de manera que podamos leer los comentarios, ver si tenemos que modificarla para adaptarla a nuestras necesidades, etc... En cambio, no es necesario que el cliente use esta librería comentada, pues, ocupará bastante más de lo que ocuparía el código ejecutable.\nExisten una serie de compresores ~JavaScript, que permiten reducir el tamaño de nuestros ficheros de código y que un navegador los pueda, igualmente interpretar. Vamos a describir aquí los dos más utilizados e interesantes:\n# [[Dojo ShrinkSafe|http://alex.dojotoolkit.org/shrinksafe/]]. Quita los comentarios y deja el código en una sola línea, quitando espacios y retornos de carro innecesarios. En la inmensa mayoría de los casos, el código resultado ocupará menos y funcionará perfectamente. Es usado por las librerías Dojo.\n# [[Dean Edwards /packer/|http://dean.edwards.name/packer/]]. Crea un código comprimido, incluyendo la propia rutina compresora en el propio código, funciona a través de expresiones regulares. El código resultante ocupa mucho menos que el resultado de ShrinkSafe pero, en ocasiones puede corromperse al aplicar la compresión, por lo que el código comprimido no funcionaría. Por ejemplo escribir un //if (algo) haz_algo(); // daría como resultado un código comprimido corrupto pues debe ser escrito de manera estándar usando llaves //if (algo) { haz_algo(); }//. Lo recomendable es comprimirlo primero con este compresor y luego probar la librería, si no funciona nuestra aplicación, comprimirla con ShrinkSafe que produce un resultado más "seguro". Este compresor es usado por jQuery, Prototype y otros.
Librería para manejo de DOM, dispone además de métodos útiles para manejo de cadenas, arrays, y otros. Unido a la librería script.aculo.us permiten realizar transiciones tipo Flash con HTML, drag and drop, etc... Es una de las librerías más usadas en Internet en los proyectos orientados a aplicaciones web. \n\n//Ir a sitio// [[web|http://www.prototypejs.org/]]
Iniciar presentación <<slideShow>>\n\n-s-\n\n<html><p style="text-align:center; border: 1px #999 solid; padding: 10px;width:550px;margin:0 auto;">\nSeminario FORJA<br />y<br />Programación JavaScript con jQuery y YAV\n<p>\n<p style="text-align:center;font-size: 12px;">José Fco. Rives y Juan Luis Serradilla Amarilla</p>\n</html>\n\n<html>\n<p style="text-align:right;">Abril 2007 · ATICA · Universidad de Murcia<br />\n</p>\n</html>\n-s-\n__Indice__\n\n* ¿Qué es JavaScript?\n** Lenguajes en entorno servidor\n** Lenguajes en entorno cliente\n** ¿Por qué necesitamos programar en entorno cliente?\n* ¿Qué es DOM?\n** Objetos. OOP (métodos y propiedades)\n** Diferencias entre navegadores\n-s-\n__Indice__\n\n* Objetos en JavaScript y programación estructurada\n** Problema de las 1000 funciones\n** Objetos literales\n** Funciones objeto\n** La clausura\n* Librerías de manejo DOM\n** Prototype\n** Mootools\n** jQuery\n-s-\n__Indice__\n\n* Modelos de programación comunes (MVC y Orientado a Eventos)\n** Programación MVC\n*** JAVA\n*** PHP\n*** Ruby\n** Programación orientada a eventos\n** JAVA GWT\n** ASP.NET + ATLAS\n** Visual C++\n-s-\n__Indice__\n\n* jQuery y JavaScript no intrusivo\n** Programación orientada a eventos\n** No intrusivo => + accesible y + usable\n-s-\n__Indice__\n\n* Extendiendo jQuery\n** Cómo crear una extensión o plugin\n** Repositorio de plugins de jQuery\n* Validación de formularios\n** ¿Porqué validar desde el cliente?\n* Expresiones regulares\n** ¿Qué son?\n** Regexlib\n** Utilidades para probar regex\n-s-\n__Indice__\n\n* La librería YAV sobre el modelo Normaweb\n** ¿Qué es YAV?\n** Reglas de validación\n** Soluciones unidas a Normaweb (jQuery y YAV)\n* ¿Qué es AJAX?\n** Conceptos rudimentarios. El objeto XMLHttpRequest.\n** Otros AJAX, JavaScript dinámico e iframes\n\n-s-\n__Indice__\n\n* Los navegadores web modernos\n** ¿A qué llamamos navegadores modernos?\n* jQuery y AJAX\n* Optimizando el código y el tráfico\n** Conexiones de un navegador\n** Optimizando el tráfico, uniendo ficheros\n** Optimizando el tamaño, comprimiendo el código\n-s-\n\n\n\n¿Qué es JavaScript?\n-s-\n!! ¿Qué es JavaScript?\n\n* Un lenguaje de programación en un entorno cliente\n* Carga de procesamiento en el cliente\n* Lo implementa el navegador web\n* Distintas implementaciones del estándar ECMAScript\nLa guerra de los navegadores\n-s-\n!! ¿Qué es JavaScript? ~~Lenguajes en entorno servidor~~\n\n* Carga de procesamiento en el servidor\n* Tráfico HTML para la comunicación entre el cliente y el servidor\n* Implementación del lenguaje segura\n-s-\n!! ¿Qué es JavaScript? ~~¿Por qué necesitamos programar en entorno cliente?~~\n\n* El cliente realiza los cálculos ligeros, menos tráfico web\n* Velocidad en la respuesta\n* Mayor ¿interactividad?\n* Interfaces más lógicas y ¿usables?\n-s-\n\n\n¿Qué es DOM?\n-s-\n!! ¿Qué es DOM? ~~Objetos. OOP (metodos y propiedades)~~\n\n* Cada etiqueta HTML es un objeto en JavaScript\n* Ejecutar métodos de un objeto y ver o modificar sus propiedades\n-s-\n!! ¿Qué es DOM? ~~Diferencias entre navegadores~~\n\n* Distintas versiones de DOM (1 y 2)\n* Distintas implementaciones (mismo objeto, diferentes métodos)\nDe nuevo la guerra de los navegadores...\n-s-\n\n\nObjetos en JavaScript y programación estructurada\n-s-\n!!Objetos en JavaScript y programación estructurada ~~El problema de las 1000 funciones~~\n{{{\nfunction muestraMenu(){\n ...\n}\nfunction ocultaMenu(){\n ...\n}\nfunction clic_en_opcion_de_Menu(){\n ...\n}\n...........\n}}}\n-s-\n!!Objetos en JavaScript y programación estructurada ~~Objetos Literales~~\n{{{\nvar miobjeto = {\n propiedad1: "valor1",\n propiedad2: [0,1,2,3],\n metodo1: function(){\n alert(propiedad1);\n } \n}\n\nmiobjeto.propiedad1 = "Otro valor";\n}}}\n-s-\n!!Objetos en JavaScript y programación estructurada ~~Objetos Literales~~\n{{{\nvar miobjeto = new Object();\nmiobjeto.propiedad1 = "valor1";\nmiobjeto.propiedad2 = [0,1,2,3];\nmiobjeto.metodo1 = function(){\n alert(miobjeto.propiedad1);\n}\n}}}\n-s-\n!!Objetos en JavaScript y programación estructurada ~~Funciones objeto~~\n{{{\nfunction miobjeto(){\n this.propiedad1 = "valor1";\n this.propiedad2 = "valor2";\n this.metodo1 = function(){\n alert(propiedad1);\n }\n}\n\nvar mi_instancia = new miobjeto();\n}}}\n-s-\n!!Objetos en JavaScript y programación estructurada ~~La Clausura~~\n\n__La Clausura__\n* JavaScript no posee marcadores de acceso (private, public)\n* La clausura nos permite tener métodos y propiedades privadas y públicas\n\n-s-\n!!Objetos en JavaScript y programación estructurada ~~La Clausura~~\n""Primer paso"", devolvemos un objeto\n{{{\nfunction miobjeto(){\n this.propiedad1 = "valor1";\n this.propiedad2 = "valor2";\n \n return {obtenPropiedad1 : this.propiedad1}\n}\nvar mi_instancia = new miobjeto();\nalert(mi_instancia.propiedad1) ; // muestra "undefined"\nalert(mi_instancia.obtenPropiedad); //muestra "valor1"\n}}}\n\n-s-\n!!Objetos en JavaScript y programación estructurada ~~La Clausura~~\n""Segundo paso"", devolvemos un constructor (otra función)\n{{{\nfunction miobjeto(){\n propiedad1 = "valor1";\n propiedad2 = "valor2";\n \n function constructor(){\n this.sumaPropiedades = function(){\n return propiedad1 + propiedad2;\n }\n \n this.obtenPropiedad2(){\n return propiedad2;\n }\n }\n\n return new constructor();\n}\n}}}\n-s-\n!!Objetos en JavaScript y programación estructurada ~~Programación ordenada~~\n{{{\nfunction menu(){\n //métodos y propiedades privados\n datosMenu = [];\n ocultaMenu = function(){\n\n }\n \n function constructor(){\n //métodos y propiedades públicas, y código de inicialización del objeto\n this.insertaMenu = function(){\n ...\n }\n }\n\n return new constructor();\n}\n\nvar mimenu = new menu();\n}}}\n\n-s-\n\n\n\nLibrerías de manejo DOM\n-s-\n!!Librerías de manejo DOM ~~Prototype~~\n\n* Manejo DOM\n* Funciones útiles para Array, colecciones, hash, etc..\n* Librería muy extensa (69kB)\n* Posibilidad de efectos usando Scriptaculous (+130KB)\n* Compatible con navegadores modernos\n* [[Guía rápida|imgs_seminario/prototype.png]]\n-s-\n!!Librerías de manejo DOM ~~Mootools~~\n\n* La más //"joven"//\n* Super ligera (muy pocos kB de "core")\n* Modulable (podemos [[bajar|http://mootools.net/download/svn]] distintos módulos)\n* [[Guía rápida|imgs_seminario/mootools.png]]\n-s-\n!!Librerías de manejo DOM ~~jQuery~~\n\n* Enfocado a la programación orientada a eventos\n* Amplia comunidad de desarrolladores (el creador trabaja para Mozilla)\n* Usada en [[sitios|http://docs.jquery.com/Sites_Using_jQuery]] con //renombre// : SourceForge, Intel, Mozilla Addons,....\n* Poco tamaño (19kB la versión actual, 15kB anteriormente)\n* Fácil de aprender (un objeto $ y métodos para interactuar)\n-s-\n\n\n\nPatrones de diseño\n-s-\n!!Patrones de diseño · Programación orientada a eventos\n\n* Modelo-Vista-Controlador\n** JAVA\n** PHP\n** RUBY\n\n-s-\n!!Java Spring\n[img[imgs_seminario/java.jpg]]\n-s-\nPHP Code Igniter\n[img[imgs_seminario/php.jpg]]\n-s-\nRuby On Rails\n[img[imgs_seminario/ruby.jpg]]\n-s-\n!!Patrones de diseño · Programación orientada a eventos\n\n* Programación orientada a eventos.\n** JAVA GWT\n** ASP.NET C\n** Visual C++\n** ''JavaScript''\n-s-\nJava Google Web Tools\n[img[imgs_seminario/javagwt.jpg]]\n-s-\n\n\n\njQuery y JavaScript no intrusivo\n-s-\n!!jQuery y JavaScript no intrusivo\n* Programación orientada a eventos\n{{{\n$(document).ready(function(){\n $("a.external").click(function(){\n alert("Has pulsado sobre un enlace externo");\n }\n\n $("form#miformulario").submit(){\n if ( $("input#mitexto").val() == ""){ alert("no puedes dejar el campo vacío"); return false; }\n }\n});\n}}}\n-s-\n!!jQuery y JavaScript no intrusivo\n* No intrusivo + accesible + usable\n{{{\n<form id="miformulario" action="parsesubmit.php">\n <input type="text" id="mitexto" name="mitexto" />\n <input type="submit" value="enviar" />\n</form>\n}}}\n-s-\n\n\nExtendiendo jQuery\n-s-\n!!Extendiendo jQuery ~~Plugins o extensiones~~\n\n{{{\n jQuery.fn.extend({\n nuevoMetodo : function(){\n return this;\n },\n nuevoMetodo1: function(){\n return this;\n }\n }); \n\n $("selector").nuevoMetodo();\n}}}\n-s-\n!!Extendiendo jQuery ~~Plugins de 3ª personas~~\n\n* Miles de plugins en la web de [[jQuery|http://docs.jquery.com/Plugins]]\n** Drag & Drop\n** Ventanas\n** Autocompletado\n** Comboboxes\n** etc...\n-s-\n\n\n\nValidación de formularios\n-s-\n!!Validación de formularios\n\n¿Porqué validar desde el cliente?\n\nCONTRA\n* No tenemos constancia de la validación (JavaScript desactivado)\n\nPRO\n* Filtrado previo antes del envío\n* Podemos generar los errores sin envío de datos (más rápido, menos tráfico)\n* Nuestro usuario no tiene que esperar\n-s-\nExpresiones Regulares\n\n* ¿Qué son? \n ^(?=(.*[a-z]){1,})(?=(.*[\sd]){1,})(?=(.*[\sW]){1,})(?!.*\ss).{7,30}$\n\n* ¿Conoces Regexlib [[http://regexlib.com/default.aspx|http://regexlib.com/default.aspx]] ?\n\n* Utilidades para probar regex\n** The Regulator [[http://sourceforge.net/projects/regulator/|http://sourceforge.net/projects/regulator/]]\n-s-\n\n\nValidando formularios con NormaWeb y YAV\n-s-\n!!Validando formularios con NormaWeb y YAV\n¿Qué es YAV?\n\n* YAV es una librería JavaScript para validar formularios\n[[http://yav.sourceforge.net/|http://yav.sourceforge.net/]]\n\n* Simple, personalizable, robusta (más de 2 años de desarrollo)\n\n* Permite reglas simples, precondiciones, postcondiciones, funciones personalizadas\n-s-\n!!Validando formularios con NormaWeb y YAV\n\nReglas de validación\n\n* date, email, implies, integer, keypress, maxlength, minlength, notequal, equal, empty, and....\n\nMás en la web de YAV [[http://yav.sourceforge.net/en/validationrules.html|http://yav.sourceforge.net/en/validationrules.html]]\n-s-\n!!Validando formularios con NormaWeb y YAV\n\n3 Ejemplos para NormaWeb con los tres métodos de muestra de mensajes de YAV\n\n* [[A través de un Popup|formulario/formulario.html]] [[js|view-source/view-source.html#formulario/js/formulario_code.js]]\n* [[A través de un DIV|formulario/formulario1.html]] [[js|view-source/view-source.html#formulario/js/formulario1_code.js]]\n* [[A través de una variable JavaScript|formulario/formulario4.html]] [[js|view-source/view-source.html#formulario/js/formulario4_code.js]]\n\n-s-\n\n\n¿Qué es AJAX?\n-s-\n!!Qué es AJAX\n\n* Llamadas a datos de forma asíncrona desde JavaScript\n* Uso del objeto XMLHttpRequest\n* Actualización de la página en segundo plano\n* Problemas de seguridad con dominios externos (necesitamos un proxy)\n-s-\n!!Qué es AJAX\n\n¿AJAX que no es AJAX?\n\n* iframes\n** Problemas de seguridad con dominios externos (necesitamos un proxy)\n* carga dinámica de scripts con función callback\n{{{\n pidedatos = document.createElement("script");\n pidedatos.src = "http://url/miservicio.asp?get1=valor1&callback=mifuncion;\n body = document.getElementsByTagName("body")[0];\n body.appendChild(pidedatos);\n\n mifuncion = function(){ alert("Ya se han cargado los datos");\n}}}\n-s-\n!!Navegadores modernos\n\n* ¿A qué llamamos navegadores modernos?\n** IE 5.5\n** Firefox 1.0\n** Netscape 6.0\n** Opera 7.5\n** Safari 1.0\n** Konqueror 3.5\n* Navegadores que llevan en el mercado desde finales de 2000 y principios de 2001\n-s-\njQuery y AJAX\n\n{{{\n $("#capa").load("url.php",{parametro1:"valor1", parametro2:"valor2"}, function(){ alert("ya llegan los datos");});\n $.post("url.php", function(xml_resultado){ alert(xml); });\n}}}\n-s-\n\n\nOptimizando el código y el ancho de banda\n-s-\n!!Optimizando el código y el ancho de banda\n\n* Conexiones concurrentes de un navegador web\n** Por defecto 2\n* Demasiadas librerías JavaScript, CSS, etc...\n\nSolución\n* Código servidor que una ficheros\n{{{\n<script src="libreriasjs.php?file1=jquery.js&file2=yav.js..."></script>\n}}}\n-s-\n!!Optimizando el tamaño, comprimiendo el código\n\n* [[jsMin | http://www.crockford.com/javascript/jsmin.html]]\n* [[Dean Edwards Packer|http://dean.edwards.name/packer/]]\n* [[Dojo ShrinkSafe | http://alex.dojotoolkit.org/shrinksafe/]]\n\n-s-\n\nFIN ¡¡Por fin!!\n\n¿Preguntas?\n\n
Dojo ShrinkSafe es un compresor de JavaScript seguro, el resultado no está a la mínima expresión pero tiene las mayores posibilidades de que el código funcione correctamente. \n\n[[Ir a su web|http://alex.dojotoolkit.org/shrinksafe/]]
<<search>><<closeAll>><<newTiddler>><<newJournal 'DD MMM YYYY'>><<saveChanges>><<slider chkSliderOptionsPanel SideBarTabs 'histórico »' 'Ver histórico de cambios'>><<slider chkSliderOptionsPanel OptionsPanel 'opciones »' 'Cambiar opciones avanzadas de TiddlyWiki'>>\n
programación estructurada y no-intrusiva
JavaScript
<!--{{{-->\n<div id='displayArea'>\n<div id='tiddlerDisplay'></div>\n</div>\n<!--}}}-->
/***\n|''Name:''|SlideShowPlugin|\n|''Description:''|Creates a simple slide show type display|\n|''Version:''|1.5.1|\n|''Date:''|Nov 10, 2006|\n|''Source:''|http://www.math.ist.utl.pt/~psoares/addons.html|\n|''Author:''|Paulo Soares (psoares (at) math (dot) ist (dot) utl (dot) pt) and [[Clint Checketts|http://www.checkettsweb.com]]|\n|''License:''|[[BSD open source license]]|\n|''~CoreVersion:''|2.1.0|\n|''Browser:''|Firefox 1.0.4+; Firefox 1.5; InternetExplorer 6.0|\n<<tiddler SlideShowPluginDoc>>\n!Code\n***/\n//{{{\nconfig.macros.slideShow = {label: "slide show", maxTOCLength: 30};\nconfig.macros.slideShow.messages = {gotoLabel: "Go to slide:"};\nconfig.views.wikified.slideShow = {text: "slide show", tooltip: "Start slide show"};\nconfig.views.wikified.slideShow.quit = {text: "end", tooltip: "Quit the slide show"};\nconfig.views.wikified.slideShow.firstSlide = {text: "<<", tooltip: "first slide"};\nconfig.views.wikified.slideShow.previousSlide = {text: "<", tooltip: "previous slide"};\nconfig.views.wikified.slideShow.nextSlide = {text: ">", tooltip: "next slide"};\nconfig.views.wikified.slideShow.lastSlide = {text: ">>", tooltip: "last slide"};\nconfig.views.wikified.slideShow.resetClock = {text: " ", tooltip: "reset"};\n\nconfig.formatters.push( {\n name: "SlideSeparator",\n match: "^-s-+$\s\sn?",\n handler: function(w)\n {\n createTiddlyElement(w.output,"hr",null,'slideSeparator');\n }\n}\n)\n\nfunction changeStyleSheet(tiddlerName) {\n if (tiddlerName == null) tiddlerName = "StyleSheet";\n setStylesheet(store.getRecursiveTiddlerText("StyleSheetColors"),"StyleSheetColors");\n setStylesheet(store.getRecursiveTiddlerText("StyleSheetLayout"),"StyleSheetLayout");\n var theCSS = store.getRecursiveTiddlerText(tiddlerName,"");\n setStylesheet(theCSS,"StyleSheet");\n}\n\n//Excellent (and versatile) reparser created by Paul Petterson for parsing the paramString in a macro\nfunction reparse( params ) {\n var re = /([^:\ss]+)(?:\s:((?:\sd+)|(?:["'](?:[^"']+)["']))|\ss|$)/g;\n var ret = new Array() ;\n var m ;\n while( (m = re.exec( params )) != null ) ret[ m[1] ] = m[2]?m[2]:true ;\n return ret ;\n}\n\nfunction getElementsByClass(searchClass,node,tag) {\n var classElements = new Array();\n if ( node == null ) node = document;\n if ( tag == null ) tag = '*';\n var els = node.getElementsByTagName(tag);\n var elsLen = els.length;\n var pattern = new RegExp("(^|\s\ss)"+searchClass+"(\s\ss|$)");\n var j=0;\n for (var i = 0; i < elsLen; i++) {\n if ( pattern.test(els[i].className) ) {\n classElements[j] = els[i];\n j++;\n }\n }\n return classElements;\n}\n\n// 'keys' code adapted from S5 which in turn was adapted from MozPoint (http://mozpoint.mozdev.org/)\nfunction keys(key) {\n if (document.getElementById('contentWrapper').className == "slideShowMode"){\n if (!key) {\n key = event;\n key.which = key.keyCode;\n }\n switch (key.which) {\n case 32: // spacebar\n if(time>0){\n if(autoAdvance){\n clearInterval(autoAdvance);\n autoAdvance = null;\n } else {\n autoAdvance=setInterval("GoToSlide(1)", time);\n }\n }\n break;\n case 34: // page down\n case 39: // rightkey\n GoToSlide("n");\n break;\n case 40: // downkey\n GoToSlide(-1);\n break;\n case 33: // page up\n case 37: // leftkey\n GoToSlide("p");\n break;\n case 38: // upkey\n GoToSlide(1);\n break;\n case 36: // home\n GoToSlide("f");\n break;\n case 35: // end\n GoToSlide("l");\n break;\n case 27: // escape\n endSlideShow();\n break;\n }\n\n }\n return false;\n}\n\nfunction clicker(e) {\n if (!e) var e = window.event;\n var target = resolveTarget(e);\n //Whenever something is clicked that won't advance the slide make sure that the table of contents gets hidden\n if (target.getAttribute('href') != null || isParentOrSelf(target, 'toc') || isParentOrSelf(target,'embed') || isParentOrSelf(target,'object') || isParentOrSelf(target, 'pageFooter') || isParentOrSelf(target, 'navigator')){\n //Don't hide the TOC if the indexNumbers (which trigger the index) is clicked\n if(isParentOrSelf(target,'indexNumbers') || isParentOrSelf(target,'jumpInput')){\n return true;\n }\n showHideTOC('none');\n return true;\n }\n \n //Advance a slide if the TOC is visible otherwise make sure that the TOC gets hidden\n if ((!e.which && e.button == 1) || e.which == 1) {\n if (document.getElementById('toc').style.display != 'block'){\n GoToSlide("n");\n } else {\n showHideTOC('none');\n }\n }\n \n if ((!e.which && e.button == 2) || e.which == 3) {\n if (document.getElementById('toc').style.display != 'block'){\n GoToSlide("p");\n } else {\n showHideTOC('none');\n }\n return false;\n }\n}\n\nfunction isParentOrSelf(element, id) {\n if (element == null || element.nodeName=='BODY') return false;\n else if (element.id == id) return true;\n else return isParentOrSelf(element.parentNode, id);\n}\n\nGoToSlide=function(step) {\n var new_pos;\n var slideHolder = document.getElementById('slideContainer');\n //The parse float ensures that the attribute is returned as a number and not a string.\n var cur_pos = parseFloat(slideHolder.getAttribute('currentslide'));\n var numberSlides = parseFloat(slideHolder.getAttribute('numberSlides'));\n switch (step) {\n case "f":\n new_pos=0;\n break;\n case "l":\n new_pos=numberSlides-1;\n break;\n case "n":\n var numberOverlays = parseFloat(slideHolder.childNodes[cur_pos].getAttribute('numberOverlays'));\n var currentOverlay = parseFloat(slideHolder.getAttribute('currentOverlay'));\n if(numberOverlays==0 || currentOverlay==numberOverlays){\n new_pos=cur_pos+1;\n } else {\n var className="Overlay"+currentOverlay;\n var overlay=getElementsByClass(className,slideHolder.childNodes[cur_pos]);\n for(var i=0; i<overlay.length; i++) {overlay[i].className=className+' previousOverlay';}\n currentOverlay++;\n slideHolder.setAttribute('currentOverlay',currentOverlay);\n className="Overlay"+currentOverlay;\n overlay=getElementsByClass(className,slideHolder.childNodes[cur_pos]);\n for(i=0; i<overlay.length; i++) {overlay[i].className=className+' currentOverlay';}\n return false;\n }\n break;\n case "p":\n var numberOverlays = parseFloat(slideHolder.childNodes[cur_pos].getAttribute('numberOverlays'));\n var currentOverlay = parseFloat(slideHolder.getAttribute('currentOverlay'));\n if(numberOverlays==0 || currentOverlay==0){\n new_pos=cur_pos-1;\n } else {\n var className="Overlay"+currentOverlay;\n var overlays=getElementsByClass(className,slideHolder.childNodes[cur_pos]);\n for(var i=0; i<overlays.length; i++) {overlays[i].className=className+' nextOverlay';}\n currentOverlay--;\n className="Overlay"+currentOverlay;\n overlays=getElementsByClass(className,slideHolder.childNodes[cur_pos]);\n for(i=0; i<overlays.length; i++) {overlays[i].className=className+' currentOverlay';}\n slideHolder.setAttribute('currentOverlay',currentOverlay);\n return false;\n }\n break;\n default:\n new_pos=cur_pos+step;\n }\n\n if(slideShowCircularMode && new_pos == numberSlides) new_pos=0;\n if(slideShowCircularMode && new_pos<0) new_pos=(numberSlides - 1);\n if(step!=0 && new_pos>=0 && new_pos<numberSlides) {\n slideHolder.childNodes[cur_pos].style.display='none';\n slideHolder.childNodes[new_pos].style.display='block';\n slideHolder.setAttribute('currentslide',new_pos);\n var numberOverlays = parseFloat(slideHolder.childNodes[new_pos].getAttribute('numberOverlays'));\n if(step=="p"){\n var currentOverlay=numberOverlays;\n var state=' previousOverlay';\n } else {\n var currentOverlay=0;\n var state=' nextOverlay';\n }\n slideHolder.setAttribute('currentOverlay',currentOverlay);\n if(numberOverlays>0) {\n for(var i=1; i<=numberOverlays; i++){\n var className="Overlay"+i;\n var overlays=getElementsByClass(className,slideHolder.childNodes[new_pos]);\n for(var j=0; j<overlays.length; j++) {overlays[j].className=className+state;}\n }\n if(step=="p"){\n var className="Overlay"+numberOverlays;\n var overlays=getElementsByClass(className,slideHolder.childNodes[new_pos]);\n for(var j=0; j<overlays.length; j++) {overlays[j].className=className+' currentOverlay';}\n }\n }\n new_pos++;\n var indexNumbers = document.getElementById('indexNumbers');\n indexNumbers.firstChild.data = new_pos+'/'+numberSlides;\n if((new_pos==numberSlides) && !slideShowCircularMode && autoAdvance) clearInterval(autoAdvance);\n return true;\n }\n return false;\n}\n\nfunction tocShowSlide(e) {\n if (!e) var e = window.event;\n var target = resolveTarget(e);\n var slide = target.getAttribute('slideNumber');\n var cur_pos = document.getElementById('slideContainer').getAttribute('currentslide');\n var step = slide-cur_pos;\n if(step!=0) GoToSlide(step);\n showHideTOC('none');\n return;\n}\n\n//Toggle the display of the table of contents\nfunction showHideTOC(display){\n var toc = document.getElementById('toc');\n //Reset the input box\n document.getElementById('jumpInput').value = "";\n\n if (display == null || display.length == null){\n if (toc.style.display == 'none' || toc.style.display == ''){\n toc.style.display = 'block';\n document.getElementById('jumpInput').focus();\n } else {\n toc.style.display = 'none';\n }\n } else {\n toc.style.display = display;\n if (display == 'block')\n document.getElementById('jumpInput').focus();\n }\n}\n\nfunction makeSignature(title,params){\n var signature = title+store.getTiddler(title).modified;\n if(params['style']) signature += params['style'];\n if(params['repeat']) signature += "repeat";\n if(params['slidePause'] > 0) signature += params['slidePause'];\n if(params['autostart']) signature += "autostart";\n if(params['clock']) signature += params['clock'];\n if(params['noOverlays']) signature += "noOverlays";\n return signature;\n}\n\nfunction padZero(x){\n return (x>=10 || x<0 ? "" : "0")+x;\n}\n\nsetClock=function(){\n var actualTime = new Date();\n var newTime = actualTime.getTime() - clockStartTime;\n newTime = clockMultiplier*newTime+clockInterval+clockCorrection;\n actualTime.setTime(newTime);\n newTime = padZero(actualTime.getHours()) + ":" + padZero(actualTime.getMinutes())+ ":" + padZero(actualTime.getSeconds());\n var clock = document.getElementById('slideClock');\n clock.firstChild.nodeValue = newTime;\n}\n\nresetClock=function(){\n var time = new Date(0);\n if(clockStartTime>time){\n var startTime = new Date();\n clockStartTime=startTime.getTime();\n }\n}\n\nvar title;\nvar place;\nvar autoAdvance=null;\nvar autoStart=null;\nvar slideClock=null;\nvar noOverlays=false;\nvar time = 0;\nvar slideShowCircularMode;\nvar slideShowStyleSheet;\nvar slideShowParams;\nvar clockMultiplier;\nvar clockInterval;\nvar clockCorrection=0;\nvar clockStartTime;\nvar openTiddlers;\n\nconfig.macros.slideShow.handler = function(aPlace,macroName,params,wikifier,paramString,tiddler){\n if(tiddler instanceof Tiddler){\n var lingo = config.views.wikified.slideShow;\n var autostart = false;\n if (!e) var e = window.event;\n \n place = aPlace;\n title = tiddler.title;\n params = reparse(paramString);\n var onclick = function(){config.macros.slideShow.onClickSlideShow(params);};\n createTiddlyButton(aPlace,lingo.text,lingo.tooltip,onclick);\n \n var slideShowHolder = document.getElementById('slideShowWrapper');\n //If no show exist previously, create it\n if(params['autostart']){\n if(slideShowHolder != null){\n var signature = slideShowHolder.getAttribute('showSignature');\n if(signature.indexOf("autostart")==-1) autostart = true;\n } else {autostart = true;}\n if(autostart){\n slideShowParams = params;\n setTimeout(config.macros.slideShow.onClickSlideShow,100);\n }\n }\n }\n}\n\nvar disableFunction = function(e){return false;}\nvar enableFunction = function(e){}\n\nconfig.macros.slideShow.onClickSlideShow = function(newParams) {\n if(typeof(newParams)=="number") newParams=slideShowParams;\n openTiddlers = new Array;\n var viewer=document.getElementById('tiddlerDisplay');\n for(var i=0; i<viewer.childNodes.length; i++){\n var name = viewer.childNodes[i].getAttribute('tiddler');\n openTiddlers.push(name);\n }\n document.oncontextmenu = disableFunction;\n clockMultiplier = 1;\n clockInterval = 0;\n var startTime = new Date(0);\n slideShowCircularMode = false;\n time = 0;\n slideShowStyleSheet = null;\n if(newParams['style']){\n slideShowStyleSheet = eval(newParams['style']);\n } \n if(newParams['repeat']){\n slideShowCircularMode = true;\n }\n if(newParams['slidePause'] > 0){\n time = newParams['slidePause'];\n }\n if(newParams['clock']){\n clockCorrection=startTime.getTimezoneOffset()*60000;\n startTime = new Date();\n var clockType= eval(newParams['clock']);\n if(clockType != '+') {\n clockMultiplier = -1;\n clockInterval = -clockType*60000;\n }\n }\n clockStartTime=startTime.getTime();\n if(newParams['noOverlays']){\n noOverlays = true;\n }\n var contentWrapper = document.getElementById('contentWrapper');\n if (contentWrapper.className != "slideShowMode"){\n clearMessage();\n //Attach the key and mouse listeners\n document.onkeyup = keys;\n document.onmouseup = clicker;\n \n var slideShowHolder = document.getElementById('slideShowWrapper');\n story.refreshTiddler(title,"SlideShowViewTemplate",true);\n //If no show exist previously, create it\n if(slideShowHolder == null){\n createSlides(newParams);\n //If there was once waiting in the background and it matches the one we just started, resume it\n } else if (slideShowHolder.getAttribute('showSignature') == makeSignature(title,newParams)){\n \n //Remove dblClick on edit function\n var theTiddler = document.getElementById("tiddler"+title);\n theTiddler.ondblclick = function() {};\n\n // Grab the 'viewer' element and give it a signature so the show can be resumed if stopped\n var tiddlerElements = theTiddler.childNodes;\n var viewer;\n for (var i = 0; i < tiddlerElements.length; i++){\n if (tiddlerElements[i].className == "viewer") viewer = tiddlerElements[i];\n }\n theTiddler.insertBefore(slideShowHolder,viewer);\n theTiddler.removeChild(viewer);\n slideShowHolder.style.display = 'block';\n document.getElementById("pageFooter").className = "pageFooterOff";\n \n //If the show we started it totally new than the resumable one, create the new one and kill the resumable one\n } else {\n slideShowHolder.parentNode.removeChild(slideShowHolder);\n createSlides(newParams);\n }\n slideClock=setInterval("setClock()", 1000); \n if(time>0) autoAdvance=setInterval("GoToSlide(1)", time); \n story.closeAllTiddlers(title);\n toggleSlideStyles();\n } else {\n endSlideShow();\n }\n return ;\n \n}\n\nfunction endSlideShow(){\n //Set aside show so it can be resumed later\n var showHolder = document.getElementById('slideShowWrapper');\n showHolder.style.display = 'none';\n document.getElementById('contentWrapper').parentNode.appendChild(showHolder);\n document.oncontextmenu = enableFunction;\n if(autoAdvance) clearInterval(autoAdvance);\n if(slideClock) clearInterval(slideClock);\n story.refreshTiddler(title,null,true);\n story.closeAllTiddlers();\n toggleSlideStyles();\n story.displayTiddlers(null,openTiddlers,DEFAULT_VIEW_TEMPLATE);\n document.onmouseup = function(){};\n}\n\nfunction isInteger(s){\n var i;\n for (i = 0; i < s.length; i++){\n // Check that current character is number.\n var c = s.charAt(i);\n if (((c < "0") || (c > "9"))) return false;\n }\n // All characters are numbers.\n return true;\n}\n\nfunction jumpInputToSlide(e){\n if (!e) {\n e = window.event;\n e.which = e.keyCode;\n }\n if(e.which==13){\n var jumpInput= document.getElementById("jumpInput").value;\n if(isInteger(jumpInput)){\n var step=jumpInput-document.getElementById('slideContainer').getAttribute('currentslide')-1;\n if (GoToSlide(step)){\n showHideTOC('none'); \n }\n }\n }\n return;\n}\n\n//Used to shorten the TOC fields\nfunction abbreviateLabel(label){\n var maxTOCLength = config.macros.slideShow.maxTOCLength;\n if(label.length>maxTOCLength) {\n var temp = new Array();\n temp = label.split(' ');\n label = temp[0];\n for(var j=1; j<temp.length; j++){\n if((label.length+temp[j].length)<=maxTOCLength){\n label += " " + temp[j];\n } else {\n label += " ...";\n break;\n }\n }\n }\n return label;\n}\n\ncreateSlides = function(newParams){\n var lingo = config.views.wikified.slideShow;\n\n //Remove dblClick on edit function\n var theTiddler = document.getElementById("tiddler"+title);\n theTiddler.ondblclick = function() {};\n\n // Grab the 'viewer' element and give it a signature so the show can be resumed if stopped\n var tiddlerElements = theTiddler.childNodes;\n var viewer;\n for (var i = 0; i < tiddlerElements.length; i++){\n if (tiddlerElements[i].className == "viewer") viewer = tiddlerElements[i];\n }\n viewer.id = 'slideShowWrapper';\n viewer.setAttribute("showSignature",makeSignature(title,newParams));\n\n //Hide the text that comes before the first H1 element (I think I may put this into a cover page type thing)\n while(viewer.childNodes.length > 0 && viewer.firstChild.nodeName.toUpperCase() != "HR" && viewer.firstChild.className!="slideSeparator") {\n viewer.removeChild(viewer.firstChild);\n }\n \n //Cycle through the content and each time you hit an H1 begin a new slide div\n var slideNumber = 0;\n var slideHolder = document.createElement('DIV');\n slideHolder.id = "slideContainer";\n \n while(viewer.childNodes.length > 0){\n //Create a new slide a append it to the slide holder\n if (viewer.firstChild.nodeName.toUpperCase() == "HR" && viewer.firstChild.className=="slideSeparator"){\n slideNumber++;\n var slide = document.createElement('DIV');\n slide.id = "slideNumber"+slideNumber;\n slide.className = "slide";\n if (slideNumber > 1) {\n slideHolder.setAttribute('currentslide',0);\n slide.style.display='none';\n } else {\n slide.style.display='block';\n }\n slideHolder.appendChild(slide); \n viewer.removeChild(viewer.firstChild);\n } else {\n if(viewer.firstChild.nodeName=="SPAN" && viewer.firstChild.className=="" && viewer.firstChild.hasChildNodes()) {\n var anchor=viewer.firstChild.nextSibling;\n for (var ii=0;ii<viewer.firstChild.childNodes.length;ii++) {\n var clone=viewer.firstChild.childNodes[ii].cloneNode(true);\n viewer.insertBefore(clone,anchor);\n }\n viewer.removeChild(viewer.firstChild);\n } else {\n slide.appendChild(viewer.firstChild);\n }\n }\n }\n \n //Stick the slides back into the viewer\n viewer.appendChild(slideHolder);\n slideHolder.setAttribute('numberSlides',slideNumber);\n \n //Create the navigation bar\n var pagefooter = createTiddlyElement(viewer,"DIV","pageFooter","pageFooterOff");\n var navigator = createTiddlyElement(pagefooter,"SPAN","navigator");\n\n //Make it so that when the footer is hovered over the class will change to make it visible\n pagefooter.onmouseover = function () {pagefooter.className = "pageFooterOn"};\n pagefooter.onmouseout = function () {pagefooter.className = "pageFooterOff"};\n\n //Create the control button for the navigation \n var onClickQuit = function(){endSlideShow();};\n createTiddlyButton(navigator,lingo.quit.text,lingo.quit.tooltip,onClickQuit);\n createTiddlyButton(navigator,lingo.firstSlide.text,lingo.firstSlide.tooltip,first_slide);\n createTiddlyButton(navigator,lingo.previousSlide.text,lingo.previousSlide.tooltip,previous_slide);\n createTiddlyButton(navigator,lingo.nextSlide.text,lingo.nextSlide.tooltip,next_slide);\n createTiddlyButton(navigator,lingo.lastSlide.text,lingo.lastSlide.tooltip,last_slide); \n createTiddlyButton(navigator,lingo.resetClock.text,lingo.resetClock.tooltip,resetClock,"button","slideClock"); \n\n var indexNumbers = createTiddlyElement(pagefooter,"SPAN","indexNumbers","indexNumbers","1/"+slideNumber)\n indexNumbers.onclick = showHideTOC;\n var toc = createTiddlyElement(pagefooter,"UL","toc");\n var ovl=1;\n for (var i=0;i<slideHolder.childNodes.length;i++) {\n if(!noOverlays) {\n var ovl=1;\n while(1){\n var className="Overlay"+ovl;\n var overlays=getElementsByClass(className,slideHolder.childNodes[i]);\n if(overlays.length>0){\n for(var j=0; j<overlays.length; j++) {overlays[j].className+=' nextOverlay';}\n ovl++;\n } else {break;}\n }\n }\n slideHolder.childNodes[i].setAttribute("numberOverlays",ovl-1);\n slideHolder.setAttribute("currentOverlay",0);\n\n //Loop through each slide and check the header's content\n var tocLabel = null; \n for (var j=0;j<slideHolder.childNodes[i].childNodes.length;j++) {\n var node = slideHolder.childNodes[i].childNodes[j];\n if(node.nodeName=="H1" || node.nodeName=="H2" || node.nodeName=="H3" || node.nodeName=="H4") {\n var htstring = node.innerHTML;\n var stripped = htstring.replace(/(<([^>]+)>)/ig,"");\n tocLabel = abbreviateLabel(stripped);\n var tocLevel="tocLevel"+node.nodeName.charAt(1);\n var tocItem = createTiddlyElement(toc,"LI",null,tocLevel);\n var tocLink = createTiddlyElement(tocItem,"A",null,"tocItem",tocLabel);\n tocLink.setAttribute("slideNumber",i);\n tocLink.onclick=tocShowSlide;\n }\n }\n }\n \n\n //Input box to jump to s specific slide\n var tocItem = createTiddlyElement(toc,"LI",null,"tocJumpItem",config.macros.slideShow.messages.gotoLabel);\n var tocJumpInput = createTiddlyElement(tocItem,"INPUT","jumpInput");\n tocJumpInput.type="text";\n tocJumpInput.onkeyup=jumpInputToSlide;\n}\n\nvar next_slide= function(e){GoToSlide(1);}\nvar first_slide= function(e){GoToSlide("f");}\nvar previous_slide= function(e){GoToSlide(-1);}\nvar last_slide= function(e){GoToSlide("l");}\n\nfunction toggleSlideStyles(){\n var contentWrapper = document.getElementById('contentWrapper');\n if (contentWrapper.className == "slideShowMode"){\n contentWrapper.className = "";\n window.applyPageTemplate();\n if(slideShowStyleSheet) changeStyleSheet();\n } else{\n contentWrapper.className = "slideShowMode";\n window.applyPageTemplate("SlideShowPageTemplate");\n if(slideShowStyleSheet) changeStyleSheet(slideShowStyleSheet);\n }\n}\n\nsetStylesheet("/***\sn!Slide Mode Styles\sn***/\sn/*{{{*/\sn#contentWrapper.slideShowMode #slideContainer{\sn display: block;\sn}\sn\sn#contentWrapper.slideShowMode .Comment{\sn display: none;\sn}\sn\sn#contentWrapper.slideShowMode .nextOverlay{\sn visibility: hidden;\sn}\sn\sn#contentWrapper.slideShowMode .currentOverlay{\sn visibility: visible;\sn}\sn\sn#contentWrapper.slideShowMode .previousOverlay{\sn visibility: visible;\sn}\sn\sn#jump{\sn text-align: right;\sn}\sn\sn.pageFooterOff #navigator{\sn visibility: hidden;\sn}\sn\sn.pageFooterOn #navigator{\sn visibility: visible;\sn}\sn\sn#contentWrapper.slideShowMode #slideClock{\sn cursor: pointer; margin: 0 5px 0 5px; border: 1px solid #db4\sn}\sn\sn#contentWrapper.slideShowMode,\sn #contentWrapper.slideShowMode #displayArea{\sn width: 100%;\sn font-size: 1.5em;\sn margin: 0 !important;\sn padding: 0;\sn}\sn\sn#slideContainer{\sn display: none;\sn}\sn\sn.indexNumbers{\sn cursor: pointer;\sn}\sn\sn#navigator{\sn visibility: hidden;\sn bottom: 0;\sn}\sn\sn#toc{\sn display: none;\sn position: absolute;\sn font-size: .75em;\sn bottom: 2em;\sn right: 0;\sn background: #fff;\sn border: 1px solid #000;\sn text-align: left;\sn}\sn\snul#toc, #toc li{\sn margin: 0;\sn padding: 0;\sn list-style: none;\sn line-height: 1em;\sn}\sn\sn.tocJumpItem{\sn margin-right: 2em;\sn}\sn\sn.tocJumpItem input{\snmargin-right: 1em;\sn border: 0;\sn}\sn\sn#toc a,\sn#toc a.button{\sn display: block;\sn padding: .1em;\sn}\sn\sn#toc .tocLevel1{\snfont-size: .8em;\sn}\sn\sn#toc .tocLevel2{\sn margin-left: 1em;\sn font-size: .75em;\sn}\sn\sn#toc .tocLevel3{\sn margin-left: 2em;\snfont-size: .75em;\sn}\sn\sn#toc .tocLevel4{\sn margin-left: 3em;\snfont-size: .65em;\sn}\sn\sn#toc a{\sn cursor: pointer;\sn}\sn\snh1{\sn min-height: 1em;\sn}\sn\sn.slide h1{\sn min-height: 0;\sn}\sn\sn/* The '&gt;' selector is ignored by IE6 and earlier so the proper rules are given */\sn#pageFooter{\sn position: fixed;\sn bottom: 2px;\sn right: 2px;\sn width: 100%;\sn text-align: right;\sn}\sn\sn/* This is a hack to trick IE6 and earlier to put the navbar on the bottom of the page */\sn* html #pageFooter {\sn position: absolute;\sn width: 100%;\sn text-align: right;\sn right: auto; bottom: auto;\sn left: expression( ( -20 - pageFooter.offsetWidth + ( document.documentElement.clientWidth ? document.documentElement.clientWidth : document.body.clientWidth ) + ( ignoreMe2 = document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft ) ) + 'px' );\sn top: expression( ( -10 - pageFooter.offsetHeight + ( document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight ) + ( ignoreMe = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop ) ) + 'px' );\sn}\sn\sn\sn\sn/*}}}*/","slideShowStyles");\n//}}}
<!--{{{-->\n<div class='title' macro='view title'></div>\n<div class='viewer' macro='view text wikified'></div>\n<!--}}}-->
//{{{\n\nconfig.options.txtUserName = "TuNombre";\n\nconfig.macros.allTags.noTags = "No hay artículos con etiquetas";\nconfig.macros.allTags.tooltip = "Abrir artículos con la etiqueta ";\nconfig.macros.closeAll.label = "cerrar todos";\nconfig.macros.closeAll.prompt = "Cerrar todos los artículos que están abiertos (pero no los que están siendo redactados)";\nconfig.macros.list.all.prompt = "Todos los artículos en orden alfabético";\nconfig.macros.list.missing.prompt = "Artículos que tienen enlaces a ellos pero no existen";\nconfig.macros.list.orphans.prompt = "Artículos que no tienen enlaces de ningún otro artículo";\nconfig.macros.list.shadowed.prompt = "Artículos ocultos con contenido por defecto";\nconfig.macros.newJournal.label = "nuevo diario";\nconfig.macros.newJournal.months = ["Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre","Diciembre"];\nconfig.macros.newJournal.prompt = "Crear un nuevo artículo con la fecha y hora actual";\nconfig.macros.newTiddler.label = "nuevo artículo";\nconfig.macros.newTiddler.prompt = "Crear un nuevo artículo";\nconfig.macros.newTiddler.title = "Nuevo Artículo";\nconfig.macros.permaview.label = "enlace permanente";\nconfig.macros.permaview.prompt = "Enlace a una URL que muestra todos los artículos que están abiertos";\nconfig.macros.saveChanges.label = "guardar cambios";\nconfig.macros.saveChanges.prompt = "Guardar todos los cambios a un archivo";\nconfig.macros.search.label = "buscar";\nconfig.macros.search.prompt = "buscar en este TiddlyWiki";\nconfig.macros.search.successMsg = " artículos encontrados que tienen: ";\nconfig.macros.search.failureMsg = "Ningún artículo encontrado que tuviera: ";\nconfig.macros.timeline.dateFormat = "DD MMM YYYY";\nconfig.macros.tagging.label = "etiquetado: ";\nconfig.macros.tagging.labelNotTag = "no etiquetado";\nconfig.macros.tagging.tooltip = "Lista de artículos etiquetados con '%0'";\nconfig.macros.plugins.skippedText = "(Esta extensión no ha sido ejecutado porque fue añadido al inicio)";\nconfig.macros.plugins.noPluginText = "No hay extensiones instaladas";\nconfig.macros.plugins.confirmDeleteText = "Estás seguro de querer borrar estos artículos:\sn\sn%0";\nconfig.macros.refreshDisplay.label = "actualizar";\nconfig.macros.refreshDisplay.prompt = "Actualizar la vista de TiddlyWiki";\nconfig.macros.importTiddlers.readOnlyWarning = "No puede importar artículos en un TiddlyWiki de solo lectura. Intenta abrir el TiddlyWiki desde el disco duro como file:// URL";\nconfig.macros.importTiddlers.defaultPath = "http://www.tiddlywiki.com/index.html";\nconfig.macros.importTiddlers.fetchLabel = "importar";\nconfig.macros.importTiddlers.fetchPrompt = "Archivo a importar";\nconfig.macros.importTiddlers.fetchError = "Hay problemas al importar el archivo";\nconfig.macros.importTiddlers.confirmOverwriteText = "¿Estás seguro de querer sobrescribir estos artículos:\sn\sn%0";\nconfig.macros.importTiddlers.wizardTitle = "Importar artículos desde otro archivo TiddlyWiki";\nconfig.macros.importTiddlers.step1 = "Paso 1: Localizar el archivo TiddlyWiki";\nconfig.macros.importTiddlers.step1prompt = "Introducir la URL o la ruta : ";\nconfig.macros.importTiddlers.step1promptFile = "...abrir un archivo: ";\nconfig.macros.importTiddlers.step1promptFeeds = "...o seleccionar un rss pre-definido: ";\nconfig.macros.importTiddlers.step1feedPrompt = "Elegir...";\nconfig.macros.importTiddlers.step2 = "Paso 2: Cargando el archivo TiddlyWiki";\nconfig.macros.importTiddlers.step2Text = "Por favor espere mientras se carga el archivo desde: %0";\nconfig.macros.importTiddlers.step3 = "Paso 3: Elegir los artículos a importar";\nconfig.macros.importTiddlers.step4 = "%0 artículo(s) importados";\nconfig.macros.importTiddlers.step5 = "Hecho";\n\nconfig.messages.backupFailed = "Falló al guardar el archivo de respaldo";\nconfig.messages.backupSaved = "Archivo de respaldo guardado";\nconfig.messages.cantSaveError = "No es posible guardar cambios con este navegador. Usa FireFox si puedes";\nconfig.messages.customConfigError = "Se han producido problemas al cargar las extensiones. Ver pluginManager para más detalles";\nconfig.messages.pluginError = "Error: %0";\nconfig.messages.pluginDisabled = "No ejecutado, deshabilitado via 'sytemConfigDisable'";\nconfig.messages.pluginForced = "Ejecutado, forzado via 'systemConfigForce' ";\nconfig.messages.pluginVersionError = "No ejecutado porque este plugin necesita una nueva versión de TiddlyWiki";\nconfig.messages.nothingSelected = "No hay nada seleccionado. Debe seleccionar primero una o más opciones";\nconfig.messages.emptyFailed = "Falló al guardar una plantilla vacía";\nconfig.messages.emptySaved = "Plantilla vacía guardada";\nconfig.messages.tiddlerLinkTooltip = "%0 - %1, %2";\nconfig.messages.externalLinkTooltip = "Enlace externo a %0 ";\nconfig.messages.invalidFileError = "El archivo original no parece ser un TiddlyWiki: ";\nconfig.messages.macroError = "Error ejecutando macro ";\nconfig.messages.mainFailed = "Falló al guardar el archivo principal de TiddlyWiki. Tus cambios no han sido guardados";\nconfig.messages.mainSaved = "Archivo principal de TiddlyWiki se guardó ";\nconfig.messages.noTags = "No hay artículos etiquetados";\nconfig.messages.notFileUrlError = "Necesitas guardar este TiddlyWiki a un archivo antes de que puedas guardar cambios";\nconfig.messages.overwriteWarning = "Un artículo llamado '%0' ya existe. Presiona OK para sobreescribirlo";\nconfig.messages.rssFailed = "Falló al guardar archivo RSS";\nconfig.messages.rssSaved = "Archivo RSS guardado";\nconfig.messages.savedSnapshotError = "Parece que este TiddlyWiki ha sido guardado incorrectamente. Por favor vea el sitio TiddlyWiki para más detalles";\nconfig.messages.subtitleUnknown = "(desconocido )";\nconfig.messages.undefinedTiddlerToolTip = "'%0' aún no existe";\nconfig.messages.unsavedChangesWarning = "¡ADVERTENCIA! Hay cambios que aún no han sido guardados\sn\snPresiona OK para guardarlos\snPresiona CANCEL para perder los cambios";\nconfig.messages.dates.months = ["Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre","Diciembre"];\nconfig.messages.dates.days = ["Domingo", "Lunes","Martes", "Miércoles", "Jueves", "Viernes", "Sábado"];\nconfig.messages.dates.shortMonths = ["Ene", "Feb", "Mar", "Abr", "May", "Jun", "Jul", "Ago", "Sep", "Oct", "Nov", "Dic"];\nconfig.messages.dates.shortDays = ["Dom", "Lun", "Mar", "Mie", "Jue", "Vie", "Sab"];\nconfig.messages.shadowedTiddlerToolTip = "El artículo '%0' no existe aún, pero tiene un valor oculto predefinido";\n\nconfig.messages.messageClose.text = "cerrar";\nconfig.messages.messageClose.tooltip = "cerrar el panel de mensajes";\n\nconfig.shadowTiddlers.OptionsPanel = "'Estas opciones para personalizar TiddlyWiki están guardadas en tu navegador'\sn\sn'Tu nombre de usuario para firmar tus textos. Escríbelo como una PalabraWiki (por ej., JuanBloggs)'\sn<<option txtUserName>>\sn<<option chkSaveBackups>> GuardaRespaldos\sn<<option chkAutoSave>> AutoGuardar\sn<<option chkGenerateAnRssFeed>> GenerarUnArchivoRSS\sn<<option chkRegExpSearch>> BuscaRegExp\sn<<option chkCaseSensitiveSearch>> BuscaSensitivoMayúscula\sn<<option chkAnimate>> ActivarAnimaciones\sn\sn Vea las OpcionesAvanzadas\snPluginManager\snImportTiddlers";\nconfig.shadowTiddlers.SideBarOptions = "<<search>><<closeAll>><<permaview>><<newTiddler>><<newJournal 'DD MMM YYYY'>><<saveChanges>><<slider chkSliderOptionsPanel OptionsPanel 'opciones »' 'Cambiar opciones avanzadas de TiddlyWiki'>>";\nconfig.shadowTiddlers.SideBarTabs = "<<tabs txtMainTab Fecha 'Tiddlers cronológicamente' TabTimeline Título 'Tiddlers por título' TabAll 'Etiquetas' 'Tiddlers que estén etiquetados' TabTags 'Más' 'Más opciones' TabMore>>";\nconfig.shadowTiddlers.TabMore = "<<tabs txtMoreTab 'Perdidos' 'Artículos que no tienen contenido' TabMoreMissing 'Huerfanos' 'Artículos no enlazados' TabMoreOrphans 'Ocultos' 'Artículos ocultos con valores por defecto' TabMoreShadowed>>";\nconfig.shadowTiddlers.OpcionesAvanzadas = "<<option chkOpenInNewWindow>> Abre Enlaces En Ventana Nueva\sn<<option chkSaveEmptyTemplate>> Crea Plantilla Nueva (cuando guarda se generará una plantilla nueva) \sn<<option chkToggleLinks>> Hacer clic en enlaces a artículos que ya están abiertos, los cierran.\sn^^(desactívalo con Control u otra tecla modificadora)^^\sn<<option chkHttpReadOnly>> Solo lectura cuando se abre desde web\sn<<option chkForceMinorUpdate>> Trata las ediciones de un artículo existente como cambios menores y preserva la fecha y tiempo\sn^^(desactívalo com la tecla Shift al hacer clik con el boton 'guardar' o por usando las teclas Ctrl-Shift-Enter^^";\n\nconfig.views.editor.defaultText = "Escribe el texto para '%0'.";\nconfig.views.editor.tagChooser.popupNone = "No hay etiquetas definidas";\nconfig.views.editor.tagChooser.tagTooltip = "Agregar la etiqueta ";\nconfig.views.editor.tagChooser.text = "etiquetas";\nconfig.views.editor.tagChooser.tooltip = "Escoje etiquetas existente para añadir a este artículo";\nconfig.views.editor.tagPrompt = "Escribe etiquetas separadas con espacios, [[usa doble paréntesis cuadrado]] si es necesario, o escoje una existente ";\n\nconfig.views.wikified.tag.labelNoTags = "sin etiquetas";\nconfig.views.wikified.tag.labelTags = "etiquetas: ";\nconfig.views.wikified.tag.openTag = "Abrir etiqueta '%0'";\nconfig.views.wikified.tag.tooltip = "Mostar artículos etiquetados con '%0'";\nconfig.views.wikified.tag.openAllText = "Abrir todos";\nconfig.views.wikified.tag.openAllTooltip = "Abrir todos estos artículos";\nconfig.views.wikified.tag.popupNone = "No hay otros artículos etiquetados con '%0'";\n\nconfig.views.wikified.defaultText = "El artículo '%0' no existe aún. Doble clic para crearlo";\nconfig.views.wikified.defaultModifier = "(perdido)";\nconfig.views.wikified.shadowModifier = "(artículo oculto incluido)";\nconfig.views.wikified.createdPrompt = "creado";\n\nconfig.commands.editTiddler.text = "editar";\nconfig.commands.editTiddler.tooltip = "Editar este artículo"\nconfig.commands.editTiddler.readonlyText = "ver";\nconfig.commands.editTiddler.readonlyText = "Ver el fuente de este artículo";\n\nconfig.commands.closeTiddler.text = "cerrar";\nconfig.commands.closeTiddler.tooltip = "Cerrar este artículo";\n\nconfig.commands.closeOthers.text = "cerrar otros";\nconfig.commands.closeOthers.tooltip = "Cerrar todos los otros artículos";\n\nconfig.commands.saveTiddler.text = "guardar";\nconfig.commands.saveTiddler.tooltip = "Guardar este artículo";\n\nconfig.commands.cancelTiddler.text = "cancelar";\nconfig.commands.cancelTiddler.tooltip = "Deshacer cambios de este artículo";\nconfig.commands.cancelTiddler.warning = "¿Estás seguro de cancelar los cambios de '%0' ?";\nconfig.commands.cancelTiddler.readOnlyText = "hecho";\nconfig.commands.cancelTiddler.readOnlyTooltip = "Ver este artículo nomalmente";\n\nconfig.commands.deleteTiddler.text = "borrar";\nconfig.commands.deleteTiddler.tooltip = "Borrar este artículo";\nconfig.commands.deleteTiddler.warning = "¿Estás seguro de querer borrar '%0' ?";\n\nconfig.commands.permalink.text = "enlace permanente";\nconfig.commands.permalink.tooltip = "Enlace a este artículo";\n\nconfig.commands.references.text = "referencias";\nconfig.commands.references.tooltip = "Mostrar artículos que enlacen a éste";\nconfig.commands.references.popupNone = "No hay referencias";\n\nconfig.commands.jump.text = "saltar";\nconfig.commands.jump.tooltip = "Saltar a otro artículo abierto";\n//}}}
/*{{{*/\n/*Blackicity Theme for TiddlyWiki*/\n/*Design and CSS by Saq Imtiaz*/\n/*Version 1.0*/\n/*}}}*/\n/*{{{*/\nbody{ font-family: "Neue Helvetica", Helvetica, "Lucida Grande", Verdana, sans-serif;\n background-color: #fff;\n color: #333;}\n\n#topMenu {position:relative; background:#282826; padding:10px; color:#fff;font-family:'Lucida Grande', Verdana, Sans-Serif;}\n#topMenu br {display:none;}\n\n#topMenu a{ color: #999;\n padding: 0px 8px 0px 8px;\n border-right: 1px solid #444;}\n#topMenu a:hover {color:#fff; background:transparent;}\n\n#displayArea {margin-left:1em; margin-bottom:2em; margin-top:0.5em;}\n\n\na, a:hover{\ncolor:#333;\ntext-decoration: none; background:transparent; \n}\n\n.viewer a, .viewer a:hover {border-bottom:1px dotted #333; font-weight:bold;}\n\n\n.viewer .button, .editorFooter .button{\ncolor: #333;\nborder: 1px solid #333;\n}\n\n.viewer .button:hover,\n.editorFooter .button:hover, .viewer .button:active, .viewer .highlight,.editorFooter .button:active, .editorFooter .highlight{\ncolor: #fff;\nbackground: #333;\nborder-color: #333;\n}\n\n.tiddler .viewer {line-height:1.45em;}\n.title {color:#222; border-bottom:1px solid#222; font-family:'Lucida Grande', Verdana, Sans-Serif; font-size:1.5em;}\n.subtitle, .subtitle a { color: #999999; font-size: 0.95em;margin:0.2em;}\n.shadow .title{color:#999;}\n\n.toolbar {font-size:90%;}\n.selected .toolbar a {color:#999999;}\n.selected .toolbar a:hover {color:#333; background:transparent;border:1px solid #fff;}\n\n.toolbar .button:hover, .toolbar .highlight, .toolbar .marked, .toolbar a.button:active{color:#333; background:transparent;border:1px solid #fff;}\n\n/***\n!Sidebar\n***/\n#sidebar { margin-bottom:2em !important; margin-bottom:1em; right:0;\n}\n\n/***\n!SidebarOptions\n***/\n#sidebarOptions { padding-top:2em;background:#f3f3f3;padding-left:0.5em;}\n\n#sidebarOptions a {\n color:#333;\n background:#f3f3f3;\n border:1px solid #f3f3f3;\n text-decoration: none;\n}\n\n#sidebarOptions a:hover, #sidebarOptions a:active {\n color:#222;\n background-color:#fff;border:1px solid #fff;\n }\n\n#sidebarOptions input {border:1px solid #ccc; }\n\n#sidebarOptions .sliderPanel {\n background: #f3f3f3; font-size: .9em;\n}\n\n#sidebarOptions .sliderPanel input {border:1px solid #999;}\n#sidebarOptions .sliderPanel .txtOptionInput {border:1px solid #999;width:9em;}\n\n#sidebarOptions .sliderPanel a {font-weight:normal; color:#555;background-color: #f3f3f3; border-bottom:1px dotted #333;}\n\n\n#sidebarOptions .sliderPanel a:hover {\ncolor:#111;\nbackground-color: #f3f3f3;\nborder:none;\nborder-bottom:1px dotted #111;\n}\n/***\n!SidebarTabs\n***/\n .listTitle {color:#222;}\n#sidebarTabs {background:#f3f3f3;}\n\n#sidebarTabs .tabContents {background:#cfcfcf;}\n\n#sidebarTabs .tabUnselected:hover {color:#999;}\n\n#sidebarTabs .tabSelected{background:#cfcfcf;}\n\n#sidebarTabs .tabContents .tiddlyLink, #sidebarTabs .tabContents .button{color:#666;}\n#sidebarTabs .tabContents .tiddlyLink:hover,#sidebarTabs .tabContents .button:hover{color:#222;background:transparent; text-decoration:none;border:none;}\n\n#sidebarTabs .tabContents .button:hover, #sidebarTabs .tabContents .highlight, #sidebarTabs .tabContents .marked, #sidebarTabs .tabContents a.button:active{color:#222;background:transparent;}\n\n#sidebarTabs .txtMoreTab .tabSelected,\n#sidebarTabs .txtMoreTab .tab:hover,\n#sidebarTabs .txtMoreTab .tabContents{\n color: #111;\n background: #f3f3f3; border:1px solid #f3f3f3;\n}\n\n#sidebarTabs .txtMoreTab .tabUnselected {\n color: #555;\n background: #AFAFAF;\n}\n\n\n\n/***\n!Tabs\n***/\n.tabSelected{color:#fefefe; background:#999; padding-bottom:1px;}\n .tabSelected, .tabSelected:hover {\n color: #111;\n background: #fefefe;\n border: solid 1px #cfcfcf;\n}\n\n .tabUnselected {\n color: #999;\n background: #eee;\n border: solid 1px #cfcfcf;\n padding-bottom:1px;\n}\n.tabUnselected:hover {text-decoration:none; border:1px solid #cfcfcf;}\n.tabContents {background:#fefefe;}\n\n\n\n\n\n.tagging, .tagged {\nborder: 1px solid #eee;\nbackground-color: #F7F7F7;\n}\n\n.selected .tagging, .selected .tagged {\nbackground-color: #f3f3f3;\nborder: 1px solid #ccc;\n}\n\n.tagging .listTitle, .tagged .listTitle {\ncolor: #bbb;\n}\n\n.selected .tagging .listTitle, .selected .tagged .listTitle {\ncolor: #333;\n}\n\n.tagging .button, .tagged .button {\ncolor:#ccc;\n}\n.selected .tagging .button, .selected .tagged .button {\ncolor:#aaa;\n}\n\n.highlight, .marked {background:transparent; color:#111; border:none; text-decoration:underline;}\n\n.tagging .button:hover, .tagged .button:hover, .tagging .button:active, .tagged .button:active {\nborder: none; background:transparent; text-decoration:underline; color:#333;\n}\n\n\n\n.popup {\nbackground: #cfcfcf;\nborder: 1px solid #333;\n}\n\n.popup li.disabled {\ncolor: #000;\n}\n\n.popup li a, .popup li a:visited {\ncolor: #555;\nborder: none;\n}\n\n.popup li a:hover {\nbackground: #f3f3f3;\ncolor: #555;\nborder: none;\n}\n\n\n\n#messageArea {\n\nborder: 4px dotted #282826;\nbackground: #F3F3F3;\ncolor: #333;\nfont-size:90%;\n}\n\n#messageArea a:hover { background:#f5f5f5; border:none;}\n\n\n#messageArea .button{\ncolor: #333;\nborder: 1px solid #282826;\n}\n\n#messageArea .button:hover {\ncolor: #fff;\nbackground: #282826;\nborder-color: #282826;\n}\n\n\n\n\n\n\n.tiddler {padding-bottom:10px;}\n\n.viewer blockquote {\nborder-left: 5px solid #282826;\n}\n\n.viewer table, .viewer td {\nborder: 1px solid #282826;\n}\n\n.viewer th, thead td {\nbackground: #282826;\nborder: 1px solid #282826;\ncolor: #fff;\n}\n.viewer pre {\nborder: 1px solid #ccc;\nbackground: #f5f5f5;\n}\n\n.viewer code {\ncolor: #111; background:#f5f5f5;\n}\n\n.viewer hr {\nborder-top: dashed 1px #222; margin:0 1em;\n}\n\n.editor input {\nborder: 1px solid #ccc; margin-top:5px;\n}\n\n.editor textarea {\nborder: 1px solid #ccc;\n}\n\nh1,h2,h3,h4,h5 { color: #282826; background: transparent; padding-bottom:2px; font-family: Arial, Helvetica, sans-serif; }\nh1 {font-size:18px;}\nh2 {font-size:16px;}\nh3 {font-size: 14px;}\n/*}}}*/
\nVeamos con un ejemplo, las ventajas de esta técnica. \n\n!!!Mostrando un popup de forma no intrusiva\nEn la forma tradicional de programar con ~JavaScript nos habremos encontrado muchas veces con un código como el siguiente:\n{{{\n<a href="javascript:window.open('pagina.html')">Mi página</a>\n}}}\n\n''¿Qué ocurre si no tenemos ~JavaScript activado?''\n\nEn un segundo nivel podríamos intentar abrir la página desde un enlace definido de forma más estándar e indicando la función a ejecutar dentro del manejador de eventos del enlace:\n{{{\n<a href="#" onclick="window.open('pagina.html')">Mi página</a>\n}}}\n\nAhora por lo menos, no tendremos un enlace no válido en caso de no tener ~JavaScript activo, aunque aún no ejecutará en caso de estar desactivado. Simplemente se desplazará al comienzo de la página.\n\nCuando estamos dentro de un manejador de eventos, dentro de ese código, el objeto ''this'' hace referencia al objeto que ha generado el evento, en nuestro código, el enlace, así que podemos usar este objeto para usar las propiedades de ese objeto:\n{{{\n<a href="pagina.html" onclick="window.open(this.href)">Mi página</a>\n}}}\n\n''¿Qué ocurre ahora?''. Si no tenemos ~JavaScript activo, aún podremos abrir la página. Pero si tenemos ~JavaScript activado, al no detener la propagación del evento (devolviendo false en su manejador de eventos), al hacer clic sobre el enlace, este intentará abrir la página en una nueva ventana, como popup, pero también ejecutará el comportamiento normal de abrir la página enlazada. Una forma más elegante y funcional para este código sería.\n\n{{{\n< a href="pagina.html" target="_blank" onclick="window.open(this.href);return false;">Mi página</a>\n}}}\n\n''Si tenemos ~JavaScript activo, se ejecutará el código del evento onclick y como detendremos la propagación, no ejecutará el comportamiento normal de abrir el enlace de la forma estándar. Si NO tenemos ~JavaScript activo, al hacer clic sobre el enlace abriremos la página en una nueva ventana de la forma estándar.''\n\n!!!Separando el código HTML de código JavaScript\nPodemos dar un paso más para separar mejor, la capa de representación (HTML) de nuestro código de ejecución (JS). Aunque para este ejemplo podremos usar las funciones estándar de ~JavaScript, nosotros nos vamos a ayudar de la librería [[jQuery]] para escribir menos código y hacerlo más claro.\n\nPrimero quitaremos el código del evento onclick de nuestros enlaces:\n{{{\n< a href="pagina.html" target="_blank" class="popup clase1 clase2">Mi página</a>\n}}}\n\nComo vemos, hemos indicado varias clases para nuestro enlace que abre una ventana con popup, //clase1// y //clase2// son clases de ejemplo, podremos indicar varias clases en el atributo class de un elemento HTML separándolas con espacios. Hemos indicado una clase más que será la clase ''popup''. Para nuestro código, todos los enlaces de la clase popup tendrán un comportamiento especial, que será abrir la página con un popup. En el código ~JavaScript de nuestra página, usando la librería [[jQuery]] como apoyo tendríamos algo así:\n\n{{{\n//Código de inicialización de la página\n$(document).ready(function(){\n //Obtenemos todos los elementos a de la clase popup para insertarles un nuevo manejador de eventos\n $("a.popup").click(function(){\n window.open(this.href);\n return false;\n });\n});\n\n}}}\n\n''Esto es ~JavaScript no intrusivo!!!''\n\nHemos conseguido separar el código HTML de nuestro código ~JavaScript, y además no perdemos la funcionalidad básica al no tener activo el intérprete de scripts.
!!~JavaScript no intrusivo\nSe define el ~JavaScript no intrusivo como la técnica de separar el código HTML de la programación ~JavaScript, teniendo especial cuidado en no mermar la funcionalidad básica de la página HTML en caso de no tener el intérprete ~JavaScript activo en el navegador.\n\nVer [[Técnicas de programación no-intrusiva]].\n\n!!Ejemplos de validación basados en los formularios de ~NormaWeb\nPara realizar la validación de formularios, hemos utilizado también la programación no intrusiva. De manera que si no tuviésemos activado el intérprete, el formulario se podría enviar perfectamente salvo que ''no validaría en la parte del cliente''. Por esto es importante hacer dos validaciones, desde el cliente y desde el servidor ''¿por qué?''. Vamos a ver las [[ventajas e inconvenientes de la validación desde el cliente y desde el servidor]].\n\n!!!Validación de formularios con ~JavaScript en ATICA\nHemos dejado tres ejemplos diferentes, según el tipo de alerta configurado en YAV, el primero configurado para mostrar los errores a través de un ''alert'' simple. Los tres ejemplos están basados en la plantilla HTML de ~NormaWeb original y CSS original. La accesibilidad por tanto depende de las plantillas anteriores.\n^^[[Descargar el código completo de los ejemplos|aticajsform.zip]]^^\n\n* [[Formulario con mensaje de alerta|formulario/formulario.html]] . ~~Ver código:~~ [[formulario.html|view-source/view-source.html#formulario/formulario.html]], [[formulario_code.js|view-source/view-source.html#formulario/js/formulario_code.js]]\n\nEl segundo mostrando los errores en la propia página. Tanto para el segundo como para el tercer ejemplo, se ha añadido una nueva clase CSS a la plantilla de estilo para marcar los campos con error de la clase inputError (clase que usa para marcar los errores la librería YAV). Aunque marcar de color un input no es accesible (pero tampoco inaccesible), es una ayuda visual para aquellos que puedan seguirla, y se marca además con mensajes de error accesibles.\n* [[Formulario con panel de errores y marcas de color|formulario/formulario1.html]]. ~~Ver código:~~ [[formulario.html|view-source/view-source.html#formulario/formulario1.html]], [[formulario_code.js|view-source/view-source.html#formulario/js/formulario1_code.js]]\n\ny el tercero recogiendo los errores en una variable, para mostrarlos encima de cada campo que ha generado el error, además se ha modificado los campos relacion_universidad que eran option, por un select para dar más variedad en el ejemplo.\n* [[Formulario con panel de errores y marcas de color 2|formulario/formulario4.html]]. ~~Ver código:~~ [[formulario.html|view-source/view-source.html#formulario/formulario4.html]], [[formulario_code.js|view-source/view-source.html#formulario/js/formulario4_code.js]]\n\nExplicaremos el código del cuarto ejemplo pues reune todos los conceptos aplicados en los anteriores ejemplos. \n^^Ir a [[Explicación del código JavaScript del ejemplo]]^^
<!--{{{-->\n<div class='toolbar' macro='toolbar closeTiddler closeOthers +editTiddler permalink references jump'></div>\n<div class='title' macro='view title'></div>\n<div class='subtitle'><span macro='view modifier link'></span>, <span macro='view modified date [[DD MMM YYYY]]'></span> (<span macro='message views.wikified.createdPrompt'></span> <span macro='view created date [[DD MMM YYYY]]'></span>)</div>\n<div class='tagging' macro='tagging'></div>\n<div class='tagged' macro='tags'></div>\n<div class='viewer' macro='view text wikified'></div>\n<div class='tagClear'></div>\n<!--}}}-->
Organismo encargado de los estándares relacionados con la web. [[Ir a su web|http://www.w3c.es/]]
Es el objeto ~JavaScript para realizar peticiones de datos asíncronas (en segundo plano) a un servidor web. Desarrollado por primera vez por Microsoft para su versión 5 de Internet Explorer, aunque no maduro hasta la versión 5.5 //Ver// [[Bibliografía]].\n\nLas técnicas de programación con peticiones de datos en segundo plano, se han denominado comúnmente [[AJAX]].
Muchos programadores intentan detectar el tipo del navegador para saber si podemos usar una propiedad o método determinado a través de su cadena "useragent", pero esto es sensiblemente problemático pues, existen navegadores que permiten cambiar esta cadena para "pasar" precisamente estas comprobaciones, usando la cadena "useragent" de un navegador reconocido.\n\nEs por esto que es más recomendable, siempre comprobar si el objeto o propiedad existe y de esta manera usarlo o no.\n* [[Técnica de detección de objetos|http://www.quirksmode.org/js/support.html]]\n* [[Detectando el navegador usando detección de objetos1|http://www.javascriptkit.com/javatutors/objdetect.shtml]]
jQuery es una librería de poco peso (15 kB en las versiones preliminares y 20kB en las versiones actuales) que nos permite mejorar y automatizar la programación en ~JavaScript de forma muy sensible. La librería se compone de métodos útiles para distintos usos:\n\n* Funciones del núcleo principal de jQuery. El selector $ por si solo contiene una serie de métodos comunes, también podemos crear nuevos métodos a través de [[plugins o librerías para jQuery|http://docs.jquery.com/Plugins]].\n* Manejo de DOM. Permite obtener objetos y manejarlos, con métodos intuitivos y autoexplicativos. El selector $ permite seleccionar objetos DOM mediante la especificación CSS1, CSS2, CSS3 y XPATH, ej: //$("p").click(); $("a.clase").show(); $("#id input:checked").hide(); ....//\n* Manejo simplificado del CSS para los elementos DOM seleccionados. //$("a").addClass("clase"); $("p").css({height: "100px", width: "50px"}); ...//\n* Funciones útiles de ~JavaScript. //$.each(); $.grep(); ...//\n* Manejo de eventos. //$(document).ready(); $("div#capa").hover(); ....//\n* Efectos simples. //$("a").show("slow"); $("p").slideDown("fast"); .... //\n* Métodos para llamadas ~JavaScript asíncronas (AJAX). //$("#capa").load("mipagina.html"); $("#resultado_formulario").post("form.cgi",{username:"usuario",pass:"pass"}); ...//\n* Muchos más métodos a partir de plugins.\n\nAdemás, jQuery permite la concatenación de funciones, similar a la técnica usada con PHP5.\n{{{\n//En una sola línea especificamos el manejador de eventos para todos los enlaces "a", \n// les ponemos la clase CSS "clase" e insertamos texto al comienzo del enlace.\n$("a").click(function(){ alert("click"); }).addClass("clase").prepend("Haz clic en el siguiente enlace: ");\n}}}\n\n\n!!Páginas que usan jQuery\n* [[Vodafone UK|http://shop.vodafone.co.uk/index.cfm]]\n* [[SourceForge.net|http://sourceforge.net/project/downloading.php?group_id=166901&use_mirror=heanet&filename=drake_0.3.1_beta_rev1285.7z&96980624]]\n* [[Otras muchas páginas en el listado de jQuery|http://docs.jquery.com/Sites_Using_jQuery]]
Conocemos como navegadores modernos, a aquellos que han aparecido a partir de Julio de 2000, fecha de aparición de la versión final de __Internet Explorer__ 5.5 . Esta versión ya contenía la mayoría de las mejoras que podemos encontrar en lo que llamamos //navegadores modernos//, como:\n* Soporte de capas mejorado\n* Soporte de técnicas [[AJAX]] usando el objeto XMLHttpRequest a través de un ~ActiveX.\n* Soporte para la mayoría de las funciones básicas del ~ECMAScript.\n\nPero es con Internet Explorer 6 (en Octubre de 2001) cuando las mejoras por la inclusión de un soporte mejorado a los estándares de la W3C, hacen de Internet Explorer un navegador más parecido a cualquier otro navegador que no fuera de Microsoft. //Ver// [[Bibliografía]].\n\nNos hemos referido primero a Internet Explorer por ser hasta ahora el navegador más usado por la comunidad de Internet. Pero también podemos catalogar como navegadores modernos, a los siguientes:\n\n* __Opera__ 7 (11 de Enero de 2003) ^^Ver [[Bibliografía]]^^\n** Motor de render Presto\n** DOM\n** CSS 2\n** XMLHttpRequest a partir de Opera 7.5 (fallaba la implementación del objeto en Opera 8.x, vuelve a funcionar en las versiones 9.x)\n* __Netscape__ 6 (año 2001) ^^Ver [[Bibliografía]]^^\n** Motor de render Gecko\n** DOM\n** CSS\n** XMLHttpRequest\n* __Firefox__ 1 (año 2004) ^^Ver [[Bibliografía]]^^\n** Motor de render Gecko\n** DOM\n** CSS 2 (CSS 3 a partir de la versión 1.5)\n** XMLHttpRequest\n** ~JavaScript 1.6 a partir de la versión 1.5\n* __Safari__ 1 (junio de 2003) ^^Ver [[Bibliografía]]^^\n** Motor de render KHTML\n** DOM\n** CSS 2\n** XMLHttpRequest\n* __Konqueror__ 3.5 ^^Ver [[Bibliografía]]^^\n** Motor KHTML\n** DOM\n** CSS 2\n** XMLHttpRequest\n\nEn versiones anteriores de estos navegadores tenemos una degradación de sus características, el soporte del objeto XMLHttpRequest se pierde, y es muy posible que se soporten versiones inferiores del estándar CSS y ~JavaScript.
Es un ejemplo claro de la problemática existente cuando accedemos directamente al DOM desde los distintos navegadores, usando las propiedades y métodos nativos de estos objetos que implementa cada navegador.\n\nEn primer lugar tenemos que normalmente, el programador que quiere inicializar o ejecutar código cuando el navegador ha cargado la página web usa __window.onload__. Pero esto tiene una serie de fallos:\n* Si otra librería usa window.onload, el código declarado sustituirá el manejador del evento (en Internet Explorer).\n{{{\nwindow.onload = function(){\n alert("hola Mundo");\n}\n\nwindow.onload = function(){\n alert("adios Mundo"); //solamente veremos adios\n}\n}}}\n\n* Existe un retraso en la ejecución de este código pues, aunque puede que hayamos ya la estructura DOM de la página, es posible que el evento no salte puesto que debe esperar a la carga completa de la página (terminar de cargar todas las imágenes). En realidad sólamente es necesario esperar a que esté cargada la estructura DOM para estar seguros de que podemos ejecutar nuestro código que maneje el DOM sin errores. La problemática estriba en que los navegadores tienen distintos manejadores de eventos para el mismo evento (~DOMContentLoaded, domready,....). //Ver una descripción más detallada en la el// [[blog de Peter Michaux|http://peter.michaux.ca/article/553]].\n\nLa solución es crear una librería que detecte en qué navegador estamos (ver [[detección de navegador a través de objetos]]) para usar el evento de DOM cargado para usarlo en vez de window.onload .\n# [[Solución de DeanEdwards|http://dean.edwards.name/weblog/2005/09/busted/]] y esta [[otra solución|http://dean.edwards.name/weblog/2006/06/again/]]\n# [[Solución en brothercake|http://www.brothercake.com/site/resources/scripts/domready/]]\n\nEs en este tipo de casos es cuando es necesario una librería DOM que permita abstraernos de todos estos problemas. ''jQuery'' dispone de un método //ready// para ejecutar código una vez cargado los objetos DOM. Los manejadores de eventos son encadenables.\n\n{{{\n<script type="text/javascript" src="jquery.js"></script>\n<script type="text/javascript">\n $(document).ready(function(){\n alert("hola Mundo");\n });\n\n $(document).ready(function(){\n //código de inicialización 2, no sobreescribe el anterior sino que se encadena\n alert("adios Mundo");\n });\n</script>\n}}}\n
!!!Validación de formularios en la parte del cliente\n!!!!Ventajas\n* Velocidad en el proceso. Al no tener que enviar datos incorrectos al servidor, no perdemos el tiempo de recarga de la página mostrando los errores que envía el servidor.\n* Es un filtro preliminar para los parámetros que le llegan al servidor. Aunque es importante que no contemos siempre con ellos (se puede tener ~JavaScript desactivado, o un hacker puede enviar datos directamente por POST o GET sin usar el código de la página, peligro de SQL inyection).\n!!!!Contras\n* No funcionarán si no tenemos ~JavaScript activado.\n\n!!!Validación de formularios en la parte del servidor\n!!!!Ventajas\n* Si el servidor responde podemos estar seguros que la validación se lleva a cabo. Es aquí donde deberemos filtrar posibles ataques de SQL inyection o caracteres incorrectos.\n!!!!Contras\n* Necesita que se envíen los datos, y un segundo envío del servidor de toda la página con los mensajes de error. Más tráfico web generado, tanto para el cliente como para el servidor.