Está en la página 1de 532

Todos 10s nombres propios de programas, sistemas operativos, equipos hardware,

etc., que aparecen en este libro son rnarcas registradas de sus respectivas
compafiias u organizaciones.

Reservados todos 10s derechos. El contenido


de esta obra e s t i protegido por la ley, que
establece penas de prision y/o rnultas, adernas
de las correspondientes indernnizaciones por
dafios y perjuicios, para quienes reprodujeren,
plagiaren, distribuyeren o cornunicasen publi-
carnente, en todo o e n parte, unaobra literaria,
artistica o cientifica, o su transforrnacion,
interpretacion o ejecucion artistica fijada en
cualquier tip0 de soporte o cornunicada a
traves de cualquier rnedio, sin la preceptiva
autorizacion.

Copyright 0 2000 by Anaya Multimedia.


Authorized translation from the English language edition published by O'Reilly &
Associates, Inc.
Copyright 0 1999 by O'Reilly & Associates, Inc.
All rights reserved

0 EDlClONES ANAYA MULTIMEDIA (GRUPO ANAYA, S.A.), 2000


Juan lgnacio Luca de Tena, 15. 28027 Madrid.
Deposit0 legal: M. 37.777-2000
ISBN: 84-41 5-1070-9
Printed in Spain.
Imprime: Artes Grdficas Guemo, S.L.
Febrero, 32. 28022 Madrid
Sobre el autor
Jerry Brandendaugh es todo un experto en el desarrollo de aplicaciones y diri-
ge un grupo tecnico en Los h g e l e s , California. Su sitio Web, que surgio con 10s
primeros destellos de JavaScript, es uno de 10s recursos relacionados con este len-
guaje de programacion mas antiguos de Internet. Ha desarrollado aplicaciones
para Netscape y First Union National Bank.
Agradeci mieptos,
Mi nombre aparece en la portada, per0 la verdad es que he contado con la ayu-
da de mucha gente. Me gustaria dar las gracias a todos ellos.
por el lado ttcnico, quiero dar las gracias a Steve Quint y James Chan, Jim Es-
ten, Bill Anderson, Roland Chow, Rodney Meyers, Matthew Mastracci, Giorgo
Bragq, Brock Bauchamp y a todos 10s que me han ayudado a aprender JavaScript
y otros lenguajes de programacion. Me creo en el deber de homenajear a Patrick
Clark, que me ha ayudado a crear aplicaciones online. Gracias a Richard Koman,
mi editor, por escuchar mis ideas y permitirme plasmarlas en papel y tambien a
Tara McGoldrick y Rob Romano, por la gran ayuda que me han prestado duran-
te la elaboracion de esta obra.
Por el lado emocional, me gustaria dar las gracias a mi esposa Rondine Braden-
baugh, por ayudarme a sentarme delante del monitor y animarme a que siguie-
se escribienda una noche tras otra (asi durante meses). A mis padres, por su
ayuda y aliento, gracias a 10s cuales he llegado a adquirir estos conocimientos.
Tambitn quiero dar las gracias a alguien muy importante en esta obra: a1 lec-
tor. Es 61 quien ha fenido que acercarse hasta la tienda, seleccionar este libro y
pagar por el. Todos sabemos que hay otros libros de Java. Per0 han seleccionado
el mio. Gracias, de verdad, por darme la oportunidad de demostraros que vues-
tra inversion ha merecido la pena.
Contenido

Sobre el autor .................................................................................... 5


Agradecimientos ................................................................................ 6

Prefacio 21

Qut debemos sabe ................. ......... ... ............ 22


Convenios con las ..... .... 22
Estructura del libro .... ........,...... ....... ................ ......... .......................... 22
Requisitos para la ejec 22
Analisis de la sintaxis 22
TCcnicas de JavaScript .............................. 22
Posibles ampliaciones ...... ....................... ........ 23
.............. ...................................................... 23
.................................. ............. 23

Introduccion 25

Ventajas de JavaScript 25
25
.............. ................. 26
Reducci6n de la carga del servidor ................................................. 26
JavaScript esta creciendo .. ......................................................... 26
Es posible que no quede otra alternativa .......... 27
Pero aGn hay mas ......................................................................... 27
Estrategia para la programaci6n en JavaScript 27
Propiedades de la aplicaci6n ........................................................... 28
Audiencia ........... .............. 28
iC6mo puedo salvar todos estos obstaculos? .......................,......... 29
Exploradores cruzados ............................................................. 30
Una pequeiia degradacih o cambio en la efectividad ................ 30
Apuntar bajo ............................................................................ 30
8 Contenidos

C6mo utilizar JavaScript con estas aplicaciones ........... 31


Reutilizar todo el c6digo que sea posible .... ......................... 31
Aislar JavaScript ......................... ........................................... 32
La declaraci6n de las variables globales se harB a1 principio ........... 32
La declaracion de las funciones del constructor apareceran
desputs de la de las variables globales .................................. 32
La definicion de las funciones se harB de arriba hacia abajo,
siguiendo un orden cronol6gico .............,... 32
Cada funci6n desarrollarB una 6nica operation ............................. 33
Se utilizaran tantas variables locales como sea posible ................... 33
Avanzar .......................... ............................................................ 33

1 Motor de busqueda para el cliente

Requisitos para la ejecucion .. ....................................................... 37


AnBlisis de la sintaxis ........................................................ 38
nav.htm1 ........................................................ 39
records.js ......................................... ........................... 43
Las variables globales ... ............................................ 44
Las funciones . ..................................................... 46
validate() .................................................... ............. 46
convertstring () ............................... ............................ 47
allowArray () .................. ................................................ 48
requireAll() ............................................................ 50
verifyManage() ................................................ ................. 53
noMatch() ................................... .............................. 54
formatResults(1 ................................................... 55
Cabecera HTML y el titulo ........................................................ 55
Mostrar el titulo, description y URL de 10s documentos ............ 57
Agregar 10s botones de avance y retroceso ................................ 58
prevNextResults().... .................................................. 59
El cddigo HTML ............................................................................ 64
Construir una base de datos en JavaScript .............. .................. 65
Posibles aplicaciories........................ ..................................... 65
Compatibilidad con JavaScript 1.O ................................................ 65

i
Contenidos 9

Compatibilidad a cambio de funcionalidad ..................................... 66


Dificultar su ruptura ................................................................ .... 67
Mostrar carteles publicitarios ..,..................................................... 67
Incluir opciones para refinar la busqueda .....,................................ 67
Conjunto de agrupaciones ............................................................. 68

2 Examen online 69

Requisitos de la ejecucion ..... ......................................... 72


Anhlisis de la sintaxis .. .............................................................. 72
index.htm1.................................................................................. 73
questions.js, el archivo c6digo fuente en JavaScript .. ........... 76
.......... ................................................. 79
.............................................................. 83
Variables globales ..................................... 84
Funciones .............................................................. 85
itemReset() ....................................................................... 85
shuffle() ...... ............... 85
buildQuestion() .............................................................. 86
gradeTest0 .................................................................... 91
printResults() ............. 93
chickenout () .............................................................. 95
. .
Posibles ampliaciones ................i.. ........................................... 96
A prueba de trampas ........ ............. 96
Eliminar las respuestas del array .............................................. 96
Eliminar gradeTest()y modificar bui 97
Modificar printResults0 ... ................... 98
Convertir la aplicacion en una encuesta 99

3 Diapositivas interactivas 101

Requisitos para la ejecucion ................................................................ 103


AnAlisis de la sintesis .......................................................................... 103
Variables de la aplicacion ....................................................................
. I
108
Capas DHTML predeterminadas .................................................... 109
10 Contenidos

Variables propias de 10s exploradores ....... ......................... 109


Variables relacionadas con la imagen ............................................. 110
Variables relacionadas con la ejecuci6n automatica ....... 111
Funciones de la aplicaci6n .............. .......................................... 111
Funciones relacionadas con las capas .................... 113
genLayer() ... ................... ......................... 113
slide()............................................................................... 114
genScreen() .. ...... 116
Elemenkos de la diapositiva ....................................................... 122
Funciones relacionadas con la imagen 122
preLoadImages0 .............. .................. ....................................... 123
imageswap() .................................. .................... 123
Funciones de navegaci6n 124
refslide(),hideslide(), showslide() y menuManager0 124
.......................................... 126
setslide() ...........................................,.............................. 128
autopilot () ......... ........ ......................... 128
automate() ................................. ...................................... 130
Posibles ampliaciones ..... .......................... 130
Mostrar diapositivas alea 131
Animar GIF o secuencias de imageries ................... 131
Animar las diapositivas ...... .......................................... 131

4 lnterfaz para un motor de busqueda multiple 133


Requisites para la ej 135
Analisis de la sintaxis .......................................... 135
Algo de memori 141
Carga dinarnica de imagenes ......................... 142
Iniciar 10s motores ................................................................ 144
enginelinks() ... ......................... 145
Administracih de capas ........................... ................................ 145
Carga previa de imagenes .......... 149
149
imageswap()........................................................ 151
......................... 151
Contenidos 11

Posibles ampliaciones: agregar un control para el usuario .................. 153


Paso 1 ............................................................. 155
Paso 2 ....................... .............................. 155
Paso 3 ................................................................................. 156
Paso 4 ........................................ ................... 156
Paso 5 .................................................................. 156

5 ImageMachine 159
Requisitos para la ejecuci6n .......................................... 162
Analisis de la sintaxis ................................................................. 163
Paso 1: carga de paginas ... ............... .................... 174
Paso 2: introducir 10s valores predeterminados y 10s pares
de imiigenes ............................................. 175
Paso 3: completar la ruta de las imagenes, 10s atributos HREF,
etc. ... .......................................... ............ ................. 176
captureDefaultProfile() ....... ............................. 176
generateEntryForm0 ................................................................ 178
genJavaScript() ................... ...... ............ ........ 181
. .
La hora de las decisiones ........................................................... 184
Generar el c6digo ................................................. 185
Paso 4: seleccionar ((Preview))para ver el codigo en acci6n ............. 186
Paso 5: seleccionar ((ChangeInfo))para hacer cambios ................... 186
Posibles ampliaciones: afiadir atributos a la plantilla ............. 187
Paso 1: agregar campos ............ ... ... ... ... ............ ...... ... .............. ...... 188
Paso 2: crear arrays con setArrays().......................... 188
Paso 3 : capturar 10s nuevos valores predeterminados .. ...... ............ 189
Paso 4: agregar campos de texto en generateEntryForm0 .............. 189
Paso 5: referenciar y asignar 10s nuevos valores en
genJavaScript0 ............. .................... ... ... ...... ... ... ................ 190
Paso 6: generar HTML adicional en genJavaScript0 190

6 lmplementacion de archivos de codigo fuente en JavaScript 193

arrays.js ....................................... .......... .......................................... . 194


Us0 practico ......... ..................... ... ....................... ...... ............... 194
12 Contenidos

..
Version necesaria ...................................................................... 194
Funciones ................................................................................. 194
. .
cookies .js ............................... .................................... 198
Us0 prhctico ............................................................................. 198
Versidn necesaria ...................................................................... 199
Funciones ................................................................................. 199
dhtml.js ..................... ......................................... 202
Us0 practico ............................................................................. 202
..
Version necesaria ...................................................................... 202
Funciones ................................................ ...................... 202
events.js ................................................. ................................. 204
Us0 prhctico ............................................................................. 204
..
Version necesaria ...................................................................... 204
Funciones ..................................................... .......... 204
frames.js ............................................................................................ 210
Us0 prhctico ............................................................................. 210
Versidn necesaria ...................................... ............................ 210
Funciones ............. ..................... ..... 210
images.js ........................................................................................... 213
. .
Us0 practico ............................................................................. 213
Version necesaria ........ 213
Funciones ........ ................................... 213
navbar.js ............................................................................................ 214
Us0 prActico ............................................................................. 214
Version necesaria ....... ............... 214
Funciones ....... ............................................................... 214
numbers .js ........................................................................................ 216
Us0 prActico ............................................................................. 216
Version necesaria ..... ......... ... 216
Funciones ................................................................................. 216
objects.js ............................................................................................ 218
Us0 practico ............................................ ........................ 218
Versidn necesaria ................................. 218
Funciones ................................................................................. 218
strings.js ............................................................................................ 223
Contenidos 13

Us0 practico ............................................................................. 223


Versi6n necesaria ...................................................................... 223
Funciones ................................................................................. 223
Posibles ampliaciones ......................................................................... 230

7 Preferencias del usuario basadas en cookies 231

Requisitos para la ejecucion ........................................... 233


Analisis de la sintaxis ......................................................................... 234
prefs.htm1 ......................................................................................... 235
Forma de las preferencias ........ ................... 244
Carga de las preferencias almac 246
Preparar las imageries ................................................................... 249
Efectuar cambios .................................... .................... 252
Paso 1: repetici6n a travks de formObj ...................................... 253
Paso 2: escribir la informacion en 10s archivos cookie .............. 256
Paso 3: ofrecer a 10s usuarios la posibilidad de ver 10s
nuevos cambios ........ ..................................... 256
Borrar el formulario ................................................................. 256
dive.htm1 ............. .................................................. 257
Anhlisis del archivo cookie ................................. .................... 260
Trabajar con lo desconocido .......... ..................................... 262
Posibles aplicaciones ........................................................................... 267
Mas opciones para modificar el aspect0 de la composicion ... 268
Aiiadir temas .......................................... ....................... 268
Permitir que 10s usuarios creen sus propios vinculos ..................... 268
Marketing a traves de anuncios directos .................................... 269

8 Shopping Bag: un carro de la cornpra en JavaScript 271


. . . de Shopping Bag ..................................................................
Revision 271
Paso 1: cargar la aplicaci6n ........................................................... 272
Paso 2: bdsqueda y selecci6n de productos .................................... 273
Paso 3 : revisar y modificar el pedido ............................................. 276
Paso 4: comprobaci6n ................................................................... 277
14 Contenidos

2 78
Analisis de la sintaxis ................................. 2 79
........................................ 28 1
Miembros de alto nivel 282
285
Propiedades del producto .................. .............................. 290
291
Crear productos y categorias ............................... 293
Crear la bolsa de la compra ........................ ..................... 29.5
Paso 2: mostrar 10s productos .......... .............................. 296
manager.htm1 ........... 298
306
..................... 308
.............................. 309
31 1
Paso 3: mostrar todas las categorias .............................. 312
..................... 313
iD6nde est6 el c6digo DHTML? .............................. 313
314
Buscar por productos ............ ................................... 316
31 7
Buscar en la base de datos ya existente .......................................... 318
Navegaci6n por categorias/productos ...,.......... .................. 319
El c6digo del vinculo ....................................... 32 3
Paso 5 : cambiar el ordenhegistro ............... ..................... 322
325
Guardar el registro de la factura ......................................... 328
Ajustar showBag( ): mostrar 10s totales ......... 330
((CheckOut)) .................................... .............................. 33 1
Completar la pantalla 334
334
((ResetQt y s ...... .... ...................................... ...
)) 334
((ChangeBag)) ............................. .............................. 335
Las funciones olvidadas ................................................. 336
336
Contenidos 15

Crear productos mi% inteligentes ................................................... 336


Agregar capacidades de busqueda mas refinadas ............................ 33 7
Incluir cookies .............................................................................. 338

9 Cifrado en JavaScript 339


C6mo funcionan 10s cifrados ...................................... 34 1
Piratear el codigo ..............................................................
I .
344
Caesar ........................... .................................... 344
................................................................ 345
............. 346
...................................... 346
352
Definicih del sistema de cifrado por sustituci6n ....... ...... ... ........... 356
. .,
Sustitucion basica ........................................... ................. 356
Diferentes soluciones para distintos cifrados _.............. 358
Algoritmo Caesar ........ ..................... ........................... 359
Algoritmo Vigenere .. ............. 360
C6mo se modifica shift1 362
Cada Substitutioncipher tambiCn es un objeto Cipher ................... 363
Creaci6n de cada caso de Substitutioncipher ........................ .......... 363
Seleccionar un sistema de cifrado ............ 366
Una nota final ............. ...................................... 36 7
. . 367
Posibles ampliaciones ............................................................

10 Cyber Greetings: arrastrar y soltar sobre el correo electronico 371


Requisitos para la ejecuci6n .......................... ............. 3 7s
Analisis de la sintaxis ........ ...................................... 3 75
Los otros dos documentos ............................................................. 377
Moverse por un terreno conocido ............ ..... 382
iHay sitio para todos! ... ...................................... 382
Registrar el movimiento del rat6n ................................................. 386
Llamar a todos 10s iconos ................................................. 386
Mover 10s iconos ..................................... ............. 387
DespuCs de la carga de 10s documentos 389
16 Contenidos

Las variables ................................................................................. 395


Mostrar las tarjetas ep pantalla ..................................................... 398
Moverse a travks de todas las imhgenes ......................................... 399
Conservar en pantalla 10s iconos que se han arrastrado ................ 402
Probar el trabajo ........................................................................... 405
Crear la tarjeta ............................................... 406
Envio de la tarjeta ...................................................... 409
El servidor .................................................................................. 411
Posibles ampliaciones ....... ............................... 412
Agregar un vinculo de regreso a Greetings ..................................... 412
Agregar temas ............................................... 412
Anuncios ........ 412
Mejorar la interactividad de las tarjetas ......................................... 412

11 Ayuda contextual 413


Requisitos para la ejecucion ................................................................ 415
Andlisis de la sintaxis .......................................... 415
Ayuda contextual 41 7
Mostrar y ocultar 420
Crear capas ................. ................... 422
Mostrar la infprtpacion . . ................................................................. 424
Controlar la ubicacion del vinculo .................. 426
Posibles ampliaciones .... .......................................... 42 8
Tabla de contenidos ................................................................ 428
Archivqs de ayuda en 10s que se pueda buscar information ........... 429
Consulte con un profesional .......................................................... 429
Atencion telefonica ...... ..... ....... 429

Epflogo 43 1

A Referencia de JavaScript 433


Compatibilidad con el explorador ....................................................... 433
Objetos. mCtodos y propiedades ......................................................... 434
Anchor ......................................................................................... 434
Contenidos 17

Applet .......................................................... .....,,... .......I.. .........


434
Area ............................................. ...................................... 436
Array ........................................................................................... 436
Boolean ........... ..................................................... 436
..................................,.................. ............................ 43 7
......................................... ................. ...................... 438
Date ............................. ................................................. 439
Document .... .......................................... 443
Event ................................................................ 445
Fileupload ................................... ........................................ 44 7
Form ......................,... ................................................... 448
Frame ............ ........................................... 449
Function ................................................... .......................... 449
Hidden ......................................... ........................................ 45 1
History .... .................. ............................................... 45 1
................................................. 452
............................................. ......................
45 3
.................................... 45 3
JavaClass ................ .......................................... 454
........................................ 454
JavaPackage .......................................... .......................455
Layer ...................................... ..................................... 455
Link ....................... .......................................... 45 8
............................................ 45 9
.......................................... .........................460
MimeType ........................... ......................................... 462
Navigator ............. ............................................ 463
.......................................... 464
......................... 464
Object ................................. .. ......................................... 465
Option . ............... ......................................... 466
.............................................. ................ 46 7
Password ............................... .............. .......................... 468
Plugin .................................... .......................................... 469
Radio .................... .................................... 469
RegExp ............................. .................................. 470
.............................. ............................... 472
Screen ........... .............................. ............................. 473
Select ...................... ............................. ...............474
String ............................... ................................ 475
............................ .............................. 478
sun ................. ............................... ........................... 479
Text ............................ .............................. 479
480
.............................. ................................ 481
.................... 485
Controladores de eventos .. ............................. 486
.................................. 486
............................... ............................. 487
onchange ............... ............................... .................. 487
onclick ............................ ................................. 4a 7
.............................. 4aa
................................ ......................... 48a
onError ........................ ................................. .............. 489
onFocus ................................. ............................... 489
.................................. 490
onKeyPress ......... .............................. ....................... 490
onKeyUp ....................... ................................ 491
................................ 491
.............................. 492
onMouseMove ........ ............................... ................... 492
onMouseOut .................... ................................. 493
onMouseOver .............................. ............................... 493
........................... ............................. 493
onMove ...................... .............................. ................. 494
onReset ................................ .................................. 494
onResize ....................................... ................................ 495
onselect ................ ............................ .......................... 495
onsubmit .......................... ............................. ............. 495
...................... ............................... 496
Contenidos 19

B Web Resources 49 7

49 7
Referencia a JavaScript ................................... ..................... 498
Preguntas y respuestas sobre JavaScript . ............................ 499
DHTML Reference ............................. ................................ 499
Document Object Model Reference ......................................... 500
Perl/CGI Reference ............ ........................................ 500
Graphics Resources ...... ........................................... 500
Aplicaciones similares ......................................... 501
Motores de busqueda para el cliente ......................... 501
ExAmenes online .......................................... 501
.................. 502
Interfaces para motores de busqueda multiples ............................. 503
Generador de secuencias ..................... ............................ 503
Bibliotecas .......................... ......... ................................. 504
Cookies ........... ...................... ......................................... 505
Carros de la compra ......... .......................................... 505
Encriptadores ............. ........................................... 506
Concepto ccarrastrar y soltar)) ... ........................ .......... 507
Ayuda contextual ......................................... 508

C Scripts en Per1 509

Perl/CGI . ........ ................ 509


............................................
LQuk tiene Perl que es o? ..... ....... ...... ................. 509
...... ....... ..,....... .......... ............ 510
..... . ......... ... ................. .. ................
510
I ? . ... ... ................. .... ....................
511
Obtener Perl ................................................ ............................
511
Script de Shopping Bag: bag.pl ............... .............................. 512
Obtener la informacion del product0 ................................. 5 14
Guardar la informacion en un archivo del servidor Web ................ 515
Mostrar informacion a1 comprador ......... .... ..................... 520
Configurar el Script .. ...................................... 520
20 Contenidos

El script de CyberGreeting: greet.pl .................................................... 521


. . del script ................................................................
Configuracion 522
Obtener la informacih de la tarjeta .............................................. 524
Guardar la tarjeta en un archivo h i c o .......................................... 525
Imprimir una pAgina de confirmacih ........................................... 526

hdice alfabetico 529


Prefacio

Habia algo que se me escapaba. Alli estaba 0,apilando libros de JavaScript o


revisando pagina Web tras pagina, tratando de asimilar todo el c6digo y 10s
conceptos posibles. Per0 despuks de asimilar algun concept0 nuevo o aprender
alguna tecnica de un maestro de la programacih, no sabia quk hacer aparte del
ejemplo que habia estudiado. Era como encontrarse en medio de una cocina
llena de ingredientes, per0 sin ninguna receta a mano. Tenia todos 10s conceptos
y conocimientos de JavaScript, per0 no sabia como aplicarlos para solucionar
10s problemas de todos 10s dias. Obviamente, 10s libros de JavaScript tienen
aplicaciones, per0 ninguna es lo suficientemente importante como para traba-
jar con ella en la Web. Es decir, el p6quer esta bien, y las aplicaciones capaces de
trabajar con hojas de calculo, per0 no iba a publicar ninguna de estas aplicacio-
nes en mi pagina Web.
Bien, pues aqui tenemos las recetas. No solo para determinar la identidad de
un explorador Web o para darle la vuelta a una imagen, sin0 que esta obra esta
llena de aplicaciones que se pueden utilizar en u n sitio Web. Las aplicaciones
estan listas para utilizarlas en la vida real. Se pueden copiar a una carpeta del
sertidor Web (0en el ordenador con el que se trabaje normalmente) y ejecutar-
las. Los capitulos que veremos a lo largo de la lectura estan ordenados de tal
forma que permiten comprender cuales son las tareas mas comunes de la Web,
como la busqueda de sitios Web, recopilacion de informacih, creaci6n de im5-
genes, visualizacion de presentaciones online, compras virtuales, etc. Obviamen-
tej se pueden modificar para adaptarlas a nuestras necesidades, per0 en cualquier
caso estan listas para ejecutarlas. Ademas, cada aplicacion incluye una explica-
ci6n para que todos comprendamos como funciona.
,
22 Prefacio

Que debemos saber


No se trata de una obra para principiantes. Aqui no aprendera JavaScript. Apren-
dera a utilizarlo. Tampoco hay que ser un veterano de JavaScript, per0 si algo
como i n f o . r e p l a c e ( / < g , "&lt;"), new I m a g e 0 y v a r itemArray=tl
le suena a chino, asegdrese de tener a mano un libro donde se le explique la sin-
taxis de Java mientras trabaja.

Convenios con las fuentes


Cursiva. Se utiliza con 10s nombres de 10s archivos, las rutas de 10s directorios,
URL y nombres de objetos, variables, arrays y otras entidades.
Monoespaciada. Se utiliza con las etiquetas HTML, ejemplos o fragmentos de
codigo, funciones y otras referencias similares.
Monoespaciada con c u r s i v a . Se utiliza con el texto que tiene que insertar el
usuario y para las secciones de texto que se han de sustituir.
Monoespaciada con negrita. Se usa con el texto que aparece en pantalla.

Estructura del libro


En general, todos 10s capitulos siguen el mismo esquema con las siguientes
secciones.

Requisitos para la ejecucion


En esta secci6n se Vera el entorno necesario para ejecutar la aplicacion. Gene-
ralmente se tratara de un explorador Web que sea compatible con Netscape Na-
vigator o con Microsoft Internet Explorer. TambiCn trata temas relacionados
con la resolucion del monitor, escalabilidad, etc.

Analisis de la sintaxis
Cuando terminemos de jugar con la aplicacion y queramos saber quC c6digo es
el responsable de tales acciones, acudiremos aqui. En esta seccion se hablara del
codigo, normalmente, analizandolo linea a linea. Es la seccion mas larga del capi-
tulo, asi que nos tendremos que poner comodos antes de adentrarnos en ella.

Tecnicas de JavaScript
Segun avancemos en el analisis del codigo, habra una serie de puntos en 10s
que deberemos detenernos y destacar alguna ttcnica que merece la pena recordar.
Prefacio 23

Posibles ampliaciones
En esta seccion veremos las posibilidades que hay para ampliar el codigo. Hay
veces en que hare alguna sugerencia y otras en las que incluso ofrecere codigo
hecho por mi. Otras, sefialark una direccion de Internet a traves de la que se pue-
de bajar otra version de codigo, mas ejemplos, etc. En cualquier caso, le aseguro
que con 10s ejemplos que veremos en la obra no se detendra ni un instante a pre-
guntarse "Bien, icomo se supone que voy a colocar esto en mis paginas Web?"

El codigo
El libro esta lleno de aplicaciones. No se sorprenda. Va a ver mucho codigo. Al-
gunas aplicaciones contienen varios cientos de lineas y ocupan varias paginas.
' En otros casos, Vera que el c6digo se repite mucho y es que prefiero que sea asi
a que tenga que estar pasando paginas hacia delante y hacia atras hasta que lo-
calice la seccion de la que se esta hablando.
Uno de 10s inconvenientes que tiene escribir el codigo en el libro es precisamen-
te ese, que esta en el libro. Tenemos que adaptarnos a las liniitaciones fisicas de
la pagina. Esta tiene un ancho determinado, por lo que habra ocasiones en las
que el codigo saltara automaticamente a la siguiente linea. Para mejorar la le-
gibilidad del codigo, lo he llenado de comentarios. La verdad es que 10s chicos de
maquetacion han hecho un gran trabajo a1 adaptar el formato del codigo a las
limitaciones de las paginas. Per0 aun sigue habiendo ocasiones en las que resul-
ta mas sencillo mirar el codigo a traves de la pantalla de u n editor.

Desarrollo y pruebas
Sin mas particular, he mostrado el hardware y el software que he usado para
desarrollar el c6digo de este libro. He verificado el material en un entorno Windows,
asi que quiza 10s usuarios de Unix o Macintosh encuentren algun problema.
Hardware: IBM ThinkPad 55/P75/16M Compaq Presario/P233/1OOM, IBM
Aptiva C23/P120/129M, Dell Optiplex/P2-266/128M, Sun SPARC 20.
Sistemas operativos: Win95, WinNT Workstation 4.0, WinNT Server 4.0 y So-
laris 2.5.
Exploradores Web: Netscape Navigator 3.0, 3.04 Gold, 4.0, 4.04, 4.07, 4.08,
4.5; Microsoft InternetExplorer 3.0, 3.02, 4.0, 4.01, 5.00.
Resoluciones: 640x480, 800x600, 1024x768, 1152x900, 1280~1024.
Obviamente, no he probado todas las aplicaciones con estas condiciones. Pero
he intentado que el c6digo fuese lo suficientemente robusto como para que fun-
cionase con la mayoria de ellas.
Introduccion

/-

Este libro se sale de lo normal. Trata sobre la escritura de grandes aplicaciones


utilizando JavaScript. No es lo que la mayoria de la gente que estuviese intere-
sada en JavaScript compraria. Generalmente, este lenguaje de programacion
esti relacionado con efectos como girar una imagen, crear contadores para 10s
visitantes, deteccibn de exploradores, etc.

Ventajas de JavaScript
Ninguno de 10s lenguajes de programacion del mercado se puede considerar
como el mas adecuado para la creacion de aplicaciones. Cada uno tiene sus ven-
tajas e inconvenientes. Los ultimos avances en JavaScript y la proliferation de
tecnologias como DHTML, Java y Macromedia Flash, han colocado a JavaScript
en una posicion ventajosa para aprovechar estas herramientas en la creacion de
grandes soluciones para la Web. A continuacion mostramos algunas razones
apoyando el us0 de JavaScript para crear aplicaciones:

F a d de aprender, rapido y potente


Como JavaScript es muy sencillo de aprender, se puede empezar a trabajar con
el desde el principio. Es ideal para agregar ciertas funciones rapidas a una pigi-
na Web. Una vez que se conocen las bases del lenguaje, no hay que esforzarse
mucho para crear grandes aplicaciones.
JavaScript tambien es un lenguaje muy potente de alto nivel. Es cierto que no
se puede hacer nada directamente a1 nivel de la mhquina, per0 es capaz de tra-
bajar con muchas propiedades de 10s exploradores Web, piginas Web y, a veces,
26 lntroduccion

con el propio sistema donde se ejecuta el explorador. N o necesita una fase de


compilacion como Java o C y el explorador no ha de cargar ninguna maquina
virtual para ejecutar el codigo. Solo hay que crear el codigo y cargarlo.
JavaScript tambitn utiliza una arquitectura orientada a objetos parecida a la
+
de Java o C+ . Tambih se trabaja con propiedades como las funciones del cons-
tructor o la estratificacion en jerarquias. Con lo que hay m8s opciones para uti-
lizar el codigo.

Usabilidad
JavaScript es, con diferencia, el lenguaje de programacion que mas se utiliza
en la Web. Hay publicadas millones de paginas Web que incorporan elementos
que lo usan. La mayoria de 10s exploradores Web pueden trabajar con 61 (aunque
en el caso de Microsoft estemos hablando de JScript). Y tanto Microsoft como
Netscape siguen investigando otras formas de sacar mas provecho a este len-
guaje. Gracias a estos factores podemos asegurar que la mayoria de 10s usua-
rios de Internet podran trabajar con JavaScript.

Reduccion de la carga del servidor


Esta era una de las principales razones por la que 10s desarrolladores Web han
adoptado JavaScript. Se puede hacer cargo de gran parte de las funciones del
cliente de las cuales se encargaba el servidor. Uno de 10s mejores ejemplos es la
validacion. Antiguamente, el usuario introducia la informacion de la validacion
y se la enviaba en codigo HTML a1 servidor, que por medio de CGI se aseguraba
de que todo era correcto.
Si 10s datos no tenian ningun error, el script en CGI procesaba la informacion
satisfactoriamente. Si aparecia algun error, el script devolvia u n mensaje a1
usuario indicandole la naturaleza del error que habia localizado. Para ello, el
servidor tenia que lanzar otro mensaje HTML. Y se repetia el proceso. De esta
manera, cada vez que el usuario cometia un error, se tenia que repetir todo el
proceso y esperar a que el usuario le indicase donde se encontraba el fallo.
Con JavaScript es posible validar 10s elementos antes de que el usuario se 10s
envie a1 servidor. De esta forma se reduce la cantidad de transacciones que se
efectuan a travks de http y las posibilidades de que se genere un error durante la
insercion de datos. JavaScript tambiin puede leer y escribir cookies, una opera-
cion que hasta ahora unicamente podia desarrollar el servidor Web.

JavaScriptesta creciendo
Cuando JavaScript salio a la luz, se provoc6 un gran revuelo en la comunidad
informatica debido fundamentalmente a la aparici6n de 10s objetos Imagen y de
lntroduccion 27
I

las matrices docurnento.irnagenque nos permitian crear giros de una imagen. La


version 1.2 de JavaScript fue la que entr6 con fuerza en el rnercado. Las com-
puertas estaban abiertas. Ahora 10s programadores tenian en las manos un
lenguaje de programacion capaz de trabajar con DHTML, capas y unas cuantas
mejoras sobre cualquier otro lenguaje de programacion del momento. Era de-
masiado bonito para ser realidad.
Per0 no se par6 aqui. JavaScript se habia convertido en el modelo de disefio para
EMCA-262, un lenguaje de programacion estandar. Alo menos, habia una empre-
sa que habia desarrollado un entorno el cual ejecutaba JavaScript desde la linea
de comandos. Macromedia incorporo una serie de llamadas personalizadas de
JavaScript en su tecnologia Flash. ColgFusion, de Allaire, habia integrado JavaScript
en una tecnologia basada en XML: WDDX. JavaScript esta mejorando con el
paso del tiempo. Cada vez hay mas propiedades, mas opciones y mas libros.

Es posible que no quede otra alternativa


Hay algunas ocasiones en las que no existe alternativa. Supongamos que nuestro
ISP no permite que se ejecuten scripts en CGI. iQui podemos hacer si queremos
incluir mensajes de correo que Sean generados automaticamente a partir de un
formulario o si queremos aprovechar la tecnologia cookie? Tendremos que fijar-
nos en las soluciones del lado del cliente. Posiblemente JavaScript constituya la
mejor opci6n que podemos utilizar para incluir en el lado del cliente las opcio-
nes propias de un servidor.

Per0 aun hay mas


Aun se me ocurren otras ventajas que se pueden afiadir a la lista. Per0 a pesar
de todas las ventajas de la tecnologia basada en 10s servidores, las aplicaciones
en JavaScript se han abierto un hueco en la Web.

Estrategia para la programacion en JavaScript


Siempre que se cnea una aplicacion, tanto si se utiliza JavaScript como si no,
conviene seguir una estrategia. No$ ayudara a organizar nuestros pensamien-
tos y el codigo, de tal forma que aceleraremos el desarrollo y la correccion de
errores. Hay publicaciones de renombre que desaconsejan este sistema de pro-
gramacion de aplicaciones, per0 lo normal es que el usuario trabaje con la es-
trategia que mas se adapte a su forma de ser. Asi que no nos vamos a alargar
mas en este punto, Per0 recuerde, que antes, entre o despuks de las etiquetas
< S C R I P T > < / S C R I P T > apareceran cabeceras. Para determinarlas bastara con
contestar a las preguntas iqui? i q u i h ? icomo?
28 Introduccion

Propiedades de la aplicacion
En primer lugar, i q u t va a realizar la aplicacion?Tenemos que ser muy preci-
sos. iQue no ofrecera la aplicacibn? Supongamos que queremos desarrollar un
formulario HTML para enviar un mensaje de correo electronico. Tendremos que
considerar las siguientes cuestiones:

. iCuantos campos incluira el formulario?


Los usuarios, iintroduciran ellos mismos su direccion de correo o la se-
leccionarhn de una lista?
iQueremos validar 10s datos que han sido introducidos antes del envio? En
este caso, icomo procederemos con la validacion? iActuando sobre el men-
saje? iSobre la direccion de correo? iSobre ambas?
LQuC ocurrirh despues de enviar el mensaje? iLe mostraremos a1 usuario
otra pagina o lo dejaremos tal cual?

La verdad es que podriamos seguir formulando preguntas. Lo bueno es que, si


dedicamos algo de tiempo a estas preguntas, nos haremos con una idea mas
acertada de lo que queremos hacer.

Audiencia
Es muy importante identificar quiCn va a utilizar la informacibn, puesto que
de esta forma podremos determinar las propiedades de la aplicacion. Hemos de
establecer unas respuestas muy concretas a las siguientes preguntas:
iQu6 exploradores Web utilizaran nuestros visitantes? iQut versiones:
2.x, 3.x, 4.x o superior?
iSe va a utilizar la aplicacion a travLs de Internet, de una intranet o local-
mente en ordenadores individuales?
iPodemos determinar la resolucion del monitor que tendran 10s usuarios?
iQuC tipo de conexiones tendran 10s usuarios? iUn mbdem de 56k? iUna
linea RDSI? iUna ADSL?

Es posible que a1 principio pensemos que estas preguntas no tienen nada que
ver con JavaScript. El tipo de conexibn, ipero quC mas da? No vamos a tener
que configurar un router ni nada por el estilo. Ni tenemos que contar con el cer-
tificado de Cisco. En cualquier caso, vamos a contestar una por una estas pre-
guntas y veremos que importancia tiene cada una de ellas.
El tema del explorador Web es muy interesante. Generalmente, si es muy re-
ciente podremos utilizar las ultimas versiones de JavaScript. Por ejemplo, si la
audiencia trabaja con NN 2.x y con MSIE 3.x (aunque no veo por que iban a ser
lntroduccion 29

asi las cosas), podemos automatizar el giro de las imagenes. Las versiones de
JavaScript y JScript de estos servidores no pueden trabajar con 10s objetos Ima-
gen o documento.imagen.
Como la mayoria de la gente ha actualizado la version de su explorador a 4.x,
tambitn podran trabajar con el giro de imagenes y con 10s objetos mencionados.
Es decir, que podemos hacer que nuestras versiones se ejecuten en mas de u n
tip0 de explorador Web o bien centrarnos para una version especifica.
iDonde se encuentra la aplicacion? iEn Internet, en una intranet o en el orde-
nador de alguien que lo haya convertido en una especie de kiosco? La respuesta
a esta pregunta nos darh una serie de pistas muy interesantes. Por ejemplo, si la
aplicacion se encontrara en Internet podremos estar seguros de que la gente
utilizara casi cualquier tip0 de explorador que podamos imaginar para llegar a
ella. Si la aplicacion esta restringida a una intranet o a una mhquina local, las
opciones se reducen. Es posible que casi todos 10s usuarios utilicen el mismo ex-
plorador Web. Durante la creacion de este libro, trabajt de consultor para una
filial de Microsoft. Si mis aplicaciones no funcionan en Netscape Navigator, la
verdad es que no importa, porque todos 10s usuarios trabajaran con MSIE.
Otro tema a tener en cuenta es la resolucion del monitor. Podemos incluir una
tabla de 900 pixeles de ancho y si 10s usuarios que acceden a ella unicamente
trabajan con una resolucion de 800x600 se perderan parte de 10s datos. Pero,
ipodemos establecer una resolucion fija para todos 10s usuarios? Si la aplicacion
se publicara en Internet, la respuesta es no. Per0 si vamos a trabajar a travts de
una intranet, es posible que tengamos algo mas de suerte. Algunas empresas
estandarizan el hardware, software, exploradores, monitores, e incluso tambitn,
resoluciones.
El tipo de conexion tambitn tiene su efecto sobre la aplicacion. Supongamos
que vamos a crear una animacion en la que se ven escenas correspondientes a la
ultima pelicula de Steven Spielberg. Bien, la mayor parte de 10s usuarios que se
conecten con un modem estandar de 56K posiblemente vean antes la pelicula en
el cine que el trailer en la pagina Web porque tarde demasiado tiempo en cargar-
se el cbdigo. Hay que tener en cuenta el ancho de banda a la hora de crear una
aplicacion.

iComo puedo salvar todos estos obstaculos?


Pues la verdad es que no es nada sencillo. No podemos acomodarnos a todas las
versiones de 10s exploradores Web, resoluciones de pantalla o velocidades de ac-
ceso. Entonces iqut? iComo puedo hacer que todo el mundo est6 contento con
’+
la animacion de 500K de mi pagina Web?
Vamos a ver una serie de posibles soluciones. Lo mejor que se puede hacer es
leerlas todas y luego tomar una decision.
30 lntroduccion
1 1

Exploradores cruzados
Este mCtodo igualitario basado en "lo mejor para todos" se basa en la utilizacion
de codigo comun que puedan comprender la mayoria de 10s exploradores Web.
Cuando hablo de la mayoria de 10s exploradores, me estoy refiriendo a1 MSIE 4.x
y a NN 4.x. Si hacemos que nuestra aplicacion utilice propiedades que compren-
dan ambos exploradores Web nos estaremos asegurando que la mayoria de 10s
usuarios la podran ejecutar sin ningun problema.

Una pequeiia degradacion o cambio en la efectividad


Es una especie de corolario de la estrategia que hemos visto en la seccidn ante-
rior. Por ejemplo, si el script encargado de la animacion se carga en un explora-
dor que no puede trabajar con 61, como MSIE 3.x, el usuario se encontrara con
una serie de desagradables errores de ejecucion. Se puede utilizar un sistema de
deteccion del explorador para cancelar estas cargas. Del mismo modo, se puede
utilizar para cargar la pagina que mejor se adapte a la resolucion del sistema.

Apuntar bajo
Este sistema da por sentado que todos 10s usuarios tienen un explorador NN
2.0, que trabajan con pantallas de 640x480, modems de 14.4K y su equipo es
un Pentium a 33 MHz. Las malas noticias seran que no podran utilizar ningun
c6digo de JavaScript 1 .O. Ninguna animacion, ni capas, ni expresiones regula-
res, ni tecnologias externas (eso si, por lo menos podemos utilizar cuadros). Las
buenas noticias son que la gran mayoria de la poblacion de Internet podra
utilizar la aplicacion. En realidad, 10s ultimos cambios que se han efectuado a
JavaScript no permiten afirmarlo con total seguridad. La verdad es que tengo
que admitir que he apuntado realmente bajo, per0 no es muy extraiio encon-
trarse con que gran parte de 10s navegantes de la Web trabajan con las versiones
3.x de NN y MSIE. Esta forma de trabajar, tambikn tiene sus ventajas.

Apuntar alto
Si 10s usuarios no tienen MSIE 5 .O, podremos asumir que no saben lo suficien-
te de informtitica como para utilizar nuestra aplicacion, por lo que 10s daremos
de lado. Nos pondremos a codificar, disponiendo de acceso a1 modelo de docu-
mentos de MSIE, el modelo de eventos, la construccion de datos, etc. Obviamen-
te, a1 rFchazar a una gran parte de la audiencia, nos aseguramos que la aplicacion
tardara mas tiempo en quedarse obsoleta.

Ofrecer varias versiones de la misma aplicacion


Si queremos abarcarlo todo, podemos escribir varias versiones de la misma
aplicacion. Por ejemplo, una para NN y otra para MSIE. El mCtodo es el mismo,
Introduccion 31

pero nos podemos encontrar con un problema. Volvamos a1 tema del tipo de
conexion. Como no suele ser posible determinar el tip0 de conexion que utiliza-
ran 10s usuarios, podemos hacer que Sean ellos 10s que escojan el tipo de aplica-
cion que desean ejecutar. Por medio de un par de vinculos, podemos hacer que
aquellos que dispongan de una conexion rapida, carguen 10s efectos mas espec-
taculares, mientras que el resto podra acceder a una version reducida de la mis-
ma aplicacion.

Como utilizar JavaScriptcon estas aplicaciones


Estas son las bases. En este libro hemos incluido un par de estrategias con las
aplicaciones que iremos viendo poco a poco. Por un lado hago mencion a1 siste-
ma de trabajo JavaScript, o 10s convenios de codificacion. De esta forma sabre-
mos en todo momento por donde van 10s tiros y c6mo se puede personalizar la
aplicacion.
L o primer0 que tengo en cuenta a la hora de plantearme una aplicacion es de-
terminar si 10s visitantes de mi sitio Web la van a utilizar. Todas las aplicaciones
se crean para solucionar algun tipo de problema. Buscar, enviar correo, mostrar
ayuda online, determinar las preferencias del usuario, probar, recopilar infor-
macion, crear secuencias animadas, etc. son algunas de las propiedades que quie-
ren ver 10s navegantes de Internet. Si una aplicacion no pasa las pruebas de
justificacion, no merece la pena dedicarle mas tiempo.
DespuCs tengo que decidir si JavaScript me servira para dotar a la aplicacion
de las funciones deseadas. Es muy sencillo. Si la respuesta es si, entonces empie-
zo a trabajar. Si por el contrario es no, utilizo otro lenguaje de programacion.
Una vez que perfilo estos aspectos de la aplicacion, me voy a mi editor de tex-
tos. Y aqui es donde entran en escena 10s convenios del codigo.

Reutilizar todo el codigo que sea posible


Aqui es donde utilizamos 10s archivos de codigo. Es decir, sabremos que estas
aplicaciones utilizaran archivos de codigo cuando nos encontremos con la si-
guiente sintaxis:

< S C R I P T LANGUAGE="JavaScriptl.l" SRC="algunArchivoJS.js"></SCRIPT>

Donde algunArchivoJS.js es el nombre del archivo donde se encuentran 10s script


que se utilizaran varias veces en la sintaxis. Algunas de las aplicaciones que ve-
remos a lo largo del libro utilizan archivos de codigo. Tiene sentido. iPor quC
volver a inventar la rueda? Tambitn podemos utilizar estos archivos para ocul-
tar codigo a lo largo de la aplicacion. Puede que encontremos de gran utilidad
32 lntroduccion

guardar una parte del codigo dentro de u n archivo y utilizarlo por medio de
este tip0 de llamadas. Merece la pena utilizar archivos de codigo con JavaScript.
De hecho, uno de 10s capitulos esta dedicado a ello.
Algunas aplicaciones contienen codigo que simplemente se ha copiado de un
punto y pegado en otro. Este tipo de codigo es el que se podria usar en 10s archi-
vos de 10s que hablamos, per0 he utilizado este otro sistema porque este es un
libro que trata de ensefiar, no de marear a1 lector con referencias a c6digo que se
encuentra en otro lugar de donde deberia aparecer. De esta forma, el codigo
siempre estara delante cuando se le necesite y se evita tener que estar retroce-
diendo constantemente a otros puntos del libro. Una vez que se compruebe que
la aplicacion funciona correctamente, merecera la pena plantearse la creacion
de archivos de codigo.

Aislar JavaScript
Siempre que sea posible, ajustaremos el contenido de nuestra aplicacidn a las
etiquetas <SCRIPT></SCRIPT> en vez de hacerlo a <HEAD></HEAD>.

La declaracion de las variables globales se hara al principio


Incluso en 10s casos en 10s que se vayan a usar con una cadena vacia o cuando
e s t h sin definir, la declaracion de las variables globales y 10s arrays tendra
lugar a1 principio del script. Es u n convenio que se utiliza en varios lenguajes de
programacion y que ayuda a gestionar las variables, sobre todo cuando se van
a utilizar a lo largo de todo el script. De esta manera no nos volveremos locos
cuando tengamos que cambiar alguna de ellas. Sabremos que se encuentran
situadas en la parte superior del codigo del script y las podremos localizar sin
problemas.

La declaracion de las funciones del constructor apareceran


despues de la de las variables globales
Normalmente incluyo funciones que crean objetos definidos por el usuario en
la parte superior del script. La raz6n es muy sencilla. Los objetos 10s creo a1
principio del script.

La definicion de las funciones se hara de arriba hacia abajo,


siguiendo un orden cronologico
En otras palabras, intento definir las funciones con el mismo orden en que se
las llamara desde la aplicacion. A la primera de ellas la llamo first (primera); a la
segunda, segunda (second), etc. Hay veces en las que este sistema es totalmente
lntroduccion 33

inviable. Sin embargo lo utilizo siempre que puedo, porque mejora la organiza-
cion del scipt y las posibilidades de que las llamadas a las funciones sigan un
orden determinado.

Cada funcion desarrollara una unica operacion


Intento limitar la finalidad de cada funcion a una operacion independiente,
como por ejemplo, validar 10s datos que ha introducido el usuario, hacerse con
la configuracion de una cookie, automatizar la representacion de imagenes en
pantalla u ocultar capas. Pero, como de costumbre, una cosa es la teoria y otra
muy distinta, la practica. En uno de 10s capitulos veremos una serie de excep-
ciones a esta regla. Hay ocasiones en las que, para que una funcion desarrolle
una unica operacion, hay que dedicarles varias decenas de lineas.

Se utilizaran tantas variables locales corn0 sea posible


Lo hago para ahorrar memoria. Como las variables de JavaScript se eliminan
en el momento en que finaliza la ejecucion de su funcion, el sistema recupera la
memoria que les habia asignado. Si una variable aun se utilizara mas veces a lo
largo de una aplicacion, entonces la hart global.

Avanzar
Bien, con todos estos puntos ya nos hemos hecho una idea de la estructura que
tendran las aplicaciones en JavaScript. Per0 ha llegado la hora de empezar a
divertirse.
Capitulo 1 -
Motor de busqueda para el cliente

Todos 10s sitios Web pueden trabajar con un motor de busqueda pero, ipor qut
cargar a1 servidor con todas las consultas? El Motor de busqueda para el cliente
permite que 10s usuarios puedan buscar a traves de las paginas Web sin necesi-
tar para nada a1 servidor Web. En vez de enviar las peticiones a una base de
datos o a un servidor de aplicaciones, cada usuario descarga una base de datos
con las paginas Web solicitadas. Dicha base de datos no es otra cosa que un
array en JavaScript. Cada registro se guardara en un elemento del array.
Esta forma de trabajar tiene algunas ventajas, como por ejemplo la reduction
de la carga de trabajo del servidor y una mejora en el tiempo de respuesta. Asi de
sencillo. Hemos de recordar que las limitaciones de la aplicacion dependeran
unicamente de 10s recursos del sistema del usuario, sobre todo de la velocidad
del procesador y de la cantidad de memoria libre. En cualquier caso, puede
tratarse de una gran utilidad para u n sitio Web. En la figura 1.1 se puede ver la
primera pantalla de la interfaz.
La aplicacion cuenta con dos metodos booleanos de busqueda: AND y OR.
Podemos buscar 10s documentos a partir de su titulo y descripcion, o bien por el
URL. Su manejo es muy sencillo. Basta con introducir 10s ttrminos que se quie-
ren localizar y pulsar Intro. Este es el desglose de la opci6n de busqueda:
Cuando se introduzcan tkrminos separados por espacios, la aplicacion
mostrara todos 10s registros que contengan cualquiera de las palabras
deseadas (se utiliza el booleano OR).
+
Si se coloca un signo delante de la cadena de consulta, se obtendran 10s
registros en 10s que aparezcan todos 10s tkrminos especificados (booleano
AND).
36 Motor d e busqueda para el cliente

Si se preceden 10s tkrminos deseados por url:, el registro mostrara la


direccion (total o parcial) de 10s registros que contengan cualquiera de 10s
tkrminos deseados.

Client-Side Search Engine I

To search... Enter... For example..

Any terms words with spaces JavaScript application


All terms +[words with spaces] +JavaScrlptapplication
By URL url:[url portions with spaces] ur1:hotsyteweb

Case does not matter.

Figura 1 . 1 . Pagina principal de la interfaz

En la figura 1.2 tenemos 10s resultados que se han obtenido a1 desarrollar una
busqueda. Se ha utilizado el sistema predeterminado (sin prefijos) y el tkrmino
que se ha buscado ha sido javascript. Todas las busquedas generan sobre la
marcha una pagina de resultados donde se muestran 10s resultados de las ulti-
mas busquedos, seguida por un enlace a una pagina de ayuda.
Tambikn se pueden buscar documentos por su URL. La figura 1 . 3 nos muestra
una busqueda donde se ha utilizado el prefijo url: para indicarle a1 motor de
busqueda que unicamente ha de mostrar 10s documentos que tengan html en su
URL. Aunque tambikn se incluira la descripcion del documento, lo primero que
aparecera en la pantalla sera el URL (el localizador del documento). Este sistema
unicamente permite busquedas sencillas. Per0 no ha de preocuparnos, porque la
mayoria de 10s usuarios se contentaran con eso. N o hay mucha gente que uti-
lice algoritmos para complicar las opciones de la busqueda.
La aplicacion puede limitar el numero de resultados que mostrara por pagina
y crear botones para acceder a la pagina anterior o posterior a la de resultados.
De esta forma, se mejorara el aspect0 de la presentacion y el usuario no se
perdera entre tantos registros. El numero de documentos que apareceran por
pagina dependera de nosotros. Por defect0 se mostraran 10.
Motor de busqueda para el cliente 37

ena5uipr &@
Cbent-Side Search Engine

Search Puer/: javasctfpr


-
Seirch Resub: 1 10 of 37

Figura 1.2. Pagina de resultados de una busqueda tipica

Requisitos para la ejecucion


La version de la aplicaci6n de la que hablaremos en las siguientes paginas
requiere un explorador que pueda trabajar con JavaScript 1.1. Es decir, todos
10s usuarios que trabajen con Netscape Navigator 3 y 4 y Microsoft Internet
Explorer 4 y 5 podran ejecutarla. Los usuarios de IE 3 , no. Si le preocupa la
compatibilidad con todas las versiones, no se preocupe. Veremos quC podemos
hacer para que 10s usuarios de IE 3 tambien puedan utilizar esta aplicacion (con
la consecuente perdida de funcionalidad, claro) en el apartado "Posibles aplica-
ciones" de este capitulo.
Todas las aplicaciones de cliente dependen de 10s recursos de la maquina del usua-
rio, un hecho que es especialmente cierto en este caso. Es cierto que daremos por
supuesto que el usuario dispone de 10s suficientes recursos del sistema como para
ejecutar la aplicacion sin ningun problema, pero en el momento en que tenga mas
de 6.000 6 7.000 registros en la base de datos, Vera como se reduce considerable-
mente el rendimiento de la aplicacion y parecera que se cuelga la miiquina.
Sinceramente, yo no he tenido ningun problema con la aplicaci6n y he traba-
jado con cerca de 10.000 registros, con MSIE 4 y Navigator 4. El archivo de
38 Motor de busqueda para el cliente

codigo ocupaba mas de 1 MB y lo he probado con diferentes cantidades de


memoria RAM (entre 24 y 128 MB). Intent6 lo mismo con NN 3.0 Gold y me
encontr6 con un error por sobrecarga del sistema (demasiadosregistros para un
array).

ud hfml
ClientSide Seuch Engine

Search Query: uri. hnnl


Search Resuttr: 1 - 10 of 38

Figura 1.3. Pagina de resultados basados en la busqueda de registros URL


Por otro lado, utilice la version 1.O de JavaScript con MSIE 3.02 en un ThinkPad
de IBM y no pude trabajar con mas de 250 registros. El portatil era tan lento
que parecia que el mecanismo que hacia girar el disco duro era un hamster
corriendo dentro de una noria. Pero normalmente, la mayoria de 10s usuarios
contaran con mejores recursos que 10s vistos en este caso.

Analisis de la sintaxis
Esta aplicacion se compone de tres archivos HTML (index.htrnZ, nav.htrnZ y
rnain.htrnZ) y de un archivo fuente en JavaScript (records.js).Los tres archivos
HTML incluyen una cabecera, donde se introducen 10s ttrminos que se quieren
buscar, una zona de trabajo y una pagina predeterminada con las instrucciones
de uso.
M o t o r de busqueda para el cliente 39

nav.htmI
El corazon de la aplicacion se encuentra dentro del archivo navhtrnl. De hecho,
ademas de este archivo, el unico sitio donde veremos algo de JavaScript sera en
las paginas de resultados que se crearan sobre la marcha. Vamos a echar un
vistazo a1 codigo. Lo tenemos en el ejemplo 1.1.
Ejemplo 1.1. Codigo fuente del archivo nav.htm1

1 <HTML>
2 <HEAD>
3 <TITLE>Search Nav Page</TITLE>
4
5 <SCRIPT LANGUAGE="JavaScriptl.1" SRC="records.js"></SCRIPT>
6 <SCRIPT LANGUAGE="JavaScriptl.I">
7 <!-
8
9 var SEARCHANY = 1;
10 var SEARCHALL = 2;
11 var SEARCHURL = 4;
12 var searchType = ";
13 var showMatches = 10;
14 var currentMatch = 0;
15 var copyArray ' = new Array ( ) ;
16 var docObj = parent.frames[ll.document;
17
1 8 function validate(entry) {
19 if (entry.charAt(0)== " + " ) {
20 entry = entry.substring(1,entry.length);
21 searchType = SEARCHALL;
22 I
23 else if (entry.substring(0,4)== "url:") {
24 entry = entry.substring(5,entry.length);
25 searchType = SEARCHURL;
26 I
27 else { searchType = SEARCHANY; I
28 while (entry.charAt(0) == ' ' ) {
29 entry = entry.substring(1,entry.length);
30 document.forms[O].query.value = entry;
31 I
32 while (entry.charAt(entry.length - 1) == ' {
33 entry = entry.substring(0,entry.length - 1);
34 document.forms[O].query.value = entry;
35 I
36 if (entry.length < 3 ) I
37 alert("You cannot search strings that small. Elaborate a
little.");
38 document. forms [ O 1 .query.focus ( ) ;
39 return;
40 I
40 Motor de busqueda para el cliente

41 convertString(entry);
42 }
43
44 function convertString(reentry) {
45 var searchArray = reentry.split( " " ) ;
46 if (searchType == (SEARCHANY I SEARCHALL)) {
requireAll(searchArray); 1
47 else ( allowAny(searchArray); }
48 1
49
50 function allowAny(t) (
51 var findings = new Array(0);
52 for (i = 0 ; i < profiles.length; i++) {
53 var compareElement = profiles[i].toUpperCase();
54 if(searchType == SEARCHANY) {
55 var refineElement = compareElement.substring(0,
56 compareElement.indexOf(' IHTTP'));
57 1
58 else (
59 var refineElement =
60 compareElement.substring(compareElement.index0f >IHTTP'),
61 compareElement.1ength);
62 1
63 for ( j = 0; j < t.length; j++) (
64 var comparestring = t[jl.toUpperCaseO;
65 if (refineElement.indexOf(compareString)! = -1)
66 findings[findings.lengthl = profiles[il;
67 break;
68 1
69 1
70 1
71 verifyManage(findings);
72 1
73
74 function requireAll(t) {
75 var findings = new Array();
76 for (i = 0; i < profiles.length; i++) I
71 var allconfirmation = true;
78 var allstring = profiles[il.toUpperCaseO;
79 var refineAllString = allString.substring(0,
80 allString.indexOf('IHTTP'));
81 for (j = 0; j < t.length; j + + ) (
82 var allElement = t[jl.toUpperCaseO;
83 if (refineAllString.indexOf(al1Element) == -1) {
84 allconfirmation = false;
85 continue;
86 1
87 1
88 if (allconfirmation) I
89 findings[findings.length] = profiles[il;
90 1
Motor de busqueda para el cliente 41

91 }
92 verifyManage(findings);
93
94
95 function verifyManage(resu1tSet) {
96 if (resultSet.length == 0) ( noMatch0; }
97 else {
98 copyArray = resultSet.sort0;
99 formatResults(copyArray, currentMatch, showMatches);
100 }
101 1
102
103 function noMatch0 {
104 docObj.open0;
105 docObj.writeln('<HTML><HEAD><TITLE>SearchResults</TITLE>
</HEAD> +
106 '<BODY BGCOLOR=WHITE TEXT=BLACK>' +
107 'ITABLE WIDTH=90% BORDER=O ALIGN=CENTER><TR><TD VALIGN=TOP>'+
108 '<FONTFACE=Arial><B><DL>' +
109 '<HR NOSHADE WIDTH=100%>"' + document.forms[01 .query.value +
110 "' returned no results.<HR NOSHADE WIDTH=100%>' +
111 '</TD></TR></TABLE></BODY></~TML>');
112 docObj .close0 ;
113 document.forms[Ol.query.selectO;
114 I
115
116 function formatResults(results, reference, offset)
117 var currentRecord = (results.length < reference + offset ?
118 results.length : reference + offset);
119 docObj.open0;
120 docObj.writeln('<HTML>\n<HEAD>\n<TITLE>Search Results</TITLE>\n
</HEAD>' +
121 '<BODY BGCOLOR=WHITE TEXT=BLACK>' +
122 '<TABLE WIDTH=9O% BORDER=O ALIGN=CENTER CELLPADDING=3><TR><TD>' +
123 '<HR NOSHADE W I D T H = l O O % > < / T D > < / T R > < T R 2 < T D VALIGN=TOP>' +
124 '<FONT FACE=Arial><B>Search Query: <I>' +
125 parent.frames[O].document.forms[O].query.value+ '</I><BR>\n'+
126 'Search Results: <I>' + (reference + 1) + ' - ' +
127 currentRecord + of ' + results.length + '</I><BR><BR></FONT>'+
128 '<FONT FACE=Arial SIZE=-l><B>' +
129 '\n\n<!--Begin result set //-->\n\n\t<DL>');
130 if (searchType == SEARCHURL) {
131 for (var i = reference; i < currentRecord; i++) (
132 var divide = results[il.split("~");
133 docObj.writeln('\t<DT>'+ '<A HREF="' + divideL21 + "'>I +
134 divide[2] + '</A>\t<DD>'+ '<I>' + divide[l] +
'</I><P>\n\n');
135 I
136 }
137 else {
138 for (var i = reference; i < currentRecord; i + + ) {
42 Motor de busqueda para el cliente

139 var divide = results[il .split('1 ' ) ;


140 docObj .writeln('\n\n\t<DT>'+ ' < A HREF="' + dividela] + " ' > ' +
141 divide[O] + '</A>\t<DD>'+ '<I>'+ divide[l] + '</I><P>');
142 1
143 1
144 docObj.writeln('\n\t</DL>\n\n<!--End result set //-->\n\n');
145 p r e v N e x t R e s u l t s ( r e s u I t s . l e n g t h , reference, offset);
146 docObj.writeln('<HRNOSHADE WIDTH=100%>' +
147 '</TD>\n</TR>\n</TABLE>\n</BODY>\n</HTML>');
148 docobj .close( ) ;
149 document.forms[Ol.query.selectO;
150 )
151
152 function prevNextResults(ceiling, reference, offset) {
153 docObj.writeln('<CENTER><FORM>');
154 if(reference > 0 ) t
155 docObj.writeln('iINPUTTYPE=BUTTON VALUE="Prev ' + Offset +
156 ' Results" ' +
157 'onClick="parent.frames[O].formatResults(parent.frames[0].copyArray,
158 ' + (reference - offset) + ' , ' + offset + ' ) " > ' ) ;
159 }
160 if(reference >= 0 && reference + offset < ceiling) {
161 var trueTop = ((ceiling - (offset + reference) < offset) ?
162 ceiling - (reference + offset) : offset);
163 var howMany = (trueTop > 1 ? " s " : " " ) ;
164 docObj.writeln('<INPUTTYPE=BUTTON VALUE="Next + trueTop +
165 ' Result' + howMany + "' ' +
166 'onClick="parent.frames[O] .formatResults(parent.frames[O]
167 .copyArray, +(reference + offset) + ' , ' + offset + ' ) " > ' ) ;
168 }
169 docObj.writeln('</CENTER>');
170 )
171
172 / / - - >
173 </SCRIPT>
174 </HEAD>
175 <BODY BGCOLOR="WHITE">
176 <TABLE WIDTH="95% '' BORDER="0 " ALIGN="CENTER">
177 <TR>
178 <TD VALIGN=MIDDLE>
179 <FONT FACE="Arial">
180 <B>Client-Side Search Engine</B>
181 </TD>
182
183 <TD VALIGN=ABSMIDDLE>
184 <FORM NAME="search"
185 onsubmit="validate(document.forms [O].query.value); return false;" >
186 <INPUT TYPE=TEXT NAME= query" SIZE=" 3 3 " >
I'

187 <INPUT TYPE=HIDEN NAME="stadin" VALUE="">


188 </FORM>
189 </TD>
190
Motor de busqueda para el cliente 43

191 <TD VALIGN=ABSMIDDLE>


192 <FONT FACE="Arial">
193 < B > < A HREF="main.html" TARGET="main">Help</A></B>
194 </TD>
195 </TR>
196 </TABLE>
197 </BODY>
198 </HTML>

Hay mucho codigo. Per0 es muy fhcil comprender quC hace. Empezaremos
desde arriba e iremos bajando poco a poco. Afortunadamente, el codigo se ha
escrito para que ejecute una funcibn detras de otras siguiendo mas o menos el
mismo orden.
Las examinaremos en el siguiente orden:

El archivo de codigo fuente records.js.


Las variables globales.
Las funciones
El codigo HTML

records.js
El primer elemento que veremos sera el archivo de codigo fuente en JavaScript,
record.js. En la linea 5 tenemos la etiqueta < S C R I P T > .
Contiene un conjunto de elementos denominados perfiles. Vamos a suprimir su
contenido, aunque se ha de ver como una unidad. Asi que, despuks de
descomprimir el contenido del archivo ZIP que se puede bajar de la direccion de
Internet que hemos comentado con anterioridad, abriremos nuestro editor de
texto y cargaremos el archivo recordsjs. Hemos de tener cuidado, porque se
trata de nuestra base de datos. Cada uno de 10s elementos forma parte de una
cadena de tres eslabones. Por ejemplo:

"http://www.serve.com/hotsyteIHotSyte-The JavaScript ResourcelThe " +


"Hotstyle home page featuring links, tutorials, free scipts, and more"

Las distintas partes del registro estan separadas por una barra vertical (I).
Estos caracteres son de gran utilidad cuando imprimimos el contenido del regis-
tro en la pantalla. La segunda parte es el titulo del documento (no tiene nada
que ver con el contenido de las etiquetas TITLE) y la tercera es la descripcion del
documento. La primera, que no hemos comentado, es el URL.
Por cierto, no hay ninguna regla que impida utilizar otro caracter que no sea
" I " para separar las partes del registro. Per0 hemos de asegurarnos de seleccio-

nar u n caracter que no vaya a utilizar el usuario como parte de la cadena de


44 Motor de busqueda para el cliente

clonsulta (por ejemplo, & " o -[%). No conviene utilizar la barra invertida.
JavaScript la interpreta como el caracter Esc y mostrara unos resultados bas-
tante extrafios.
LPor quk se incluye todo este material dentro del archivo de c6digo fuente?
Por dos razones: modularidad y limpieza. Si el sitio web tiene varios cientos de
paginas Web, es posible que queramos disponer de un programa que genere el
c6digo que contiene todos 10s registros. En cualquier caso, siempre estara mejor
organizado si se encarga de su generation u n archivo codigo fuente en
JavaScript.
Podemos utilizar esta base de datos con otras aplicaciones de bdsqueda. Todo
lo que hemos de hacer es incluir el archivo records.js dentro del codigo. Ademas,
de esta forma evitaremos mostrar el codigo en el archivo HTML.

Las variables globales


Las lineas 9 a 16 del ejemplo 1.1 declaran e inician las variables globales.

var SEARCHANY = 1;
var SEARCHALL = 2;
var SEARCHURL = 4;
var searchType = " ;
var showMatches = 10;
var currentMatch = 0;
var copyArray = new Array( ) ;
var docObj = parent.frames [ 1 I .document;
46 M o t o r de busaueda Dara el cliente

currenma tch
Determina quC registro aparecera el primero en la pagina de resultados.
copyArray
Copia el array temporal de registros usados para mostrar 10s siguientes o an-
teriores resultados.
docObj
Variable que se refiere a1 objeto del documento del segundo cuadro. No tiene
demasiada importancia para la aplicacibn, per0 ayuda a controlar el codigo
porque podremos acceder a1 objeto (parent, frames [ 11 . document) varias
veces durante la impresion de resultados. docObj se refiere a este objeto, con lo
que se reduce la cantidad de codigo. Ademas se puede actuar sobre 61 para efec-
tuar modificaciones.

Las funciones
A continuation vamos a ver las funciones mas importantes:

validate()
Cuando el usuario pulsa Intro, la funcion validate ( ) de la linea 18 determi-
na quC quiere encontrar el usuario y procede a buscarlo. N o olvidemos estas
tres opciones:

Buscar el titulo y la descripcion del documento donde aparezca cualquiera


de 10s tCrminqs solicitados.
Buscar el titulo y la descripcion del documento donde aparezcan todos 10s
tkrminos solicitados.
Buscar el URL o ruta del documento donde aparezca cualquiera de 10s
tkrminos solicitados.

validate ( ) determina quC se ha de localizar y como, evaluando 10s prime-


ros caracteres de la cadena recibida. iComo se especifica el metodo a utilizar?
Con la variable searchType. Si el usuario quiere localizar todos 10s tkrminos, el
valor de la variable sera SEARCHALL. Si el usuario unicamente quiere el titulo y
la descripcion del documento, validate ( ) asignarh a todo el valorfalse (con-
figuration predeterminada). Si quiere localizar el URL, el valor de searchType
sera cero. Asi es como suceden las cosas:
La linea 19 muestra el metodo charAt ( ) del objetostring que busca el signo +
como primer caracter. Si lo encuentra, el mCtodo de busqueda pasara a la se-
gunda opcion (la que trabaja con el booleano AND).
Motor de busaueda Dara el cliente 47

if (entry.charAt(0) == " c " ) {


entry = entry.substring(1,entry.length);
\
searchType = SEARCHALL;
1

L a linea 23 muestra el mCtodo substring ( ) del objeto String que busca la


cadena "url :". Si la encuentra, configurara searchType como corresponda:

if (entry.substring(0,4)== "url:") {
entry = entry.substring(5,entry.length);
searchType = SEARCHURL;
I

iY quC pasa con 10s mCtodos substring ( ) que se encuentran entre las lineas
20 y 24? Bien, despuCs de que validate ( ) sepa quk tiene que localizar y como
ha de hacerlo, estos indicadores (+ y url:) dejaran de ser necesarios. Por lo
tanto, validate ( ) elimina el numero de caracteres necesarios del inicio de la
cadena y continua trabajando.
+
Si en el inicio de la cadena no hay ningun ni url:, validate ( ) asignara el
valor SEARCHANY a la variable searchType y procede a limpiar la cadena antes de
llamara a convertstring ( 1 . Las declaraciones while que se encuentran entre
las lineas 28 y 32 eliminan 10s espacios en blanco que se encuentran a1 principio
y a1 final de la cadena.
DespuCs de determinar las preferencias del usuario y eliminar 10s espacios en
blanco sobrantes, validate ( ) se tiene que asegurar que queda algo dentro de
la cadena de busqueda. En la linea 36 se procede a la verificacion. Mira si la
cadena de busqueda contiene, por lo menos, tres caracteres. Si hay menos, no se
generara ningun resultado. De todas formas, siempre podemos modificar esta
configuracion :

if (entry.length < 3 ) {
alert("You cannot search strings that small. Elaborate a little.");
document.forms[0l.query.focus~);
return;
I

Si todo va bien, validate ( ) llamara a convertstring ( ) y le entregara una


copia limpia de la cadena de busqueda (entry).
convertstring0
convertstring ( ) se encarga de dos operaciones relacionadas entre si: divide
la cadena en 10s elementos del array y llama a la funcion apropiada de busque-
da. El mCtodo split ( ) del objeto String divide la cadena de busqueda del usua-
48 Motor de busqueda para el cliente

rio sirviendose de 10s espacios en blanco y guarda el resultado dentro del array
searchArray. Esto tiene lugar en la linea 45:

var searchArray = reentry. split ( '' '' ) ;

Por ejemplo, si el usuario introduce la cadena "desarrollo JavaScript en cliente"


dentro del campo de busqueda, searchArray contendra 10s valores desarro-
110, JavaScript, en y cliente para 10s elementos 0, 1 , 2 y 3 reSpeCtiVa-
mente. convertstring ( ) llamara a1 metodo de busqueda adecuado de acuerdo
con el valor de all. L o podemos ver en las lineas 46 y 47:

if (searchType == (SEARCHANY I SEARCHALL)) { requireAll(searchArray); )


else { allowAny(searchArray); }

Como podemos ver, se llama a una de las dos funciones. El comportamiento de


ambas es muy parecido, per0 tiene sus diferencias. Vamos a ver con detenimiento
ambas funciones: allowArray ( ) y requireall ( 1 .
allowArray()
A esta funcion se la llama cuando la aplicacion unicamente ha encontrado un
documento, es decir, tiene el numero minimo de aciertos requeridos. Este es el
contenido de las lineas 50 a 68:

function allowAny(t) {
var findings = new A r r a y ( 0 ) ;
for ( i = 0; i < profiles.length; i + + ) {
var compareElement = profiles [ i 1 . touppercase ( ) ;
if(searchType == SEARCHANY) {
var refineElement = compareElement.substring(0,
compareElement.indexOf('IHTTP')) ;
}
else {
var refineElement =
compareElement.substring(compareElement.indexOf('IHTTP'),
compareElement.1ength);
}
for ( j = 0 ; j < t.length; j + + ) {
var comparestring = t[jl.toUpperCaseO;
i f (refineElement.indexOf(compareString) ! = - 1 )
findings[findings.lengthl = profiles[il;
break;
1

Lo que se esconde dentro de las tres funciones de busqueda son una serie de
cadenas de comparacion anidadas por medio de buclesfor. El primero se encarga
Motor de blisqueda para el cliente 49

de repetir cada uno de 10s elementos del array profiles (desde el archivo de codi-
go fuente). Para cada uno de ellos, el segundo bucle hace lo propio con 10s
tkrminos de la peticion que recibe de convertstring( .
Para asegurarse de que el usuario no pierde documentos por haber escrito 10s
tkrminos en mayusculas o minusculas, en las lineas 5 3 y 64 se declaran las
variables,locales compareElernent y cornparestring, respectivamente, y se inicia la
version en mayusculas de cada uno de 10s registros de la cadena. Ahora, no
importara que el usuario escriba “JavaScript”, ’2avascript” o ”jAvasCRIpt”.
alloyArray ( ) aun tiene que determinar si buscar5 el titulo y descripcion del
documento o se basara unicamente en el URL. Asi que la variable local
refineElement, la subcadena que se comparara con 10s terminos de la peticih, se
configurara de acuerdo con el valor de searchvpe (lineas 55 6 5 9 ) . Si searchType
es igual a SEARCHANT, el valor de refineElernent sera el de la subcadena que
contenga el titulo y descripcion del documento. Por el contrario, si searchType es
igual a SEARCHURL, el valor de reJneElernent sera tal que unicamente mostrara
el URL del documento.
LRecuerda 10s simbolos ? Gracias a ellos JavaScript distingue las diferentes
partes del registro. Asi, el mktodo substring( ) devuelve una cadena que em-
pieza en 0 y termina en el caracter que se encuentra antes del primer ‘‘ I HTTP”,
o bien devuelve una cadena que empieza con el primer I HTTP” y termina en el
I‘

ultimo elemento. Ahora comparamos lo que tenemos con 10s valores que ha
introducido el usuario. Miramos la linea 6 5 :

if (refineElement.indexOf(compareString)! = -1) {
findings[findings.lengthl = profiles[il;
break;
}

Si compareString se encuentra dentro de refineElernent, tendremos un resulta-


do. Se afiade registro original (no la version reducida en la que unicamente se
muestra el URL) a1 array findings que se encuentra en la linea 6 6 . Podemos
utilizar findings. length como indexador para seguir asignando elementos.
En el momento en que encontramos un documento que cumple lo establecido
en 10s criterios de la busqueda, no habra que continuar comparandolo con el
resto de tkrminos. En la linea 6 7 tenemos la declaracion que se encarga de dete-
ner el bucle de comparacion cuando se localiza un documento. L a verdad es que
no es necesario, per0 reduce la cantidad de proceso.
Despuks de repetir el proceso con todos 10s registros y terminos de busqueda,
allowAny ( ) entrega 10s resultados que ha guardado en el array findings a la
funcion ver if yManage ( ) (lineas 95 a 101 ). Si la busqueda tuvo kxito, la funcion
f ormatResu1ts ( ) se encargara de imprimir 10s resultados. Si no es asi, la fun-
50 Motor de busqueda para el cliente

cion noMatch ( ) se encargara de comunicar a1 usuario que su busqueda no tuvo


kxito. Las funciones formatResults ( ) y noMatch( ) las veremos mas tarde.
Vamos a terminar el analisis de 10s metodos de busqueda con requireAll( ) .
requireAll()
+
Al colocar el simbolo delante de 10s tCrminos que se quieren encontrar hare-
mos que sea la funcion requireAll( ) quien se haga cargo del proceso. Su
funcionamiento es idkntico a allowAny ( ) , con la salvedad que el documento
tendra que contener todos 10s tCrminos especificados por el usuario. Con
allowAny ( ) , 10s registros se aiiadian a1 conjunto de resultados en el momento
en que se veia que contenia alguno de 10s terminos solicitados. Con esta fun-
cion, tendremos que esperar a que se complete la comparacion de todos 10s
terminos, antes de aiiadirlo a1 conjunto de resultados. La funci6n empieza en la
linea 74.

function requireAll(t) (
var findings = new Array();
for (i = 0 ; i < profiles.length; i++) {
var allconfirmation = true;
var allstring = profiles[il .touppercase();
var refineAllString = allString.substring(0,
allString.indexOf(‘ IHTTP‘)) ;
for (j = 0; j < t.length; j++) (
var allElement = t[jl.toUpperCase();
if (refineA11String.indexOf(allElement) = = -1) {
allconfirmation = false;
continue ;
1
1
if (allconfirmation) (
findings[findings.length] = profiles[i];
}
I
verifyManage(findings);
}

A primera vista, las cosas se parecen mucho a allowAny(). Los buclesfor ani-
dados, la conversion a mayusculas y la variable de confirmation. Todo aparece
aqui otra vez. Per0 las cosas cambian en las lineas 79-80:

var refineAllString = allString.substring(0,


allString.indexOf(’IHTTP’));

ObsCrvese que la variable searchType no se ha comprobado para determinar si


se continuara con la comparacih de elementos, tal y como pasaba en la linea
50 con la funcion allowAny ( ) . No hace falta. So10 se llamara a requireAl1 ( )
Motor de blisqueda para el cliente 51

si el valor de la variable searchType es SEARCHALL (linea 46). En la busqueda del


URL no se incluye el metodo booleano AND, por lo que se procedera con la
comparacion del titulo y la descripcion del documento.
La funcion requireAll( ) es algo mas molesta. Como todos 10s tkrminos que
introduce el usuario, se tiene que encontrar dentro de la cadena de comparacion
y la logica que se seguira en el proceso tendrh que ser mas restrictiva que la
vista en allowAny ( 1. Veamos las lineas 83 a 86:

i f (refineA11String.indexOf(allElement) == -1) {
allconfirmation = false;
continue ;
I

Sera mas sencillo rechazar el documento en el momento en que se viese que


no contiene el primer elemento de la cadena, en vez de proceder a comparar el
resto del documento. Por lo tanto, en el momento en que se vea que se da esta
situacion, la declaraci6n continue le dice a JavaScript que pase a1 siguiente
registro.
Si se han comparado todos 10s documentos con 10s registros y el valor de la
variable aZZConfirmation sigue siendo verdadero, habremos encontrado un re-
sultado. El valor de aZZConfirmation sera falso cuando el registro no coincida con
el primer tCrmino. El registro se aiiade a1 array temporalfindings de la linea 89.
Esta condicion es mas complicada de conseguir, per0 10s resultados que se ob-
tendran de la busqueda seran mas especificos.
Una vez que se complete la evaluation de todos 10s registros, se entregara
findings a verifyManagement ( ) para comprobar 10s resultados. Si hay algu-
no, se llamara a formatResults ( ) . Si no es mi, requireAll ( ) h n a r a a
noMatch ( ) para que informe de las malas noticias a1 usuario.
52 Motor de busqueda para el cliente
54 Motor de busqueda para el cliente

copyArray = resultSet.sort0;
formatResults(copyArray, currentMatch, showMatches);
1

Tanto allowAny ( ) y r e q u i r e A l l ( ) llaman a verifyManage ( ) despuks de


su ejecucidn y entregan el contenido de findings como un argumento. En la
linea 96 podemos ver que v e r i fyManage ( llamara a la funcion noMatch (
cuando el array resultset (una copia de findings) no contenga nada.
Per0 si resultset contiene por lo menos un registro, a la variable globalcopyArray
se le asignara la version de todos 10s elementos de resultset. No hace falta orde-
narlos, per0 siempre conviene (por cuestiones de presencia) mostrar el conjunto
de resultados con algo de orden. Ademas, asi no tendremos que preocuparnos
por el orden en que se colocan 10s registros en el array profiles. Podemos seguir
aiiadikndolos hasta el final con la tranquilidad de que se ordenaran a1 final.
Entonces, ipor quk tenemos que hacer otra copia de 10s registros? Recordemos
quefindings es un array local y por lo tanto temporal. En el momento en que se
concluye la busqueda (es decir, cuando la aplicacion ejecuta una de las funcio-
nes de busqueda), se elimina findings y se liberara memoria. No hay ninguna
razon para congelar una parte de la memoria que se puede utilizar en otra cosa.
Per0 si que necesitaremos acceder mas tarde a esos registros.
Como la aplicacion mostrara (por ejemplo) 10 registros por pagina, el usuario
unicamente Vera una parte de 10s resultados. La variable copyArray es global, asi
que, a1 ordenar 10s resultados temporales y asignarlos a copyArray, dejaremos
intactos 10s registros. Ahora 10s usuarios podran ver 10, 15 resultados a la vez,
o la cantidad que quiera. Esta variable global guardara 10s resultados hasta que
el usuario haga una nueva busqueda.
La ultima accion que desarrollaverifyManage ( es llamar a f ormatResu1ts ( 1,
analizando un numero de indice (currentMatch), indicando cual sera el primer
registro y la cantidad de ellos que mostrara por pagina (showMatches).Tanto
currentMatch como showMatches son variables globales. No se eliminan despuks
de que finalice la ejecucion de la funcion. Las necesitaremos mas adelante.
noMatch()
Si no se obtiene ningun resultado, noMatch ( ) sera quien se encargue de darle
las malas noticias a1 usuario. En nuestro ejemplo se mostrara una pagina donde
le indicara a1 usuario que su busqueda no ha dado ningun resultado. Per0 se
puede personalizar. Empieza en la linea 103:

function noMatch ( ) {
docObj . open ( ) ;
docObj.writeln('<HTML><HEAD><TITLE>SearchResults</TITLE></HEAD>'t
'<BODY BGCOLOR=WHITE TEXT=BLACK>' +
Motor de bljsqueda para el cliente 55

‘<TABLE WIDTH=90% BORDER=O ALIGN=CENTER><TR><TDVALIGN=TOP>’i


‘<FONT FACE=Arial><B><DL>‘ +
‘<HR NOSHADE WIDTH=100%>”’ + docurnent.forms[0l.query.value +
‘‘ returned no results.<HR NOSHADE WIDTH=100%>’ +
‘</TD></TR>c/TABLE></BODY></HTML>’);
docObj .close( ) ;
document.forms[O].query.select~);
>

formatResults()
El trabajo de esta funci6n es mostrar 10s resultados a1 usuario con un formato
agradable. No es nada complicado, per0 se encarga de hacer bastantes cosas.
Estos son 10s ingredientes con 10s que trabaja para escribir 10s resultados en la
pantalla:
Una cabecera HTML, un titulo y un cuerpo de texto.
El titulo, descripcih y URL de cada uno de 10s registros, ademas de un
enlace a1 URL de cada registro.
Botones para avanzar y retroceder a 10s registros posteriores y anteriores
(respectivamente), siempre que Sean necesarios, claro.

Cabecera HTML y el titulo


La cabecera HTML y el titulo no representan ninguna dificultad. Las lineas
comprendidas entre la 116 y la 129 se encargan de la impresion de la cabecera,
titulo e inician 10s contenidos del cuerpo del texto:

function formatResults(results, reference, offset) {


var currentRecord = (results.length < reference + offset ?
results.length : reference + offset):
docObj .open( ) :
docObj.writeln(‘<HTML>\n<HEAD>\n<TITLE>Search Results</TITLE>\n</
HEAD>‘ + ‘<BODY BGCOLOR=WHITE TEXT=BLACK>’ +
‘<TABLE WIDTH=90% BORDER=O ALIGN=CENTER CELLPADDING=3><TR><TD>‘+
‘<HR NOSHADE WIDTH=lOO%></TD></TR><TR><TD VALIGN=TOP>‘ +
‘<FONT FACE=Arial><B>SearchQuery: <I>‘ +
parent.frames[O].docurnent.forms[O].query.value + ‘</I><BR>\n’+
‘Search Results: <I>‘ + (reference + 1) + ‘ - ’ +
currentRecord + ‘ of ‘ + results.length + ‘</I><BR><BR></FONT>’ +
’<FONT FACE=Arial SIZE=-l><B>’ +
‘\n\n<!-Begin result set //->\n\n\t<DL>’);

Antes de imprimir la cabecera y el titulo, hemos de determinar el registro que


mostraremos en primer lugar. Sabemos que empieza en resul t s [reference1 .
Y debemos mostrar 10s registros offset a menos que reference + offset sea mas
grande que el numero total de registros. Para determinar cual es mayor, volve-
56 Motor de bhqueda para el cliente

remos a utilizar u n operador ternario. A la variable currentRecord se le asigna


este valor en la linea 117. En breve trabajaremos con este valor.
Ahora, formatResults ( ) imprime la cabecera y el titulo HTML. El cuerpo
empieza con una tabla centrada y una regla horizontal. La aplicacibn mostrara
a1 usuario la cadena que ha buscado (linea 125), que procede del campo que
tiene este valor:

Per0 las cosas empiezan a complicarse algo mas en la linea 126. Aqui se inicia
el conjunto de resultados. L a linea del texto impreso que aparece en la pagina
mostrara una parte de 10s registros y el numero total de documentos localiza-
dos. Por ejemplo:

Search Results: 1 - 10 of 3 8

Necesitaremos tres numeros. El primer registro del subconjunto que aparece


en pantalla, el numero de registros que se mostraran en pantalla y la longitud
de copyArray, donde se guardan 10s documentos localizados. Per0 vamos a verlo
paso a paso. Recordemos que esta logica no es la que se suele utilizar para
mostrar 10s registros. Pero permite que el usuario sepa cuantos documentos se
han localizado y cual de ellos sera el primero en aparecer en la pantalla. Las
cosas ocurren asi:

1. Se asigna el numero del registro actual a la variable reference y procedemos


a su impresion.
2. Aiiadimos otro numero llamado offset, que indica la cantidad de registros
que se mostraran por pagina (en este caso 10).
+
3 . Si la suma reference offset es mayor que el numero total de aciertos, se
mostraran todos 10s documentos en la pantalla. En cualquier otro caso, se
imprimira la cantidad que determine la suma (este valor ya se ha
determinado y se encuentra en currentRecord).
4. Se imprime el numero total de registros.

Los pasos 1 y 2 son muy sencillos. Recordemos el codigo de verifyManage(),


sobre todo la linea 9 9 :

formatResult(copyArray,currentMatch, showMatches);

La variable local resuZts es una copia de copyArray. A la variable reference se le


+
asigna el valor de currentMatch, asi que la suma reference offset es en realidad
la suma de currentMatch +
showResuZts. En las primeras lineas de este codigo
Motor de busclueda Dara el cliente 57

(13 y 14 para ser exactos) el valor de showMatches era 10 y currentMatch 0.


Luego el valor inicial de reference sera 0 y el de la suma reference + offset sera 10.
El primer paso termina en el momento en que se imprime reference. En el segun-
do paso tienen lugar las matematicas.
En el tercero, utilizamos un operador ternario (lineas 1 1 7-1 18) para determi-
nar si el resultado de la suma de reference + offset es mayor que el numero total
de documentos localizados. Si reference es 20 y hay 38 documentos en total, a1
aiiadir 10 a reference obtendremos 30. La pantalla nos mostrara lo siguiente:

Search Results: 20 - 30 of 38

Per0 si reference es 30 y tenemos 38 registros, a1 aiiadir 10 a reference, obten-


dremos 40. L a pantalla nos mostrara lo siguiente:

Search Results: 20 - 3 8 of 38

No puede ser. El motor de busqueda no puede mostrar 10s registros 39 y 40 si


unicamente ha encontrado 38. De esta forma sabremos que hemos alcanzado el
final de 10s registros. Luego se mostrara el numero total de registros en vez del
resultado de la suma reference + offset. Esto nos lleva a1 cuarto paso y a1 final del
proceso:

Search Results: 30 - 40 of 38

Nota: La funcion formatResults ( ) puede trabajar con caracteres


especiales como \ n y \t. El primer0 se utiliza como salto de linea y el
segundo implica una tabulacion. Nos 10s podemos encontrar en el codigo
HTML y unicamente se utiliza para dar cierto formato a1 contenido de la
pagina. N o afecta a1 funcionamiento de la aplicacion. Como pueden llegar
a confundir, 10s utilizark de forma muy esporadica en el resto del libro.

Mostrar el titulo, descripcion y U R L de 10s documentos


Ahora que ya sabemos cuales son 10s resultados, vamos a imprimirlos en la
pagina. El codigo responsable de ello lo encontramos entre las lineas 1 3 0 y 143.

if (searchType == SEARCHURL) {
for (var i = reference; i < currentRecord; i++) {
var divide = r e s u l t s [ i ] . s p l i t ( ’ ~ ’ ) ;
docObj.writeln(’\t<DT>’ + ‘ < A HREF=”’ + divider21 + ’ “ > ’ +
divide[2] + ’ < / A > ‘ + ’\t<DD>‘ + ’ < I > ‘ + divide[l] + ’ < /
I><P>\n\n‘);
I
1
58 Motor de blisqueda para el cliente

else {
for (var i = reference; i < currentRecord; i++) {
var divide = results[i].split(’I ‘ ) ;
docObj.writeln( ‘\n\n\t<DT>’+ ’ < A HREF=”’ + divide[2] + ’ ” > ’ +
divide[O] + ‘ < / A > ‘ + ‘\t<DD>’+ ’<I>’+ divide[l] + ’ < / I > < P > ’ ) ;
}
1

Las lineas 131 y 138 muestran 10s dos bucles que desarrollan la misma opera-
cion que currentRecord, con una salvedad, el orden de 10s elementos impreso es
distinto. Nos volvemos a encontrar con la variable searchType. Si es igual a
SEARCHURL, la aplicacion mostrara el URL como un hipervinculo. Si equivale a
SEARCHANY o SEARCHALL se mostrarh el titulo del documento dentro del
hipervinculo.
Ya hemos determinado el tip0 de busqueda, per0 icomo mostramos 10s resul-
tados? Unicamente necesitaremos un subconjunto de registros, separarlos por
titulo, descripcion y URL y colocarlos en la pantalla como queramos. Este es el
buclefor que utilizaremos en ambos casos (es decir, tanto si se utiliza la busque-
da URL como si no):

for (var i = reference; i < 1astRecord; i++) {

Ahora llegamos a la parte de 10s registros. Para ello volvemos a1 archivo


records.js. Cada elemento de projiles es una cadena que identifica el simbolo 1
como separador de partes. Y asi las mostraremos en pantalla:

var divide = results[il.split(’~’);

Para cada elemento, a la variable local divide se le asigna un array de elemen-


tos separados por el caracter I. El primer elemento (divide [ 0 3 ) es el URL, el
segundo ( d i v i d e [ 1I ) es el titulo del documento y el tercero, (divide [ 2 3 ), es la
descripcion del documento. Cada uno de estos elementos se imprimira en una
pagina que acompafiara a1 conjunto HTML (he seleccionado las etiquetas <DL>,
<DT> y <DD>). Si el usuario busca 10s URL de 10s documentos, este aparecera
como hipervinculo. En cualquier otro caso, lo que aparecera como texto enlaza-
do sera el titulo.

Agregar 10s botones de avance y retroceso


Lo unico que nos queda es agregar 10s botones de avance y retroceso para que
el usuario pueda ver 10s registros anteriores y posteriores a 10s que tiene en
pantalla. Para ello utilizamos la funcion prevNextResu1t s ( 1, que veremos en
breve. Aqui tenemos unas cuantas lineas de formatResults ( ) :
Motor de blisqueda para el cliente 59

docObj.writeln('\n\t</DL>\n\n<!-End result set //->\n\n');


prevNextResults(results.1ength. reference, offset);
docObj.writeln('<HR NOSHADE WIDTH=100%>' +
'</TD>\n</TR>\n</TABLE>\n</BODY>\n</EODY>\n</~TM~>'~;
docObj .close();
1

Esta parte de la funcion, que se llama prevNextResuZts(1, aiiade codigo HTML a1


final del todo. No hay mucho que comentar a1 respecto pero, en cualquier caso,
lo veremos en breve.
prevNextResuIts()
Si hemos llegado hasta aqui sin gritar de desesperacih, la verdad es que esta
funcion no nos va a parecer nada del otro mundo. El contenido de
prevNextResuZts[) es el siguiente, empezando en la linea 152:

function prevNextResults(cei1ing. reference, offset) {


docObj.writeln('<CENTER><FORM>');
if(reference > 0 ) {
docObj.writeln('<INPUT TYPE=BUTTON VALUE="Prev ' + offset +
' Results" onClick="' +
parent.frames[Ol.formatResults(parent.frames[0l.copyArray, ' +
(reference - offset) + ' , ' + offset + ' ) " > ' ) ;
}
if(reference >= 0 & & reference + offset < ceiling) {
var trueTop = ((ceiling - (offset + reference) < offset) ?
ceiling - (reference + offset) : offset);
var howMany = (trueTop > 1 ? ' s " : " " ) ;
docObj.writeln('<INPUT TYPE=BUTTON VALUE="Next ' + trueTop +
' Result' + howMany + "' onclick="' +
parent.frames[0l.formatResults(parent.frames[0l.copyArray, ' +
(reference + offset) + ' , ' + offset + ' ) " > ' ) ;
I
docObj.writeln('</CENTER>');
I

Esta funcion imprime un formulario HTML centrado en la parte baja de la


pagina de resultados con un par de botones. En la figura 1.3 tenemos la pagina
de resultados y podemos ver en su parte inferior 10s botones de 10s que habla-
mos. Hay tres combinaciones posibles con 10s botones:

Solo un boton "Next" (que aparecera en la primera pagina). No habra


registros anteriores a 10s que aparecen en pantalla.
Un boton "Prev" y otro "Next" (se mostrarhn en todas las paginas interme-
dias, es decir, en todas menos en la primera y la ultima). Con ellos podremos
acceder a 10s registros anteriores y posteriores a 10s que aparecen en pantalla.
60 Motor de bcisqueda para el cliente

Solo un boton "Prev" (que aparecera en la ultima pagina). No habra


registros posteriores a 10s que aparecen en pantalla.

Tres combinaciones. Dos botones. Es decir, que la aplicacion ha de saber cuan-


do tiene que mostrar u ocultar un boton. En las siguientes lineas se describen las
circunstancias en las que se puede dar cada combinacion.
Solo "Next". LCuAndo deberemos mostrar el boton Next? En todas las
paginas de resultados menos en la ultima. Es decir, siempre que el ndmero
del ultimo registro (reference + offset) de la pagina de resultados sea menor
que el numero de registros. Ahora, icuando aparecera este b o t h solo en
la pantalla? En la primera pagina de resultados. Es decir, cuando el valor
de reference sea 0 (este valor lo obtenemos de currentMatch).
"Prev" y "Next". LCuando apareceran 10s dos botones en la pantalla? Como
Next aparecera en todas las paginas menos en la ultima, "Prev" deberia
aparecer en todas menos en la primera, este boton aparecera siempre que
+
el valor de reference sea mayor que cero y "Next" cuando reference offset
sea menor que el numero de registros.
Solo "Prev". iCuando imprimiremos el boton "Prev" y bajo qut circuns-
tancias se excluira "Next"? Cuando se muestre la ultima pagina de
resultados. En otras palabras, cuando el valor de reference + offset sea
mayor o igual que el numero total de registros.

Esta explicacion puede parecer demasiado basica, pero por lo menos ya sabe-
mos cuando tenemos que incluir 10s botones. Las declaraciones que se encargan
de ello son las que se encuentran en las lineas 154 y 160. Incluyen uno o dos
botones, dependiendo del subconjunto de registros que aparezcan en pantalla y
de 10s que queden fuera.
Motor de blisqueda para el cliente 61

Ambos botones llamaran a la funcion f ormatResults ( ) cada vez que el cliente


haga clic sobre ellos. L a unica diferencia la tenemos en 10s argumentos que
pasan, que representan un conjunto distinto de resultados. Ambos botones se
62 Motor de blisqueda para el cliente

parecen mucho. Per0 se diferencian en el atributo VALUE. En las lineas 155-156


tenemos el principio del boton "Prev":

docObj.writeln('<INPUT TYPE=BUTTON VALUE="Prev ' + offset + ' Results''


' +

Ahora vemos el b o t h "Next", que se encuentra en las lineas 164-165:

docObj.writeln('<INPUT TYPE=BUTTON VALUE="Next ' + trueTop + ' Result'


+ howMany

Ambas lineas contienen 10s atributos TYPE y VALUE de 10s botones y un


numero que indica la cantidad de resultados anteriores y posteriores a 10s que
tenemos en pantalla. Como el numero de 10s resultados posteriores sera siempre
el mismo (offset),el boton "Prev" lo mostrara en pantalla. Es decir, "Prev 10
Results". El numero de 10s resultados posteriores puede cambiar. Dependera de
si el valor de offset o el numero de resultados restantes es menor de oflset. Para
controlar este aspecto, se asigna el valor (cualquiera que sea) a la variable trueTop.
Observese que el b o t h "Prev" siempre contiene la palabra "Results" (resulta-
dos). Tiene sentido. El valor de showMatches no cambia en toda la aplicacion. En
este caso siempre sera de 10. Por eso el usuario siempre podra contar con ver 10s
10 resultados anteriores. Per0 esto no se cumple con el boton "Next". Suponga-
mos que el ultimo subconjunto de elementos solo contiene un registro. No debe-
riamos mostrar u n boton a1 usuario que diga "Next 1 Results" (Siguientes 1
resultados). Gramaticalmente es incorrecto. Para corregirlo, prevNextResu1ts ( )
contiene una variable local llamada howMany que vuelve a utilizar el operador
ternario. Lo encontramos en la linea 163:

var howMany = (trueTop > 1 ? " s " : ") ;

Si el valor de trueTop es mayor que 1, el de howMany sera s. Si trueTop es igual


a 1, howMany contendra una cadena vacia. Como se puede ver en la linea 165,
howMany se imprime justo despues de la palabra "Results". Si unicamente hay
un registro, la aplicacion nos mostrarh la palabra "Result" en singular. Per0 si
hay mas, el usuario la Vera en plural.
El ultimo paso de ambos botones es indicarles q u i han de hacer cuando el
usuario hace clic sobre ellos. Como ya hemos mencionado anteriormente, el
evento onclick de ambos botones llama a la funcion formatResults ( ) . Las
lineas 157 y 158 y 166-167 escriben la llamada a formatResults ( 1 que se
efectua desde el controlador del evento onclick de ambos botones. Este es el
primer conjunto (la ultima parte de la llamada a document. writeln ( ) ) :
Motor de blisqueda para el cliente 63

‘onClick=”’+parent.frames[0].formatResults(parent.frames[0].copyArray,
+ (reference - offset) + ’ , ’ + offset + I ) ” > ’ ) ;

El encargado de determinar 10s argumentos sera el operador ternario que se escri-


be sobre la marcha. Se pasan tres argumentos (una vez que JavaScript genera el
codigo, claro) y son: copyArray, reference - offset y offset. El b o t h “Prev” siempre
trabajara con ellos. Obskrvese c6mo se escribe formatResults ( y copyArray:
parent.frames[Ol.formatResults( ... 1 ;
Y
parent.frames[O].copyArray

Al principio nos puede parecer algo extrafio. Per0 no olvidemos que la llamada
a f o r m a t R e s u l t s ( ) no se inicia en nav.htrnl ( p a r e n t . f r a m e s [ 0 ] ) , sin0 en el
marco del resultado p a r e n t . f r a m e s [ 1] que no tiene ninguna funcion que se
llame f o r m a t R e s u l t s ( ) ni ninguna variable llamada copyArray. Por lo tanto,
las funciones y variables necesitan estas referencias.
El b o t h ”Next” obtiene una llamada similar del controlador del evento onCZick.
Pero, u n momento. iNo deberiamos tener en cuenta la posibilidad de que offset
tenga menos resultados que copyArray como hicimos con f o r m a t R e s u l t s ( )
cuando tratabamos de determinar el plural/singular de la palabra ”Results” No.
La funcion f o r m a t R e s u l t s ( ) ya tiene en cuenta esa decision. Todo lo que
tenemos que hacer es sumar reference a offset y pasarlo. Vamos a ver el conteni-
do de las lineas 166-167, de nuevo la segunda parte de metodo de llamada
document .writeln():

’onClick=”parent.frames[0].formatResults(parent.frames[0l.copyArray,
+ (reference + offset) + ‘ , ’ + offset + ‘)”>‘);
64 Motor de busaueda Dara el cliente

El codigo HTML
El archivo navhtml apenas contiene codigo HTML. Este es su contenido (em-
pezando en la linea 174):

</HEAD>
<BODY BGCOLOR="WHITE" >
<TABLE WIDTH="95%" BORDER="0 " ALIGN="CENTER">
<TR>
<TD VALIGN=MIDDLE>
<FONT FACE= "ATial " >
<B>Client-Side Search Engine</B>
< / TD>

<TD VALIGN=ABSMIDDLE>
<FORM NAME = " search"
onsubmit="validate(document.forms[0l.query.value); return false;">
<INPUT TYPE=TEXT NAME= "query" SIZE="33 " >
<INPUT TYPE=HIDDEN NAME="standin" VALUE="">
< /FORM>
</TD>

< T D VALIGN=ABSMIDDLE>
<FONT FACE= "Aria1 " >
< B x A HREF="main.html" TARGET="main">Help</A></B>
</TD>
</TR>
< /TABLE>
</BODY>
</HTML>

No hay nada nuevo. Tenemos un formulario en una tabla. Al enviarlo se ejecu-


tara el codigo que se ha convertido. La unica pregunta es Komo podemos en-
viarlo sin tocar ningun b o t h ? Segun la especificacion 2.0 de HTML, la mayoria
de 10s exploradores (incluyendo Navigator y MSIE) permiten enviar un formu-
lario de un unico campo de texto.
Motor de busqueda para el cliente 65

Construir una base de datos en JavaScript


Eventualmente, podemos querer sustituir 10s registros que tengamos por 10s
nuestros. Lo haremos en tres pasos:

1 . Abrimos el archivo recordxjs con nuestro editor


2. Eliminamos 10s archivos que haya alli y que se parezcan a:

var profiles = new Array(

3 . Con cada registro que queramos agregar, utilizaremos la siguiente sin-


taxis:

“Your_Page_TitlelYour_Page_Descriptionlhttp://your-page-url/
file-name.html”,

Entre 10s parCntesis podremos incluir tantos elementos como queramos. Per0
hemos de asegurarnos de incluir la coma despuks de cada registro (menos en el
ultimo). ObsCrvese que tambien ahora el titulo, descripcion y URL del documento
estan separados por el caracter I. No debemos utilizarlo dentro de 10s titulos,
descripciones o URL, ya que nos encontrariamos con problemas. Ademas, si in-
cluimos comillas dobles (”), aparte de las que aparecen en 10s extremos, hemos de
asegurarnos de inchir la barra invertida delante de ellas (es decir, \“ en vez de ”).

Posibles aplicaciones
El motor de busqueda es muy util. Per0 seguro que se puede mejorar con unos
cuantos cambios, como por ejemplo:

Hacerlo compatible con JavaScript 1 .O


Hacerlo mas resistente
Mostrar carteles
Agregar la posibilidad de refinar las busquedas
Desarrollar agrupaciones

Compatibilidad con JavaScript1.O


Todos lo sabemos. La version de la mayoria de 10s exploradores Web con 10s
que trabajan 10s usuarios son la 4.x 6 5.x. Mas que nada porque son gratuitas.
Per0 aun hay gente que trabaja con las versiones 3.02 de MSIE o 2.0 de NN. Y
66 Motor de busqueda para el cliente

aun me sorprendo con el numero de visitantes que navegan por mis paginas
Web con navegadores anteriores a estos dos.
Como un motor de busqueda es una de las aplicaciones mas importantes del
sitio Web conviene que consideremos la posibilidad de hacerlo compatible con
JavaScript 1 .O. Todo lo que tenemos que hacer es recorrer el codigo que hemos
visto a1 principio del capitulo, linea a linea, ver quk propiedades no acepta la
version 1.O de JavaScript y cambiarlas todas.
Bien. Yo ya lo he hecho. Ahora le toca a usted. En esta seccion veremos aque-
110s aspectos que permiten que la aplicacion funcione con 10s exploradores
JavaScript 1.O. Hay tres cambios:

No hay archivo fuente en JavaScript


Ninguna ordenacion del array (con el metodo sort())
Una equivalencia para el mktodo split ( )

N N 2.x y MSIE 3.x no pueden trabajar con 10s archivos de codigo fuente .js
(aunque hay algunas excepciones). La solucion es incluir el array de perfiles
dentro del archivo nav.html. Con el segundo cambio eliminaremos la llamada a
result.sort ( ) de la linea 90. Es decir, que 10s resultados no se ordenaran
alfabkticamente, sino en el mismo orden cronologico con que aparecen dentro
de profiles. El ultimo cambio sera eliminar el mktodo split ( ) . JavaScript 1.0
tampoco lo admite. La solucion alternativa lo solucionara, per0 se pierde en
r endimiento.

Compatibilidad a cambio de funcionalidad


Los cambios que veremos en esta seccion permitiran que nuestra aplicacion sea
compatible con 10s antiguos exploradores Web, per0 a cambio de funcionalidad
y administracion de codigo.
Sin la ayuda de 10s archivos .js, tendremos que incluir el contenido del array
profiles dentro del archivo nav.html. De esta forma, complicaremos notable-
mente la inclusion de estos registros en otras busquedas.
El metodo sort ( ) , aunque no sea de gran importancia para la operacion, es
una propiedad muy importante. La gente tendra que ver todos 10s subconjuntos
de mCtodos porque 10s registros no apareceran en ningun orden en particular.
Obviamente, podemos colocar 10s resultados del array en orden alfabttico, per0
tampoco es sencillo. 0 bien, podemos escribir nuestro propio metodo de ordena-
cion con JavaScript 1.0. El metodo split ( ) sera el ultimo de nuestros proble-
mas. La version de nuestra aplicacion para JavaScript 1 .O tendr6 que evitarlo,
per0 no es nada complicado.
Motor de busaueda Dara el cliente 67

Dificultar su ruptura
Podemos pasar el conjunto de caracteres como parte de la cadena de busqueda.
iPor q u i no agregar una funcion que elimine 10s caracteres de la cadena que se
utilice como separadores? De esta forma, sera mas complicado que la aplicacion
se llegue a romper.

Mostrar carteles publicitarios


Si nuestro sitio Web recibe muchas visitas, ipor quk no aprovecharlo para
ganar algo de dinero?
iComo? De la siguiente manera. Supongamos que queremos mostrar
aleatoriamente cinco carteles (sin ningun orden especial). Si tenemos varios URL
de imagenes dentro de un array, podremos cargar una de ellas. Lo hacemos asi:

var adImages = new Array("pcAd.gif", "modemAd.gif", "webDevAd.gif") ;

A continuacion, podemos mostrar una en la pagina de resultados:

document.writeln('<IMG SRC=' + ads[Math.floor(Math.random(ads.length))I


+
'>');

lncluir opciones para refinar la busqueda


En esta parte nos divertiremos con la programacibn. Por ejemplo, supongamos
que el usuario puede seleccionar 10s elementos que quiere buscar a partir de un
array. Podra ajustar la busqueda de acuerdo con sus necesidades.
Consideremos por un momento la opcion de mostrar un conjunto de casillas
de verificacion en un campo de texto de nav.htrnl. Algo asi:

<INPUT TYPE=CHECKBOX NAME="group" VALUE="97 >1997 Records<BR>


'I

<INPUT TYPE=CHECKBOX NAME="group" VALUE="98">1998 Records<BR>


<INPUT TYPE=CHECKBOX NAME="group" VALUE="99 " > 1 9 9 9 Records<BR>

Utilizaremos este conjunto para determinar qu6 arrays buscaremos. En este


caso projZes97, projioles98 y projiZes99.
Hay muchas cosas que podemos hacer para mejorar la capacidad de busqueda del
usuario. Una de ellas es diferenciar entre maycisculas y minusculas en las peticio-
nes. Como hemos visto en nuestro caso, hemos optado por no hacer diferencias,
per0 podemos modificar esta opci6n incluyendo una casilla de verificacion.
Tambih podemos mejorar el ajuste de la busqueda optimizando las busquedas
basadas en elementos booleanos. Ahora trabajamos con dos: AND y OR. Per0
68 Motor de blisqueda para el cliente

podemos incluir otros como NOT, ONLY o EVEN. En lineas generales, estos son
sus significados:
AND
El registro ha de contener 10s dos terminos que se encuentren a derecha y a
izquierda de AND.
OR
El registro ha de contener cualquiera de 10s dos ttrminos que se encuentren a
derecha y a izquierda de OR.
NOT
El registro no puede contener el termino que se encuentre a la derecha de NOT.
ONLY
El registro unicamente podra contener este termino.
LIKE
El registro contendra cualquier termino que se parezca a1 deseado.

Conjunto de agrupaciones
Otra tkcnica que se usa mucho es establecer conjuntos de agrupaciones. Se
trata de grupos predeterminados de palabras que devuelven automaticamente
una serie de resultados predeterminados. Por ejemplo, si un usuario incluye el
termino "fondos de mutualidad" en la cadena de peticion, automaticamente se
puede generar una lista de resultados que contengan registros de 10s productos
que ofrezca nuestra compaiiia de seguros. Esta tecnica necesita mas prepara-
cion, per0 es ideal para las aplicaciones de busqueda.
Capitulo 2
Examen online

La aplicacion que veremos en este capitulo se trata de u n examen online. Sirve


de guia para todos 10s examenes, pruebas, etc. de varias opciones que queramos
realizar a travks de la web. En 10s siguientes puntos disponemos de una gran
flexibilidad.

Podemos determinar el numero de preguntas que tiene que responder el


usuario.
Las preguntas y respuestas se mezclaran aleatoriamente cada vez que se
cargue la aplicacion, con lo que garantizaremos que cada usuario accedera
a un examen unico.
Podemos afiadir o eliminar preguntas. La aplicacion ajustara su funcion de
mezcla, su administracion y valoracion.
Podemos eliminar las respuestas de la aplicacion para evitar que se hagan
trampas, y obligar a que 10s usuarios envien las respuestas a1 servidor.

Para cargar la aplicacion, abriremos el archivo index.htm1 con el explorador


Web. En la figura 2.1 tenemos la pantalla de inicio. iSe puede imaginar que las
preguntas que apareceran en el examen estan controladas por JavaScript? Prue-
be la aplicacion. Se trata de un examen de 50 preguntas. Preguntas que cubren
aspectos relacionados con JavaScript, con el codigo propio del cliente y del ser-
vidor, Liveconnect, errores y mucho mas. No es demasiado sencillo, per0 por lo
menos es entretenido.
Una vez que se inicie el examen, veremos que cada pregunta ofrece varias res-
puestas. En el momento en que seleccionemos una de ellas, la aplicacion saltara
automaticamente a la siguiente pregunta. No podemos retroceder para modificar
70 Examen online

las respuestas. Unicamente tendremos una oportunidad para hacerlas. En la fi-


gura 2.2 podemos ver el formato de las preguntas.

Strap in Buck0 This ain’t no JavaScript vacation

Figura 2.1. PBgina de inicio de la prueba

Question 1 of 50
You unsuccessfullyuse nwigrtor.prefennces0 to alter browser preferences. How come?

r You’re using e*port instead of impon


r You’re using import instead of export
C That% me wrong method
r You’re not using a signed script

Figura 2.2. Pregunta con varias respuestas

Cuando se conteste la ultima pregunta, se cornpararan las respuestas del usuario


con verdaderas. Se valorara el nivel de aciertos y se mostraran 10s resultados.
En la figura 2.3 tenemos la pagina Web de resultados. Aqui podremos ver la pre-
gunta, sus cuatro posibles respuestas y la que ha seleccionado el usuario. Si la
respuesta ha sido correcta, el texto aparecera en verde. Si no, en rojo.
Para comprender las respuestas fallidas, la aplicacion incluye una explicacion
que se mostrara en pantalla en el momento en que se coloque el cursor sobre el
texto que aparece en rojo. Podemos ver este texto en la parte superior de la pan-
talla de la figura 2.4.
Bien. Este es el primer contact0 que mantenemos con la aplicacion. En el diagra-
ma 2.5 tenemos su estructura, para que comprendamos c6mo se desarrollara
desde el punto de vista del usuario. Las lineas discontinuas indican que el usuario
Examen online 71

dispone de una accion opcional o que la aplicacion se queda esperando a que el


usuario desarrolle una accion determinada. Veamos 10s cinco pasos del proceso.

You scored 45/50 correctly.


Ranking You a n an exhalted JavaScriptguru
Pass the mouse pointer arrow over the r4d text for an explanation ofthore you ntirrsed

Here IShow you scored


I
Question 1
Which best explains getSelection(J7

a Returns the VALUE of a selected OPTION


b Returns document URL of Ihe window in focus
c Returns the value of CU~SDIselected w x t
d Returns the VALUE of a checked radio input

Quesbon 2

Figura 2 . 3 . Resultados del examen

Aqui tenemos el proceso:

1. El usuario hace clic en el boton "Begin" (iniciar). La aplicacion escribira la


primera pregunta en la pantalla y esperara a que el usuario seleccione una
de las cuatro posibles respuestas o a que haga clic en el boton "Quit Now"
(salir ahora).
2. Si el usuario selecciona una de las respuestas, la aplicacion la guarda y
decide si se ha completado el examen o si ha de mostrar la siguiente pre-
gunta. Si nos encontramos en el primer supuesto (es decir, la respuesta que
acaba de responder el usuario era la ultima del examen), entonces salta-
remos a1 paso 4. Si no, se mostrara la siguiente pregunta.
3 , Per0 si el usuario hace clic en el b o t h de salida "Quit Now",se le pedira que
confirme su selection. Si hace clic en "OK" (aceptar), la aplicacion saltara a1
paso 4. Si el usuario hace clic en "Cancel"(cancelar), regresara a1 punto en
el que se quedo del examen.
4. Cuando se finaliza (0 cancela) el examen, se procede a la comprobaci6n de
las respuestas del usuario con las correctas. Luego se imprimen 10s resul-
tados en la pantalla.
72 Examen online

5. Segun se revisen 10s resultados, el usuario podra colocar el raton sobre una
de las respuestas fallidas (texto en rojo) para que la aplicacion muestre mas
informacion.

Question 4
Select the method added in JmaScnpt 1 2

a concat0
b link0
c spittg)
d pno
’* i

Question 6
What is one disadvantage of using the Function constructor7

a Srnpts can use tt to create functions at run time


b It cannot define small functions
c Functions defined this way are no1 cotnptled and ene~uteslower
d There 1s no reason not 10 use $1

Figura 2.4. Explication de las preguntas fallidas

Requisitos de la ejecucion
Todo el codigo ha sido escrito en JavaScript 1 . 1 , motivo por el que unicamente
se podra trabajar con exploradores que Sean del tip0 Navigator 3.x (0superior)
y MSIE 4.x (0 superior). El examen incluye t a m b i h un total de 400 preguntas.
La verdad es que no creo que nadie lo vaya a utilizar para proporcionar un cer-
tificado a sus alumnos, por lo que consider0 que 400 preguntas son mas que
suficientes.

Analisis de la sintaxis
En la figura 2.5 podemos comprobar el esquema que muestra 10s pasos que rea-
lizara el usuario desde el momento en que inicia el examen hasta que lo termi-
na. Si queremos comprender lo que ocurre en realidad deberiamos trabajar con
otro esquema que resulte mas completo y analizar el codigo fuente correspon-
diente a 10s archivos.
Examen online 73

Figura 2 . 5 . Esquema de la aplicacion desde el punto de vista del usuario

En la figura 2.6 tenemos ese esquema. Los cuadros discontinuos indican que el
proceso se hara antes o despues de la prueba (por ejemplo, durante la carga de las
paginas Web). Las flechas discontinuas indican que se esta esperando que el usua-
rio haga algo. La funcion asociada con cada proceso aparecera en cursiva.
Compare el grafico de la figura 2.5 con el de la figura 2.6. Es la mejor forma de
detectar las diferencias. El esquema es el mismo, solo que ahora podemos ver
que ocurre antes y despues de que el usuario seleccione una opcion del test.

index.html
La aplicacion tiene tres archivos: index.htm1, administer.htmZ y questions.js.
Como index.htm1 es el archivo desde el que se iniciara el proceso, empezaremos
con 61. En el ejemplo 2.1 podemos ver su contenido.
74 Examen online

El usuario
selecciona BEGlN
(iternReset())

I
El usuario ve
I
I

a inforrnacion
sobre la respuesta

Figura 2.6. Esquema de flujo de JavaScript

Ejemplo 2.1. Codigo fuente de index.htm1

1 <HTML>
2 <HEAD>
Examen online 75

3 <TITLE>JavaScript On-line Test</TITLE>


4 <SCRIPT LANGUAGE="JavaScriptl.l">
5 <!--
6 var d u m y l = '<HTML><BODYBGCOLOR=WHITE></BODY></HTML>';
7 var dummy2 = '<HTML><BODY BGCOLOR=WHITE><FONT FACE=Arial>' +
8 'Strap in Bucko: This ain\'t no JavaScript vacation . . . </BODY>
</HTML> ' ;
9 //-->
10 </SCRIPT>
11 < /HEAD>
12 <FRAMESET ROWS="90,* " FRAMEBORDER=O BORDER=()>
13 <FRAMESET COLS="250,* " >
14 <FRAME SRC="administer.html" SCROLLING=NO>
15 <FRAME SRC="javascript: self .dummyl">
16 < /FRAMESET>
17 <FRAME NAME="questions" SRC="javascript: self . d u m y 2 " >
18 </FRAMESET>
19 </HTML>

Como habremos visto, utilizar u n marco de trabajo basado en la Web no re-


presenta ninguna ventaja. En primer lugar, es anidado. Es decir, que tenemos
un cuadro dentro de otro. El externo se encuentra en la linea 12 y define dos fi-
las, la primera tiene 90 pixeles de alto y la segunda rellena el resto de la ventana.
El cuadro de 90 pixeles contiene otro marco de trabajo. Declara dos columnas,
la primera tiene 250 pixeles de ancho y la segunda rellena el resto de la ventana.
En la figura 2.7 tenemos el aspect0 que tendra el marco de trabajo. Tambikn se
muestra el atributo SRC de cada uno de ellos.

administrator.html javascript: self.dummy1

Figura 2.7. Cuadros anidados de index.htm1

administerhtml se vale del atributo SRC para crear la etiqueta FRAME pero, ique
hay de las otras dos? Las dos variables dummy definen las piiginas HTML. Es
decir, que dummy1 y dummy2 representan paginas HTML sin ningun nombre.
Unicamente existen dentro de la aplicacion. Estas variables se definen en las li-
neas 7 y 8. Cada una de ellas contiene una pequeiia parte de HTML. No mucho,
lo suficiente para que funcionen. index.htm1 utiliza el protocolo j avascript :
76 Examen online

para valorar las expresiones que se encuentran dentro de dummy 1 y dummy2 y


las devuelve como un URL de cada atributo SRC.
Ahora pasamos a1 cuadro. Hemos rellenado 10s tres cuadros usando una unica
pagina HTML (administer.htmZ).Vamos a halar se economia.

questions.js, el archivo codigo fuente en JavaScript


Vamos a continuar con questions.js, el archivo que contiene el codigo fuente en
JavaScript a1 que se llama desde administer.htmZ, que veremos en el ejemplo 2.2.
Ejemplo 2 . 2 . El inicio del c6digo fuente de questions.js

1 function question(answer, support, question, a, b , c, d) {


2 this.answer = answer;
3 this.support = support;
4 this.question = question;
5 this.a = a;
6 this.b = b;
I this.c = c;
8 this.d = d ;
9 return this;
10
11 var units = new Array(
12 new question(" a " , "The others are external objects. ' I ,

13 "Choose the built-in JavaScript object:" , "Image",


"mimeType ,
'I

14 "Password", "Area") ,
15 / / etc . . .
16

Obviamente, se trata de una version abreviada del archivo. El array units es


mayor ( 7 5 elementos en total), pero aqui podemos ver que cada uno de 10s ele-
mentos de units es u n objeto de una pregunta, tal y como se ha definido en la
funci6n question(), en las lineas 1-10.
Esta aplicacih se basa en 10s objetos JavaScript que define el usuario (objetos
creados por nosotros). Si el concept0 de 10s objetos de JavaScript le parece com-
plicado, conviene que revise documentacion donde se expliquen. Es la mejor
forma de comprender el Modelo de objetos de JavaScript. Mientras tanto, puede
usar la informacion que se facilita en el siguiente parrafo como curso acelerado.
Un objeto es un conjunto de datos estructurados. Cada objeto puede tener o
estar asociado, con dos tipos de entidades (propiedades y metodos). Las propie-
dades tienen a@, como el numero 6, la expresion a * b o una cadena de ca-
racteres. Los metodos hacen algo, como calcular la circunferencia de un circulo
o cambiar el color del fondo de un documento. Consideremos el objeto document
de JavaScript. Cada documento tiene cosas (document.bgColor; document.fgCoZor;
Examen online 77

etc.) y hacen cosas (documet.open(),document. write(), document.cZose(),etc.) Las


propiedades tienen cosas. Los mktodos hacen algo.

Para crear objetos podemos empezar por una funcion constructora:

function myFirstConstructor(arg1, arg2, argn) I


this.property1 = argl;
this.property2 = arg2;
this.propertyn = argn;
return this;
1

Se parece a cualquier funcion que pudiksemos crear, solo que aqui se utiliza la
clave t h i s para referirse a si misma. Los argumentos que pasemos se podran
asignar a propiedades o manipular de distintas maneras. Una vez que tenga-
mos el constructor, tendremos que iniciar las variables con el nuevo operador:

var myFirstObject = new myFirstConstructor(6, a*b, "Jimmy"),


var mySecondObject = new myFirstConstructor(6, a*b, "Jimmy"),
var myThirdObject = new myFirstConstructor(6, a*b, "Jimmy"),
78 Examen online

La implementacion de objetos en nuestro scipt es muy facil. Los que creamos,


que no son otra cosa que muestras de la funci6n question ( ) del constructor,
unicamente contiene propiedades. Las lineas 2-8 identifican siete propiedades de
cada question ( ) : una respuesta, una explicacion, una pregunta (el texto) y
varias posibilidades: a, b, c y d-. A continuacion podemos ver el contenido de las
lineas 1-10:

this.answer = answer;
this.support = support;
this.question = question;
this.a = a;
this.b = b;
this.c = c;
this.d = d;
return this;
>
Las propiedades y mktodos se asignan generalmente a objetos. Para ello se uti-
liza la notacion this.De esta manera, cada uno de 10s elementos pertenecientes
a units utiliza el operador new con objeto de crear un nuevo caso de ques tion ( ) ,
a1 que se le entregan 10s siete parametros. En la linea 9 , encontramos la siguien-
te sintaxis:

return this;

Esta linea devuelve una referencia a la variable iniciada (en nuestro caso, cada
uno de 10s elementos units).Ahora cada elemento de units es question. Es el
sistema que se ha de utilizar para crear, eliminar y manipular las preguntas de
nuestro examen. Podemos agregar mas preguntas utilizando la misma sintaxis
con otros elementos units:

new question ( "your-answer-letter" , "your-explanation" ,


"your-question-text" , "option-a" , " o p t i o n b ", "option-c" , "option-d");

Hemos colocado la repuesta como primer argument0 de la funcion puesto que


parece mas sencillo contar con una cadena de un caracter a1 principio del argu-
mento, en lugar de tenerlo a1 final. Algunas de las preguntas resultan bastante
largas. De esta manera podemos facilitar su localizacion y su modificacion cuando
sea necesario.
Puede parecer innecesario crear un objeto para cada pregunta. Per0 de esta
forma facilitaremos considerablemente las cosas, sobre todo cuando tengamos
que trabajar con las propiedades de cada pregunta. Vamos a ver el contenido del
archivo administer.html.
Examen online 79

Nota: Para no usar 10s objetos de JavaScript en nuestras aplicaciones, debe-


mos pensar en cambiar el estilo de trabajo. Los objetos tienen ventajas. Me-
joran el aspecto del codigo. Tambih tienen la ventaja de las jerarquias, la
transferencia de propiedades de u n objeto a otro creado a partir de 61.

administer. ht mI
A continuacion colocamos cada objeto en su sitio. Vamos a hacer que trabajen
para nosotros. Se trata de otra de las aplicaciones donde el cerebro de la aplica-
cion en JavaScript se encuentra en el cuadro superior de la pantalla, mientras se
deja el cuadro inferior para que interactue con el usuario. Podemos separar la
aplicacion en una serie de procesos. En la tabla 2.1 tenemos la descripcion de
dichos procesos y las variables y funciones de JavaScript asociados con ellos.
Tabla 2.1. Procesos del examen y las funciones JavaScript asociadas

Proceso Descriucion JavaScript asociado


Configuracion Declara e inicia las variables variables qIdx,correct,
del entorno globales y mezcla aleatoria- HowMany,stopOK,nextQ,
mente las preguntas. results,aFrame,qFrame.
arrays keeper,rank,
questions,answers.
funciones itemReset ( ) ,
shuffle( ) .
Administracion Escribe el conjunto de funciones
del examen respuestas en la pantalla y buildQuestion ( 1,
registra la que seleccione makeButton ( 1, qUiZ&S
el usuario. chickenout ( 1 .
Evalua el Compara las respuestas del funcion gradeTest ( ) .
examen estudiante con las correctas.
Imprime 10s Imprime todas las respuestas, funcion printResults ( ) .
resultados las correctas y las incorrectas,
junto con la calificacion final.
Muestra la Imprime las explicaciones en funciones explain ( ) , y
explicacion parent.frame [ 1I . show ( ) .
Reinicia el Determina todas las variables variables qIdx,correct,
entorno necesarias a su valor original. s topOK.
array keeper.
funciones cleanslate ( ) ,
shuffle ( 1 .
80 Examen online

Vamos a verlo detenidamente un momento. Vamos a revisar el codigo corres-


pondiente a adrninister.htmZ del ejemplo 2 . 3 .

Ejemplo 2.3. Codigo fuente de administer.htm1


1 <HTML>
2 <HEAD><TITLE>On-lineJavaScript Test</TITLE>
3 <SCRIPT LANGUAGE= JavaScriptl.1 SRC="questions.js " ></SCRIPT>
I'

4 <SCRIPT LANGUAGE="JavaScript1. I">


5 var qIdx = 0;
6 var correct = 0;
7 var howMany = 50;
8 var keeper = new Array();
9 var rank = new Array('No offense, but you need help.',
10 'Ummm. . . Well . . . Few have done worse.',
11 'Ehhh. . . You know some. Keep at it.',
12 'You seem to have a working knowledge.',
13 'Better than the average bear.','Youare an adequate
JavaScripter.',
14 'You are a formidable JavaScripter.',
15 'You are an excellent JavaScripter.',
16 'You are an exhalted JavaScript guru.'
17 );
18 var stopOK = false;
19 var nextQ = ";
20 var results = ' ' ;
21 var aFrame = parent.fLames [ 11 ;
22 var qFrame = parent.framesf21;
23 function shuffle0 {
24 for (var i = 0; i < units.length; i++) {
25 var j = Math.floor(Math.random0 * units.length);
26 var tempunit = units[il;
21 units[il = units[jl;
28 units[jl = tempunit;
29 }
30 }
31 function itemReset0 {
32 qIdx = 0;
33 correct = 0;
34 StopOK = false;
35 keeper = new Array ( ) ;
36 shuffle ( ;
37 }
38 function buildQuestion0 {
39 if (qIdx == howMany) {
40 gradeTest ( ) ;
41 return;
42 1
43 nextQ = '<HTML><BODY BGCOLOR=WHITE><FONTFACE=Arial>' +
44 '<H2>Question + (qIdx + 1) + ' of ' + howMany + '</H2>'-f
45 '<FORM>'+ ' < B > '+ units[qIdx].question + '</B><BR><BR>'
+
Examen online 81

46 makeButton("a", units[qIdxl .a) +


47 makeButton("b",units[qIdxl .b) +
48 makeButton("c",units[qIdxl .c) +
49 makeButton("d", units[qIdxl .d) +
50 '</FORM></BODY></HTML>';
51 qFrame.location.replace(''javascript:parent.frames[O].nextQ");
52 qIdx++ ;
53 if(q1dx > = 2 & & !stopOK) { stopOK = true;
54 )
55 function makeButton(optLtr, optAnswer) {
56 return <INPUT TYPE=RADIO NAME="answer" VALUE=" ' + OptLtr +
57 ' onClick="parent.frames[Ol.keeper[parent.frames[Ol.qIdx -
"

11 =
58 this.value; parent.frames[Ol.buildQuestion()">'+ optAnswer +
' <BR> ;
59 I
60 function chickenout0 {
61 if(stop0K & & confirm('Stopping early? Are you really a ' +
62 'JavaScript Chicken?')) {
63 gradeTest ( ) ;
64 )
65 1
66 function gradeTest0 {
67 for (var i = 0; i < qIdx; i++) {
68 if (keeper[il = = units[il .answer) {
69 correct++;
70 I
71 }
72 var idx = Math.ceil((correct/howMany) * rank.length - 1) < 0 ?
0 :
73 Math.ceil((correct/howMany) * rank.length - 1);
14 printResults(rank[idxl);
75 itemReset ( ) ;
76 I
77 function printResults(rankin9) {
78 results = '<HTML><BODY BGCOLOR=WHITE LINK=RED VLINK=RED
ALINK=RED> ' +
79 '<FONT FACE=Arial>' +
80 '<H2>Youscored ' + correct + ' / ' + howMany + ' correctly.<
H2>' +
81 '<B>Ranking: <I>' + ranking +
82 '</I><BR>Passthe mouse over the red text for an explanation
of ' +
83 'those you misssed.</B>' +
84 '<BR><BR><FONT SIZE=4>Here is how you scored: </FONT><BR><BR>';
85 for (var i = 0; i < howMany; i++) {
86 results += '\n\r\n\r\n\r<B>Question' + (i + 1) + '</B><BR>'
87 + units[i].question + '<BR><BR>\n\r<FONTSIZE=-l>' +
88 'a. ' + units1il.a + '<BR>'+
89 'b. ' + units[i].b + '<BR>' +
90 'c. ' + unitsLi1.c + '<BR>'+
82 Examen online

91 ' d . ' + units[i].d + '<BR></FONT>';


92 if (keeper[il == unitsli] .answer) I
93 results += '<B><I><FONTCOLOR=GREEN>' +
94 'You answered this correctly ( ' + keeper[il + ' ) . ' +
95 '</FONT></I></B>\n\r<BR><BR><BR>';
96 1
97 else {
98 results += '<FONT FACE=Arial><B><I>' + '
99 ' < A HREF=" " onMouseOver="parent.frames[O].show();' +
100 parent.frames[O].explain(\" + units[il .support + ' \ ' I ; '
101 + ' return true" onMouseOut="parent.frames [ 0 1 . explain ( \ '
\');"' +
102 'onClick="return false;" > ' +
103 'The correct answer is: ' + units[il.answer +
104 '</A></FONT></I></B>\n\r<BR><BR><BR>';
105 1
106 1
107 results += '\n\r</BODY></HTML>';
108 qFrame.location.replace("javascript:
parent.frames[Ol .results");
109 )
110 Eunction show() { parent.status = " ; )
111 Eunction explain(str) {
112 with (aFrame.document) I
113 open ( ) ;
114 writeln('<HTML><BODY BGCOLOR=WHITE><FONT FACE=Arial>' + Str +
115 '</FONT></BODY></HTML>');
116 close ( ) ;
117 )
118 )
119 function cleanslate ( ) {
120 aFrame.location.replace('javascript:parent.dummy1');
121 qFrame.location.replace('javascript: parent.dumy2');
122
123 </SCRIPT>
124 < /HEAD>
125 <BODY BGCOLOR=WHITE onLoad="cleanSlate( ) ; " >
126 <FONT FACE= " Aria 1 " >
127 <FORM>
128 <INPUT TYPE=BUTTON VALUE= "Begin"
129 onclick="itemReset( ) ; buildQuestion(); " >
130 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
131 <INPUT TYPE=BUTTON VALUE="Quit N o w " onclick="chickenOut( ) ;" >
132 </FORM>
133 < /FONT>
134 < / BODY>
135 :/HTML>

Este archivo tan grande se puede separar en cuatro secciones. En primer lugar
tenemos la llamada a1 fichero questions.js.A continuacion la definicion de algu-
nas variables globales. DespuCs aparecen las funciones y por ultimo, tenemos
Examen online 83

unas cuantas lineas de HTML. Vamos a empezar por aqui, mas que nada porque
es la parte mas sencilla.

El cuerpo HTML
Cuando se completa la carga de adrninister.htrnl, entonces se llama a la funcion
cleanslate ( 1 en la linea 125:

<BODY BGCOLOR=WHITE onLoad="cleanSlate ( ) : " >

cleanslate ( ) utiliza el metodo replace ( ) del objeto de localizacion con ob-


jeto de sustituir 10s URL de parent. frames [ 11 (olo que es lo mismo, aFrame)
y de parent. frames [ 2 ] (es decir, qFrame) que contiene el valor de las varia-
bles dummy1 y dummy2 que hemos definido anteriormente en indexhtml. Vea-
mos las lineas 119-122.

function cleanslate0 {
aFrame.location.replace('javascript: parent.dummy1');
qFrame.location.replace('javascript: parent.dummy2');
}

Ya lo hicimos en index.htrnZ, iverdad? Si. Ya lo hemos hecho. Per0 esta vez, nos
aseguramos de que si el usuario vuelve a cargar administer.htm1 por la razon
que sea, el cuadro que se encuentra en la parte superior de la pagina, siempre se
abrira como una pagina en blanco, y que el cuadro inferior se abrira con el men-
saje "bucko". Ademas, el usuario no podra acceder a1 contenido anterior de la
pagina, motivo por el cual si este consistiese en una pregunta, no podra corregir
su respuesta.
El resto del HTML no es otra cosa que un formulario con dos botones. No nos
vamos a extender en esta parte. El archivo administer.htm1 tiene un formulario
con dos botones. Cada uno de ellos llama a funciones diferentes cuando se hace
clic sobre ellos. Este es el c6digo que se encuentra en las lineas 127-132:

<FORM>
<INPUT TYPE=BUTTON VALUE="Begin"
onclick="itemReset ( ) ; buildQuestion(): " >
&nbsp;&nbsp;&nbsp;&nbsp:&nbsp;
<INPUT TYPE=BUTTON VALUE="Quit Now" onclick="chickenOut ( ) ; " >
< /FORM>

ObsCrvese que el boton "Begin"llama a itemReset ( ) y a buildQuestion ( ) ,


mientras que "Quit NOW"llama a chickenout ( ) . Con lo que podemos entrar
en la seccion dedicada a las funciones.
84 Examen online

Variables globales
Justo despuks del codigo que llama a1 archivo questionsjs de la linea4, tenemos
las variables globales que se utilizaran en la aplicacion. Se encuentran en las
lineas 5-22:

var qIdx = 0;
var correct = 0;
var howMany = 50;
var keeper = new Array( ) ;
var rank = new Array('No offense, hut you need help.'
' Ummm... Well . . . Few have done worse.',
'Ehhh. . . You know some. Keep at it.',
'You seem to have a working knowledge.',
'Better than the average hear.',
'You are an adequate JavaScripter.',
'You are a formidable JavaScripter.',
'You are an excellent JavaScripter.',
'You are an exhalted JavaScript guru.'
);
var stopOK = false;
var nextQ = ";
var results = ";
var aFrame = parent.frames[l];
var qFrame = parent.frames [2];

La lista que tenemos a continuacion describe la funci6n de cada una de estas


variables globales. Las veremos mas despacio cuando estudiemos las funciones.

qldx. Una variable que se utiliza para visualizar la pregunta que figura en
la pantalla.
carrect. Variable que se utiliza para registrar el numero de respuestas
correctas.
howMany. Numero fijo que determina el numero de preguntas que quiere
responder el usuario (independiente del nombre que se encuentra en el
array units)
keeper. Array que inicialmente esta vacio donde se guardan las respuestas
del usuario.
rank. Array de cadenas que indican 10s distintos niveles e eficacia.
stopOK. Variable que contiene un valor booleano que indica cuando se
puede detener el examen antes de que Cste llegue a su fin.
nextQ. Cadena vacia a la que se le asigna repetidamente el valor de HTML
que representa cada una de las preguntas del examen.
results. Cadena vacia a la que se le asigna el valor de HTML que representa
10s resultados del examen.
Examen online 85

aFrame. Referencia sencilla a1 segundo cuadro.


qFrame. Referencia sencilla a1 primer cuadro.

Funciones
Ahora vamos a ver las funciones. Empezamos con itemReset().

itemReset()
L a primera funcion a la que se llama en esta aplicacion es itemReset ( 1 . Tiene
lugar cuando el usuario selecciona el boton "Begin" (lineas 128-129):

<INPUT TYPE=BUTTON VALUE= "Begin"


onclick="itemReset( ) ; buildQuestion(); ":

itemReset ( ) asigna a las variables globales su valor original y mezcla las pre-
guntas en un array de elementos. Su codigo se encuentra en las lineas 31-37:

function itemReset0 {
qIdx = 0;
correct = 0;
stopOK = false;
keeper = new Array( ) ;
shuffle();
1

El usuario aun no ha visto la primera pregunta y JavaScript ya ha reiniciado


todas las variables globales. iPor qu6? Bien. Supongamos que hemos hecho una
vez el examen y unicamente hemos obtenido dos respuestas bien. Podriamos
hacer clic en el boton "Begin"y volver a probar suerte. Por eso se reunian 10s va-
lores de las variables. Ahora el usuario accedera a otras preguntas nuevas.
No se ha incluido la variable howMany. Este valor serh siempre el mismo. Ini-
cialmente, 10s valores de las variables nextQ y resu2ts son unas cadenas vacias,
por lo que no habra que reiniciarlas. No hace falta. Si observamos el contenido
correspondiente a las lineas 43 y 86 respectivamente, veremos que la aplicacih
asigna el valor de estas variables sobre la marcha.
Una vez que cada variable tiene su valor, Len qu6 momento llamamos a la fun-
cion shuffle ( ) de la linea 36?

shuffle()
Esta pequeiia funcion permite que el administrador de la aplicacion trabaje
con cierta flexibilidad. Se encarga de desordenar las preguntas, con lo que casi se
garantizara que cada usuario accedera a una version unica del examen. Para
hacernos una idea de las posibilidades, consideremos por u n momento que el
86 Exarnen online

numero total de combinaciones de preguntas es n (n-1), donde n es el numero


de preguntas. Asi que, para u n examen de diez preguntas, tendriamos 90 com-
binaciones posibles. Para uno de 20 preguntas, tendriamos 380 posibles combi-
naciones. Este examen cuenta con 50 preguntas, por lo que tendremos un total
de 2.450 combinaciones.
El examen tambikn sera unico porque, aunque el array units tiene 75 pregun-
tas preparadas, el valor de howMany es 50. Cuando se completa la mezcla de
preguntas, la aplicacion utilizara las primeras 50. Como hay 75, es muy posible
que dos usuarios no reciban las mismas preguntas. Por lo tanto, este test nos
ofrece miles de combinaciones diferentes. Resulta increible lo sencillo que es este
proceso.
Se encuentra en las lineas 23-30:

function s h u f f l e 0 {
for (var i = 0; i < units.length; i++) I
var j = Math.floor(Math.random() units.length);
var tempunit = units[il;
units[i] = units[jl;
units[j] = tempunit;
1
I

Para cada elemento del array units:

1. Tomamos un entero cualquiera que se encuentre comprendido entre 0 y


units.length - 1.
2 . Determinamos el valor de la variable localternpunit. Sera igual a1elemento
del indice con el que se estk trabajando (units[ i I ).
3 . Asignamos a1 valor del elemento del indice actual el valor del elemento del
indice aleatorio (units[ j ] ).
4. Determinamos el valor del elemento del indice aleatorio y le asignamos el
valor de la variable local tempunit.

En otras palabras, repetimos sistematicamente a travks de todos 10s elementos


del array e intercambiamos cada uno de ellos por el valor de otro elemento se-
leccionado al azar. A continuacion asignamos este valor al elemento del array.
Las preguntas se han guardado ordenadas aleatoriamente y ahora esperan a1
usuario.
buildQuestion0
Esta funci6n actua como el examinador. Hemos visto en el flujo del programa
que la funcion buidQuestion ( ) aparece varias veces. Tiene una gran respon-
sabilidad. Empieza en la linea 38 y termina en la 54.

I
Examen online 87

function buildQuestion0 {
if (qIdx == howMany) {
gradeTest ( ) ;
return;
)
nextQ = '<HTML><BODYBGCOLOR=WHITE><FONTFACE=Arial>' +
'<H2>Question ' + (qIdx + 1) + ' of ' + howMany + '</H2>'+
' < F O R M > ' + '<B>'+ units[qIdx].question + '</B><BR><BR>'
+
makeButton("a", unitsrqIdx1 .a) +
makeButton ( "b", units [qIdxl .b) +
makeButton("c",units[qIdxl .c) +
makeButton("d", units[qIdxl .d) +
'</FORM></BODY></HTML>';
qFrame.location.replace("javascript:parent.frames Lo1 .nextQ");
qIdx++;
if(q1dx >= 2 & & !stopOK) { stopOK = true; )
1

En la parte superior del codigo, buidQuestion( ) comprueba que el valor de


la variable qldx sea igual a1 de howMany. Si es asi, el usuario habra contestado
a la ultima pregunta del examen y tendra que procederse a su evaluacion. En la
lhea 40 se llama a la funcion gradeTest ( ) .
88 Examen online

Si aun no se ha completado el examen, buidQuestion ( ) procedera a generar


la siguiente pregunta generando una cadena que representara toda una pagina
HTML. Este hecho tiene lugar en las lineas 43-50. Si examinamos nextQ, vere-
mos que la pagina HTML contiene un indicador del numero de pregunta y el
numero total de las preguntas del examen. Se encuentra en la linea 44:

'<H2>Question + (qIdx + 1) + ' of + howMany + '</H2>'

A continuation, veremos que se abre una etiqueta FORM a la que sigue el texto
de la pregunta. Si hacemos un poco de memoria, recordaremos que el texto de la
pregunta se encuentra dentro de la propiedad question de cada elemento units.
Luego no ha de sorprendernos encontrarnos este codigo en la linea 45:

'<FORM>' + '<B>'+ units[qIdx].question

Y donde encontremos una etiqueta FORM, cerca tendremos 10s elementos del
formulario. En realidad, esto es todo lo que se necesita para construir una pagi-
na HTML. Este formulario unicamente tiene cuatro elementos (cuatro botones
de opcion). En vez de escribir el codigo HTML encargado de la creacion de 10s
cuatro botones de opcion dentro de la funcion (casi seria repetir cuatro veces lo
mismo), tenemos la funcionmakeButton ( ) que se encargara de generarla. Uni-
camente tendremos que pasarle la letra y el texto de cada una de las opciones
(lineas 46-49). Este es el contenido de la funcion makeButton ( ) que se encuen-
tra en las lineas 55-59.

function makeButton(optLtr, optAnswer) {


return '<INPUTTYPE=RADIO NAME="answer" VALUE=" ' + optLtr +
"' onClick="parent.frames[O].keeper[parent.frames[Ol.qIdx - 11 =
this.value; parent.frames[O].buildQuestion()">' + optAnswer +
' <BR> ;
I

Esta funcion se limita a devolver una cadena que representa un boton de opcion
con una serie de atributos personalizados (VALUE) que equivalen a a, b, c o d,
Examen online 89

seguido del texto que aparecera a su derecha. Los atributos VALUE proceden de
optLtr y el texto de optAnswer.
N o olvidemos que quien lleva las riendas del examen es el mismo usuario, por
eso sera 61 quien determinara el momento en que se pasara a la siguiente pregun-
ta. En JavaScript esto quiere decir que, a1 asociar las expresiones de cada opci6n
con el controlador de eventos onclick, ocurriran un par de cosas.
En primer lugar, el array keeper guardara la letra asociada a la respuesta selec-
cionada por el usuario. Para determinar a quC elemento se le asignar6 la respues-
ta del usuario, utilizamos el siguiente codigo:

parent.frames [Ol .qIdx - 1

La variable qldx guardara un registro con la cuenta de las preguntas que ayu-
dara a la hora de asignar el siguiente elemento que seleccione el usuario.
La segunda "cosal' que ocurre es que JavaScript llama a buidQuestion ( 1 para
que imprima la siguiente pregunta en la pantalla o para que proceda con la va-
loracion del examen, si la respuesta contestada era la ultima. Tanto keeper como
buidQuestion ( ) empiezan en parent. frames [ 01. Puesto que la informacion
se guardara aqui, tendremos que acceder a ellos desde el cuadro superior de la
pagina.
Cuando completemos la construccion del formulario, lo unico que nos queda-
ra por hacer (por lo menos, en lo que se refiere a1 codigo HTML) es cerrar las
etiquetas y cargar el contenido en las ventanas. VCanse las lineas 50-5 1 :

'</FoRM></BoDY>I/nTML>';
qFrame.location.replace("javascript: parent.framesI0l.nextQ");

Cargamos el valor nextQ en el cuadro inferior de la pantalla. Observese que la


aplicacion, para imprimir las preguntas en la pagina, utiliza el rnttodoreplace( )
del objeto de localizacion en vez de configurar la propiedad location.href o
bien document .write( 1 . En esta aplicacion, este hecho es bastante importan-
te. replace ( 1 carga un URL determinado en el explorador (en nuestro caso, el
URL es una cadena de HTML de cuya valoracion se encargarii el protocolo
j avascript: ), per0 sustituye la pagina actual del historial. De esta forma se
evita que el usuario pueda retroceder para modificar las repuestas. Si el usuario
hace clic en el b o t h "Back', la aplicacion cargara la piigina anterior a indexhtml.
Antes de abandonar buidQuestion ( ) nos encargaremos de dejar todo bien
limpito. Veamos las lineas 52-53:

qIdx++;
if(q1dx >= 2 && !stopOK) { stopOK = t r u e ; 1
90 Examen online

A1 aumentar 4Zdx en una unidad, estaremos preparando a la aplicacion para


llamar de nuevo a la funcion buidQuestion ( ) . No olvidemos que en la linea 39,
si 4Zdx es mayor que el numero de preguntas del examen (este numero se ha de-
finido en la variable howMany), sera hora de proceder con la valoracion de resul-
tados. La declaracion if de la linea 5 3 determina si el usuario puede detener el
examen. El c6digo con el que estamos trabajando requiere que el usuario com-
plete todas las preguntas antes de poder hacer clic en el boton "Quit Now". En
cualquier caso, podemos configurarlo como mas nos guste.
Examen online 91

gradeTest()
La funcion gradeTest ( ) tiene dos fines. El primer0 es comparar las respues-
tas de 10s usuarios con las respuestas correctas, llevando la cuenta de 10s aciertos.
La segunda, es calcular el indice de acierto y responder basandose en el numero
de respuestas acertadas. Se define en las lineas 66-76 del codigo:

for (var i = 0; i < qIdx; i++) (


if (keeperci] == units[il.answer) {
correct++;
I
}
var idx = Math.ceil((correct/howMany) * (rank.length - 1 ) ) < 0 ? 0 :
Math.ceil((correct/howMany) * (rank.length - 1 ) ) ;
printResults(idx);
itemReset ( ) ;
1

El array keeper contiene cada una de las letras (a,b, c o d) asociadas con las res-
puestas que podra escoger el usuario en cada una de las preguntas. Cada ele-
mento del array units es un objeto de una pregunta que contiene una propiedad
answer (a,b, c o d).gradeText ( ) actua sobre cada uno de 10s elementos keeper
y compara su valor con el de la propiedad answer del elemento units correspon-
diente. Si hay un acierto, entonces el valor de la variable correct se aumentara
una unidad.
Obskrvese que esta funcion no guarda un registro con las respuestas correctas.
De hecho, esto nunca ocurrira en esta aplicacion. La funcion se limita a deter-
minar el numero de respuestas correctas y a calificar el examen basandose en
dicho numero. Cuando revisemos la funcion printResults ( volveremos a
revisar el array keeper. TambiCn conviene destacar que gradeTest ( ) no actua
sobre todas y cada una de las preguntas howMany. No importa la cantidad de
preguntas que tenga el examen. El usuario solo las podra contestar una vez.
Una vez que se obtienen 10s resultados, la variable correct contendra el numero
de respuestas correctas. gradeTest ( ) solo tiene que determinar la calificacion
del usuario. Las lineas 72-73 se encargan de ello.

var idx = Math.ceil((correct/howMany) * (rank.length - 1 ) ) < 0 ? 0 :


Math.ceil( (correct/howMany) * (rank.length - 1)) ;

Funciona de la siguiente manera. Queremos asignar una de las calificaciones


de 10s elementos del array rank de la linea 9. Si queremos seleccionar uno de estos
elementos, necesitaremos un entero comprendido entre 0 y r a n k . lenght - 1.
La funcion gradeTest ( ) seleccionara u n entero a travks de u n proceso de tres
pasos:
92 Examen online

1. Calcula el porcentaje de respuestas correctas (correct / howMany).


2. Multiplica el porcentaje por (rank.lenght - 1).
3. Redondea el product0 a1 siguiente numero entero.

El resultado del proceso se guarda en la variable local idx, que es un entero pro-
porcional a la efectividad del usuario, cuyo valor estara comprendido entre 0 y
rank. length.En otras palabras, no importa la cantidad de preguntas que haya.
El usuario siempre recibira una calificacion que se basara en el numero de res-
puestas acertadas. El siguiente ejemplo nos ayudara a comprenderlo mejor. Su-
pongamos que la configuracion del array rank es la siguiente:

var rank = new Array( "Los he visto mejores", " N o esta mal", "Bien",
"Muy bien" , "Excelente")

rank. length es 5 por lo que, si el examen tiene 50 preguntas, la escala de


calificaciones sera la siguiente:

Preguntas correctas Entero resultante Valoracion (rankfinit])


~

0-9 0 "Los he visto mejores"


10-19 1 "No esta mal"
20-29 2 "Bien"
30-39 3 "Muy bien"
40-50 4 "Excelente"

Es decir, que aproximadamente tenemos howMany / rank. length respuestas


por cada calificacion (a excepcion del ranking mas alto). N o importa si hubiese
2 6 200 preguntas. El funcionamiento seria el mismo.
Este sistema de calificacion tiene la ventaja de ser efectivo, per0 la gran desven-
taja de ser demasiado crudo. Generalmente, la mayoria de 10s sistemas de eva-
luacion son mas complejos. En la mayoria de 10s colegios, si el resultado esta
por encima del 90%, se otorga una A, si se encuentra entre el 80-89%, entonces
el resultado sera una B, entre el 70-79% sera una C y 60-69% una D. Todo lo
que se encuentre por debajo de 60%sera F. Si lo deseamos, podemos utilizar este
sistema de calificacion. Ya lo veremos un poco mas adelante, dentro de este mis-
mo capitulo.
gradeTest ( ) ya ha completado su trabajo. Ahora la variable idx se le entrega
a la funcion printResults ( ) para que se encargue de mostrarla en pantalla.
A continuacion se llama a itemeset ( ) para que deje todo como estaba, con sus
valores iniciales.
Examen online 93

printResults0
L a aplicacion ya conoce la calificacion del usuario. Ahora se la tiene que mos-
trar. La f u n c i 6 n p r i n t R e s u l t s ( ) se encarga de mostrar en pantalla 10s siguien-
tes elementos:

El numero de respuestas correctas y el total de preguntas.


La calificacion del usuario basandose en la valoracion de gradeTest ( ) .
Las preguntas del examen, cada una de ellas con sus cuatro posibles res-
puestas.
La opcion del usuario, tanto si era correcta como si no.
Un texto asociado a las preguntas incorrectas para que el usuario conozca
el por quC su respuesta estaba mal.

Las lineas 77-84 se hacen cargo de 10s dos primeros puntos:

function printResults(ranking) {
results = '<HTML></HEAD><BODYBGCOLOR=WHITE LINK=RED VLINK=RED
ALINK=RED>'+
'<FONT FACE=Arial>' +
'<H2>You scored ' + correct + ' / ' + howMany + correctly.</H2>' +
'<B>Ranking: <I>' + ranking +
'</I><BR>Passthe mouse over the red text for an explanation of ' +
'those you misssed.</B>' +
'<BR><BR><FONTSIZE=Q>Here is how you scored: </FONT><BR><BR>';

Las variables correct y howMany representan el numero de respuestas correctas


y el total de preguntas (respectivamente) y r a n k [ r a n k I d x ] representa la cade-
na que indica la efectividad del usuario. L a impresion en pantalla de la pregunta
y de las posibles respuestas se desarrolla a travCs de un buclefor que tiene lugar
entre las lineas 85 y 91.

for (var i = 0; i < howMany; i++) {


results += '<B>Question + (i + 1) + '</B><BR>'t
units[i].question + '<BR><BR><FONTSIZE=-l>' +
'a. ' + unitsli1.a + '<BR>'+
'b. ' + unitsri1.b + '<BR>' +
'c. ' + unitsri1.c + '<BR>' +
'd. ' + units[i].d + '<BR></FONT>';

Para cada repetition de 0 a howMany - 1, a resuZt se le asignara una cadena


que contiene el numero de la pregunta (i + l), su texto ( u n i t s [ 1I .q u e s t i o n )
junto con las cuatro respuestas posibles ( u n i t s [ i ] .a, u n i t s [ i I .b, u n i t s [ i I .c
y u n i t s [ i I .d). El resto de codigo mejora el aspect0 de la informacidn en la
pantalla.
94 Examen online

La ultima pieza del puzzle es mostrar a1 usuario las respuestas correctas en


verde y las incorrectas en rojo, junto con un texto explicativo asociado. Lo ve-
mos en las lineas 92-106:

if (keeper[il == units[il.answer) {
results += '<B><I><FONTCOLOR=GREEN>' +
'You answered this correctly ( ' + keeper[il + ' ) . ' +
'</FONT></I></B>\n\r<BR><BR><BR>';
I
else {
results += '<FONT FACE=Arial><B><I>'+ '
' < A HREF=" " onMouseOver="parent.frames[Ol .show(); ' +
parent.frames[Ol.explain(\" + units[i].support + ' \ ' ) ; ' +
' return true" onMouseOut="parent . frames 101 .explain ( \ ' \ * ) ;" ' +
'onClick="return false; " > ' +
'The correct answer is: ' + units[i].answer +
'</A></FONT></I></B>\n\r<BR><BR>';
I
I

Para cada pregunta, el usuario seleccionara una respuesta, que puede ser correc-
ta o incorrecta. Para determinarlo se utiliza una declaracion if-else. Si keeper [ i 3
es igual a units [ i ] .answer, la respuesta del usuario habra sido la correcta.
Por lo tanto, un texto en verde mostrara el contenido de la respuesta (keeper[ i 3 ).
Si no coinciden, aparecera un texto en rojo que permitira acceder a una infor-
maci6n adicional en la cual se mostrara informacion acerca de la respuesta en
parent. frames [ 1] . Este cuadro se utilizara para mostrar este tip0 de infor-
macion a1 usuario.
Las respuestas que se hayan contestado correctamente se mostraran como
texto simple. Per0 las incorrectas apareceran en rojo en un texto que tiene u n
hipervinculo. El evento onMouseOver de cada uno de estos vinculos se encargara
de llamar a las dos funciones antes de devolver el valor true. Se trata de show ( )
y explain ( ) . La primera es muy sencilla. Imprime una cadena vacia en la barra
de estado para evitar cualquier distraccion generada a partir de onMouseOver.
Su codigo se encuentra en la linea 10.

function show() ( parent.status = "; )

La funcion explain ( ) acepta una cadena como argument0 y utiliza el mtto-


do documento. write ( para mostrar, a travts de c6digo HTML, la informa-
cion en parent. frames [ l l . Revisamos el contenido de las lineas l 11-1 18.

function explain(str) {
with (aFrame.document) {
open();
Examen online 95

writeln('<HTML><BODYBGCOLOR=WHITE><FONT FACE=Arial>' + str +


'</FONT></BODY></HTML>');
close ( ) ;

Aunque ya nos hemos tenido que hacer cargo del evento onMuoseOver, la fun-
cion explain ( ) aun necesitara mas atenciones. Observese que se vuelve a lla-
mar a esta funcion en la linea 101, en el controlador del evento onMouseOut.
Pero esta vez, se le entrega una cadena vacia, de tal forma que parecera que se
limpia el contenido de aFrame despuks de cada evento onMouseOver.
Ahora, solo nos queda evitar que se desencadene alguna accion cuando el usuario
haga clic sobre el hipervinculo de texto que se activa cuando se coloca el punte-
ro sobre d . En la linea 102 encontramos onClick="returnfalse;". Con esta
declaracion cancelamos la carga de cualquier documento que aparezca en el URL
del atributo HREF.
N o olvidemos que aun nos encontramos dentro del buclefor. El proceso ante-
rior se repetira con las respuestas comprendidas entre 0 y howMany - 1. Obvia-
mente, despuks de la ejecucion del bucle, el valor de la variable results sera una
gran cadena que contenga el numero de la respuesta correcta, el numero total de
preguntas del examen, el texto de todas ellas y de sus posibles respuestas, y la
seleccionada por el usuario o la respuesta correcta. En las lineas 107-109 se afia-
de algo de codigo para finalizar el HTML, cargar la cadena dentro del cuadro
inferior de la pantalla y cerrar la funcion.

results += '\n\r</BODY></HTML>';
qFrame.location.replace("javascript: parent.frames[Ol.results");
}

chickenOut()
Permitir que el usuario abandone el examen antes de completarlo representa
un pequeiio problema. La verdad es que no nos hace falta esta opcion para nada
y podemos eliminarla de nuestras implementaciones. La he aiiadido para que el
usuario tenga algo mas de libertad. Su codigo esta entre las lineas 60-65:

function chickenout0 {
if(stop0K & & confirm('Stopping early? Are you really a ' +
'JavaScript Chicken?') I
gradeTest ( ) ;
1
1

Si el usuario puede elegir y confirma que desea salir antes de completar el exa-
men, se llamara a la funcion gradeTest ( ) . Recordemos que u n usuario puede
96 Examen online

salirse del examen en cualquier momento, despues de contestar a la primera pre-


gunta. Originalmente, el valor de la variable stopOK es f a l s e . Per0 cuando el
usuario confirma su acci6n (es decir, cuando qZDx es mayor que l ) ,pasara a ser
t r u e . Vease la linea 5 3 .
La funci6ngradeTest ( ) comparara las respuestas de todas las preguntas, aun-
que el usuario no las haya contestado. Este sistema perjudica mucho a1 usuario,
per0 es el precio que se ha de pagar por abandonar el examen antes de tiempo.

Posibles ampliaciones
Esta aplicacion se puede modificar de muchas formas. Per0 las dos mas obvias
(a mi parecer) es mejorar el sistema para que no se pueda hacer trampas a1 ser-
vidor y hacer que la aplicacion tambikn se pueda hacer cargo de encuestas en
vez de limitarse a examenes.

A prueba de trampas
Una de las primeras cosas que tenemos que plantearnos a la hora de desarro-
llar la aplicacion es que 10s usuarios podran revisar las preguntas bajandose el
archivo donde se encuentra en c6digo fuente en JavaScript. Puede llevar algo de
trabajo revisar el codigo para localizar la respuesta correcta a cada pregunta,
per0 se puede hacer.
Podemos eliminar este riesgo no enviando las respuestas con la aplicacion y
solicitando a 10s usuarios que envien sus respuestas a1 servidor para que este se
encargue de su evaluacion. N o haremos la evaluaci6n del examen en el servidor,
per0 tampoco trabajaremos con gradeTest ( ) . Puede ser un poco mas laborio-
so, per0 10s principios a seguir son 10s mismos.
Para eliminar la propiedad de evaluacion y permitir que se pueda enviar el exa-
men a1 servidor, tendremos que hacer lo siguiente:
Eliminar cualquier representacion de las respuestas del objeto y el array del
archivo questions.js.
Eliminar gradeText ( ) y a continuacion sustituir la llamada que se efec-
tua desde b u i l d Q u e s t i o n ( ) con p r i n t R e s u l t s ( ) .
ModificarprintResul t s ( ) de tal forma que el usuario pueda ver sus res-
puestas e incluir estos datos dentro de un formulario HTML para enviar-
selas a1 servidor.

Eliminar las respuestas del array


Vamos a eliminar t h i s . answer y t h i s . s u p p o r t del constructor de pregun-
tas que esta en el archivo question.js. Tendremos que cambiar la siguiente:
Examen online 97

function question(answer, support, question, a, b, c, d) {


this.answer = answer;
this.support = support;
this.question = question;
this.a = a;
this.b = b;
this.c = c;
this.d = d;
return this;
}

function question(question, a, b, c, d) {
this.question = question;
this.a = a;
this.b = b;
this.c = c;
this.d = d;
return this;
}

ObsCrvese que las variables answer y support tambiCn se han eliminado. Ahora
que no las tenemos en el constructor, las eliminaremos de todas las llamadas que
se efectuen desde el operador new que hay en cada elemento de units. En otras I

palabras, eliminaremos 10s dos primeros argumentos de 10s elementos de units.

Eliminar gradeTest() y modificar buildquestion()


Como ya no tenemos respuestas ni explicaciones, no hay ninguna razon para
evaluar el examen ni para mostrar 10s resultados. Es decir, que podemos desha-
cernos de la funcion gradeText ( ) . Eliminaremos las lineas 66-67 del archivo
adrninister.htrnZ. Es decir, que eliminaremos las llamadas a gradeTest ( ) que se
encuentren en buidQuestion ( ) , en la linea 40. En realidad, lo que queremos
hacer es sustituirlo con una llamada a printResults ( ) con objeto de que el
usuario pueda ver sus respuestas y Cstas se incluyan en un formulario HTML.
Las lineas 39-42:

if (qIdx == howMany) {
gradeTest ( ) ;
return;
}

se cambiaran por:

i f (qIdx == howMany) I
printResults ( ) ;
98 Examen online

return;
1

Modificar printResults0
En la funcion PrintResults ( ) sera donde tenga lugar la mayor parte del tra-
bajo. La linea 84 de administer.htm1 contiene el siguiente codigo:

'<BR><BR><FONTSIZE=4>Here is how you scored: </FONT><BR><BR>';

La cambiamos por:

'<BR><BR><FONTSIZE=4>Here is how you scored: </FONT><BR><BR>'+


'<FORM ACTION="your-server-script-URL" METHOD=POST>';

Y sustituimos las lineas 92-105 por lo siguiente:

results += '<INPUT TYPE=HIDDEN NAME="question' + (i + 1) + VALUE="'


' ' I +
keeperLi1 + '"><B><I><FONT>COLOR=GREEN>Youchose ' + keeper[i] +
'</I></B></FONT><BR><BR>';

De esta forma hemos eliminado parte de la funci6n que determina si el usuario


ha contestado correctamente a la respuesta. Si lo ha hecho, mostrara el texto en
verde. En caso contrario, en rojo. Por ultimo, la linea 107 dice lo siguiente:

results += '\n\r</BODY></HTML>';

Y la cambiamos por:

results += '<INPUT TYPE=SUBMIT VALUE="Submit"> </FORM></BODY></HTMLz';

Cada respuesta se escribe como un valor de un campo oculto, cuyo nombre se


corresponde con el numero de la pregunta. La variable iterativa i se utiliza para
crear un nombre unico para cada uno de 10s campos ocultos y se asocia con el
numero de la pregunta y con la respuesta del usuario. Cada vez que se incrementa
la variable i para actualizar la posici6n del bucle for (i++),se creara un nuevo
campo oculto. El modelo que se sigue es questionl, question2, question3, y asi
hasta el infinito.
Los cambios que hemos realizado en PrintResults ( ) todavia mostraran las
preguntas, las cuatro respuestas posibles y tambiCn la respuesta del usuario. Per0
no se procedera a la calificacion del examen. Todo lo que puede hacer el usuario
es clic en el b o t h de envio con objeto de que sea el servidor quien se encargue de
la evaluation.
Examen online 99

Convertir la aplicacion en una encuesta


Como las encuestas (teoricamente) no tienen respuestas correctas-incorrectas,
para modificar esta aplicacion para que se haga cargo de encuestas tendremos
que hacer 10s cambios que acabamos de ver y otra adicional: ajustar el conteni-
do. Bastara con cambiar 10s elementos de units para que reflejen las preguntas
de la encuesta con sus respectivas opciones. Gracias a 10s cambios que hemos
realizado en el apartado anterior, el usuario podra ver 10s resultados antes de en-
viarlos para que el departamento de marketing proceda a su analisis.
Capitulo 3
Diapositivas interactivas

Con esta aplicacion 10s usuarios podran ver un conjunto de diapositivas, en


cualquier orden o bien de principio a fin, dejando que sea la propia aplicacion
quien lo haga, mostrando cada ficha durante una cantidad de tiempo que podra
determinar el usuario. Cada diapositiva es una capa DHTML que contiene una
imagen y una descripcion en texto. Entre todas, contienen casi todas las combi-
naciones posibles de texto, graficos y DHTML. Con ellas, se dara una vuelta por
un reino animal imaginario. En la figura 3.1 podemos ver la pantalla de inicio.
ObsCrvese que el contenido de la diapositiva aparece en el centro de la pantalla
y en la parte superior izquierda hay dos graficos con las etiquetas "Automate" y
"<Guide>". Las fechas de estas ultimas (< y >) seran las que permitan a1 usua-
rio pasar a la siguiente o a la anterior diapositiva.
Los usuarios tambikn pueden saltar directamente a cualquiera de las diapositivas
de la aplicacion haciendo clic en "Guide". Se abrira un menu con 10s nombres de
las fichas. Para saltar a la deseada, bastara con hacer clic sobre el nombre corres-
pondiente. Si queremos cerrar el menu, tendremos que hacer clic en "Guide" de
nuevo. La figura 3.2 nos muestra este menu.
En 10s capitulos anteriores, las aplicaciones recorrian 10s procesos de la aplica-
cion de principio a fin. Es decir, el usuario siempre empezaba en el mismo sitio
(introduciendo texto o contestando alguna pregunta) y terminaba igual (en una
pagina de resultados o con la calificacion de su examen). La aplicacion que ve-
remos en este capitulo es distinta. Los usuarios pueden saltar a cualquier lado
aprovechando las propiedades del programa. Por lo tanto, lo mejor que podemos
hacer es describir el codigo de la aplicacion per0 desde el punto de vista de sus
propiedades, en vez de repasarlo de principio a fin.
102 Diapositivas interactivas

Automaw
Animal Kingdom Slideshow
Common Name:
Bird
S d a d k kNmc:

Bomb-& Cu-us
Absrract
l h s q c d cicahyc has
brcn known to reek out and
sod bcshly-washed veluclcr

Figura 3.1. Primera diapositiva

Animal Kingdom Slideshow


Common Name:
Con,
Sdcntinc Name:

G&O*

Absrrrrt
l h r m s l IS conridcrcd a
moover and shaker. and
tcndr to nvlk thmgr for dl

Figura 3.2. El nombre seleccionado representa a la dispositiva de la pantalla


DiaDositivas interactivas 103

Requisitos para la ejecucion


En el momento en que afiadimos una "D" a "HTML"y obtenemos DHMT, esta-
remos hablando de MSIE 4.x y Navigator 4.x. Todas las fichas se basan en enti-
dades DHTML. Si queremos crear una aplicaci6n que funcione con versiones
anteriores de estos navegadores, no hay problema. Per0 en este caso, estamos car-
gando una serie de imagenes (antes de proceder a su visualizaci6n) en el explo-
rador. La verdad es que podria llegar a ser complicado proceder con la carga de
cientos de imagenes si no se utilizan las propiedades DHTML.

Analisis de la sintesis
I

El scipt se encuentra dentro del archivo index.html. El ejemplo 3.1 nos muestra
su contenido.

Ejemplo 3.1. index.htm1

1 <HTML>
2 <HEAD>
3 <TITLE>The Slideshow</TITLE>

5 <STYLE TYPE='' text/ CSS >


6 #menuconstraint { height: 800; )
7 </STYLE>
8
9 <SCRIPT LANGUAGE="JavaScriptl.2">
10 <!--
11 var dWidLyr = 450;
12 var dHgtLyr = 450;
13 var curslide = 0;
14 var zIdx = -1;
15 var isvis = false;
16
17 var NN = (document.layers ? true : false);
18 var sWidPos = ( ( N N ? innerwidth : screen.availWidth) / 2 ) -
19 (dWidLyr / 2 ) ;
20 var sHgtPos = ( ( N N ? innerHeight : screen.avai1Height) / 2 ) -
21 (dHgtLyr / 2 ) ;
22 var hideName = ( N N ? 'hide' : 'hidden');
23 var showName = (NN ? 'show' : 'visible');
24
25 var img = new Array();
26 var imgOut = new Array();
27 var imgOver = new Array ( ) ;
28 var imgPath = 'imagesf';
29
30 var showspeed = 3500;
104 Diapositivas interactivas

31 var touron = false;


32
33 function genLayer(sName, sLeft, sTop, sWdh, sHgt, sVis, copy) {
34 if ( N N ) (
35 document.writeln('<LAYERNAME="' + sName + "' LEFT=' +
sLeft +
36 ' TOP=' + sTop + ' WIDTH=' + sWdh + HEIGHT=' + sHgt +
37 ' VISIBILITY="' + sVis + "" + ' Z-INDEX=' + (++zIdx) +
'>' +
38 copy + '</LAYER>');
39 )
40 else {
41 document.writeln('<DIV I D = " '+ sName +
42 "' STYLE="position:absolute;overf1ow:none;left:' + SLeft +
43 'px; top:' + sTop + 'px; width:' + sWdh + 'px; height:' +
sHgt +
44 'px;' + visibility:' + sVis + ' ; z-Index=' + (++zIdx) +
3 ">' +

45 copy + '</DIV>');
46 I
47 }
48
49 f function slide(imgStr, scientific, copy) {
50 this.name = imgStr;
51 imagePreLoad(imgStr);
52 this.copy = copy;
53 this.structure =
54 '<TABLE WIDTH=500 CELLPADDING=lO><TR><TD WIDTH=60%
VALIGN=TOP>' +
55 '<IMG SRC=' + imgPath + imgStr + '.gif></TD>'+
56 '<TD WIDTH=40% VALIGN=TOP><H2>Common Name:</H2><H2><1>' +
57 camelCap(imgStr) + '</I></H2><H3>ScientificName:
</H3><H3><1>'+
58 scientific + '</I></H3>'+ '<B>Abstract:</B><BR>'+ copy 4
59 '</TD>~/TR></TABLE>';
60
61 return this;
62 1

64 function imagePreLoad(imgStr) {
65 img [img.length] = new Image ( ) ;
66 img[img.length - 11.src = imgPath + imgStr + '.gif';
67
68 imgOut [imgOut.length] = new Image ( ;
69 imgOut[imgOut.length - 11.src = imgPath + imgStr + 'out.gif';
70
71 imgOver[imgOver.lengthl = new Image();
72 imgOver[imgOver.length - 11.src = imgPath + imgStr +
'over.gif';
73 I
74
DiaDositivas interactivas 105

75 var slideshow = new Array(


76 new slide('bird','Bomb-zis Car-zes', 'This winged creature has
77 been known to seek out and soil freshly-washed vehicles.'),
78 new slide('walrus', 'Verius Clueless', 'These big fellas good
79 fishers, but toothbrushing is another story.'),
80 new slide('gator','Couldbeus Luggajus', 'These reptiles often
81 play mascots for large college sporting events.'),
82 new slide('dog', 'Makus Messus', 'Man\'sbest friend? Yeah,
83 right. No wonder these mammals get a bad rep.'),
84 new slide('pig','Oinkus Lotsus', 'Humans with questionable
85 eating habits are often compared to these farm creatures.'),
86 new slide('snake', 'Groovius Dudis', 'Slick and sly with a
87 watchful eye.'),
88 new slide('reindeer', 'Redius Nosius', 'Though co-workers used
89 to laugh and call him names, he eventually won the respect of
90 team. ' ) ,
91 the entire new slide('turkey','Goosius Is Cooktis', 'Celebrated
92 and revered for an entire year, then served as dinner
93 shortly after.'),new slide('cow', 'Gotius Milkus', 'This animal
94 is considered a moover and shaker, and tends to milk things
95 for all they\'re worth. Udderly shameful.'),
96 new slide('crane', 'Whooping It Upus', 'Not to be confused with
97 a piece of heavy construction equipment. Rumored as the
98 source of the nickname <I>birdlegs</I>.')
99 ):
100
101 function camelCap(str) {
102 return str.substring(0, l).toUpperCase() + str.substring(1);
103 1
104
105 function genScreen0 {
106 var menuStr = " ;
107 for (var i = 0 : i < slideShow.length; i++) {
108 genLayer('s1ide'+ i, sWidPos, sHgtPos, dWidLyr, dHgtLyr,
109 (i == 0 ? true : false), slideShow[il.structure);
110 menuStr += ' < A HREF="" onMouseOver="hideStatus( ) : if ( ! touron)
111 { setslide(' + i + ' ) ; ' +
112 ' imageswap(\" + slideShow[il.name + ' \ ' , ' + i + ' ,
true)1 :
113 return true: ' +
114 ' onMouseOut="if(!tourOn){ setslide(' + i + ' ) ; ' +
115 ' imageswap(\" + slideShow[il.name + ' \ ' , ' + i + ' ,
false)) ;
116 return true: '' ' +
117 ' onClick="return false;"><IMG NAME="' +
slideShow[il .name +
118 "' SRC="'+ imgPath + slideShow[i].name +
119 'out.gif" BORDER=O></A><BR>';
120 1
121
122 genLayer('automation',sWidPos - 100, 11, 100, 200, true,
106 Diapositivas interactivas

123 '<A HREF="javascript: autoPilot ( 1 ; "


onMouseOver="hideStatus( ) ;
124 return true: " > ' +
125 '<IMG SRC="' + imgPath + 'autornate.gif"BORDER=O></A>'
126 ):
127
128 genLayer('guide', sWidPos - 100, 30, 100, 200, true,
129 '<A HREF="javascript: if(!tourOn) { changeSlide(-l); 1''
130 onMouseOver="hideStatus( ) : return true: " > ' +
131 '<IMG SRC="' + imgPath + '1eftout.gif" BORDER=O></A>' +
132 '<A HREF="javascript: if ( ! touron) { menuManager ( ) ; 1 "
133 onMouseOver="hideStatus( 1 ; return true;" > ' +
134 '<IMG SRC="' + imgPath + 'guideout.gif"BORDER=O></A>' +
135 '<A HREF="javascript: if(!tourOn) { changeSlide(1); ) "
136 onMouseOver="hideStatus( 1 ; return true;" > ' +
137 '<IMG SRC="' + imgPath + 'rightout.gif"BORDER=O></A>'
138 ):
139
140 genLayer('menu',sWidPos - 104, 43, 100, 200, false,
141 ' <DIV ID="menuConstraint"><TABLE><TD>
' +
142 menuStr + '</TD></TABLE></DIV>'
143 ):
144 )
145
146 function refSlide(name) (
147 if ( N N ) ( return docurnent.layers[namel; )
148 else { return eval( 'document.all.' + name + ' .style' ; )
149 )
150
151 function hideslide(narne) (
152 refSlide(name).visibility = hideName:
153 )
154
155 function showSlide(name) (
156 refSlide(name).visibility = showName;
157 )
158
159 function menuManager0 (
160 if (isvis) ( hideSlide('menu'); >
161 else { showSlide('menu');)
162 isvis = !isVis;
163 1
164
165function changeSlide(0ffset) {
166 hideSlide('s1ide' + curslide);
167 curslide = (curslide + offset < 0 ? slideShow.length - 1 :
168 (curslide + offset == slideShow.length ? 0 : curslide +
offset));
169 showSlide('s1ide' + curslide);
170 }
171
172 function setslide (ref) {
Diapositivas interactivas 107

173 if (touron) { return; 1


174 hideSlide('s1ide' + curslide);
175 curslide = ref;
176 showSlide('s1ide' + curslide);
171 1
178
179 function imageSwap(imagePrefix, imageIndex, isover) (
180 if (isover) ( document[imagePrefixl.src =
imgOver[imageIndexl.src; 1
181 else ( document[imagePrefix].src = imgOut[imageIndexl.src; 1
182 1
183
184 function hidestatus0 window.status = " ; 1
185
186 function autopilot0 (
187 if (touron) (
188 clearInterval(auto);
189 imageSwap(slideShow[curSlidel .name, curslide, false);
190 }
191 else {
192 auto = setInterval('automate0 ' , showspeed);
193 imageSwap(slideShow[curSlidel.name,curslide, true);
194 showslide ( 'menu' ;
195 visible = true;
196 I
197 tourOn = !touron;
198 >
199
200 function automate ( ) (
201 imageSwap(slideShow[curSlide].name, curslide, false);
202 changeSlide(1);
203 imageSwap(slideShow[curSlide].name, curslide, true);
204 }
205
206 / / - - >
207 < /SCRIPT>
208 </HEAD>
209 <BODY BGCOLOR=WHITE>
210 <CENTER>
211 <FONT FACE=Arial>
212 <H2>Animal Kingdom Slideshow</H2>
213 </FONT>
214 </CENTER>
215 <SCRIPT LANGUAGE="JavaScriptl.2">
216 < ! - -
217 genScreen ( ) ;
218 / / - - >
219 </SCRIPT>
220 < /FONT>
221 </BODY>
222 < /HTML>
108 DiaDositivas interactivas

Variables de la aplicacion
Vamos a empezar fijandonos en las variables y en otros detalles. Luego pasare-
mos a las funciones. Este es el contenido de las lineas 5-7:

<STYLE TYPE="text/css">
#menuconstraint { height: 800; )
</STYLE>

Define una hoja de estilos donde 10s elementos se superponen unos a otros, lla-
mada rnenuCunstraint y una unica propiedad: un alto de 800 pixeles. Se aplicara
a todas las diapositivas para asegurarse de que el usuario las puede ver. En otras
palabras, si la resolucion del monitor del usuario tiene una altura inferior a 800
pixeles, la aplicacion mostrara barras de desplazamiento. Este hecho es muy
util cuando nuestras imagenes Sean muy grandes o tengamos que trabajar con
muchas copias. Por lo menos, 10s usuarios que no dispongan de dicha resolu-
cion podran desplazarse por el contenido de la pantalla para ver el resto de la
diapositiva. En las lineas 11-31 tenemos las variables:

var dWidLyr = 450;


var dHgtLyr = 450;
var curslide = 0;
var zIdx = -1;
var isvis = false;

var NN = (document.layers ? true : false);


var sWidPos = ((NN ? innerwidth : screen.availWidth) / 2) -
(dWidLyr / 2);
var sHgtPos = ( ( N N ? innerHeight : scr en.availHeight) / 2) -
(dHgtLyr / 2);
var hideName = (NN ? 'hide' : 'hidden')
var showName = (NN ? 'show' : 'visible'

var img = new Array ( ) ;


var imgOut = new Array ( ) ;
var imgOver = new Array ( ) ;
var imgPath = 'images/';

var showspeed = 3500;


var touron = false;

Las variables se dividen en cuatro grupos:


Capas DHTML predeterminadas.
Variables determinadas por el explorador Web.
Variables relacionadas con la imagen.
Variables relacionadas con la ejecucion automatica.
Diapositivas interactivas 109

Capas DHTML predeterminadas


Las variables dWidLyr y dHgtLyr se limitan a declarar el ancho y el alto prede-
terminado de las diapositivas. L a variable curslide siempre contendrh un valor
que sera el indice de un array que se corresponde con la vista de la diapositiva que
se encuentra en la pantalla. L a variable zldx asigna un valor de indice z a todas
las capas que se creen en esta parte de la vista. La variable z l d x asignara un va-
lor a Ias nuevas capas y la variable isVis contendra un valor booleano que in-
dicara si la capa es visible o no.
~~ ~ ~ ~

Nota: Generalmente hablo de las diapositivas como capas DHTML o sim-


plemente capas. No hemos de confundirlas con las etiquetas LAYER de
Netscape, que se utilizan para crear estas capas en Navigator. En otras pa-
labras, LAYER es una etiqueta propia de Navigator, no de MSIE.

Variables propias de 10s exploradores


El valor de las siguientes variables, N N , sWidPos, sHgPog, showName y hideName
se determinara en consonancia con el explorador donde se cargue la aplicacidn.
La variable NN de la linea 1 7 sera true si existe la propiedad layer del objeto do-
cumento. En otras palabras, si el explorador que carga la aplicacion es Netscape
Navigator 4.x, esta implernentacion del modelo de objetos de Netscape puede
trabajar con capas:

var NN = (document.layers ? true : false);

En cualquier otro caso, el scipt asumira que se trabaja con MSIE 4.x y asignara
a NN el valor false. El modelo de ohjrtos de Microsoft hace referencia a las capas
layers del objeto styles de docurnentall. Las variables sWidPos y sHgPos contie-
nen 10s valores de las coordenadas x e y de la esquina superior izquierda, donde
se colocara la capa, de tal forma que Cste sera el centro de la ventana del explo-
rador (no de la pantalla). Esto se determina a partir del valor de NN y de las
variables sWidPos y sHgPos. Este es el contenido de las lineas 18-21.

var sWidPos = (INN ? innerwidth : screen.availWidth) / 2) -


(dWidLyr / 2);
var sHgtPos = ( ( N N ? innerHeight : screen.availHeight) / 2) -
(dHgtLyr / 2 ) ;

iC6mo localizamos 10s valores de las coordenadas x e y? Podemos calcular el


centro de coordenadas dividiendo el ancho de la ventana por 2 con objeto de
obtener la coordenada x y el alto de la ventana del explorador por la misma
110 Diapositivas interactivas

cantidad para determinar el valor de la coordenada y. En otras palabras, el cen-


tro de coordenadas de la ventana equivaldra a1 ancho de ventana (en pixeles) / 2,
alto de ventana (en pixeles) / 2 .
Ahora ya tenemos el centro de la ventana. Como estas coordenadas tambien
serhn el centro de las capas, podremos calcularlas restando la mitad de sWidLyr
del valor x del centro de coordenadas y la mitad de sHgLyr del valor y.
Las otras dos variables propias de 10s exploradores que nos quedan por ver son
las cadenas que contienen el nombre del estado (visible u oculto) de las capas, de
acuerdo con el DOM que se utilice. El contenido de las lineas 22 y 2 3 es el que
vemos a continuacion:

var hideName = (NN ? 'hide' : 'hidden');


var showName = (NN ? 'show' : 'visible');

De acuerdo con el DOM perteneciente a Netscape, las capas ocultas tienen su


propiedad visibility configurada a hide, mientras que el valor de la misma pro-
piedad del DOM de Microsoft sera hidden. Por el contrario, las capas visibles de
Netscape tendran el valor show de visibility, mientras que las de Microsoft sera
visible.
Y de acuerdo con el DOM de Netscape, 10s valores del alto y ancho de la venta-
na se encuentra dentro de las propiedades innerwidth e innerHeight del objeto
window, mientras Microsoft guarda estos valores en las propiedades availwidth
y availHeight del objeto screen. Como utilizamos la variable N N justo para esto,
JavaScript sabra a quC propiedades se ha de dirigir.

Variables relacionadas con la imagen


El siguiente grupo de variables se componen de arrays que controlaran las ima-
genes. Su codigo se encuentra en las lineas 2 5 - 2 8 :

var img = new Array( ) ;


var imgOut = new Array( ) ;
var imgOver = new Array( ) ;
var imgPath = 'images/';

Es muy sencillo. Las imagenes que se guardan en el array img representan las
imagenes de las diapositivas. Las que se encuentran en el array imgOut se utili-
zan para el menu. Las que se encuentran dentro de imgOver se utilizan para el
menu de ejecucion automatica. Volveremos a ver las imagenes relacionadas con
la reproduccion automatica cuando estudiemos la funcion swapImage ( ) .
La ultima variable, imgPath contiene el valor de la ruta del servidor donde se
encuentran las imagenes. Podemos hacer que esta ruta sea absoluta o relativa.
Diarsositivas interactivas 111

Las absolutas contiene la localizacion completa de 10s archivos, el nombre del


dominio y el host, la direccion IP del servidor Web, o bien la letra de una unidad
de disco local (C : \). A continuacion aparecerB el nombre del directorio donde se
encuentran situadas las imagenes. Aqui podemos ver un par de ejemplos de
todo esto:

var imgPath = 'http://www.serve.com/hotsyte/';


var imqPath = 'C:\\Winnt\\Profiles\\Administrator\\Desktop\\';

Para generar una barra invertida como las utilizadas en el sistema operativo
de Windows tendremos que utilizar dos (\\). Si no lo hacemos, JavaScript pen-
sara que queremos escribir:

C:WinntProfilesAdministratorDesktop;

No se trata de algo que sea incorrecto. Lo unico que provocara sera un error de
sintaxis.

Variables relacionadas con la ejecucion autornatica


Las dos ultimas variables, showspeed y touron, representan la velocidad con la
que cambia la diapositiva que aparece en la pantalla para que aparezca la si-
guiente, en aquellos casos en los que la visualizacion de diapositivas se encuen-
tra en modo automatico. Su codigo se encuentra situado dentro de las lineas 3 0
y 31:

var showspeed = 3500;


var tourOn = false;

La variable showspeed se mide en milisegundos. Podemos aumentar el tiempo


entre cambios. Por ejemplo, si queremos que sea de 10 segundos, tendremos que
escribir 1 0 0 0 0. Podemos intentar que se muestren a toda velocidad. Si el valor
que insertemos en 1 0 nos encontraremos que cuando se cargue la primera ima-
gen aun no se habra procedido con la ejecucion del modo automatico, por lo
que el valor de tourOn sera f a l s e .

Funciones de la aplicacion
Las funciones de la aplicacion se agrupan en tres categorias diferentes: crea-
cion de la capa, control de la imagen y navegacion/pantalla. En la tabla 3 . 1 po-
demos ver la descripcion de cada una de ellas y t a m b i h se especifica la clase a la
que pertenece.
112 DiaDositivas interactivas

Tabla 3.1. Funciones de la aplicacion y sus descripciones

Nombre de la funcion Categoria Descripcion


genLayer ( Capas Genera las diapositivas.
slide ( ) Capas Constructor de objetos para cada
diapositiva.
imagepreload ( ) Imagenes Carga la imagen de la barra de
navegacion y de la diapositiva
antes de que tengan que aparecer
en pantalla.
camelcap ( ) Capas Convierte en mayuscula la
primera letra del nombre de la
diapositiva.
genScreen ( ) Capas Llama a genLayer ( ) y coloca
todas las capas.
hideslide ( ) Capas Oculta las capas.
showslide ( ) Capas Muestra las capas.
refSlide ( ) Capas Devuelve una referencia a las
capas basada en el explorador.
menuManager ( ) Capas Oculta o muestra el menu de las
diapositivas.
changeslide ( ) Capas Cambia la diapositiva que aparece
en la pantalla. Para ello se basara
en las acciones de la ejecucion
automatica o en las flechas de
"Guide".
setslide ( ) Capas Cambia la diapositiva que aparece
en pantalla basandose en 10s
eventos del raton.
imageswap ( ) Imagenes Cambia la diapositiva de la panta-
lla basandose en las opciones del
menu.
hidestatus ( 1 Navegacion Asigna el valor "" a la barra de
estado de la ventana.
autopilot ( ) Navegacion Controla el mod0 de ejecucion
automatica.
automate ( ) Navegacion Se encarga del avance automatic0
de las diapositivas.
DiaDositivas interactivas 113

Funciones relacionadas con las capas


Como la mayor parte de la configuracion de las capas estan relacionadas con
las funciones de las capas, parece logic0 empezar por ellas.

genlayero
Esta funcion DHTML vale para todos 10s exploradores. Cualquier cosa que se
quiera mostrar en la pantalla, independientemente de su tamaiio, cantidad de co-
lores, etc, debe pasar por esta funcion. Su c6digo esta en las lineas 33-47:

function genLayer(sName, sLeft, sTop, swdh, sHgt, SVis, copy) {


if (NN) {
document .writeln( <LAYER NAME=" ' + sName + ' " LEFT= ' + sLeft +
' TOP=' + sTop + ' WIDTH=' + sWdh + ' HEIGHT=' + sHgt +
' VISIBILITY='" + sVis + "" + ' z-Index=' + (++zIdx) + ' > ' t
copy + '</LAYER>');
1
else {
document.writeln('<DIVID="' + sName +
"' STYLE="position:absolute;overf1ow:none; left:' + sLeft +
'px; top:' + sTop + 'px; width:' + sWdh + 'px; height:' + sHgt +
'Px;' + ' visibility:' + sVis + ' ; z-Index=' + (++zIdx) + " ' > ' +
copy + '</DIV>');
1
1

Esta funcion contiene una unica declaracion. genLayer ( ) desarrolla la mis-


ma funcion basica en ambos bloques de codigo. Uno funciona con Netscape; el
otro con MSIE. Debido a la rigidez del modelo de objetos, este es el sistema que
hay que utilizar.
En la linea 3 4 se utiliza la variable N N para determinar si el explorador del
usuario es Netscape Navigator o (posiblemente) Microsoft Internet Explorer. Si
NN es true,entonces el explorador sera Navigator. En cualquier otro caso se
supondra que es MSIE.
Observemos 10s argumentos de la linea 3 3 . SonsiVarne, sLeff, STOP,sWdh, sHgt,
sVis y copy. Independientemente del explorador que se utilice, todas ellas reali-
zan la misma accion. sName representa el nombre que asociaremos con la capa.
d e f t especifica el numero de pixeles que se dejara entre la parte izquierda de la
pantalla y la diapositiva. STOPespecifica lo mismo, per0 tiene en cuenta la parte
superior de la pantalla. sWdg y sHgt indican 10s pixeles que determinaran las
dimensiones de la diapositiva. sVis contiene t r u e o false,dependiendo de si la
capa es visible o no (respectivamente). copy contiene la cadena que se mostrara
con el contenido de la capa. El contenido podria ser HTML, aunque no ocurre
nada si se utiliza texto plano.
114 DiaDositivas interactivas

Independientemente de cual sea el explorador, la funci6n genLayer ( ) llama a1


metodo document.wri teln ( ) y construye una etiqueta LAYER para Navigator
o una D I V para MSIE.
slide()
La funcion slide ( ) es u n constructor de objetos. Los diferentes ejemplos de
s 1ide ( ) contienen detalles sobre cada diapositiva, como el nombre del animal,
el texto descriptivo y el contenido HTML. Veamos a continuacion el contenido de
las lineas 49-62:

function slide(imgStr, scientific, copy) {


this .name = imgstr;
imagePreLoad(imgStr);
this.copy = copy;
this.structure =
'<TABLE WIDTH=500 CELLPADDING=lO><TR><TDWIDTH=608 VALIGN=TOP>' +
'IIMG SRC=' + imgPath + imgStr + '.gif></TD>'+
'<TD WIDTH=40% VALIGN=TOP><H2>Common Name:</H2>iH2><1>' +
camelCap(imgStr) + '</I></H2><H3>Scientific Name: </H3><H3><1>'+
scientific + 'i/I></H3>' + '<B>Abstract:</B><BR>'+ copy +
'</TD></TR></TABLE>';

return this;
}
DiaDositivas interactivas 115

else ( / / Ea %a
/ / Trabeja can JavaScr€pt
/ / ep. e j . documeint:.layerr, atc.
3

La funcion slide ( ) acepta tres argumentos: imgStr, scientifk ycopy. El prime-


ro representa el nombre del animal que se muestra en la diapositiva. Podriamos
decir que es la espina dorsal de cada diapositiva. Creo que es un buen momento
para revisar el convenio de nombres de la aplicacion. Los nombres asociados
con cada objeto slide se determinan en la linea 50.

this.name = imgstr;

imgStr aun se utiliza unas cuantas veces. Comprobamos las lineas 53-59. Aqui
se establece la propiedad strucutre:

this.structure =
'<TABLE WIDTH=500 CELLPADDING=lO><TR><TD WIDTH=60% VALIGN=TOP>' +
'<IMG SRC=' + imgPath + imgStr + '.gif></TD>' +
'<TD WIDTH=40% VALIGN=TOP><H2>Common Name:</H2><H2><1>'+
camelCap(imgStr) + '</I></H2><H3>Scientific Name: < / H 3 > < H 3 > < 1 > '+
scientific + ' < / I > < / H 3 > '+ '<B>Abstract:</B><BR>'+ copy +
'</TD></TR></TABLE>';

Para crear la imagen de una diapositiva sobre la marcha, slide ( ) combina el


HTML de la etiqueta <IMG> con las variables imgPath e imgStr, seguido de ' g i f ' .
Si imgStr es igual a pig, el HTML de la diapositiva sera:

<IMG SRC='images/pig.gif'>

La propiedad structure define el contenido de la diapositiva como si se tratase


de una tabla HTML de una fila y dos columnas. La celdilla de la izquierda con-
tendra la imagen, y la de la derecha contendra el texto de la descripcion. En la li-
nea 5 7 se utiliza imgStr de nuevo para asignar un nombre comun a1 animal.

camelCap(imgStr)

Tecnica de JavaScript:us0 de convenios para nombres


Los convenios de nombres. Los hemos visto en todas parks. Consideremos
por un momento c6mo mejorarfa la aplicaci6n si utilizlisemos nombres
116 Diapositivas interactivas

La funcion camelcap ( ) de las lineas 89-90 se limita a devolver una cadena de


trxtu a la que ha convertido su primera letra en may~scula.Es una simple regla
de formato que pretende mejorar el aspecto del documento. Obskrvese tambien
que el argument0 scientific contiene el nombre cientifico del animal.
Cuando parece que imgStr empieza a funcionar, slide ( ) se lo entrega a la fun-
cion preLoadImages ( ) para que proceda con la siguiente vuelta (ver linea 51).
Esta funcion se encarga de precargar la imagen que se mostrara en la diapositi-
va. Lo veremos en breve.
genScreen()
L a funcion genscreen ( ) utiliza la capacidad de la aplicacion para crear capas
que distribuyan elementos por la pantalla. Es, con diferencia, la funcion que
tiene mas codigo. Acapara las lineas 105-144. Ademas de controlar la creacion
y ubicacion de 10s elementos, tambitn se encarga de las propiedades de navega-
ci6n de la aplicacion gracias a las caracteristicas dinamicas de JavaScript.
Diapositivas interactivas 117

function genScreen0 {
var menuStr = ' I ;

for (var i = 0; i < slideShow.length; i++) {


genLayer('s1ide' + i , sWidPos, sHgtPos, dWidLyr, dHgtLyr,
(i == 0 ? true : false), slideShow[il.structure);
menuStr += ' < A HREF="" onMouseOver="hideStatus();' if(!tourOn)
{ setslide(' + i + ' ) ; +
' imageswap(\" + slideShow[i].name + ' \ ' , ' + i + ' , true))
return true; ' +
' onMouseOut="if(!tourOn) { setslide(' + i + ' ) ; ' +
' imageswap(\" + slideShow[i].name + ' \ ' , ' + i + ' , false)
return true;'' +
' onClick="return false;"><IMG NAME="' + slideShow[il .name +
SRC="
I I4 + imgPath + slideShow[il.name +
I

'out.gif"BORDER=O></A><BR>';
1

genLayer('automation', sWidPos - 100. 11, 100, 200, true,


'<A HREF="javascript: autopilot ( ) ; " onMouseOver="hideStatus( ) ;' i

return true: " > ' +


'<IMG SRC="' + imgPath + 'automate.gif" BORDER=O></A>'
1;

genLayer('guide', sWidPos - 100, 30, 100, 200, true,


'<DIV ID="menuConstraint">' +
'<A HREF="javascript: if(!tourOn) { changeSlide(-l); 1 ' ' ' +
'onMouseOver="hideStatus( ) ; return true; " > ' +
'<IMG SRC="' + imgpath + '1eftout.gif" BORDER=O></A>' +
' < A HREF="javascript: if(!tourOn) { menuManager0; 1 ' ' ' +
'onMouseOver="hideStatus();return true;">'+
'<IMG S R C = " ' + imgpath + 'guideout.gif" EORDER=O></A>' +
'<A HREF="javascript: if(!tourOn) { changeSlide(1); 1 " ' +
'onMouseOver="hideStatus( ) ; return true; " > ' +
'<IMG S R C = " ' + imgpath + 'rightout.gif"EORDER=O></A></DIV>'
);

genLayer('menu',sWidPos - 104, 4 3 , 100, 200, false,


'<DIV ID="menuConstraint"><TABLE><TD>'+
menuStr + '</TD></TABLE></DIV>'
);

Esta funci6n es la responsable de controlar la creacion de todas las capas de las


diapositivas y de 10s tres enlaces de navegacion (uno para el menu de las dia-
positivas, otro para las imagenes "<Guide>" y un tercero que se utiliza para la
imagen "Automate").El buclefor de las lineas 106-122 se encarga de las capas y
genera el contenido del menu:

var menuStr = " ;


for (var i = 0; i < slideShow.length; i++) {
118 DiaDositivas interactivas

genLayer('s1ide' + i, sWidPos, sHgtPos, dWidLyr, dHgtLyr,


(i == 0 ? true : false), slideShow[i].structure);
menuStr += '<A HREF="" onMouseOver="hideStatus( 1 ; ' +
'if( ! touron) { setslide ( ' + i + ' ) ; imageswap(\' ' +
slideShow[i].name + ' + i + ' , true)); return true;"' +
' \ I ,

' onMouseOut="if( ! touron) { setslidel ' + i + ' ) ; ' +


' imageswap(\" + slideShow[i].name + ' + i + ' , false)); '
I \ ' , i
'return true;" onClick="return false;"><IMG NAME="' +
slideShow[i] .name + "' SRC="' + imgPath + slideShow[il .name +
'out.gif" BORDER=O></A><BR>';

Al actuar sobre todos 10s elementos del array slideshow, la creacion de cada
capa se facilita mucho con la funcion genLayer ( ) . Veamoslo con detalle:

genLayer('s1ide' + i, sWidPos, sHgtPos, dWidLyr, dHgtLyr,


(i == 0 ? true : false), slideShow[i].structure);

Pasa como argument0 una buena cantidad de valores. En la tabla 3.2 podemos
ver a cada uno de ellos.
Tabla 3.2. Argumentos para genLayer()

Valor Descripcion
'slide' + i Crea un nombre unico (pero indexado) para
cada una de las diapositivas, algo asi como
slide0, slidel, etc.
sWidPos La distancia en pixeles que separa el lado
izquierdo de la ventana y la diapositiva.
sHgtPos La distancia en pixeles que separa la parte
superior de la ventana y la diapositiva.
dWidLyr h c h o predeterminado de la diapositiva, en
este caso 450.
dHgtPos Alto predeterminado de la diapositiva, en
este caso 450.
( i == 0 ? true : false) Determina si la diapositiva aparecera en
pantalla (true)o no (false).Al principio,
todas las diapositivas estaran ocultas, menos
la primera. Esto ocurre cuando i = 0.
s1ideShow[ i] . structure Contenido de la diapositiva, que incluye
texto y grafico, incluidos en una tabla.
Procede del constructor de la diapositiva.
Diapositivas interactivas 119

La funcion genLayer ( ) se llama las veces que determine slideshow.lenght


para crear una capa para cada diapositiva. Da igual el numero de diapositivas
que tengamos. Esta linea se hara cargo de todas ellas. El resto del codigo corres-
pondiente a genscreen ( ) esta dedicado a obtener tres capas de navegacion en
la pantalla. Per0 antes de seguir veamos otro us0 del bucle for. Revisemos el
resto del codigo:

menuStr += <A HREF="" onMouseOver="hideStatus( ) ; ' +


'if(!tourOn) { setslide(' + i + ' ) ; imageswap(\" +
slideShow[i].name + ' \ ' , ' + i + ' , true)}; return true;"' +
' onMouseOut="if(!tourOn) { setslide(' + i + ' ) ; imageswap(\" +
slideShow[i].name + ' \ ' , ' + i + ' , false)}; return true;"' +
' onClick="return false;"><IMG NAME=" ' + slideShow[il .name +
' " SRC="' + imgPath + slideShow[i] .name +
'out.gif" BORDER=O></A><BR>';

Ya lo vimos en la linea 1 10, per0 entonces la variable rnenustr se inicio con una
cadena vacia. Ahora esta cadena contendra codigo HTML que se encargara de
mostrar una imagen detras de otra. En la figura 3 . 2 podemos ver su resultado.
Para cada capa, rnenuStr sera igual a si mismo y a la imagen asociada con la dia-
positiva. Antes de adentrarnos con las dobles comillas, vamos a ver las necesi-
dades de cada par de imagenes.

1 . Una etiqueta < A HREF> de apertura.


2. Codigo para el controlador de eventos onMouseOver que se disparara cuan-
do el usuario pase el cursor sobre la imagen vinculada.
3 . Codigo para el controlador de eventosonMouseOut que se disparara cuando
el usuario aparte el cursor de la imagen vinculada.
4. Codigo para el controlador de eventosonCZick para que evite que desarrolle
ninguna accion si el usuario hace clic sobre la imagen vinculada (en cual-
quier caso, podremos modificarlo si asi lo deseamos).
5. Una etiqueta <IMG> con un unico NAME y SRC.
6 . Una etiqueta < / A > de cierre.

El primer elemento es trivial. Bastara con utilizarlo.


El segundo es algo mas complicado. Para eliminar cualquier texto de la barra
de estado, lo primero que hace el controlador onMuoseEvent es asignar un valor
vacio a la barra. Para ello llama a la funcion hidestatus ( ) . Lo vemos en la
linea 1 8 4 del codigo.
Luego, per0 solo en 10s casos en 10s que el usuario estk viendo las diapositivas
por su cuenta, sin trabajar con el mod0 automatico, el evento onMouseOver ne-
cesitara llamar a la funcion setSlide ( ) . Como la veremos mas adelante, de mo-
mento nos bastara con recordar que se le ha entregado el valor i.
120 Diapositivas interactivas

Como si onMouseOver no tuviese bastante que hacer, aun tendremos que aiia-
dir mas codigo para la funcion imageswap ( ) . Esta funcion controla la secuen-
cia de imagenes. Como tambien veremos mas adelante, bastara con que
recordemos que el codigo JavaScript tiene tres valores que se han entregado con
el metodo slideshow[ i I .name,el valor de i el booleano true.
El tercer elemento tiene 10s mismos requisitos que orzMouseOver, con la excep-
cion de que no se efectuara ninguna llamada a hidestatus ( ) porque ya se ha-
bra ocultado el contenido de la barra de estado y el ultimo c6digo que se ha
pasado a imageswap es el booleano false (en vez de true).
El cuarto elemento es bastante sencillo. Unicamente tendremos que aiiadir
onClick="false".De esta forma se cancela cualquier clic que pudiese hacer el
usuario.
Y 10s requisitos del quinto elemento se satisfaran con este codigo:

'<IMG NAME="' + slideShow[il.name + ' " S R C = " ' + imgPath +


slideShow[il .name + 'out.gif" BORDER=O>'

La etiqueta <IMG> obtiene un nombre unico de slideshow[ i I .name.Este me-


todo tambien se utiliza con la variable imgPath y con la cadena "out.gif"para
crear el codigo de <IMG>.
El elemento 6 t a m b i h es muy sencillo. Afiadiremos la etiqueta <BR> a1 final
del todo y ya habremos terminado.
El valor de la variable rnenuStr sera ella misma y la cadena procedente del ul-
timo codigo del buclefor que hemos visto.
Ahora, ique pasa con menuStr? Como menuStr posee 10s contenidos HTML y
JavaScript del menu de la diapositiva, se pasara como argument0 en la llamada
a genLayer0 en las lineas 140-143:

genLayer('menu',sWidPos - 104, 43, 100, 200, false,


'<DIV ID="menuConstraint"><TABLE><TD>'+
menuStr + '</TD></TA%LE></DIV>'
) ;

He dejado la llamada para el final porque las otras dos capas de navegacion
que se crean se colocan encima del menu de la diapositiva y creo que tiene mas
sentido generarlos en este orden. Observese que he utilizado la etiqueta < D I V >
con el atributo I D con valor menuconstraint.De esta forma nos aseguramos
que la diapositiva tendra una altura de 800 pixeles.
Necesitaremos hacer dos llamadas a genLayer() para completar la composicion
de la diapositiva. Una es para mostrar la imagen vinculada con la que se inicia-
ra y finalizara la propiedad de representacion automatica. La otra es para la
Diapositivas interactivas 121

imagen vinculada que muestra y oculta el menu que contiene las flechas que
permiten avanzar o retroceder. La creacion de la capa para la imagen vinculada
a la ejecucion automatica no tiene mucho misterio. Miremos las lineas 122-126:

genLayer('automation', sWidPos - 100, 11, 100, 200, true,


' < A HREF="javascript: autopilot ( ) ; " onMouseOver="hideStatus( ) ;' +
'return true;"><IMG SRC="images/automate.gif"BORDER=O></A>'
);

El protocolo j a v a s c r i p t : se usa en el atributo HREF para llamar a la funcion


a u t o p i l o t ( ) y el controlador del eventoonMouseOverllama a h i d e s t a t u s ( ) .
Como estamos deseando vernos las caras con algo mas desafiante, vamos a
meternos con la ultima parte del codigo. La llamada a genLayer ( ) de las lineas
128-138 crea la capa final. Contiene tres imagenes: dos flechas y la palabra
"Guide". Su aspecto sera "<Guide>".

genLayer('guide', swidPos - 0 0 , 30, 100, 200, true,


'<A HREF="javascript: if ( touron) { changeslide(-1); I " ' t
'onMouseOver="hideStatus(; return true;">'+
'<IMG SRC="' + imgPath + 1eftout.gif" BORDER=O></A>' +
' <A HREF="javascript: if ( touron) { menuManager ( ) ; 1 " ' +
'onMouseOver="hideStatus( ; return true;">'+
'<IMG SRC="' + imgPath + 3uideout.gif" BORDER=O></A>' +
'<A HREF="javascript: if(!tourOn) { changeSlide(1); } " ' +
'onMouseOver="hideStatus( ) ; return true; " > ' +
'<IMG SRC="' + imgPath + 'rightout.gif" BORDER=O></A>'
);

El codigo para cada imagen sera muy similar. Ya lo vimos con las imagenes
vinculadas. De hecho, las flechas son imagenes vinculadas pero tienen el evento
onMouseOver condicionado a la llamada a c h a n g e s l i d e ( ) . Si se pasa el valor -1,
la aplicacion mostrara la dispositiva anterior a la que aparece en pantalla. Per0 si
pasa el valor 1, mostrara la siguiente diapositiva. En breve veremos la funcion
c h a n g e s l i d e ( ) . L a imagen vinculada <Guide> mostrara u ocultara el menu
de la diapositiva. El encargado de controlar esta situacion SeramenuManager ( ) .
Antes de entregar el resto del codigo a g e n s c r e e n ( ) , conviene destacar que la
llamada se hace desde entro de la etiqueta <BODY> antes de que se cargue la pa-
gina. MSIE no puede crear las capas despuCs de la carga del documento, por lo
que tendremos que hacerlo antes. Este es el contenido de las lineas 215-219:

<SCRIPT LANGUAGE="JavaScript1.2">
<!--
genScreen ( 1 ;
//-->
</SCRIPT>
122 DiaDositivas interactivas

Elementos de la diapositiva
Ya nos hemos encontrado en mas de una ocasion con el array slideshow. Cada
uno de sus elementos contiene un bloque de propiedades de un objeto slide. Este
es su codigo, el encargado de mostrar las diapositivas de 10 animales:

var slideshow = new Array(


new slide('bird', 'Bomb-zis Car-zes', 'This winged creature has ' +
'been known to seek out and soil freshly-washed vehicles.'),
new slide('walrus', 'Verius Clueless', 'These big fellas ' +
'good fishers, but toothbrushing is another story.'),
new slide('gator', 'Couldbeus Luggajus', 'These reptiles ' +
'often play mascots for large college sporting events.'),
new slide('dog','Makus Messus', 'Man\'sbest friend? Yeah, right. +
'No wonder these mammals get a bad rep.'),
new slide('pig', 'Oinkus Lotsus', 'Humans with questionable eating +
'habits are often compared to these farm creatures.'),
new slide('snake', 'Groovius Dudis', 'Slick and sly with a ' +
'watchful eye.'),
new slide('reindeer', 'Redius Nosius', 'Though co-workers used to +
'laugh and call him names, he eventually won the respect of the ' +
'entire team.'),
new slide('turkey', 'Goosius Is Cooktis', 'Celebrated and revered +
'for an entire year, then served as dinner shortly after.'),
new slide('cow', 'Gotius Milkus', 'This animal is considered a ' +
'moover and shaker, and tends to milk things for all they\'re +
'worth. Udderly shameful.'),
new slide('crane','Whooping It Upus', 'Not to be confused with a +
'piece of heavy construction equipment. Rumored as the source of
the ' + 'nickname <I>birdlegs</I>.')
);

Compara 10s valores que se le pasa en cada llamada a1 constructor slide con 10s
argumentos que espera recibir. El primer0 es el nombre del animal (y la ima-
gen); el siguiente es el nombre cientifico. Y cada uno lleva asociado un texto
descriptivo, La linea 85 tiene codigo HTML que incluye algo de texto. No hay
ninguna raz6n por la que no nos podamos construir el contenido de la diaposi-
tiva basandonos en este concepto, incluso definiendo las capas de las propias
diapositivas.
Si la lista es muy larga, podemos colocarla dentro de un archivo de codigo fuen-
te en JavaScript para despejar de alguna forma el codigo de la aplicacion. En este
caso, como unicamente tenemos diez elementos, lo hemos dejado asi.

Funciones relacionadas con la imagen


Una vez que conocemos las funciones relacionadas con las diapositivas, vamos
a centrarnos en las encargadas de controlar las imagenes.
Diapositivas interactivas 123

preloadlmages()
Esta funcion se encarga de cargar la imagen antes de que sea necesaria. Su co-
dig0 se encuentra en las lineas 64-73:

function imagePreLoad(imgStr) {
img [ img . length] = new Image ( ) ;
img[img.length - 11.src = imgPath + imgStr + ' . g i f t ;

imgOut[imgOut.length] = new I m a g e O ;
imgOut[imgOut.length - 11.src = imgPath + imgStr + 'out.gif';

imgOver[imgOver.lengthl = new I m a g e O ;
imgOver[imgOver.length - 11.src = imgPath + imgStr + 'over.gif';
}

Esta funcion crea u n objeto Image nuevo y carga las tres fuentes a las que hace
referencia a la vez. De esta forrna se aumenta el tiempo de carga de la aplica-
cion, per0 se evita que 10s usuarios tengan que esperar a que se carguen las ima-
genes cuando saltan de una ficha a otra.
Las variables irngPath e imgStr se concatenan junto con gif, out.gif y over.gif,
respectivamente, para hacer las asociaciones pertinentes a cada diapositiva. Por
ejemplo, a la diapositiva de la vaca (cow) se le asociaran las imagenes cow.gif,
cowout.gif y cowover.gif,
imageswap()
Esta funcion se encarga de la secuenciacion de las imagenes si el usuario decide
pasarlas manualmente colocando el punter0 sobre el nombre de alguno de 10s
animales de la lista o cuando es la aplicacion quien se encarga de hacerlo por tra-
bajar en modo automatico. El c6digo lo tenernos en las lineas 179-182:

function imageSwap(imagePrefix, imageIndex, isover) (


if (isover) ( document[imagePrefix].src = imgOver[imageIndexl.src; }
else ( document[imagePrefix].src = imgOut[imageIndexl.src; }
)

Gran parte de 10s scipt encargados de pasar las imagenes trabajan con dos fun-
ciones independientes: una para onMouseOver y otra para onMouseOut. En cual-
quier caso, podemos combinarlas en una unica funcion.
Los argumentos imageprefix, imagelndex e isOver representan la cadena base
que se utiliza para nombrar a la imagen (imgStr).El indice de la imagen deseada
(el valor de i del bucle for que se encuentra en la funcion genscreen ( ) ) y un
valor booleano se utilizaran para indicar cuando se utilizaran las imagenes que
se encuentran dentro de 10s array imgOver o de imgOut.
124 Diapositivas interactivas

Para tratar de clarificar las cosas, vamos a revisar el contenido correspondiente


alas lineas 105-120 de la funciongenscreen ( ) . Obskrvese que se crea JavaScript
dinhmicamente en la linea 112:

imageSwap(\" + slideShow[i].name + ' \ ' , ' + i + ', true));

Cuando se escribe esto en la pantalla, el valor de i sera 0:

imageSwap('bird', 0, true):

Una vez que se llama a la funcion, entonces podremos ver la imagen en la pan-
talla. Como isOver es t r u e , entonces:

document [bird]. src = imgOver [ 0 I . src :

e imgover [ 0 I . s r c es images/birdover.gif. Si isOver es f a l s e , entonces la ima-


gen sera imgout [ 0 I . s r c , es decir, images/birdout.gif.

Funciones de navegacion
La funcion de la diapositiva se ha encargado de crear su contenido y de contro-
lar como aparecerBn en la pantalla. Las funciones de la imagen permiten que se
carguen antes de que se necesiten y la ejecucion automatica. Ahora vamos a ver
como se convierten todos estos elementos en una diapositiva. Es decir, las fun-
ciones de navegacion.
refslide(), hideslide(), showslide() y menuManager()
Ya hemos creado las diapositivas y se han cargado las imagenes correspondien-
tes. Ahora queremos hacer cosas con ellas, es decir, mostrar en pantalla la que
queramos y ocultar el resto. Antes de manipularlas, tendremos que hacer una
referencia. Es muy sencillo, iverdad?. La referencia la haremos basandonos en el
nombre de la capa. La verdad es que si y no. Tendremos que utilizar el nombre
de la capa, pero no olvidemos que MSIE y Navigator usan distintos modelos de
objetos. La funcion r e f s l i d e ( ) se hara cargo de ello. L a tenemos en las lineas
146-149:

function refSlide(name1 {
if ( N N ) ( return document.layers[namel; 1
else { return eval('document.al1.' + name + '.style'); 1
1

Si el usuario trabaja con Navigator, ref S l i d e ( ) devolvera una referencia a


documento. layers [name]. Per0 si tiene MSIE, ref S l i d e ( ) devolvera una
Diapositivas interactivas 125

referencia utilizando eval ( ' document. all. ' + name + style ' 1. hi, po-
dremos modificar la visibilidad de la capa, sin que importe el explorador que uti-
licemos. Luego, no ha de sorprendernos encontrarnos ambas funciones en las
lineas 151-157.
No solo es asi de sencillo, sino que podremos acceder a estos miembros en el
momento en que queramos:

function hideSlide(name1 {
refSlide(name).visibility = hideName;
>
function showSlide(name) {
refSlide(name).visibility = showName;
I

Ambas funciones llaman a ref Slide ( ) y, segun reciben el nombre, se lo en-


tregan como argumento. A primera vista, el codigo puede parecer algo extraiio.
iComo puede ref Slide ( ) tener la propiedad visibility? La verdad es que no la
tiene. Sin embargo, debemos recordar que ref Slide ( ) devuelve una referencia
a la diapositiva, referencia que tiene la propiedad visibility.Si queremos ocultar
126 Diapositivas interactivas

una dkpositiva determinada, tendremos que usar la funci6n ref Slide ( ) para
establecer una referencia para ella y configurar el valor de la propiedad visibility
del objeto devuelto a hidel\rarne.El valor de hideName, si recordamos, lo vimos en
la linea 22, era hide o hidden, dependiendo del explorador que utilizase el usua-
rio. El mismo cddigo se utiliza para mostrar diapositivas, con la excepcion de que
la propiedad visibility tendra que ser showName, obviamente, dependiendo del
explorador utilizado (linea 23).
Las funciones hideslide ( ) y showslide ( ) se utilizan para ocultar y mostrar
las diapositivas y el menu. No se las llamara directamente, sino que se utilizara
la funci6n menuManager ( ) :

function menuManager0 {
if (isvis) { hideSlide('menu'); 1
else ( showSlide('menu'); 1
isvis = !isVis;
1

Siempre que aparezca el menu de las diapositivas en la pantalla, el valor de la


variable isvZs sera true. En otro caso, sera false.Asi que menuManager ( ) mos-
trara el menu slide si isns es false y lo ocultara cuando sea true.

changeslide()
Ahora que podemos establecer referencias correctamente a las diapositivas,
independientemente del explorador que utilice el usuario para ejecutar la aplica-
cibn, necesitaremos una funcion que cambie la diapositiva que se encuentra en
la pantalla por la siguiente. En realidad tenemos dos funciones: changeslide (
y setslide ( ) .
El cambio de una dispositiva por otra es un proceso que se desarrolla en tres
pasos:

1. Oculta la dispositiva que se encuentra en la pantalla.


2. Determina cual sera la siguiente dispositiva.
3. La muestra en pantalla.

Los pasos 1 y 3, a estas alturas, nos pareceran bastante sencillos. Per0 el segun-
do es mas complicado de lo que puede parecer. Hay dos situaciones en las cuales
podremos cambiar las diapositivas. La primera ocurrira cuando queramos cam-
biar las dispositivas una a una, hacia delante o hacia atras, siguiendo una se-
cuencia. Esta situacion nos la encontramos cuando utilizamos las flechas del
menu. La segunda situacion nos la encontraremos cuando trabajemos con el mo-
do automatico de la aplicacion. La funci6n changeslide ( ) se encargara de am-
bos supuestos. veamos el contenido de las lineas 165-1 70:
DiaDositivas interactivas 127

function changeSlide(0ffset) {
hideSlide('s1ide' + curslide);
curslide = (curslide + offset < 0 ? slideShow.length - 1 :
(curslide + offset == slideShow.length ? 0 : curslide + offset));
showSlide('s1ide' + curslide);
}

Lo primero que pasara sera la llamada a hideslide ( ) . En la llamada se uti-


lizara el valor de la expresion slide ' + curslide.Originalmente, el valor de
I

curslide es 0 (linea 13). Como este numero hace referencia a la dispositiva que se
encuentra en la pantalla, la funcion hideslide ( ) ocultara slide0, que es la dia-
positiva del pajaro. Bien. iQuC diapositiva se mostrara a continuacion?
Recordemos que changeslide ( espera el parametro onset, el cual podra ser
16 - 1.El primer valor hace que se muestre la siguiente diapositiva, mientras que
el segundo mostrara la anterior a la que se encuentra en pantalla. ComocurSZide
es un entero que representa el indice a1 que pertenece la imagen de la pantalla, a1
aiiadir 1 , su valor cambiara a 1, luego a 2 , etc. Como - 1 mostrara la diapositiva
anterior a la que se encuentra en la pantalla, si Csta fuese la numero 3, pasarA a
mostrar la numero 2 y luego la 1, etc.
Todo ira bien, hasta el momento en que intentemos acceder a ' slide ' + -1
o ' slide +slideshow.length. Esta dispositiva no existe, por lo que nos po-
I

demos encontrar con una pantalla en blanco o con u n error de sintaxis. Enton-
ces, ic6mo podemos evitar que curslide sea menor que cero?
La respuesta la tenemos en las lineas 167-168:

curslide = (curslide + offset < 0 ? slideShow.length - 1 :


(curslide + offset == slideShow.length ? 0 : curslide + offset));

El valor de curslide vendra determinado por el uso de un conjunto de operado-


res ternarios. Aqui tenemos una traduccion del codigo:

Si curslide + offset es menor que 0, entonces curslide equivale a


slideShow.lenght -1
Y
Si curslide + offset es igual a slideShow.lenght entonces curslide
equivale a 0
Y curslide equivale a curslide + offset

Si sumamos onset a curslide, haremos que curslide sea demasiado pequeiio,


por lo que curslide tendra que ser slideshow.lenght - 1. Si aiiadimos offset
a curslide, entonces sera demasiado grande, por lo que simplemente se le asig-
nara el valor 0. En cualquier otro caso, a curslide se le podra asignar su valor
mas onset.
128 Diapositivas interactivas

En el momento en que determinamos el valor de curslide, se podra proceder a


llamar a la funcion showslide ( ) desde la linea 169.
setslide()
La funcion changeslide ( ) es una de las dos funciones que se utiliza para
cambiar las diapositivas. Mientras que changeslide ( ) se limita a cambiar la
diapositiva que hay en pantalla por la anterior/siguiente, la funcion setslide ( )
oculta la imagen que se encuentra en la pantalla y muestra la diapositiva que est6
asociada con el numero del indice que recibe como argumento. Se encuentra en
las lineas 172-1 77:

function setSlide(ref) I
if (touron) { return; 1
hideSlide('s1ide' + curslide);
curslide = ref:
showSlide('s1ide' + curslide);
}

La primera linea comprueba el valor de touron para determinar si la aplicacion


se esta ejecutando en mod0 automatico. Si es asi, lo devolvera de inmediato
porque no habra ninguna razon para cambiar el orden de las diapositivas. Ya se
encarga la aplicacion de hacerlo.
Al igual que ocurria con changeslide ( ) , setslide ( ) oculta la diapositiva
de la pantalla. En cambio, a diferencia de ella, no le importa el valor de curslide.
sets1 ide ( ) asignara el valor del parametro refa curSZide y mostrara la diapo-
sitiva asociada con dicho numero.
autoPilot()
Como nos podemos imaginar, la funcion autopilot ( ) controla la reproduc-
cion autornatica. Desde el mismo vinculo de la pantalla se activara o desactivara.
Su definicion se encuentra entre las lineas 186-198:

function autopilot0 {
if (touron) {
clearInterval(auto);
imageSwap(slideShow[curSlidel .name, curslide, false);
1
else {
auto = setInterval('automate()' , showspeed);
imageSwap(slideShow[curSlidel .name, curslide, true);
showSlide('menu');
visible = true;
1
tourOn = !touron:
1
Diapositivas interactivas 129

Para saber si el mod0 automatico esta activo o no, la funcion autopilot (


consultara el valor de la variable touron. Si es fase, estara desactivado. Por lo
tanto, la funcion utiliza el mktodo setInterva1 ( ) del objeto window para lla-
mar a la funcion automate ( ) (que veremos a continuacion) cada showspeed
milisegundos.
Estaria bien ver avanzar el menu de las diapositivas con el contenido de la pan-
talla, resaltando el nombre de la diapositiva. Como el usuario debe colocar el cur-
sor del raton sobre la imagen "Automate"para llamar a la funcion autopilot ( ) ,
ksta se encargara de mostrar el menu (si no estuviese ya en pantalla) y de mar-
car el nombre de la imagen que hay en la pantalla. La funcion automate ( ) se
encargara del resto, por eso unicamente lo tendremos que hacer una vez.
En cualquier caso, si el mod0 automatico ya se estuviese ejecutando (por ejem-
plo, touron es false), autopilot ( ) utilizara el mktodo clearInterval()
del objeto window para cancelar la llamada a setInterva1 ( ) asociada a la va-
riable auto. Para que las cosas sigan claras, por lo menos habra que hacer una
ultima llamada a imageswap ( ) para que se desactive el nombre de la imagen
que se encuentra en pantalla y se iguale con el del resto,
La ultima cosa que hara autopilot ( ) sera cambiar el valor de touron por el
contrario a1 que tenga.
130 DiaDositivas interactivas

automate()
automate ( ) es una pequeiia funcion que se encarga de mostrar las diapositivas,
para lo cual desarrolla tres operaciones:
1. Simula un evento onMouseOut que oculta la imagen que se encuentra en la
pantalla. Para ello llama a imageswap ( .
2. Avanza a la siguiente diapositiva con una llamada a changeslide ( ) .
3. Simula un evento onMouseOver para activar la siguiente diapositiva del
menu para mostrarla en la pantalla. Utiliza una llamada a imageswap ( ) .
Aqui tenemos las lineas 200-204:

function automate ( ) {
imageSwap(slideShow[curSlidel .name, curslide, false);
changeSlide(1);
imageSwap(slideShow[curSlidel .name, curslide, true);
1

Una nota final. Ambas llamadas a imageswap ( ) se entregan mediante un va-


lor de curslide, dando la ilusi6n de que la misma imagen del menu se puede pasar
hacia delante y hacia detrAs. No olvidemos que la llamada a changeslide ( )
cambia el valor de curSZide.Asi, la segunda llamada a imageswap ( ) hara que se
muestre la diapositiva correcta.

Posibles ampliaciones
Al igual que ocurre con casi todas las aplicaciones que utilizan DHTML, pode-
mos utilizar esta aplicaci6n para desarrollar un sin fin de tareas en las que ten-
ga que mostrar diapositivas. Vamos a ver unas cuantas.
Diapositivas interactivas 131

Mostrar diapositivas aleatoriamente en mod0 automatico


iPor que no mezclar las diapositivas? Podemos generar un numero aleatorio
entre 0 y slideshow.lenght-1.A continuation, llamaremos a setslide ( 1.
Este es el aspecto que mostrara la funcion:

function randomslide0 I
var randIdx = Math.floor(Math.rand0 slideShow.length);
setSlide(rand1dx);
1

En vez de llamar a changeslide ( ) en la funci6n automate ( ) , llamaremos a


randomslide ( ) :

function automate() {
imageSwap(slideShow[curSlidel.name, curslide, false);
randomslide ( ) ;
imageSwap(slideShow[curSlidel.name, curslide, true);
1

Animar GIF o secuencias de imagenes


Representan ciertas mejoras, per0 son de gran ayuda. A 10s usuarios realmente
les gusta el grado de interactividad que se consigue. Cualquier cosa que anime y
d i color a una pagina Web (excluimosla etiquetas BLINK), la hara mas atractiva.

Animar las diapositivas


Todas las diapositivas que se han creado para esta aplicacion, son estaticas. En
cualquier momento podemos jugar con el usuario a "ahora lo ves, ahora no lo
ves". Per0 las capas permaneceran en el mismo sitio. iPor que no hacer que las
diapositivas aparezcan por la izquierda de la pantalla y sigan moviendose hasta
desaparecer por la derecha?
Me limito a abrir la puerta a una nueva aplicacion dentro de otra aplicacion.
Asi que no voy a profundizar en el codigo. Per0 si que cornentart! que Netscape
tiene una libreria de archivos listos para que 10s usuarios se 10s bajen.
Todo lo que hay que hacer es visitar la pagina http://deveZoper.netscape.com/
docs/technote/dynhtmZ/sccpape/xbdhtmk.txt.
Obskrvese que el archivo tiene la extension .txt. Siempre que guardemos un
documento en un archivo local, tendremos que cambiarle la extension a .js.
Capitulo 4
lnterfaz para un motor de blisqueda mliltiple

La Web esta llena de motores especializados en busquedas multiples que estan


escritas en JavaScript. Este tipo de aplicaciones es una de las mas solicitadas y
potencialmente, una de las mas sencillas de desarrollar con este lenguaje de pro-
gramacion. Podemos capitalizar 10s datos de otra gente y convertir nuestro sitio
Web en un portal de acceso a1 universo de Internet. La que veremos aqui sera mi
version. Obviamente, se pueden encontrar otras mas robustas con las que obte-
ner mas ventajas. En la figura 4.1 tenemos la pantalla que nos encontraremos
cuando abramos el archivo index.htmZ en el explorador Web.
El manejo de esta aplicacidn es sencillo. El usuario introducira una cadena de
busqueda en la esquina inferior izquierda de la pantalla y utilizara las flecha.s
para avanzar a travCs de las opciones del menu, donde figuran distintos moto-
res de busqueda. Todo lo que tendra que hacer el usuario sera clic sobre el boton
que se corresponda con el motor que quiera utilizar y 10s resultados apareceran
en el centro de la pantalla. He utilizado la base de datos de Image Surfer para
buscar el tCrmino I'andromedal'. Los resultados 10s podemos ver en la figura 4.2.
Y ya esta. ObsCrvese que el cuadro donde apareceran 10s resultados esta rodea-
do por un borde negro. Es un simple intento de hacer que este ejemplo se parezca
a una pagina Web de verdad. El diseiio se basa en una Clara preferencia perso-
nal, per0 se puede cambiar en cualquier momento por un disefio mas basico de
dos cuadros (uno arriba y otro abajo).
Por cierto, si ha estado siguiendo uno a uno 10s capitulos anteriores, Vera que
en Cste no nos veremos las caras con un c6digo completamente nuevo. En rea-
lidad, nos basaremos en el visto en el capitulo anterior. De hecho, lo utilizare-
mos para demostrar como se puede volver a aprovechar el c6digo ya existente.
134 lnterfaz para un motor de birsqueda mirltiple

Figura 4.1. Interfaz de un motor de bdsqueda multiple

Number of results 8

Figura 4.2. Image Surfer nos muestra 10s dibujos que tiene en su base de datos
y que estkn asociados con el tkrmino "andromeda"
lnterfaz para un motor de busqueda multiple 135

Requisitos para la ejecucion


Esta aplicaci6n utiliza DHTML por lo que tendremos que trabajar con NN o bien
MSIE 4.x. He incluido 20 motores de busqueda. Podemos utilizar cientos de
motores, si lo deseamos. Per0 posiblemente el usuario nunca llegue a utilizar
tantos. No olvidemos que esta aplicaci6n se debe ejecutar en casi todas las ma-
quinas y que cuantos mas motores de busqueda tengamos, mas tardara en car-
gar la pagina.

Analisis de la sintaxis
En esta aplicacih intervendran dos archivos: index.htrnZ y rnuZti.htrnZ. El con-
tenido del primero lottenemos en el ejemplo 4.1. Utiliza cuadros anidados para
conseguir el efecto del borde.
Ejemplo 4.1.index.htm1
1 <HTML>
2 <HEAD>
3 <TITLE>Multi-Search Engine Interface Site</TITLE>
4 <SCRIPT LANGUAGE="JavaScriptl.2">
5 <!--
6 var black = '<BODY BGCOLOR=BLACK></BODY>';
I var white = '<BODY BGCOLOR=WHITE></BODY>';
8 //-->
9 </SCRIPT>
10 < /HEAD>
11 <FRAMESET ROWS="15,* , 50" FRAMEBORDER=O BORDER=O>
12 <FRAME SRC="javascript: parent .black; " SCROLLING=NO>
13 <FRAMESET COLS="15,*, 15" FRAMEBORDER=O BORDER=O>
14 <FRAME SRC="javascript: parent.black;"SCROLLING=NO>
15 <FRAME SRC="javascript: parent.white;">
16 <FRAME SRC="javascript: parent.black;"
17 SCROLLING=NO>
18 < /FRAMESET>
19 <FRAME SRC="multi.html" SCROLLING=NO>
20 < /FRAMESET>
21 < / HTML>

Las dos variables en JavaScript, black y white que se encuentran en las lineas 6
y 7, se evaluan como cadenas HTML dentro del atributo SRC de 10s marcos de la
linea 12 y 14-16 . Ya hemos visto en el capitulo 2 como se hace. Los unicos mar-
cos que ven algun tip0 de acci6n real son frames [ 2 ], que muestra 10s resulta-
dos, y frames [ 4 I , donde e s t h n 10s motores de busqueda. El resto unicamente
se utilizan meramente para el espect8culo. Vamos a ver el contenido del archivo
rnuZti.htrnZ que se encuentra dentro del ejemplo 4.2.
136 lnterfaz para un motor de busqueda multiple

Ejemplo 4.2. multi.htm1


1 <HTML>
2 <HEAD>
3 <TITLE>Multi-EngineMenu</TITLE>
4 <SCRIPT LANGUAGE="JavaScriptl.2">
5 <!--
6
7 parent.frames[2].location.href = 'javascript:parent.white';
8
9 var NN = (document.layers ? true : false);
10 var curslide = 0;
11 var hideName = (NN ? 'hide' : 'hidden');
12 var showName = (NN ? 'show' : 'visible');
13 var perLyr = 4:
14 var engWdh = 100;
15 var engHgt = 20;
16 var left = 315;
17 var top = 10;
18 var zIdx = -1;
19 var imgPath = 'images/';
20 var arrayHandles = new Array('out', 'over');
21
22 for (var i = 0; i < arrayHandles.1ength; i + + ) {
23 eval ( 'var ' + arrayHandles[ i] + ' = new Array() ' ) ;
24 1
25
26 var engines = new Array(
27 new Array( 'HotBot',
28 'http://www.hotbot.com/?MT=',
29 'http://www.hotbot.com/'),
30 new Array('InfoSeek',
31 'http://www.infoseek.com
Titles?col=WW&sv=IS&lk=noframes&qt=',
32 Ohttp://www.infoseek.com/'),
33 new Array( 'Yahoo',
34 'http://search.yahoo.com/bin/search?p='.
35 shttp://www.yahoo.com/'),
36 new Array( 'AltaVista',
37 'http://www.altavista.com/cgi-bin/query?pg=q&kl=XX&q=',
38 ohttp://www.altavista.digital.com/'),
39 new Array ( ' Lycos ,
40 'http://www.lycos.com/cgi-bin
pursuit?matchmode=and&cat=lycos' +
41 '&query=',
42 'http://www.lycos.com/'),
43 new Array('Money.com',
44 'http://jcgi.pathfinder.com/money/plus/news
searchResults.oft?' +
45 'vcssortby=DATE&search=',
46 *http://www.mOney.com/'),
lnterfaz para un motor de busqueda mliltiple 137

41 new Array( 'DejaNews',


48 'http://www.dejanews.com/dnquery.xp?QRY=',
49 'http://www.dejanews.coml'),
50 new Array( 'Insight',
51 'http:llwww.insight.comlcgi-binlbp/870162391lweb
result.html? ' +
52 'a=s&f=p&t=A&d=',
53 'http://www.insiqht.com/'),
54 new Array('Scientific American',
55 lhttp://www.sciam.com/cgi-bin/search.cgi?'+
56 'searchby=strict&groupby=confidence&docs=lOO&query=',
57 <http://www.sciam.com/cgi-bin/search.cgiO,
58 new Array('1mage Surfer',
59 'http://isurf.interpix.com/cqi-bin/isurf
keyword-search.cgi?q=',
60 >http://www.interpix.coml') I

61 new Array('MovieFinder.com',
62 'http://www.moviefinder.com/search/results/1,10,,00.html?'+
63 'simple=true&type=movie&mpos=beqin&spat=',
64 8http://www.moviefinder.com/') I

65 new Array('Monster Board',


66 'http://www.monsterboard.com/pf/search/USresult.htm?' +
67 'loc=&EmploymentType=F&KEYWORDS=',
68 #http://www.rnonsterboard.com/') ,
69 new Array('MusicSearch.com',
70 'http://www.musicsearch.com/global/search/search.cgi?QUERY=',
71 <http://www.musicsearch.com/') I

72 new Array ( ZD Net I ,

73 'http://xlink.zdnet.com/cqi-bin/texis/xlink/xlink
search.html? ' +
74 'Utext=',
75 !http:l/www.zdnet.com/'),
76 new Array('Biography.com',
17 !http://www.bioqraphy.com/cgi-bin
biomain.cgi?search=FIND&field=*,
78 'http://www.biography.com/'),
79 new Array('Entertainment Weekly',
80 'http://cgi.pathfinder.com/cqi-bin/ew/cg
pshell?venue=pathfinder&q=',
81 thttp://www.entertainmentweekly.com/'),
82 new Array('SavvySearch',
83 'http://numan.cs.colostate.edu:l969/nph-search?'+
84 'classic=on&Boolean=OR&Hits=lO&Mode=MakePlan&df=normal'
+
85 '&AutoStep=on&KW=',
86 ,http://www.savvySearch.com/'),
87 new Array('Disc0very Online',
88 !http://www.discovery.comlcgi-bin/searcher/-?'i
89 'output=title&exclude=/search&search=',
90 'http://www.discovery.com/'),
91 new Array('Borders.com',
92 +
'http://www.borders.com:8080/fcgi-bin/db2www/search/'
138 lnterfaz Dara un motor de blisaueda mljltide

93 'search.d2w/QResults?doingQuickSearch=l&srchPage=QResults~+
94 '&mediaType=Book&keyord=',
95 ~http://www.borders.com/'),
96 new Array( 'Life Magazine',
97 'http://cgi.pathfinder.com/cgi-bin/life/cg/pshell?' +
98 'venue=life&pg=q&date=all&x=15&y=l6&q=',
99 'http://www.life.com/')
100 );
101
102 engines = engines.sort ( ) ;
103
104 function imagePreLoad(imgName, idx) {
105 for(var j = 0; j < arrayHandles.length; j++) {
106 eval (arrayHandles[ j I + " [ " + idx + " I = new Image ( ) ) ;
"

107 eval (arrayHandles[j] + " [ " + idx + " I . src = ' " + imgPath +
108 imgName + arrayHandles[jI + " . jpg'" ) ;
109 }
110 1
111
112 function enginelinkso
113 genLayer('sliderule', left - 20, top + 2 , 25, engHgt, true,
114 ' < A HREF="javascript: changeSlide(1); " ' +
115 'onMouseOver="hideStatus();return true;">'+
116 '<IMG SRC="' + imgPath + 'ahead.gif" BORDER=O></A><BR>' +
117 '<A HREF="javascript: changeslide(-1); " ' +
118 'onMouseOver="hideStatus( ) ; return true;" > ' +
119 QIMG SRC="' + imgPath + 'back.gif" BORDER=O></A>');
120 lyrCount = Math.cei1
121 (engines.length / perlyr);
122 for (var i = 0; i < lyrcount; i++) {
123 var engLinkStr = '<TABLE BORDER=O CELLPADDING=O
CELLSPACING=O><TR>';
124 for (var j = 0; j < perLyr; j++) {
125 var imgIdx = (i * perLyr) + j;
126 if (imgIdx == engines.length) { break; 1
127 var imgName = nameFormat(engines[imgIdxl[OI);
128 imagePreLoad(imgName, imgIdx);
129 engLinkStr += '<TD><A HREF="javascript: ' +
130 'callSearch(document.forms[Ol.elements[Ol .value, ' +
131 imgIdx + ' ) ; " onMouseOver="hideStatus(); imageswap(\" +
132 imgName + ' \ ' , ' + imgIdx + ' , 1); return true" ' +
133 'onMouseOut="imageSwap(\" + imgName + ' \ ' , ' + imgIdx +
134 I , 0) ;"><IMGNAME="' + imgName + ' ' I SRC="' + imgPath +
135 imgName + 'out.jpg' + "' BORDER=O></A></TD>';
13 6 }
137 engLinkStr += '</TR></TABLE>';
138 genLayer('s1ide' + i, left, top, engwdh, engHgt, false,
engLinkStr);
139 I
140 I
141
lnterfaz para un motor de bifsqueda mifltiple 139

142 function genLayer(sName, sLeft, sTop, sWdh, sHgt, sVis, copy) (


143 if ( N N ) I
144 document.writeln('cLAYER NAME="' + sName + ' ' ' LEFT=' + sLeft +
145 ' TOP=' + sTop + WIDTH=' + sWdh + HEIGHT=' + sHgt +
146 ' VISIBILITY=' + sVis + ' z-Index=' + (++zIdx) + ' > ' +
147 copy + '</LAYER>');
148 I
149 else {
150 document.writeln('<DIVID="' + sName +
151 "' STYLE="position:absolute; overf1ow:none;left: ' +
152 sLeft + 'px; top:' + sTop + 'px; width:' + sWdh + 'px;
height: ' +
153 sHgt + 'px; visibility:' + svis + ' z-Index=' + (++zIdx) +
154 I " > ' + copy + '</DIV>');
155 I
156 I
157
158 function nameFormat(str) {
159 var tempArray = str.split(' ' ) ;
160 return tempArray.join(").toLowerCase~);
161 1
162
163 function hideSlide(name) { refSlide(name).visibility = hideName; }
164
165 function showSlide(name) { refSlide(name).visibility = showName; I
166
167 function refSlide(name) {
168 if ( N N ) ( return document.layers[namel; I
169 else ( return eval('document.al1.' + name + '.style');
170 }
171
172 function changeSlide(0ffset) (
173 hideSlide('s1ide' + curslide);
174 curslide = (curslide + offset i 0 ? slideShow.length - 1 :
175 (curslide + offset == slideShow.length ? 0 : curslide +
offset));
176 showSlide('s1ide' + curslide);
177 I
178
179 function imageSwap(imagePrefix, imageIndex, arrayIdx) (
180 document[imagePrefix] .src = eval(arrayHandles[arrayIdxl +
181 " [ " + imageIndex + " I . src") ;
182 I
183
184 function callSearch(searchTxt, idx) (
185 if (searchTxt == " " ) {
186 parent.frames[2l.location.href = engines idxl 121 +
187 escape(searchTxt);
188 I
189 else (
190 parent.frames[2l.location.href = engines idxl [l] +
140 lnterfaz para un motor de busqueda mdtiple

191 escape(searchTxt);
192 I
193
194
195 function hidestatus() { window.status = " ; 1
196
197 / / - - >
198 </SCRIPT>
199
200 </HEAD>
201 <BODY BGCOLOR="BLACK" onLoad="showSlide ( slide0 ' ) ;" >
202 <SCRIPT LANGUAGE="JavaScriptl.2">
203 < ! - -
204 engineLinks ( ) ;
205 / / - - >
206 </SCRIPT>
207 <FORM onSubmit="return false; " >
208 <TABLE CELLPADDING=O>
208 <TR>
210 <TD>
211 <FONT FACE=Arial>
212 <IMG SRC="images/searchtext.jpg">
213 </TD>
214 <TD>
215 <INPUT TYPE=TEXT SIZE=25>
216 < / TD>
217 </TR>
218 </TABLE>
219 </FORM>
220 </BODY>
221 < / HTML>

Mas de 200 lineas de codigo. Per0 realmente ya hemos visto gran parte. No va
a ser tan terrible como parece. Empecemos con la linea 7.

parent.frames[2].location.href ='javascript: parent.white';

Si contamos 10s cuadros de indexhtml, veremos que frames [ 2 ] es el marco en


el que se muestran 10s resultados. Al configurar la propiedad 2ocation.hrefen este
marco suavizaremos u n poco las cosas a la hora de volver a cargar la aplica-
ci6n. h i , se asigna a1 contenido del documento de resultados algun HTML "local",
de tal forma que no tengamos que esperar a que se vuelvan a cargar las cadenas
de busqueda.
Por cierto, aunque tengamos una interfaz que nos muestre 10s resultados de
una busqueda, en el momento en que hagamos clic sobre uno de 10s vinculos
que aparecen en la pantalla, estaremos a merced completamente de 10s disefiadores
del motor. Algunos permitiran navegar desde la interfaz. Otros, como Infoseek,
lnterfaz para un motor de biisqueda mliltiple 141

obligaran a que el documento aparezca en la parte superior de la ventana del


explorador Web.

Algo de memoria
Vamos a darnos una vuelta por la memoria RAM.Segun analicemos las varia-
bles que aparecen a continuacion, nos encontraremos con algunas novedades,
per0 el funcionamiento de la mayor parte coincide con el visto en el capitulo 3.
Tenemos las siguientes variables N N , curSZiede, hideName, showName, imagepath
y sldx:

var NN = (document.layers ? true : false);


var curslide = 0;
var hideName = (NN ? 'hide' : 'hidden');
var showName = (NN ? 'show' : 'visible'];
var perLyr = 4;
var engWdh = 100;
var engHgt = 20;
var left = 315;
var top = 10;
var zIdx = -1;
var imgPath = 'images/';
var arrayHandles = new Array ( ' out ' , ' over ) ;

La funcion de estas variables es la misma que tenian en el capitulo 3. perLyr de-


fine el numero de rnotores de busqueda que apareceran en la interfaz. Las varia-
bles engWdh y engHgt definen el ancho y el alto predeterminado de cada capa.
Las variables Zeft y top contienen 10s valores que determinan la posicion de las
capas. La variable arrayHandZes contiene un array que se utilizarh durante la car-
ga dinamica de imagenes. De momento bastara con que nos suenen. Las vere-
mos con mas detalle en un instante.
Estas variables son algo mas que un c6digo familiar. Vamos a comprobar las
funciones.
Lineas 142-156:

function genLayer(sName, sLeft, sTop, swdh, sHgt, sVis, copy) {


if ( N N ) (
document.writeln('<LAYERNAME="' + sName + "' LEFT=' + sLeft i
' TOP=' + sTop + ' WIDTH=' + sWdh + HEIGHT=' + sHgt +
' VISIBILITY=' + sVis + ' z-Index=' + (++zIdx) + ' > ' +
copy + '</LAYER>');
1
else {
document.writeln('<DIVID="'+ sName +
' " STYLE="position:absolute; overf1ow:none;left: ' +
sLeft + 'px; top:' + sTop + 'px; width:' + sWdh + 'px; height:' +
142 lnterfaz para un motor de busqueda multiple

sHgt + 'px; visibility:' + sVis + ' z-Index=' + (++zIdx) +


I" > ' + copy + ' < / D I V > ' ) ;
1
1

Lineas 163-177:

function hideSlide(name) [ refSlide(name).visibility = hideName; 1

function showSlide(name) ( refSlide(name).visibility = showName; 1

function refSlide(name) {
if ( N N ) { return document. layers [name]; )
else { return eval('document.al1.' + name + '.style'); 1
1

function changeSlide(0ffset) (
hideSlide('s1ide' + curslide);
curslide = (curslide + offset < 0 ? slideShow.length - 1 :
(curslide + offset == slideShow.length ? 0 : curslide + offset));
showslide( 'slide' + curslide) ;
1

Cinco funciones: genSlide ( ) , refslide ( ) , hideslide ( 1, showslide ( ) y


changeslide ( ) . Todas ellas son exactamente iguales a las vistas en el capitulo 3.
Si no tiene muy Clara la funcion de cada una, le recomiendo que vuelva a atras
y lo repase. Hay dos funciones nuevas: imagePreLoad ( ) e imageswap ( ) , que
basicamente desarrollan la misma funcion, per0 se han modificado lo suficiente
como para que merezca la pena que las tratemos con detalle.

Carga dinamica de imagenes


Uno de 10s grandes paradigmas de la Web es desarrollar operaciones estaticas
de forma dinamica. iPor qut hacer algo estaticamente cuando se puede hacer
sobre la marcha? Esto es lo que hara el codigo que veremos a continuacion con
la carga por adelantado de las imagenes. iQut sistema de trabajo utilizaremos
cuando queramos cargar las imagenes antes de que utilizarlas? Pues con un co-
dig0 como este:

var myImagelOn = new Image();


myImagelOn.src = 'images/myImgOnl.gif';
var myImagelOff = new Image();
myImagelOff.src = ~images/myImgOffl.gif';

Simple y suficiente. Per0 estas cuatro lineas unicamente se hacen cargo de un


par de imagenes iQut pasa si tenemos que trabajar con cinco o diez pares? Pues
lnterfaz Dara un motor de busaueda multide 143

que tendriamos 20 6 40 lineas. Si alguna vez tenemos que hacer algun cambio,
nos encontraremos en medio de un problema, independientemente de la canti-
dad de pares de imagenes que tengamos. Necesitaremos tres cosas:

1. Un array de objetos Image para cada conjunto de imagenes que vayamos


a utilizar. Esta aplicacion utiliza un array de imagenes cuando el cursor se
coloca sobre un vinculo y otro cuando lo abandona.
2. Un convenio de nombres para las imagenes. Este convenio ha de recoger 10s
nombres de 10s array del paso anterior.
3. El mttodo eval ( ) .

Para el paso 1 , esta aplicacion utilizara dos arrays. Uno de ellos se llamara out
y contendra objetos Image de esas imagenes que se utilizaran en el momento en
que el cursor abandone un vinculo. El otro se llamar6 over y contendra objetos
Image para 10s casos en 10s que el cursor se encuentre sobre un vinculo. Estas va-
riables se representaran en un array de cadenas llamada arrayHandles, que se
encuentran en la linea 20.

var arrayHandles = new Array('out', 'over')

Para el paso 2, utilizaremos un convenio de nombres muy sencillo. Todos 10s


pares de imagenes tendran el mismo prefijo, seguido de out.gifu over.gif, depen-
diendo de la imagen. Por ejemplo, las imagenes relacionadas con InfoSeek se lla-
maran infoseekout.gif y infoseekover.gif.
Para el paso 3 , tendremos que empezar por interactuar con cada uno de 10s ele-
mentos de arrayHandZes y usar el mCtodo eval( ) para crear arrays en el mis-
mo momento en que se tengan que hacer cargo de 10s objetos Image. Miremos las
lineas 22-24:

for (var i = 0; i < arrayHandles.length; i++) {


eval('var ' + arrayHandles[i] + ' = new A r r a y ( ) ' ) ;
}

El bucle for anterior equivale a este otro codigo:

var o u t = new Array();


var over = new Array( ) ;

Para disimular la carga anticipada, volveremos a utilizar el mktodo eval( ) en


la funcion preLoadImage ( ) con la finalidad de crear objetosImage sobre la mar-
cha y asignarles la propiedad SRC. La funcion en cuestion se define en las lineas
104-110.
144 lnterfaz Dara un motor de busaueda multide

function imagePreLoad(imgName, idx) {


for(var j = 0; j < arrayHandles.length; j++) {
eval (arrayHandles[ j ] + " [ " + idx + " I = new Image ( " ) ;
eval(arrayHandles[j] + " [ " + idx + " I .src = ' " + imgPath +
imgName + arrayHandles[ j I + " . jpg ' " ) ;
I
1

imagePreLoad( ) puede trabajar con dos argumentos, un prefijo (por ejemplo,


Infoseek) y un entero que se utiliza para asignar el elemento correspondiente del
array a1 nuevo elementoIrnage. De nuevo, el bucle for se repite conarraylfandles,
utilizando todas las cadenas de elementos para acceder a uno de 10s arrays que
se acaban de crear con objeto de asignar una referencia unica. Por ejemplo, lla-
mar a imagePreLoad( infoseek ' , 0 ) equivale a:
I

out[Ol = new Image ( ) ;


out[Ol.src = 'images/infoseekout.jpg';
over [ 0 1 = new Image ( 1 ;
over[O].src = 'images/infoseekover.jpg';

Sin embargo, estas cuatro lineas de codigo representan precisamente aquello que
quiero evitar una y otra vez. Siempre que necesitamos un nuevo par de image-
nes, podre llamar a la funci6n preLoadImages( ) . Y ademas funciona mucho
mejor.

lniciar 10s motores


La variable engines que aparece situada dentro de las lineas 26-100 representa
un array de elementos, cada uno de 10s cuales contiene a su vez otro array cu-
yos componentes cuentan con cierta informacion del motor de busqueda. L a
variable engines tiene una longitud de 20 elementos, motivo por el que unica-
mente nos fijaremos en el primero, el cual se encuentra situado dentro de las li-
neas 27-29.

new Array ( HotBot ' ,


'http://www.hotbot.com/?MT=',
'http://www.hotbot.com/'),

El elemento 0 identifica el nombre del motor de busqueda, HotBot. El elemento


1 identifica el URL con la cadena de busqueda que, cuando se suma a1 texto de
la peticion, llamara a1 motor de busqueda y mostrara la pagina de resultados.
El elemento 2 representa el URL de la pagina principal del motor de busqueda.
Cuando el usuario solicite que se busque una cadena en blanco, se utilizara en
lugar del elemento 1.
lnterfaz para un motor de bkqueda mdtiple 145

engine Links()
La funcion engineLinks ( ) se parece a la funcion genScreen ( ) que vimos en
el capitulo tercero porque es la que se encargara de la creacion de las capas. Aun-
que tambitn tiene sus diferencias. Veamos a continuacion el contenido de las li-
neas 112-140.

Administracion de capas
Lo primer0 que hace la funcion es generar la capa que contiene 10s vinculos de
navegacion:

genLayer('sliderule', left - 20, top + 2 , 2 5 , engHgt, true,


' < A HREF="javascript: changeSlide(1) ; " ' +
'onMouseOver="hideStatus ( ) ; return true; " > < I M G S R C = " ' +
146 lnterfaz para un motor de bljsqueda multiple

imgPath + 'ahead.gif" BORDER=O></AxBR><AHREF="javascript: +


'changeslide(-1);"onMouseOver="hideStatusO; return true;">' +
'<IMG SRC="' + imgPath + 'back.gif" BORDER=O></A>');

Para que ocurra esto, bastara con una sencilla llamada a genLayer ( ) . No tie-
ne ningun misterio. La capa contendra dos imagenes vinculadas: una flecha de
avance y otra de retroceso. Observese que 10s valores de 10s pixeles izquierdo y
superior que se pasan, son relativos a las posiciones izquierda y superior de las
capas de 10s motores que crearemos en u n momento.
A continuacion, la variable ZyrCount determina el numero de capas de 10s boto-
nes de 10s motores de bdsqueda que se han de crear. Obviamente, dependera del
numero de botones que tiene cada capa y del numero de motores que hemos
distribuido en el array engines. Es muy sencillo. Dividimos el numero de moto-
res de busqueda (engines.length) por el numero de motores que vayamos a
mostrar por capa (perLyr).Si el resto es distinto de cero, entonces necesitaremos
una capa m&.
lnterfaz para un motor de busqueda multiple 147

Vamos a utilizar 10s valores de la aplicaci6n engines.length es 20 y perLyr


es 4. Por tanto, la variable ZyrCount sera 5. Si hubiesemos trabajado con 21 mo-
tores, 21 / 4 = 5,25. Un resto de 0,25 implica que hay que utilizar otra capa, por
lo que el valor de ZyrCount seria de 6. Aqui tenemos de nuevo su codigo:

lyrcount = Math.ceil(engines.length / perlyr);

El operador condicional trabaja tal y como acabamos de ver. Si el resto es 0,


aplicara a ZyrCount el valor de engines.length/perLyr.En cualquier otro
caso se asignara el valor de Math.ceil (engines.lenght/perLyr). Es muy
importante determinar el valor de ZyrCount. Una vez que lo hayamos hecho,
engineLinks ( ) creara las capas de ZyrCount, tal y como se especifica en las li-
neas 122-136:

for (var i = 0; i < lyrcount; i++) {


var engLinkStr = '<TABLE BORDER=O CELLPADDING=O CELLSPACING=O><TR>';
for (var j = 0; j < perlyr; j++) {
var imgIdx = (i * perlyr) + j;
if (imgIdx >= engines.length) { break; )
var imgName = nameFormat(engines[imgIdxl [ O ] ) ;
imagePreLoad(imgName, imgIdx);
engLinkStr += '<TD><A HREF="javascript: +
callSearch(document.forms[0l.elements[Ol.value, ' + imgIdx +
' ) ; " onMouseOver="hideStatus(); imageswap(\' + imgName + +
I \ ' ,

imgIdx + I , 1); return true" onMouseOut="imageSwap(\" + imgName +


' \ ' , ' + imgIdx + ' , O);"><IMG NAME="' + imgName + SRC="' +
'I'

imgPath + imgName + 'out.jpg' + ' " BORDER=O></A></TD>';

Para cada capa, engineLinks ( ) declara la variable local englinkStr,que contie-


ne el codigo de cada imagen. Despues de crear engLinkStr, utilizando el codigo de
148 lnterfaz para un motor d e blisqueda mliltiple

la linea 123, se inicia una tabla que englobara todas las imagenes. Un bucle for
anidado repetira perLynr para crear las celdas de la tabla, dentro de las cuales ira
colocando las imagenes.
Para cada repeticion perlyr, a la variable local irngIdx se le asignara el valor ( i *
perLyr ) + j . Esta expresion no es mas que un entero cuyo valor inicial sera
0 y que se ira incrementando una unidad con cada repeticion. irngldx se utiliza-
ra para identificar el prefijo de las imagenes (que se corresponde con el nornbre
del motor de busqueda del elemento 0) y proceder con la carga previa de las ima-
genes de la que ya hemos hablado. En la tabla 4.1 tenemos un ejemplo de la mul-
tiplicacion que tiene lugar cuando el valor de perLyr es 4.

Tabla 4 . 1 . Calculo de las capas que se mostraran en pantalla (cuando perLyr es 4)


-
+ j aumenta una unidad
~~

Valor de i Valor de j (i * perlyr)


0 0, 1, 2, 3 0, 1, 2, 3
1 0, 1, 2, 3 4, 5, 6, 7
2 0, 1, 2, 3 8, 9, 10, 11
3 0, 1 , 2, 3 12, 13, 14, 15
4 0, 1, 2, 3 16, 17, 18, 19

Hay 20 enteros, 0-19.


Ahora que ya conocemos el valor correspondiente a irngldx, vamos a compro-
bar que no nos hemos pasado. En la linea 126 nos encontramos con el siguiente
c6digo:

if (imgIdx == engines.length) { break; 1


lnterfaz para un motor de busqueda multiple 149

Como el valor de imgldx se va aumentando con cada repeticion, una vez que
alcance el valor de engines. length, no habra mas motores de busqueda, con
lo que la funcion interrumpira el buclefor.

Carga previa de imagenes


Ha llegado el momento de cargar 10s pares de imagenes de cada motor de bus-
queda. Antes de que esto ocurra, necesitaremos conocer el prefijo de la imagen.
Muy sencillo. El prefijo es el nombre del motor de busqueda, per0 escrito con
minusculas. Es decir, el prefijo de las imagenes de "InfoSeek sera infoseek. El de
HotBot, sera hotbot, etc. La variable imgldx identifica el prefijo que se ha de uti-
lizar con cada imagen. Y lo hace en la linea 127:

var imgName = nameFormat(engines[imgIdxl [ O ] ) ;

El elemento 0 de cada array contiene el nombre del motor de busqueda. La


variable imgldx identifica el indice del elemento, con lo que se tendra el nombre
del motor de busqueda. Todo lo que nos queda es convertir las letras a minuscu-
las. La funci6n nameFormat ( ) se encarga de ello en las lineas 158-161:

function nameFormat(str1 I
var tempArray = str.split(' ' 1 ;
return tempArray.join(").tOLOwerCase~);

Para eliminar 10s espacios en blanco se divide la cadena que se entrega entre 10s
elementos del array y luego se vuelve a unir. Ahora imgName tiene el prefijo de
las imagenes, en minusculas y sin espacios en blanco. Esta listo para pasarselo,
junto con imgIdx a imagepreload ( ) en la linea 128.

Construir el vinculo
Ha llegado la hora de crear la imagen que se vinculara a1 codigo correspon-
diente a 10s motores de busqueda. Lo tenemos en las lineas 129-135:

engLinkStr += ' < T D > < A HREF="javascript: ' +


'callSearch(document.forms[0].elements[Ol .value, ' + imgIdx + ' ) ; " ' +
'onMouseOver="hideStatus();imageswap(\' + imgName + ' \ ' , ' +
imgIdx +
I 1 ) ; return true" onMouseOut="imageSwap(\" + imgName + ' \ ' , ' +
,

imgIdx + I , O ) ; " > < I M G N A M E = " ' + imgName +


' I ' SRC="' + imgPath +
imgName + 'out.jpg' + "' B O R D E R = O > < / A > < / T D > ' ;

Vamos a estudiarlo detenidamente. Los enlaces de cada motor de busqueda


cumpliran cuatro requisitos:
150 lnterfaz para un motor de bhqueda mdtiple

1. Un codigo que se encargue de llamar a1 motor de busqueda adecuado en


el momento en que el usuario haga clic sobre la imagen.
2. C6digo para el controlador del evento inMouseOver que se hara cargo de la
situacion en el momento en que el cursor se encuentre sobre la imagen.
3. Codigo para el controlador del evento inMouseOut que se hara cargo de la
situacion en el momento en que el cursor abandone la imagen.
4. Una etiqueta IMG con un nombre (NAME) unico y un atributo SRC que se
encargara de ajustar la ruta de la imagen.

Vamos a ver c6mo podemos satisfacer cada uno de estos requisitos.


Para el primer0 tendremos que afiadir el siguiente codigo:

HREF="javascript: callSearch(document.forms[0].elements[O].valUe, ' +


imgIdx + ' ) ; "

Al hacer clic sobre el vinculo que acabamos de crear, se llamara a la funcion


callsearch ( ) , donde se pasara document.forms [ 0 I .elements [ 0 1 .value
a1 valor correspondiente de imgldx. En breve volveremos a ver a callsearch ( ) .
De momento bastara con saber que es la solucion a1 primer requisito.
La solucion a1 segundo requisito lo tenemos en el codigo:

'onMouseOver="hideStatus();imageswap(\" + imgName + ' \ I , + imgIdx +


' , 1 ) ; return true" ' +

Este codigo se encarga de controlar la llamada a la funcion hidestatus ( 1,


que eliminara cualquier URL de la barra de estado. A continuacion llamara a la
funcion imageswap ( ) , le entregara 10s tres parametros que necesita imageName,
ingldx y u n entero (1)que se corresponde con el elemento de arrayl-landles.
El remedio para el tercer requisito sera el siguiente:

'onMouseOut="imageSwap(\' + imgName + ' \ I , + imgIdx + ' , O ) ; " > ' +

Como antes. L a unica diferencia destacable es que se pasa un 0 en vez de u n 1.


Y ahora vamos a por el cuarto requisito:

'<IMG NAME="' + imgName + S R C = " ' + imgPath + imgName + 'out.jpg' +


''I

"' BORDER=O></A></TD>';

El nombre de cada imagen lo determinara imgNarne. Asi se especificara en la


funcion imageswap ( ) . El valor del atributo SRC sera la suma de 10s valores de
imgPath, imgName y out.jpg. Como todas las imagenes se iniciaran con la posi-
cion out del punter0 (es decir, cuando el cursor no estk encima de la imagen), el
lnterfaz para un motor de busqueda mdtiple 151

valor de la etiqueta SRC se ajustara entonces a las imagenes que se correspon-


dan conout.jpg. Por ejemplo, la imagen inicial de HotBot se encontrara enimages/
HotBotout.jpg.
Las lineas 137-138 terminan de dar 10s retoques:

engLinkStr += ' < / T R > < / T A B L E > ' ;


genLayer('s1ide' + i, left, top, engWdh, engHgt, false, engLinkStr);

Es decir, engLinkStr recibe el codigo HTML que se encarga de cerrar la tabla. Lo


unico que le quedara por hacer sera utilizar genLayer ( ) para crear la capa. No
olvidemos que todas las llamadas que se hagan a genLayer ( ) se pasaran con el
valor false, es decir, que se escondera la capa. Todas las capas estaran ocultas
hasta que se cargue la pagina. Luego, en la linea 201, el controlador de eventos
onLoad revela el valor de slide ( ) .

imageswap()
Ya lo hemos visto en el tercer capitulo, per0 esta version es diferente. Veamos el
contenido de las lineas 179-1 82:

function imageSwap(imagePrefix, imageIndex, arrayIdx) {


document[imagePrefix].src = eval(arrayHandles[arrayIdxl + " [ " +
imageIndex + " ] . src" ) ;

Esta funcion se encarga de invertir las imagenes. El argument0 imagePrefix iden-


tifica la imagen que se ha de invertir. Los argumentos imagelndex y arrayldx
son 10s enteros que permiten que se acceda correctamente a1 objeto Image que se
encuentra en arrayHandZes.

callsearch()
Cuando ya se tiene el formulario HTML y las capas en su sitio, el usuario uni-
camente tendra que introducir el texto de busqueda y hacer clic en el motor de
busqueda con el que quiera trabajar. Cuando hace clic en la imagen correspon-
diente a dicho buscador, la aplicacion llamara a la funci6n callsearch ( ) . Te-
nemos su codigo en las lineas 184-193:

function callSearch(searchTxt, idx) {


if (searchTxt == " " ) {
parent.frames[2l.location.href = engines[idx1[21 -I
escape(searchTxt);
1
else {
152 lnterfaz Dara un motor de b6saueda mliltide

parent.frames[2].location.href = engines[idxl [l] t


escape(searchTxt);

L a funcion callsearch ( ) espera recibir dos argumentos. seachTxt contiene el


texto que se ha introducido en el campo de texto e idx contiene un entero que se
corresponde con la informacion que hay en el array engines sobre el motor de
busqueda. En frames [ 2 1 , la aplicacion carga uno de 10s dos documentos. Si el
usuario no introduce ninguna cadena de busqueda, frames [ 2 ] cargara la pa-
gina principal del buscador. Este URL estA en el elemento 2 de 10s componentes
del array engines. Per0 si el usuario introduce una cadena de texto, la aplicacion
cargara frames [ 2 ] con el URL y la cadena de busqueda en el motor seleccionado.

Es posible que se pregunte de donde he sacado las cadenas tan largas del ele-
mento 1 de cada array de engines.
He comprobado el codigo fuente de cada pagina de busqueda y he creado una
cadena de busqueda basada en el formulario HTML usado para enviar texto.
Empezamos con un ejemplo facil. Musicsearch.com solo tiene un campo de bus-
queda. El atributo ACTION del formulario http://www.musicsearch.com/global/
searcg.cgi.El nombre del campo es QUERY.Asi, la cadena de busqueda debe ser:

http://www.musicsearch.com/global/search/search.cgi?QUERY=
i

escape(searchTxt);
lnterfaz para un motor de bljsqueda multiple 153

Es muy sencillo. Un par nombre-valor. Per0 10s motores de busqueda tienen


muchas opciones. Vamos a considerar el buscador Savvysearch (busca en el con-
tenido de las bases de datos de otras empresas en vez de hacerlo en la suya pro-
pia). Con Savvysearch introducimos una cadena de busqueda y activamos las
casillas de verificacion correspondientes a las entidades que queremos utilizar
para localizar la informacion (motores de busqueda, grupos de noticias.. .). Tam-
b i h podemos trabajar mediante reglas de busqueda booleanas, limitar el nume-
ro de resultados y especificar la cantidad de informacion que se va a mostrar con
cada resultado.
El atributo ACTION correspondiente a1 formulario de Savvysearch es http://
numan.cs.colostate.edu~~ 96 9/nph-search. Estos son 10s elementos que necesita-
r8n 10s formularios de busqueda:
El nombre de la lista que se utilizara con las busquedas basadas en
booleanos es Boolean.
El nombre del contador de 10s resultados de la lista es Hits.
El nombre de 10s botones de opci6n es df.
El nombre de 10s campos de texto sera KW.

El valor de Boolean sera OR, el de Hits sera 10, el de df normal y el del campo de
texto KW, escape (searchTxt) . No me he inventado ningun valor. Los he sa-
cad0 de las listas de selection o de 10s posibles valores de 10s botones de opcion.
Es decir, son valores perfectamente validos en el codigo HTML.
El formulario tambitn contiende dos campos ocultos. Uno tiene el nombreMode
y el otro Autostep. El campo Mode HIDDEN tiene el valor Makeplan. El campo
Autostep HIDDEN tiene el valor on. La verdad es que no tengo muy Clara su res-
pectiva finalidad, per0 en cualquier caso no importa. Todo lo que tenemos que
hacer es incluirlos a la cadena de busqueda. Enviamos una peticion a Savvysearch
y solicitamos el siguiente URL:

http://numan.cs.colostate.edu:l969/nph-search? +
classic=on&Boolean=OR&Hits=lO&Mode=MakePlan&df=normal&AutoStep=on&KW=
+ escape (searchTxt)

Otra de las ventajas de descifrar las cadenas de peticiones es que, generalmen-


te, el valor de 10s pares nombre-valor carece de importancia. Mientras se en-
cuentren en la cadena de busqueda, las cosas funcionaran bien.

Posibles ampliaciones: agregar un control para el usuario


Segun hemos mencionado anteriormente, esta aplicacih deja a1 usuario a mer-
ced de las opciones predeterminadas del motor de busqueda. Es decir, que apenas
154 lnterfaz Dara un motor de busaueda rnirltble

tendra control sobre las busquedas. L o unico que puede hacer es introducir el
texto de la consulta. Podemos codificarlo de tal forma que 10s usuarios puedan
intervenir en propiedades como el ndmero de resultados que apareceran por
pagina, la cantidad de information que se mostrara en cada uno de ellos o la
posibilidad de trabajar con booleanos como AND, OR, LIKE, NOT LIKE. En esta
seccion veremos un ejemplo basado en HotBot.
Posiblemente, la forma mas sencilla de aumentar la funcionalidad es aumentar
el numero de resultados por pagina. Necesitaremos el par nombre-valor asocia-
do con 10s resultados de las paginas de cada buscador. En la tabla 4.2 tenemos
una serie de valores posibles para unos cuantos motores de busqueda.

Tabla 4.2.Motores de busqueda y variables para determinar el numero de resultados

Motor de Nombre Posibles valores Ejemplo


busqueda del campo
HotBot DC 10, 25, 5 0 ,100 DC=10
Busqueda Numberresults 10, 20,25, 50 Numberresults=lO
avanzada
de InfoSeek
Scientific Docs 10, 25, 50, 100 Docs=lO
American
Yahoo! N 10, 20, 50, 100 n=10

El nombre de 10s campos 10s he obtenido del c6digo de las paginas de busqueda
de 10s respectivos motores. Algunos campos unicamente aparecen con las opcio-
nes avanzadas, por lo que 10s U R L del array engines no funcionaran. Tampoco
hay que olvidar que es posible que no se pueda ajustar el numero de resultados.
Los desarrolladores de 10s motores de busqueda pueden trabajar con un valor fijo.
Si en la pagina de busqueda no encontramos una opci6n con la que modificar el
numero de resultados de la pagina, quiza podamos ponernos en contact0 con la
organizacion y preguntar si se puede modificar esta opci6n. Si no es asi, unica-
mente se podra trabajar con el valor predeterminado de ciertos buscadores.
Tambitn nos encontramos con que 10s posibles valores varian de un motor de
busqueda a otro. Tendremos que incluir algo de c6digo para compensar este efec-
to. No es muy complicado. Utilizaremos el siguiente procedimiento para agregar
una lista de selecci6n a la aplicacion. A continuaci6n la utilizaremos como guia
para incluir otras propiedades de control.

1 . Aiiadimos primer0 una lista de selecci6n a1 cuadro que contiene el campo


de texto.
lnterfaz Dara un motor de bljsaueda m l j l t i ~ l e 155

2. Incluimos un elemento extra dentro de cada nuevo array que contiene el


nombre del campo de resultados de cada uno de 10s buscadores que se en-
cuentran en el array engines.
3. Aiiadimos despues un n e w Array ( ) que contenga 10s posibles valores de
cada buscador.
4. Eliminamos el par nombre-valor predefinido de la cadena de la consulta.
5. Ajustamos el codigo de la funcion callsearch ( ) para concatenar la ca-
dena de la peticion de cada buscador.
Vamos a ver todo esto utilizando HotBot como ejemplo.
Paso 1
Agregar una lista de selecci6n no deberia ser ningun problema. Conviene que
trabajemos con 10s valores comunes a todos 10s buscadores. En este c6digo uti-
lizaremos 10s valores 1, 25, 50 y 100:
<SELECT NAME= docs > I'

<OPTION VALUE= " 10 " > 10


<OPTION VALUE= '*2 5 >2 5
<OPTION VALUE= 50 >5O
<OPTION VALUE= " 10 0 " > 10 0
</SELECT>

Paso 2
Cada ejemplo de new Array() del array engines define un motor de busqueda
con tres elementos. El elemento 0 es el nombre del motor de busqueda; el ele-
mento 1 es la cadena de la solicitud; y el elemento 2 es la pagina principal del
buscador. Aqui volvemos a tener a HotBot:
n e w Array( 'HotBot I ,

'http://www.hotbot.com/?MT=',
~http://WWW.hotbot.com/')

Al elemento 3 le asignamos el nombre del campo asociado con HotBot. Si re-


cordamos el contenido de la tabla anterior, el nombre del campo es DC. Ahora el
registro de HotBot sera ask

new Array( ' H o t B o t ' ,


~http://www.hotbot.con/?MT=',
'http://www.hotbot.com/',
'DC')

Si uno o mas buscadores no trabajan con el valor que hemos determinado (por
lo tanto, carecen de un par nombre-valor), el valor del elemento 3 serh nulo.
156 lnterfaz para un motor de bhqueda mdtiple

Paso 3
Una vez que hemos identificado el nombre, afiadiremos otro array que contie-
ne todos 10s valores con 10s que se puede trabajar. De esta forma, definiremos un
nuevo array en el elemento 4. Si volvemos a consultar la tabla 4.2, veremos que
el aspect0 del registro de HotBot sera el siguiente:

new Array ( ' HotBot ,


'http://www.hOtbot.com/?MT=',
'http://www.hotbot.com/',
'DC',
new Array(l0, 25, 50, 1 0 0 ) )

Paso 4
Este paso unicamente se aplicara si la cadena predeterminada del elemento 2
contiene el par nombre-valor de 10s resultados. Esta sera la cadena de HotBot
del elemento 2:

http://www.hotbot.com/?MT=

Como DC no aparece ahi, podemos saltarnos este paso. Per0 como ejemplo, el
buscador de Scientific American contiene el par nombre-valor, que es docs = 1 0 0.
Veamos:

http://www.sciam.com/cgi-bin/search.cgi?'+
'searchby=strict&groupby=confidence&docs=lOO&query=

Tendremos que quitarlo para que quede asi:


http://www.sciam.com/cgi-bin/search.cgi?' +
'searchby=strict&groupby=confidence&query=

Si son mas de uno de 10s buscadores que carecen de un contador de resultados


(por lo tanto, carecen de u n par nombre-valor), no tendremos que crear ningun
valor en este paso.
Paso 5
El ultimo elemento que tendremos que controlar sera el codigo encargado de la
toma de decisiones (encargado de la construccion de la cadena de la consulta an-
tes de entregarselo al buscador). Lo podemos lograr con la funci6n callsearch ( ) .
Este es el c6digo original:
function callSearch(searchTxt, idx) {
if (searchTxt == " " ) {
lnterfaz Dara un motor de bhaueda mliltble 157

parent.frames[2].location.href = engines[idxI[21 +
escape(searchTxt);
>
else {
parent.frames[2].location.href = engines[idxl [ll +
escape(searchTxt);
1
I

Si el usuario no inserta nada en la cadena de busqueda, entonces la aplicaci6n


deberia mostrar a1 usuario la pagina principal del buscador. De esta manera, el
bloque if pod^-6 continuar igual. Vamos a centrarnos a continuaci6n en el blo-
que else:

else {
if(engines[idxl [31 ! = null) {
for (var i = 0; i < engines[idx][4l.length; i++) {
var selRef = parent.frames[4].document.forms[0l.docs;
if (selRef.options[selRef.selectedIndex].value =
engines[idxl [4][il .tostring()) (
parent.frames[2].location.href = engines[idxl [ll +
escape(searchTxt) + ' t i ' + engines[idx1[31 + +
I = '

engines[idxl [41 [il;


return;
1
1
parent.frames[2].location.href = engines[idxl [ l ] +
escape(searchTxt);
1

Esta es la linea que se encarga de afiadir el par nombre-valor a la cadena de la


consulta:

parent.frames[2].location.href = engines[idxl[ll +
escape(searchTxt) + ' & ' + engines[idx1[31 + +I = '

engines[idxl [41 [il ;

Lo que tenemos aqui ahora es el URL del buscador, searchTxt, el nombre


(enginesI idx J [ 3 1 ) y el valor que haya seleccionado el usuario. Sin embargo,
esto unicamente ocurrira cuando se cumplan un par de condiciones. En caso
contrario, se le asignara la cadena predeterminada que haya sido definida en
engines [ idx] [ 1I . En primer lugar, el buscador ha de poseer la propiedad que
quiere modificar el usuario. Si no, el valor de este elemento sera nulo. Esto su-
cede en el paso 3. La siguiente declaraci6n ifverifica que engines [ idx I [ 3 ] sea
distinto de cero:

if (engines[idxl [31 ! = null) (


158 lnterfaz para un motor de busqueda multiple

Si el valor es igual a cero, no se cumple la primera condicion. Por lo tanto, se


utilizara la cadena predeterminada. Si engines [ idx] [ 3 I es distinto de cero,
JavaScript repite a traves de un numero aceptable de valores definidos en el
array engines [ idx] [ 3 I . Si el numero seleccionado en la lista de seleccion,
representado por selRef .options [ selRef .selectedIndex] .value,coin-
cidiera con uno de 10s valores del array, JavaScript concatena el URL del busca-
dor del usuario y el texto de la consulta con el par nombre-valor. A continuacion
carga frames [ 2 I con el resultado del documento y detiene la ejecucion.
Si el bucle se repite con todos 10s valores aceptables sin encontrar ninguno que
coincida con el, fallara la segunda condicion, luego se utilizara la cadena prede-
terminada.
Capitulo 5
ImageMachine

Todas las aplicaciones que hemos visto se han diseiiado para una persona: el
usuario, esa persona que navega por Internet, llega a nuestras paginas Web, re-
visa el contenido, compra nuestros productos y descarga software. Esta aplica-
cion rompera con todos 10s moldes. Se ha diseiiado para la otra cara de la moneda:
el diseiiador.
Aunque DHTML ha mejorado considerablemente el resultado de 10s eventos
que se desarrollan en la Web cuando colocamos el cursor del raton sobre alguna
flecha, cuadro, b o t h u hoja de estilo, la secuencia de imagenes es una de las
ttcnicas que mas se utiliza en la Web.
Generar un codigo en JavaScript que se encargue de desarrollar este cambio de
imagenes no es muy complicado. Per0 seguro que nos complicaremos menos la
vida si creamos una aplicacion que se encargue de ello. De esta forma, como
programadores que somos, todo lo que tendremos que hacer sera reutilizar el
codigo siempre que lo necesitemos. En la figura 5.1 se puede ver la primera pan-
talla de nuestra aplicacion: ImageMachine.
El manejo de la aplicacion es muy sencillo. Bastar6 con que tomemos un par de
decisiones sobre las imagenes. Como nos indica la figura 5.1:

1. Seleccionamosprimer0 el numero de pares de imagenes con las que desea-


mos trabajar.
2. Determinamos el borde y las dimensiones (alto y ancho) predeterminadas
de todas las imagenes. Mas tarde podremos ajustarlas individualmente.
3. Activamos la opci6n "MouseDown'l si queremos usar una tercera imagen
para el estadoonMouseDown.En cualquier otro caso, la dejaremos en blanco.
160 ImaaeMachine

4. Seleccionamosfinalmente "Proceed"para continuar o bien "Reset"para vol-


ver a empezar.

_ .. . .
+ . - ? .

Image Machine dynamically generater JaraScript code and HTML needed to create image ro110vers
Heres all you need t o get $laded

..the number of image groups

..
the width and height m pixels
the image URLr

. the URLs for the HREF attribute


optional text messages for the slatus bar

Image Machine then generates cut and paste code you can easily drop into y o ~ r w e bpages You
ran also see *hat the page look$ like before you decide to keep the code

Figura 5.1. ImageMachine lista para entrar en acci6n

Llegados a este punto, la aplicacion generara la plantilla de la figura 5.2.


Si no seleccionamos la opcion "MouseDown",tendremos dos campos HTML para
cada grupo de imagenes: uno para la imagen principal y otro para la secunda-
ria. Si seleccionamos la opcion, tendremos un campo extra. Cada imagen inclu-
ye campos donde se introducira el atributo HREF de cada vinculo y el texto que
se mostrara en la barra de estado cuando el usuario coloque el cursor sobre el
hipervinculo. Por ultimo, tenemos tres campos pequeiios donde encontramos
10s valores predeterminados para el alto, ancho y borde de cada grupo de image-
nes. Si lo deseamos, podemos cambiarlos. Vamos a dar unos ultimos pasos para
conseguir el codigo que nos interesa:
1. Escribimos la ruta de cada imagen principal (la que aparecera en la pantalla
cuando el cursor del raton estC en cualquier punto de la pantalla menos
sobre la foto) en 10s campos de 10s archivos. Estos campos son de archivos
en vez de texto, porque las imagenes se encontraran dentro del sistema
desde el que trabajamos. Repetimos esto mismo con la imagen secundaria
y con la que se activara cuando se haga clic con el boton del raton.
2. Introducimos un URL absoluto o relativo en el campo de texto asociado con
HREF para cada una de las imagenes del grupo.
162 ImaaeMachine

Cul mdpesle (he code belowinlo en HTML nle Jhe blue code repiesenls informslian you prowided

Figura 5 . 3 . El codigo que acabamos de crear

Una de las propiedades mas interesantes del c6digo que se genera con esta apli-
cacion es que reduce efectividad de acuerdo con la capacidad que tiene el explo-
rador Web utilizado para trabajar con JavaScript. Es decir, 10s exploradores que
trabajen con la versi6n 1.2 de JavaScript utilizaran i d o w e o v e r , onMouseOut y
onMouseDown, mientras que 10s exploradores compatibles con la versi6n 1.1 de
JavaScript unicamente trabajaran con onMouseOver y onMouseOut. Los explora-
dores compatibles con JavaScript 1.O se limitaran a mostrar el texto relaciona-
do en la barra de estado.
iY quk tal se lleva este sistema de trabajo con el ancho de banda? S610 se des-
cargaran las imageries que se vayan a utilizar. De hecho, si el explorador no pue-
de trabajar con ellas, no las cargara. Asi, 10s exploradores compatibles con 1.1
no descargaran la imagen relacionada con el evento onMouseDown. Y 10s com-
patibles con JavaScript 1.O no descargaran ninguna.

Requisitos para la ejecucion


Aunque el c6digo generado se puede ejecutar en cualquier explorador compa-
tible con JavaScript, nosotros (como desarrolladores que somos) debemos usar
ImaneMachine 163

un explorador compatible con JavaScript 1.2. Ciertas sustituciones de cadenas


y partes del codigo han de trabajar con esta version de JavaScript. En lo referen-
te a la escalabilidad, podemos crear c6digo para que trabaje con todas las secuen-
cias de imagenes que acepten 10s recursos del sistema. El numero maximo es 50
grupos, que es mas de lo que nunca nos encontraremos en cualquier pagina.
Por cierto, el disefio de la interfaz esta optimizado para que se vea en u n moni-
tor con una resolucih de 1024x768. La plantilla de imagenes y el cddigo nece-
sitaran una pantalla ancha.

Figura 5.4.A travks de 10s botones "Preview"y Change Info" se accede a otras
opciones

Analisis de la sintaxis
Antes de entrar a ver el codigo, debemos hacernos una idea visual de c6mo fun-
ciona el programa. En la figura 5.5 vemos el grafico de flujo basico, de principio
a fin. Empezaremos creando el formulario de la imagen y configurando 10s as-
pectos de cada secuencia. Luego, avanzaremos y retrocederemos para acceder a
la vista previa, efectuar modificaciones y generar el c6digo. Cuando tengamos
el codigo que realmente nos gusta, podremos copiarlo donde queramos.
164 ImageMachine

Seleccibn del nOmem,


dimensiones y bodes

Generacibn
del fomrulario
de la imagen

II
hsercibn de /as Mas
de la imagen, HREF
y text0 de la barn
de estado

4 Borrar

previsualizar
Generar

Guardar
4

Figura 5.5. Logica seguida en ImageMachine

ImageMachine tiene tres archivos: una p6gina marco y dos de contenidos. La


primera se encuentra en el archivo index.htrnZ y contiene 10s archivos nav.htrnZ
y base.htrnZ. index.htrnZ no tiene ni JavaScript ni ninguna sorpresa desagrada-
ble. De hecho, tenemos su contenido en el ejemplo 5 . 1 :

Ejemplo 5.1. index.htm1


1 <HTML>
2 <HEAD>
3 <TITLE>ImageMachine</TITLE>
4 </HEAD>
ImageMachine 165

5 <FRAMESET ROWS="105, FRAMEBORDER="O" BORDER="O">


*'I

6 <FRAME SRC="nav.html" NAME="nav" SCROLLING=NO>


7 <FRAME SRC="base.html " NAME="base">
8 </FRAMESET>
9 </HTML>

Si miramos el contenido de base.htm1, veremos que se trata de un codigo HTML


mas estatico. Antes de ver el c6digo del archivo nav.htm1 del ejemplo 5.2, es muy
importante comprender unas cuantas cosas sobre 61. Es muy largo (mas de 400
lineas), por lo tanto, dificil de leer. Per0 si nos lo proponemos, veremos que no es
tan complicado.

Ejemplo 5.2. nav.htm1


1 <HTML>
2 <HEAD>
3 <TITLE>ImageMachine</TITLE>
4 <SCRIPT LANGUAGE="JavaScriptl.2">
5
6 var platform = navigator.platform;
7 var lb = (platform.indexOf("Win"! = -1) ? "\n\r" :
8 (platform.indexOf("Mac" ! = -1) ? "\r" : " \ n " ) ) ;
9 var fontopen = '<FONT COLOR=BLUE>';
10 var fontclose = '</FONT>';
11
12 function genSelect(name, count, start, select) {
13 var optStr = " " ;
14 for (var h = start; h <= count; h++) I
15 optStr += "<OPTION VALUE=" + h +
16 (h == select ? " SELECTED" : + " > " + h;
" ' I )

17 1
18 document.write("<SELECTNAME=" + name + " > " + optStr +
"</SELECT>") ;
19 }
20
21 function captureDefaultProfile(form0bj) {
22 setArrays( ) ;
23 imgDefaults = formObj;
24 var imgQty = (imgDefaults.imgnumber.se1ectedIndex + 1);
25 var imgHeight = (imgDefaults.pxlheight.selectedIndex);
26 var imgWidth = (imgDefaults.pxlwidth.selectedIndex);
27 var imgBorder = (imgDefaults.defbdr.selected1ndex);
28 for (var i = 0; i < imgQty; i++) {
29 imgPrim[il = " ' I ;

30 imgRoll[il = " ' I ;

31 imgDown[i] = "'I;

32 imgLink[il = " " ;


33 imgText [i] = 'I";

34 imgWdh[il = imgwidth;
35 imgHgt[i] = imgHeight;
166 ImageMachine

36 imgBdr[il = imgBorder;
37 }
38 generateEntryForm0;
39 }
40
41 function setArrays0 {
41 imgPrim = new Array
43 imgRoll = new Array
44 imgDown = new Array
45 imgLink = new Array
46 imgText = new Array
47 imgwdh = new Array
48 imgHgt = new Array
49
50
51
52
53 function generateEntryForm0 {
54 with(parent.frames[l].document) {
55 open ( ) ;
56 writeln("<HTML><BODY BGCOLOR=FFFFEE><FONTFACE=Arial SIZE=2>"
57 + "<BLOCKQUOTE>Chooseor enter the paths of all images in
58 the '' + "columns below. Add the link path (e.g., <FONT
FACE=Courier> " +
59 "webgage.html</FONT>)or script text (e.g., <FONT
FACE=Courier>" +
60 "javascript:</FONT>)for each HREF attribute, and enter
any
'I +
61 "message you want to display in the status bar during the ''
62 + "<FONT FACE=Courier>MouseOver</FONT> event. Then choose ''
63 + "<B>Generate</B> to get your code, or <B>Preview</B> to
see the " +
64 "code in action.</BLOCKQUOTE><FORM NAME='imgProfile' +
65 "onSubmit='return false;'><CENTER><TABLEBORDER=O
ALIGN=CENTER
I' +
66 "CELLSPACING=5 CELLPADDING=5><TH ALIGN=LEFT><FONT
FACE=Arial>#" +
67 "<TH ALIGN=LEFT><FONT FACE=Arial>Primary Path" +
68 "<TH ALIGN=LEFT><FONT FACE=Arial>Rollover Path" +
69 (imgDefaults.mousedown.checked ? "<TH ALIGN=LEFT>" +
70 "<FONT FACE=Arial>MouseDown Path" : " " ) +
71 " < T R > < T D > < B R > < / T D > < / T R) >;"
72 1
73
74 for (i = 0 ; i < imgPrim.length; i++) {
75 with(parent.frames[ll.document) {
76 writeln( "<TR>" +
77 "<TD><FONT FACE=Arial SIZE=2><CENTER><B>"+ (i + 1) +
78 "</B></CENTER><TDVALIGN=BOTTOM><FONT FACE=Arial SIZE=2>" +
79 "<INPUT TYPE=FILE NAME='prim" + i + '' VALUE=''' +
imgPrim[il i
Irnage Mac hine 167

80 "'><TDVALIGN=BOTTOM><FONT FACE=Arial SIZE=2><INPUT


TYPE=FILE " +
81 "NAME='seci"+ i + " ' VALUE='" + imgRoll[i] + " ' > " +
82 (imgDefau1ts.mousedown.checked ? "<TD VALIGN=BOTTOM><FONT
83 + "FACE=Arial SIZE=2><INPUT TYPE=FILE NAME='down" + i + " '
VALUE= ' '' +
84 imgDown[i] + " ' > " : " " ) + "<TR><TD VALIGN=BOTTOM><FONT " +
85 "FACE=Arial SIZE=2> &nbsp;&nbsp;</TD>" +
86 "<TD VALIGN=BOTTOM><FONT FACE=Arial SIZE=2><INPUT
TYPE=TEXT " +
87 "NAME='href"+ i + " ' VALUE='" + imgLink[i] + " ' > " +
88 "<IMG S R C = ' i m a g e s / h r e f . j p g ' > < T D VALIGN=BOTTOM><FONT
FACE=Arial " +
89 "SIZE=2><INPUT TYPE=TEXT NAME='stat" + i + " ' VALUE="' +
90 imgText[i] + "'><IMGSRC='images/statusbar.jpg'> '' +
91 (!imgDefaults.mousedown.checked ?"<TR>" : " " ) +
92 "<TD VALIGN=BOTTOM><FONT FACE=Arial SIZE=2>" +
93 (!imgDefaults.mousedown.checked ?
94 "</TD><TDVALIGN=BOTTOM><FONT FACE=Arial SIZE=2>" : " " ) +
95 "<fNPUT TYPE=TEXT NAME='wdh" + i + ' VALUE= I' +
96 imgWdh[i] + ' ' I SIZE=3> <IMG SRC='images/wdh.jpg'> +
97 "&nbsp; <INPUT TYPE=TEXT NAME='hgt" + i + " ' VALUE='" +
98 imgHgt[i] + " I SIZE=3><IMG SRC='images/hgt.jpg'>&nbsp;" +
99 (!imgDefaults.mousedown.checked ?
100 "<TD VALIGN=BOTTOM><FONT FACE=Arial SIZE=2>" : I' ) +
101 "<INPUT TYPE=TEXT NAME= ,bdr" + i + ' VALUE= + imgBdr [i]
102 + I,,SIZE=3> <IMG SRC='images/bdr.jpg'>"+
103 "<TR><TD VALIGN=BOTTOM COLSPAN=" +
104 (!imgDefaults.mousedown.checked ? "3" : "4") +
105 "><BR><HRNOSHADE><BR></TD></TR>") ;
106 1
107 1
108
109 with(parent.frames[ll.document) {
110 writeln("</TABLE><CENTER><INPUTTYPE=BUTTON +
111 "onClick='parent.frames[O].imgValid8(this.form,true);' " +
112 "VALUE='Generate'><INPUTTYPE=BUTTON +
113 "onClick='parent.frames[O].imgValid8(this.forrn, false);' "
114 + "VALUE='Preview'><INPUT TYPE=RESET VALUE=' Clear '>"
115 + "</FORM></BODY></HTML> ) ; I'

116 close ( ) ;
117 1
118
119
120 function imgValid8(imgTemplate,mimeType) I
121 for (var i = 0; i < imgPrim.length; i++) {
122 if (imgTemplate['prim'+ i] .value == II "I'

123 imgTemplate[ 'seci' + i] .value == " " I I


124 imgTemplate['href'+ i1.value == { " ' I )

125 alert("Al1 images and HREF attributes must have URLS.");


126 return;
168 IrnageMachine

127 1
128 if (imgDefau1ts.mousedown.checked)I
129 if (imgTemplate['down' + i] .value == " " ) {
130 alert("Al1 images and HREF attributes must have URLs.");
131 return;
132 >
133 1
134 1
135 genJavaScript(imgTemplate, mimeType);
136 1
137
138 function genJavaScript(imgTemplate, mimeType) I
139 imageLinks = " ;
140
141 if (mimeType) (
142 It = "&lt;";
143 gt = " &gt ; " ;
144 br = <BR> ;
'I #*

145 HTML = true;


146 nbsp = "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
147 >
148 else {
149 It - " < ,, .
150 gt - ">" .
151 br = lb;
152 HTML = false;
153 nbsp = .II

154 >
155
156 if(imgTemp1ate ! = null) {
157 setArrays( ) ;
158 for (var i = 0; i < (imgDefaults.imgnumber.selected1ndex +
1); i++) {
159 imgPrim[i] = purify(imgTemp1ate"prim' + i].value);
160 imgRoll[i] = p u r i f y ( i m g T e m p 1 a t e " s e c i ' + i].Value);
161 if (imgDefau1ts.mousedown.checked) I
162 imgDown[i] = purify(imgTemplate['down' + il .value);
163 >
164 imgLink[i] = purify(imgTemplate['href' + i1.value);
165 imgText[i] = purify(imgTemp1ate"stat' + i].value);
166 imgWdh[i] = purify(imgTemp1ate"wdh' + i1.value);
167 imgHgt[i] = purify(imgTemp1ate"hgt' + i].value);
168 imgBdr[i] = purify(imgTemp1ate"bdr' + il .value);
169 1
170 1
171
172 if (HTML) {
173 primJavascript = "<HTML><HEAD><TITLE>ImageMachine
Code</TITLE>" +
174 "</HEAD><BODY BGCOLOR=FFFFEE><FONT FACE=Arial>" +
175 "<I>Cut and paste the code below into an HTML file. The
blue " -I
IrnageMachine 169

176 "code represents information you provided.</I>" +


177 "<BR><BR></FONT><FONTSIZE=2 FACE=Arial>" +
178 It + "HTML" + gt + "<BR>" + It + "HEAD" + gt + "<BR>" +
179 It + "TITLE" + gt + "Image Machine Code" + It + "/TITLE" +
gt ;
180 I
181 else {
182 primJavascript = "<HTML><HEAD><TITLE>ImageMachine
Code</TITLE>";
183 1
184
185 primJavascript += br + br + It + "SCRIPT
LANGUAGE=\"JavaScript\ + "'I

186 gt + br + br + " / / Define global variables in JavaScript 1.0"


+ br +
187 "var canRollOver = false;" + br + "var canClickDown = false;"
+ br +
188 br + It + "/SCR" + "IPT" + gt + br + br + It +
189 "SCRIPT LANGUAGE =\"JavaScriptl.l\""+ gt + br + br +
190 " / / Change CanRollOver to true in JavaScript 1.1" + br +
191 "canRollOver = true:" + br + br;
192
193 secJavaScript = It + "SCRIPT LANGUAGE=\"JavaScriptl.2\"" + gt +
br +
194 br + " / / Change canClickDown to true in JavaScript 1.2" + br
195 + "canClickDown = true;" + br + br;
196
197 for (var j = 0; j < imgPrim.length; j++) {
198 primJavascript += " / / Primary and rollover image sources # " +
199 (j + 1) + br +"switch" + ( j + 1) + "out = new Image(" +
200 (HTML ? fontopen : " " ) + imgWdh I1 +
201 (HTML ? 'V/FONT>,": 'I, ") +
202 (HTML ? fontopen : " " ) + imgHgt jl +
203 (HTML ? fontClose : " " ) + " ) ; " + br + "switch" + (j + 1) +
204 "out.src = "' +
205 (HTML ? fontopen : " " ) +
206 (imgPrim[jl.indexof( " : \ \ " ) != - ? pathprep ( imgPrim [ j 1 ) :
207 imgPrim[j]) +
208 (HTML ? fontclose : " " ) + ' " ; " + br + "switch" + (j + 1) +
209 "over = new Image ( " +
210 (HTML ? fontopen : " " ) + imgWdh[jl +
211 (HTML ? '"</FONT>," : 'I, ") +
212 (HTML ? fontopen : " " ) + imgHgt[jl +
213 (HTML ? fontclose : + " ) ; " + br + "switch" + (j + 1) +
' I " )

214 "over.src = +
' * I

215 (HTML ? fontopen : " " ) +


216 (imgRoll[j].indexOf(":\\")! = -1 ? pathPrep(imgRoll[jl) :
217 imgRoll[j]) +
218 (HTML ? fontclose : " " ) + '";" + br + br;
219
220 if (imgDefau1ts.mousedown.checked) {
170 IrnageMachine

221 secJavaScript += " / / MouseDown image source # " + (j + 1) +


br +
222 "switch" + (j + 1) + "down = new Image(" +
223 (HTML ? fontopen : + imgWdh[jl +
"'I)

224 (HTML ? "</FONT>,": ") + ' I I

225 (HTML ? fontopen : " " ) + imgHgt[jl +


226 (HTML ? fontclose : " " ) + " ) ; '' + br + "switch" +
227 ( j + 1 ) + "down.src = "' +
228 (HTML ? fontopen : " " ) +
229 (imgPrim[ jI . indexof ( " : \ \ " ) ! = -1 ? pathprep (imgDown[ j 1 ) :
230 imgDown[j 1 ) +
231 (HTML ? fontclose : " " ) + " ' ,. " + br + br;
232 1
233
234 imageLinks += It + " ! - - <I> Image Link # " + (j + 1) +
235 </I>//--" + gt + br + It + "A HREF=\"" +
236 (HTML ? fontopen : " " ) + imgLink[jl +
237 (HTML ? fontclose : " " ) + " \ " '' + br + nbsp +
238 "onMouseOver=\"imageSwap('switch" + (j + 1) +
239 , , 'over',false): display( "' +
I,

240 (HTML ? fontopen : " " ) + imgText[j] +


241 (HTML ? fontclose : " " ) + ' " ) ; return true;\"" + br +
242 nbsp + "onMouseOut=\"imageSwap(' switch" +
243 (j + 1) + 'out',false); display(");\"" +
" I ,

244 (imgDefaults.mousedown.checked?
245 br + nbsp + "onMouseDown=\"isDown=!isDown;
imageswap( switch" +
246 (j + 1) + 'down',true);\"" : " " ) +
" I ,

241 gt + br + It + "IMG SRC=\""+


248 (HTML ? fontopen : " " 1 + pathPrep(imgPrim[jI ) +
249 (HTML ? fontclose : " " ) + " \ " " + br + nbsp +
250 "NAME=switch" + (j + 1) + br + nbsp + "WIDTH=" +
251 (HTML ? fontopen : " " ) + imgWdh[jl +
252 (HTML ? fontclose : " " ) + br + nbsp + "HEIGHT=" +
253 (HTML ? fontopen : " " ) + imgHgt[jl +
254 (HTML ? fontClose : " " ) + br + nbsp + "BORDER=" +
255 (HTML ? fontopen : " ) + imgBdr [ j I +"

256 (HTML ? fontclose : " " ) +


257 gt + " " + It + " / A " + gt + br + br + br;
258 }
259
260 scriptclose = br + It + "/SCR" + "IPT" + gt + br + br;
261
262 swapcode = br + It + "/SCR" + "IPT" + gt + br + br +
263 It + "SCRIPT LANGUAGE =\"JavaScript\""+ gt + br + br t
2 64 (imgDefau1ts.mousedown.checked ?
265 "var isDown = false;" + br + br : " " ) +
266 " / / Conditionally perform the rollovers in JavaScript 1.0" +
br +
267 "function imageSwap(imageName, imagesuffix) { " + br +
268 nbsp + "if (!canRollOver) { return; 1 ' ' + br + nbsp +
ImageMachine 171

269 (imgDefau1ts.mousedown.checked ?
270 "if (!isDown) { " + br + nbsp + nbsp : " " ) +
271 "document[ imageName] . src = +
272 "eval(imageName + imagesuffix + \".src\"); " + br + nbsp +
216 (imgDefau1ts.mousedown.checked ? nbsp + " ) " + br + nbsp +
274 "else if (canClickDown) + br + {I'

278 nbsp + nbsp + "document[imageNamel.src = " +


276 eval(imageName + imagesuffix + \".src\");"+ br +
277 nbsp + nbsp + " I " + br + nbsp : + " I " + br + br + " ' I )

278 "function display(stuff) { window.status = stuff: 1'' +


279 br + br + It + "/SCR" + "IPT" + gt + br;
280
281 primHTML = br + It + 'n/HEAD"+ gt + br +
282 It + "BODY BGCOLOR=FFFFEE" +
283 gt + br + br + (HTML ? "<FONT COLOR=RED>" : + It + I " " )

284 .I _ - <I> Image Code Begins </I> / / - - " + gt + br +


80

285 (HTML ? fontclose : + br + br; 'I")

286
287 S ecHTML = (HTML ? "<FONT COLOR=RED>" : " " ) +
288 It + <I> Image Code Ends</I> / / - - " + gt +
' I ! - -

289 (HTML ? fontclose : " " ) + br + br +


290 (HTML ? It + "/BODY" + gt + br + It + "/HTML" + gt : " " ) +
291 br + br + "<CENTER><FORM>"+ br +
292 <INPUT
I' TYPE=BUTTON
onClick='parent.frames[O].genJavaScript(null," +
293 (HTML ? "false" : "true") + " ) : ' VALUE='" +
294 (HTML ? 'Preview' : 'Generate') + '">&nbsp;&nbsp;&nbsp;" +
295 "<INPUTTYPE=BUTTON + I'

296 "onClick='parent.frames[O].generateEntryForm();' " +


297 "VALUE= Change Info > + br + < /FORM></CENTER> + br + br + I'

298 " < /BODY></HTML> ; I'

299
300 agregate = primJavascript +
301 (imgDefau1ts.mousedown.checked ? scriptclose + secJavaScript
: " 8 9 ) +

302 swapcode + primHTML + imageLinks + secHTML;


303
304 parent.frames[l].location.href =
305 "javascript: parent.frames[Ol .agregate";
306 1
307
308 function purify(txt) { return txt.replace(/\'I\"/g, " " ) ; }
309
310 function pathPrep(path) {
311 if (path.indexOf( " : \ \ " ) ! = -1) {
312 path = path.replace(/\\/g, " / " ) ;
313 path = path.replace(/:\//. " ) / " ) ;
314 return "file:///"+ path;
315 }
316 else { return path; }
317 1
172 ImageMachine

318
319 </SCRIPT>
320 </HEAD>
321 <BODY BGCOLOR=FFFFEE>
322 <FORM>
323 <TABLE BORDER="O">
324 <TR>
325 cTD VALIGN=MIDDLE>
326 <IMG SRC="images/image-machine.qif" WIDTH=275 HEIGHT=56
HSPACE=25>
327 </TD>
328 <TD>
329 < ! - - Create a the default template / / - - >
330 <TABLE BORDER="O ALIGN="CENTER">
331 <TR>
332 <TD VALIGN="TOP">
333 <FONT FACE= Ar ial SIZE=2>
'I

334 Image Pairs


335 </TD>
336 < TD VALIGN= " TOP " >
337 <FONT FACE=" Ar ial " SIZE=2>
338 <SCRIPT LANGUAGE=" JavaScript1.2 'I >
339 <!--
340 genSelect ("imgnumber",5 0 , 1, 1);
341 //-->
342 </SCRIPT>
343 c/TD>
344 <TD VALIGN= "TOP">
345 <FONT FACE="Arial" SIZE=2>
346 Width
347 </TD>
348 < T D VALIGN="TOP">
349 <FONT FACE="Arial" SIZE=2>
350 <SCRIPT LANGUAGE="JavaScript1.2 " >
351 <!--
352 genSelect("pxlwidth",250, 0, 90);
353 //-->
354 </SCRIPT>
355 </TD>
356 <TD VALIGN="TOP">
351 <FONT FACE="Arial" SIZE=2>
358 MouseDown
359 </TD>
360 <TD VALIGN="TOP">
3 61 <FONT FACE="Arial" SIZE=2>
3 62 <INPUT TYPE=CHECKBOX NAME="mousedown">
363 </TD>
364 </TR>
365 <TR>
366 <TD VALIGN= "TOP">
3 67 <FONT FACE="Arial" SIZE=2>
ImageMachine 173

368 Border
369 </TD>
370 <TD VALIGN="TOP">
371 <FONT FACE="Arial" SIZE=%>
372 <SCRIPT LANGUAGE="JavaScript1.2">
373 <!--
374 genSelect ("defbdr",10, 0, 0 ) ;
375 //-->
316 </SCRIPT>
377 </TD>
378 < TD VAL IGN=" TOP " >
379 <FONT FACE= Ar ial S IZE=2>
'I 'I

380 Height
381 < / TD>
3 82 <TD VALIGN="TOP">
383 <FONT FACE=" Ar ia1 " SIZE=2>
384 <SCRIPT LANGUAGE="JavaScriptl.2">
385 <!--
386 genselect ( "pxlheight", 250, 0, 5 0 ) ;
3 87 //-->
388 < /SCRIPT>
3 89 < / TD>
390 <TD VALIGN="TOP">
391 <FONT FACE=" Arial " S IZE=2>
392 <INPUT TYPE=BUTTON VALUE="Proceed"
393 onClick="captureDefaultProfile(this.form);">
394 </TD>
395 <TD VALIGN="TOP">
396 <FONT FACE="Arial" SIZE=2>
397 <INPUT TYPE=RESET VALUE=" Reset " >
398 </TD>
399 </TR>
400 </TABLE>
401 </TD>
402 </TR>
403 </TABLE>
404 </CENTER>
405 < /FORM>
406 </BODY>
407 </HTML>

Este es todo el codigo de la aplicacion. Es posible que, en ciertos puntos, parez-


ca algo confuso, per0 no es tan malo como parece. Para comprenderlo mejor,
vamos a analizar la aplicacion desde el punto de vista del usuario. Consta de
cinco fases:

1 . Carga de paginas.
2. El usuario introduce 10s pares de las imagenes y las opciones predetermi-
nadas y hace clic en eI boton "Proceed".
174 ImageMachhe

3. El usuario rellena 10s campos correspondientes a las rutas de las imagenes,


atributos HREF, etc. y selecciona el b o t h "Generate"para ver el codigo.
4. El usuario hace clic en el b o t h "Preview" para ver el codigo en accion.
5. El usuario hace clic en el boton "Change Info" para proceder con 10s cam-
bios realizados.

Paso 1: carga de paginas


Todo parece muy normal. Tenemos un marco de trabajo llamado index.htm1 y
dos marcos secundarios: nav.htm1 y base.htm1. Per0 JavaScript se va a poner
manos a la obra antes de que el usuario haga nada. Me gustaria que se prestase
especial atencion a1 contenido de las lineas 323-403. Aqui tenemos el codigo de
una tabla con varias llamadas a la funcion de JavaScript. Por ejemplo:

<TD VALIGN= "TOP'' >


<FONT FACE= Ar ial'* S IZE=2>
Image Pairs
</TD>
< TD VAL IGN= '' TOP '' >
<FONT FACE="Arial" SIZE=2>
<SCRIPT LANGUAGE="JavaScriptl,2">
<!--
genSelect("imgnumber", 50, 1, 1);
f /-->
</SCRIPT>
</TD>
<TD VALIGN= "TOP">
<FONT FACE="Arial" SIZE=2>
Width
</TD>
<TD VALIGN= "TOP">
<FONT FACE="Arial" SIZE=2>
<SCRIPT LANGUAGE="JavaScript1.2">
<!--
genSelect("pxlwidth", 250, 0, 90);
//-->
</SCRIPT>
</TD>

La llamada a la funcion getselect ( ) utiliza JavaScript para crear dinamica-


mente listas de seleccion. Cada una de ellas nos permite determinar valores
predeterminados para 10s atributos de la imagen. Estas listas de seleccion fun-
cionan mejor aqui que en 10s archivos de texto porque no tenemos que preocu-
parnos por la validacion de 10s datos. Los usuarios no podran introducir ningun
valor incorrect0 (por ejemplo, un valor que no sea numkrico) en 10s campos
correspondientes a las dimensiones o a1 grosor del borde, porque 10s tendra que
ImageMachine 175

seleccionar de una lista desplegable. Per0 iquitn quiere crear una lista desplega-
ble con 250 o 300 valores diferentes? JavaScript nos puede ayudar con una
llamada a la funcion genselect ( ) para cada lista de seleccion. Veamos el con-
tenido de las lineas 12-1 9:

function genSelect(name, count, start, select) I


var optStr = " " ;
for (var h = start; h <= count; h++) {
optStr += "<OPTION VALUE=" + h +
(h == select ? " SELECTED" : " " ) + " > " + h;
1
document .write("<SELECTNAME=" + name + " > " + optStr + "</SELECT>") ;
}

L a funcion genselect ( ) espera cuatro argumentos: una cadena que conten-


dra el nombre de la lista de seleccion, el numero de opciones con que contara, el
entero que se mostrara en primer lugar (el resto de valores se calcularan in-
crementando progresivamente su valor en una unidad) y u n numero que espe-
cifica cual de todos 10s valores aparecera como seleccionado. genselect ( 1 se
limita a repetir el c6digo de start a count, con lo que crea una cadena de etiquetas
<OPTIONS>.Ya tenemos las paginas cargadas y listas. Vamos a ver qut ocurre
cuando el usuario introduce 10s valores predeterminados.

Paso 2: introducir 10s valores predeterminados y 10s pares


de imagenes
En la figura 5 . 1 podemos ver que el usuario dispone de cuatro listas de selec-
cion y de una casilla de verificacion con las que establecer 10s valores predeter-
minados. De todos ellos, el valor mas importante que se ha de configurar es el
numero de pares de imagenes. ImageMachine permite trabajar cualquier nume-
ro de pares comprendido entre 1 y 50. La verdad es que no se me ocurre ningun
caso en el que haya que trabajar con 50 pares, per0 un poco de prevision no hace
ningun dafio.
El usuario puede escoger el ancho y alto predeterminado de todos 10s pares de
imagenes. Cada lista de seleccion tiene un rango de pixeles comprendido entre 1
y 250. Mas tarde podremos cambiar el valor seleccionado, per0 de momento, lo
que tenemos funciona. El ancho y el alto predeterminados son, respectivamen-
te, 9 0 y 50. Es un rectangulo que hara las funciones de boton.
L a ultima lista de seleccion se utiliza para determinar el grosor del borde. Las
posibles medidas estan comprendidas entre 0 y 10 pixeles. Generalmente se tra-
baja con un grosor de 0, per0 la verdad es que me he encontrado con botones se-
cuenciales con un borde.
176 ImageMachine

Mediante la casilla de verificacibn, el usuario podra incluir una imagen mas a


la secuencia del evento onMouseDown (el explorador tendra que ser compatible
con JavaScript 1.2).A continuacion, el usuario hara clic en el boton "Proceed"
para generar una plantilla de imagenes que se corresponda con la informacibn
que se le acaba de proporcionar.

Paso 3: completar la ruta de las imagenes, 10s atributos HREF, etc.


Cuando el usuario hace clic en "Proceed', ImageMachine genera una plantilla de
imagen personalizada, como la de la figura 5.2. Para crearla trabaja con tres fun-
ciones: captureDefaultProf i l e ( ) , s e t A r r a y s ( ) ygenerateEntryForm( ) .

captureDefaultProfile()
La primera funcion a la que se llama cuando el usuario hace clic sobre el boton
"Proceed, es CaptureDefaultProf i l e ( 1 . En las lineas 21-39 podemos ver su
contenido:

function captureDefaultProfile(form0bj) {
setArrays ( ) ;
imgDefaults = form0bj;
var imgQty = (imgDefaults.imgnumber.selected1ndex + 1 ) ;
var imgHeight = (imgDefaults.pxlheight.selected1ndex);
var imgwidth = (imgDefaults.pxlwidth.selected1ndex);
var imgBorder = (imgDefaults.defbdr.selected1ndex);
for (var i = 0; i < imgQty; i++) (
imgPrim[il = I"';

imgRoll[i] = " ' I ;

imgDown[i] = " ' I ;

imgLink[il = "'I;

imgText [ i] = " " ;


imgWdh [ i I = imgWidth;
imgHgt[il = imgHeight;
imgBdr[il = imgBorder;
1
generateEntryForm() ;
I

La primera acci6n que desarrolla es invocar a la funcion s e t A r r a y s ( ) . En el


extract0 de codigo que tenemos a continuacion podemos ver el contenido de las
lineas 41-50. En ellas, s e t A r r a y s ( ) declara e inicia cada uno de 10s ocho arrays.
Cada uno se encargara de albergar el valor de 10s atributos de cada grupo de
irnagenes. Por ejemplo, imgPrim contiene las rutas de las imagenes principales
de todas las secuencias. imgRol2 contiene todas las rutas de las imagenes de la
secuencia que se corresponden cononMouseOver, etc. Si no se declaran 10s arrays,
s e t A r r a y ( ) se encargara de ello. Si ya se han declarado anteriormente (el usuario
ImageMachhe 177

puede haber generado c6digo que se encargue de ello), la funcion 10s reiniciara
para que su valor inicial sea cero.

function setArrays0 {
imgPrim = new Array(
imgRoll = new Array(
imgDown = new Array(
imgLink = new Array(
imgText = new Array(
imgWdh = new Array();
imgHgt = new Array( ) ;
imgBdr = new Array( ) ;
I

Despues de que se devuelva setArrays ( 1, captureDefaultProf ile ( 1 sigue


copiando el objeto formobj dentro de la variable imgDefauZts. Una cosa impor-
tante: imgDefauZts es una variable global. No se elimina, como la mayoria de va-
riables de la aplicaci6n. Guarda una copia de las opciones predeterminadas por
el usuario por si quiere pasar de la pantalla de resultados a la de configuracion
del c6digo. Este es el unico punto de la aplicacion en el que se define el valor de
imgDefauZts. Es decir, que la unica forma que tienen 10s usuarios para modificar
sus opciones predeterminadas es volviendo a hacer clic en el b o t h "Proceed'.
Cuando ImageMachhe tiene copia de las opciones predeterminadas por el usua-
rio, la funcion captureDefaultProf ile ( ) declara cuatro variables locales:

var imgQty = (imgDefaults.imgnumber.selected1ndex + 1 ) ;


var imgHeight = (imgDefau1ts.pxlheight.selectedIndex);
var imgWidth = (imgDefaults.pxlwidth.selected1ndex);
var imgBorder = (imgDefaults.defbdr.selected1ndex);

imgaty representa el numero de secuencias que quiere el usuario, imgHeight,


imgWidth e imgBorder representan la configuracibn predeterminada del alto, an-
cho y borde del boton. Estas variables permiten que la funci6n asigne valores a
10s arrays que se han declarado en la funci6n setArrays ( 1 . Su c6digo se en-
cuentra en las lineas 28-37:

for (var i = 0; i < imgQty; i++) {


imgPrim[i] = "'I;
imgRoll[i] = "'I;
imgDown[i] = "'I;
imgLink[i] = "'I;

imgText [ i] = "";
imgWdh[i] = imgWidth;
imgHgt[i] = imgHeight;
imgBdr[i] = imgBorder;
1
178 ImageMachine

El buclefor hace tantas repeticiones como secuencias desee el usuario, asignan-


do atributos a cada uno de 10s arrays que se han declarado. imgPrim contiene la
ruta de 10s eventosMouseOut. imgRoZl contiene el valor de 10s eventosMouseOver.
E imgDown contiene la ruta de 10s eventos MouseDown. imgLink e imgText con-
tienen 10s valores de 10s atributos HREF y el texto que se mostrara en la barra de
estado, respectivamente. Como el usuario determinara el valor de 10s cinco pri-
meros elementos de forma individual a travCs de la plantilla de la imagen, a
cada elemento de estos cinco arrays se le asigna inicialmente una cadena vacia.
La configuracion de 10s elementos de 10s otros tres arrays restantes sera similar.
El ancho predeterminado sera el mismo para todas las imagenes. El usuario las
podra cambiar mas tarde, per0 de momento, su configuracion sera la misma.

generateEntryForm0
Lo ultimo que hara la funcion sera llamar a generateEntryForm ( ) . Aqui es
donde realmente empiezan a ocurrir cosas. Esta funcion es la unica responsable
de la creacion de la plantilla para imagenes personalizadas segun las opciones
del usuario. Veamos el contenido de las lineas 53-1 18 el codigo.
Un total de 66 lineas de codigo para una sola funcion. La verdad es que podemos
considerar que este caso es la exception a la norma. Pero generateEntryForm ( )
unicamente desarrolla una accion: crear la plantilla personalizada. Si la dividi-
mos en tres partes nos encontraremos con que las cosas se simplifican bastante.
Las tres partes son: las cabeceras de la tabla (TH),10s campos del formulario y
10s botones. Esta funcion efectua una serie de llamadas adocument .w r i t e l n ( .
Este es el codigo encargado de escribir las cabeceras de las lineas 54-72:

with(parent.frames[l].document) {
open ( ) ;
writeln("<HTMLxBODY BGCOLOR=FFFFEE><FONT FACE=Arial SIZE=2>" +
"<BLOCKQUOTE>Choose or enter the paths of all images in the + I'

"columns below. Add the link path (e.g.,<FONT FACE=Courier>" +


"web-page.html</FONT>) or script text (e.g., <FONT FACE=Courier>"
+ "javascript:</FONT>)for each HREF attribute, and enter any + 'I

"message you want to display in the status bar during the " +
"<FONT FACE=Courier>MouseOver</FONT>event. Then choose + 'I

"<B>Generate</B> to get your code, or <B>Preview</B> to see the 'I

+ "code in action.</BLOCKQUOTE><FORMNAME='imgProfile' +
"onSubmit='return false;'><CENTER><TABLEBORDER=O ALIGN=CENTER +
"CELLSPACING=5 CELLPADDING=5><TH ALIGN=LEFT><FONT FACE=Arial>#" +
"<TH ALIGN=LEFT><FONT FACE=Arial>Primary Path" +
"<TH ALIGN=LEFT><FONT FACE=Arial>Rollover Path" +
(imgDefau1ts.mousedown.checked? "<TH ALIGN=LEFT>" +
"<FONT FACE=Arial>MouseDown Path" : " " ) +
"<TR><TD><BR></TD></TR>") ;
}
ImaaeMachine 179

Observese que la plantilla de la imagen se encuentra dentro de la tabla. Todos


10s elementos de este bloque son estaticos, exceptuando el c6digo de las lineas
69-70. Con un operador ternario, JavaScript incluira una cabecera cuando el
usuario active la casilla de verificacion "MouseDown".Per0 no aiiade nada mas.
Vamos a verlo:

(imgDefaults.mousedown.checked ? "<TH ALIGN=LEFT><FONT FACE=Arial>" +


"MouseDown Path" : )
"I'

Este el punto mas importante de todos. Si logramos comprenderlo, seremos


capaces de movernos por el resto de la funcion porque generateEntryForm ( 1
toma todas las decisiones del c6digo basandose en si el usuario ha activado esta
casilla o no. Vamos a mirar 10s campos de texto de las lineas 74-104.
Para cada elemento, ImageMachine aiiadira un TR nuevo que incluye el TD de
dos (0tres) campos FILE, un campo de texto para 10s atributos HREF, el texto
que aparecera en la barra de estado y la configuracibn para el ancho, alto y bor-
de de 10s botones. Si nos fijamos, veremos que a cada elemento del indice i de 10s
array declarados con la funci6n setArrays ( ) se le asigna un valor de un cam-
PO de texto.
No olvidemos que originalmente imgPrim, imgRoll, imgDown, irngLink e irngText
no eran mas que una cadena de texto vacia. De momento, el usuario no ha defi-
nido ningun valor para estos campos, por lo que continuan vacios. Per0 ante-
riormente se determinaron 10s valores del ancho, alto y borde. Por tanto, parece
16gico que a 10s elementos correspondientes del indice i de 10s arrays irngWidth,
irngffeight e irngBorder les Sean asignados sus valores predeterminados corres-
pondientes.
Observemos este c6digo:

(imgDefau1ts.mousedown.checked ?

Nos encontraremos con uno de estos ejemplos cuando el usuario active la ca-
silla de verificaci6n "MouseDown",puesto que de esta forma se podra incluir el
c6digo responsable de agregar una imagen mAs a la secuencia asociada con 10s
botones.
Ya nos hemos encargado de las cabeceras y de 10s campos de texto del formu-
lario. Lo unico que queda es crear 10s botones: "Generate","Preview"y "Clear".

with(parent.frames[ll.document) {
writeln("</TABLE><CENTER><INPUT TYPE=BUTTON +
"onClick='parent.frames[0].imgValid8(this.form,true);' " +
"VALUE='Generate'>iINPUTTYPE=BUTTON +
"onClick='parent.frames[O].imgValid8(this.form,false);' " +
180 IrnageMachine

"VALUE='Preview'> <INPUT TYPE=RESET VALUE=' Clear '>' +


</FORM></BODY></HTML> ) :
" 'I

close ( ) ;
1

El b o t h Tlear" es el que se encarga de borrar todo (RESET)asi que nos vamos


a centrar en 10s otros dos. Ambos invocan a la funci6n imgValid8 ( ) . Ambos
pasan una copia del formulario a la f u n c i h , per0 uno con el valor true y otro
con false.Aqui estriba la diferencia que hace que la aplicacion Cree el c6digo o
no. Lo veremos en breve.
Podemos revisar las lineas de generateEntryForm ( ) para ver c6mo se utiliza
el codigo HTML para crear el formulario. Si lo hacemos, veremos como se crea
una cadena muy grande donde se van recogiendo las opciones del usuario. La
funcion generateEntryForm ( ) se encarga de que sea el usuario quien rellene
las distintas opciones del formulario. Cuando completa 10s cuatro pasos que
hemos comentado a1 principio del capitulo, el usuario tendra que hacer clic en el
boton "Generate". De esta forma se llama a la funcion imgValid8 ( ) . La tene-
mos en las lineas 120-136:

function imgValid8(imgTemplate, mimeType) I


for (var i = 0 ; i < imgPrim.length: i++) {
if (imgTemplate[ 'prim' + i] .Value == 11'I"

imgTemplate['seci' + i] .value == II
"'I

imgTemplate['href' + il .value == " " ) {


alert("Al1 images and HREF attributes must have URLS.");
return:
I
if (imgDefaults.mousedown.checked) I
if (imgTemplate['down' + i] .value == " " ) {
alert("Al1 images and HREF attributes must have U R L s . " ) :
return:
}
I
}
genJavaScript(imgTemplate, mimeType);

Esta funcion se asegura de que el usuario ha rellenado todos 10s campos co-
rrespondientes a las rutas de las imagenes. En realidad, no es algo que se tenga
que hacer obligatoriamente, per0 nunca esta de mas comprobar si las ha inclui-
do. No olvidemos que el boton "Generate"entrega una copia del formulario, in-
cluyendo informacion de las imagenes.
Esta copia se le asigna a imgTempZate. Una vez mas, utilizando imgPrim, la apli-
caci6n repetira el proceso a travks de todos 10s campos de texto que contengan
la ruta de las imagenes. El campo que tiene la ubicacion de la imagen principal
ImaneMachine 181

se denominarh prim + i, donde i constituye un numero comprendido entre 0


e imgprim. length - 1. Los campos que contienen las rutas de las imagenes
secundarias utilizaran un sistema similar, s610 que utilizaran seci en vez de prim.
Si el usuario incluye imhgenes para el evento onMouseDown, el nombre de 10s
campos de texto sera down.

genJavaScriptO
Si hay algun campo vacio, se le comunicara a1 usuario y se volvera a ejecutar
la funci6n. Si todos tienen una cadena de texto, ImageMachine llamara a la
funci6n genJavaScript0, le pasara imgTempZate y el booleano que se encuentre
dentro de mimeType. Como podemos suponer, la funci6n genJavaScript ( ) se
encargara de crear el codigo JavaScript de la pagina Web que aparecera en la
pantalla. La funcion es muy larga, sin embargo, funciona de la misma manera
que generateEntryForm(1. Veamos las lineas de codigo 138-306.
182 ImageMachine

de JavaScript no $pcrlt91hn el c6digo que se encuentre dentro de la6 eti-


quetas cSCRLPT WGWiGE="JavaScriptl.2'> </SCRIPT>.Esto es
positivo, pues el axptoriodor no pedirii las Wgenes asociadas con el even-
to 0 n M m .

Y pensabamos que el c6digo de generateEntryForm ( era largo. En este caso


encontramos la misma cantidad de codigo, per0 la funcidn genJavaScript ( )
unicamente se encarga de una unica tarea: generar el codigo de las secuencias,
que en su mayor parte sera JavaScript.
Lo primer0 que hara genJavaScript ( ) es reiniciar la variable globalimagelink.
Ahora veremos mas de estas variables globales. Luego nos centraremos en la
funci6n genJavaScript ( ) y en c6mo determina el valor de las variables globales
de acuerdo con el valor de mimeType. Lo tenemos en las lineas 141-154:

if (mimeType) {
It = "&It;";
gt = " &gt ; an ;
br = <BR>'*;
'I

HTML = true;
nbsp = "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
1
else {
It -
- "<" .
gt = > ;
'I

br = lb;
HTML = false;
nbsp = '' I8 .
1

Si el valor de rnimeType es true, a las variables globales se les asignara el conte-


nido de una cadena de valores que haran que el codigo aparezca en pantalla.
h i , el valor de las variables It y gt sera &1t ; y &gt ; respectivamente. La variable
br sera igual a la cadena de <BR>. HTML es una variable booleana, que indica que
el usuario desea que se interprete el codigo. Lo veremos en un momento, a1 tra-
tar la generacion del c6digo. La variable nbsp sera igual a la cadena HTML de es-
pacios no divisorios, Esta variable se utiliza como equivalente HTML del tabulador.
Si mimeType es false, se obligara a que se interprete el codigo de las variables.
It y gt seran < y >, respectivamente. La variable br sera igual a1 valor de Zb que
se determinara a travks de las lineas 6-8:

var platform = navigator.platform;


var lb = (platform.indexOf("Win"! = -1) ? "\n\r" :
(platform.indexOf("Mac"! = -1) ? " \ r " : "\n"));
ImaneMachine 183

Como podemos ver, las variables platform y Zb trabajan juntas. L a primera


contiene una cadena cuyo valor es el sistema operativo para el cual se ha com-
pilado el explorador. La variable Zb se configurara de acuerdo con el valor de
pZatform. En Windows (DOS), el caracter correspondiente a1 salto de linea se
representa por \n\r.En Macintosh sera \r,mientras que en Unix se utiliza \n.
Gracias a estos caracteres se evitara que el codigo se extienda en una o dos lineas
demasiado largas. No es crucial para el desarrollo de la aplicacion, per0 cuando
ImageMachine imprime el codigo HTML y JavaScript, siempre resulta mas sen-
cillo ver quC es lo que ha generado.

Sigamos, HTML (cuyo valor es false)es una variable booleana que nos indica
cuando el usuario desea se proceda con la interpretacion del codigo. Lo veremos
a1 pasar a1 estudio de la generacion del c6digo. L a variable nbsp sera igual a una
cadena vacia.
Ahora, ImageMachine tiene la informacion de las cadenas de texto de la planti-
lla, independientemente de que el usuario quiera ver o ejecutar el codigo. L a ge-
neracion del codigo JavaScript no tendra lugar hasta que se alcance la linea 185
y se desarrollara hasta el final de la funcion.
Segun avancemos por el codigo, nos encontraremos con varias llamadas a
p a t h p r e p ( 1 . Esta funcion se encarga de volver a dar formato a la cadena don-
de se encuentra la ruta de la imagen. Esta accion unicamente se desarrollara
cuando la ruta sea local. iPor que tanto lio? Bueno, recordemos que Windows
utiliza la notacion basada en barras invertidas (\) para separar 10s directorios.
Los exploradores trabajan con otra notacion, basada en la barra inclinada (/),
184 ImageMachine

igual que 10s sistemas Unix. Por lo tanto, sera necesario convertir las barras
invertidas en barras inclinadas. En la actualidad, ciertos exploradores hacen la
conversion sobre la marcha.
El problema consiste en que JavaScript interpreta el caracter barra invertida
como parte de un caracter de separacion. Por lo tanto, JavaScript tomara la ca-
dena C:/Mi-directorio/Mi.archivo como C:Mi-directorioMi.archivo. La funcion
p a t h p r e p ( ) se encarga de todo. En las lineas 310-317 tenemos su codigo:

function pathPrep(path) I
i f (path.indexOf( " : \ \ " ) ! = -1) I
path = path.replace(/\\/g, " / " ) ;
path = path.replace(/:\//, " I / " ) ;
return "file:/ / / " + path;

else { return path; )


)

Los exploradores tambien pueden abrir documentos locales con el protocolo de


archivos, es decir, que necesitaran adjuntar el prefijo fiZe:/// a1 URL y colocar
una barra vertical ( I ) en lugar de 10s dos puntos.
La hora de las decisiones
Ya lo tenemos todo configurado para generar c6digo de acuerdo con las espe-
cificaciones de impresion o de ejecucion. Antes de que la aplicacion proceda a
generarlo, necesitara saber si basara el proceso en la informacion de la plantilla
o en la que se encuentra en 10s arrays. De acuerdo con el ciclo del usuario, que
hemos descrito anteriormente, este ha de terminar de introducir informacion en
la plantilla de la imagen. La otra situation la encontraremos cuando el usuario
ya haya generado el codigo y este saltando de una pantalla a otra a travCs de 10s
botones "Generate"y "Preview".Esto lo veremos en un momento.
Si la informacion procediera de la plantilla de la imagen (que es nuestro caso),
la funci6ngenJavaScript ( ) reiniciara 10s arrays img llamando a setArrays ( ) .
Asi, podra asignar la informacion procedente de la plantilla. ImageMachine de-
termina si llamara a s e t A r r a y s ( ) y volvera a asignar 10s valores basandose en
el contenido de imgTempZate. Hay tres formas de llamar a g e n J a v a S c r i p t ( ) :
desde "Generate","Preview"y "Change Info". Este ultimo boton pasara un valor
nulo aimgTempZate. Por tanto, siimgTempZatees distinto de cero, genJavaScript (
sabra que tiene que eliminar el valor de 10s arrays para asignarles la nueva in-
formation. En cualquier otro caso, no se modificaran 10s arrays img. Veamos
el contenido de las lineas 156-1 70 y como de desarrolla esta operation:

if(imgTemp1ate ! = null) {
setArrays ( ) ;
IrnaaeMachine 185

for (var i = 0; i < (imgDefaults.imgnumber.selectedIndex + 1); i++)


i
imgPrimli] = purify(imgTemplate['prim' + il.value);
imgRoll[i] = purify(imgTemplate['seci' + il.value);
if (imgDefau1ts.mousedown.checked) {
imgDown[il = purify(imgTemp1ate"down' + i1.value);
I
imgLink[i] = purify(imgTemplate['href' + i1.value);
imgText[il = purify(imgTemplate['stat' + il.value);
imgWdh[il = purify(imgTemplate['wdh' + il.value);
imgHgt[il = purify(imgTemp1ate"hgt' + i1.value);
imgBdr[i] = purify(imgTemplate['bdr' + il.value);
1
1

Si se modifican 10s elementos del array, 10s valores de la cadena que se les asig-
ne pasaran a travts de un proceso (rhpido y peligroso) de la mano de la funcion
purify ( ) en la linea 308.
De esta forma eliminamos todas las comillas simples y dobles de 10s valores.
N o hay nada ilegal en esto, pero JavaScript utiliza estas comillas para generar el
c6digo. A menos que esttn perfectamente aisladas con barras invertidas, las
comillas nos pueden provocar mas de un dolor de cabeza. La funcion purify ( )
se encarga de eliminarlas de todas las cadenas que se le pasan, generando una
nueva completamente limpia.

Generar el codigo
Ya ha llegado el momento que tanto esperhbamos: la generaci6n del codigo.
Tiene lugar en las lineas 185-305. Asigna todo el c6digo generado a varias va-
riables. Dichas variables seran las siguientes:
PrimJavascript. Contiene etiquetas HTML como HTML, HEAD y TITLE.
Tambitn contiene el JavaScript preliminar, el codigo JavaScript preliminar
y el asociado con la imagen de la secuencia correspondiente a 10s eventos
MouseOver y MouseOut .
secJavaScript. Contiene el c6digo asociado con el evento MouseDown de
JavaScript 1.2.
inzagelinks. Contiene una etiqueta SCRIPT de cierre.
swapcode. Contiene las funciones de JavaScript que se encargaran de las
secuencias de las imhgenes.
primcode. Contiene la etiqueta BODY y algunos comentarios HTML.
secHTML. Contiene las etiquetas HTML de cierre y 10s botones "From"que
se muestran una vez que se ha generado el c6digo.
agregate. Esta variable representa el resultado de sumar todas las variables
anteriores.
186 ImaneMachine

El buclefor de la linea 197 se repetira las veces que determine imgprim. lenght.
Con cada repeticion, se aiiaden las variables primJavascript, secJavaScript (si el
usuario selecciona la opcion MouseDown) e imageLinks a1 c6digo correspondien-
te con la siguiente repeticion del grupo de imagenes.
Las variables scriptCZose,swapcode, primHTML y secHTML no forman parte del
bucle for. Su contenido se puede determinar a travks de un operador ternario
junto con la variable HTML e imgDefauIts.mousedown.checked.
Cuando se complete el buclefor y se hayan configurado las variables, tendre-
mos que hacernos con el contenido de la pagina. Esto tiene lugar en las lineas
300-305:

agregate = primJavascript +
(imgDefau1ts.mousedown.checked ? scriptclose + SecJavaScript : "") +
swapcode + primHTML + imageLinks + secHTML;

parent.frames[ll.location.href =
" javascript : parent. frames [ 0 I .agregate" ;

Paso 4: seleccionar "Preview" para ver el codigo en accion


Es normal sentirse algo confuso llegados a este punto. Afortunadamente 10s
dos ultimos pasos son muy rapidos y sencillos. Supongamos que el usuario ya
ha revisado el codigo, per0 ahora lo quiere ver en accion. Unicamente necesitara
hacer clic en el boton "Preview". No olvidemos que este b o t h invoca a la fun-
cion genJavaScript ( ) , pero que con el boton 'Generate" el valor de mimeType
sera false. Esta es la unica diferencia. La especificacih de las variables globales
de las lineas 141-154 se reinician para interpretar el c6digo en vez de mostrarlo
en la pantalla, como haciamos antes. El resto es exactamente igual a lo visto con
el boton "Generate".

Paso 5: seleccionar "Change Info'' para hacer cambios


Ya hemos visto a1 codigo en accion y su contenido en pantalla. Supongamos que
el usuario quiere realizar algun cambio. En el momento en que haga clic sobre
"Change Info" volvera a aparecer la plantilla con la informacion del usuario en
la pantalla. Todo estara ahi ... menos las rutas de las imagenes. iPor quk no?
Porque 10s URL de las imagenes se guardan dentro del objeto FiZeLIpZoad (por
ejemplo, <INPUT TYPE=FILES>).Por razones de seguridad, este objeto so10 es
de lectura. Es decir, que tendremos que volver a escribir la ruta de 10s archivos
o bien utilizar el boton que se encuentra a la derecha de 10s mismos, para selec-
cionar el archivo en cuestion. La verdad es que es muy sencillo de cambiar.
Bastara solo con dirigirnos a generateEntryForm ( ) y cambiar TYPE=FILE por
ImageMachine 187

TYPE=TEXT.El cambio lo tendremos que repetir en tres ocasiones. El unico pro-


blema es que perderemos la posibilidad de buscar archivos locales con el raton.
Utilice el sistema que mas le guste. Cuando se completen 10s cambios, habra que
hacer clic en el boton "Generate" o en "Preview" y revisar el nuevo codigo.

Posibles ampliaciones: aiiadir atributos a la plantilla


Las aplicaciones grandes se pueden hacer aun mayores. En esta section vere-
mos como agregar atributos a la plantilla de la imagen, con las que obtendremos
un mayor control sobre el codigo que se genere. Por razones de simplicidad, nos
limitaremos a 10s atributos HSPACE y VSPACE de IMG. Se trata de u n proceso de
seis pasos:
1 . Agregar nuevos campos a la plantilla predeterminada.
2 . Crear un array para estos valores con s e t A r r a y s ( ) .
3 . Capturar 10s nuevos valores predeterminados.
4. Agregar a continuacion campos de texto de la plantilla de imagen en
generateEntryForm0.
5. Hacer despues una referencia y asignar un nuevo valor a 10s atributos de
genJavaScript ( ) .
6 . Generar el HTML necesariopara mostrar 10s atributos engenJavaScript ( .
188 ImageMachine

Paso 1:agregar campos


<TD VALIGN= "TOP">
<FONT FACE=" Aria 1" S IZE=2>
HSpace
</TD>
<TD VALIGN="TOP">
<FONT FACE= "Aria1" SIZE= 2 >
<SCRIPT LANGUAGE="JavaScript1.2">
<!--
genSelect("hspace", 25, 0, 0);
//-->
</SCRIPT>
</TD>
<TD VALIGN="TOP">
<FONT FACE= Ar ia1 " SIZE=2 >
VSpace
</TD>
<TD VALIGN="TOP">
<FONT FACE="Aria1 SIZE=2 >
I'

<SCRIPT LANGUAGE="JavaScript1.2">
< I --

genSelect("vspace", 2 5 , 0, 0);
//-->
</SCRIPT>
</TD>

Paso 2: crear arrays con setArrays()


function setArrays0 {
imgPrim = new Array( ) ;
imgRoll = new Array();
imgDown = new Array() ;
imgLink = new Array();
imgText = new ArrayO;
imgWdh = new Array();
imgHgt = new Array( ) ;
imgBdr = new Array() ;
imgHSpace = new ArrayO; / / Para HSPACE
imgVSpace = new ArrayO; / / Para VSPACE
}

De esta forma hacemos algo de sitio para guardar 10s nuevos valores predeter-
minados. Esto nos lleva a1 siguiente paso.
ImageMachine 189

Paso 3: capturar 10s nuevos valores predeterminados


En captureDefaultProfile ( ) , aiiadiremos dos variables locales llamadas
imgHspace e imgVspace y asignaremos sus valores a 10s seleccionados en la plan-
tilla predeterminada. El nuevo aspecto de captureDefaultProfile ( ) sera el
siguiente:

function captureDefaultProfile(form0bj) {
setArrays ( ) ;
imgDefaults = formObj;
var imgQty = (imgDefaults.imgnumber.se1ectedIndex + 1);
var imgHeight = (imgDefaults.pxlheight.selected1ndex);
var imgWidth = (imgDefaults.pxlwidth.selected1ndex);
var imgBorder = (imgDefaults.defbdr.selected1ndex);
var imgHspace = (imgDefaults.hspace.selected1ndex);
var imgVspace = (imgDefaults.vspace.selected1ndex);
for (var i = 0; i < imgQty; i++) {
imgPrim[i] = "'I;

imgRoll[i] = "';
imgDown[i] = " " ;
imgLink[i] = " " ;
imgText[i] = ' I " ;

imgWdh[i] = imgWidth;
imgHgt[i] = imgHeight;
imgBdr[i] = imgBorder;
imgHSpace[i] = imgHspace; / / Para HSPACE
imgVSpace[i] = imgvspace; / / Para VSPACE
1
generateEntryForm0;
1

Ahora, ImageMachine podra incluir valores predeterminados para HSPACE y


VSPACE en la plantilla de imageries.

Paso 4: agregar campos de texto en generateEntryForm0


Ahora podemos agregar cadenas de HTML a la funcibn generateEntryForm (
para incorporar 10s dos nuevos campos de texto. Vamos a colocar su propio TR
debajo deI resto. Lo podremos modificar m6s adelante. El aspecto de las lineas
103-106 antes del cambio sera el siguiente:

"<TR><TDVALIGN=BOTTOM COLSPAN= " +


(!imgDefaults.mousedown.checked ? " 3 " : " 4 " ) +
"><BR><HRNOSHADE><BR></TD></TR>");

DespuCs de agregar el nuevo cbdigo, quedara de la forma que podemos ver a


continuacibn:
190 ImageMachhe

"<TR><TD VALIGN=BOTTOM><INPUT TYPE=TEXT NAME='hsp + i +


*'IVALUE='" + imgHspace[i] + SIZE=3> HSPACE </TD>" +
I t *

"<TR><TD VALIGN=BOTTOM><INPUT TYPE=TEXT NAME='vsp" + i +


I" VALUE='" + imgVspace[i] + SIZE=3> VSPACE </TD></TR>" +
''I

"<TR><TDVALIGN=BOTTOM COLSPAN=" +
(!imgDefaults.mousedown.checked ? "3" : " 4 " )+ " 2 " +
"<BR><HRNOSHADE><BR>c/TD></TR>") ;

Este codigo aiiade dos campos adicionales para cada grupo de imagenes y mos-
trara el valor predeterminado de cada uno. El usuario podra cambiarlo cuando
quiera.

Paso 5: referenciar y asignar 10s nuevos valores


en genJavaScript0
Una vez que el usuario opta por generar el codigo, la aplicacidn necesitara guar-
dar la informacion que se encuentra en 10s campos de texto de la plantilla. Bas-
tara con agregar en las lineas 158-169 el codigo que se encargue de hacerlo:

for (var i = 0; i < (imgDefau1ts.imgnumber.selectedIndex + 1); i++) {


imgPrim[i] = purify(imgTemplate['prim' + i].Value);
imgRoll[i] = purify(imgTemplate['seci' + i].value);
if (imgDefau1ts.mousedown.checked) {
imgDown[i] = purify(imgTemplate['down' + i].value);
}
imgLink[i] = purify(imgTemp1ate"href' + il.value);
imgText[i] = purify(imgTemplate['stat' + i].Value);
imgWdh[i] = purify(imgTemplate['wdh' + i] .value);
imgHgt[i] = purify(imgTemplate['hgt' + i].value);
imgBdr[i] = purify(imgTemplate['bdr' + i] .value);
imgHSpace[i] = purify(imgTemplate['hsp' + i1.value);
imgVSpace[il = purify(imgTemplate['vsp' + i1.value);
1

Las dos ultimas lineas del bloque muestran la aplicacion asignando 10s valores
del formulario de la plantilla de imagen a 10s elementos irngHspace e irngvspace.
Ya casi lo tenemos. Solo nos queda asegurarnos de que se incluyen 10s nuevos
atributos en el proceso de generacion, impresion o interpretacion del codigo.

Paso 6: generar HTML adicional en genJavaScript0


El nuevo codigo se le aiiadira a la variable imagelinks. Las ultimas lineas de la
cadena seran las que se muestran a continuacion:

(HTML ? fontclose : " " ) + br + nbsp + "HEIGHT=" +


(HTML ? fontopen : ' " " 1 + imgHgt[jl +
ImageMac hine 191

(HTML ? fontclose : " " ) + br + nbsp + "BORDER=" +


(HTML ? fontopen : " " ) + imgBdr[jl +
(HTML ? fontclose : " " ) +
gt + " " + It + "/A" + gt + br + br + br;

Todo lo que se ha de hacer es copiar unas cuantas h e a s y cambiar HEIGHT por


HSPACE, imgHgt por imgHspace, BORDER por VSPACE e imgBdr por imgvspace.
Este es el aspect0 que mostrara la nueva versih:

(HTML ? fontclose : " " ) + br + nbsp + "HEIGHT=" +


(HTML ? fontopen : " " ) + imgHgt[jl +
(HTML ? fontclose : " " ) + br + nbsp + "BORDER=" +
(HTML ? fontopen : " " ) + imgBdr[jl +
(HTML ? fontclose : " " ) + br + nbsp + "HSPACE=" +
(HTML ? fontopen : " " ) + imgHSpace[jl +
(HTML ? fontclose : " " ) + br + nbsp + "VSPACE=" +
(HTML ? fontopen : " " ) + imgVSpace[jl +
(HTML ? fontclose : " " ) +
gt + ( ' ' ( + It + " / A " + gt + br + br + br;

Hemos aiiadido dos atributos para las imagenes. Podemos aiiadir tambiCn una
etiqueta ALT para el contenido de dicha imagen. Este tipo de modificaciones rea-
lizadas a la etiqueta I MG no tiene ningun limite. Con la etiqueta <A> tambitn se
puede hacer muchas cosas. Podemos personalizarla para las imagenes compues-
tas a partir de mapas de bits, etc.
Capitdo 6
Implementaci6n de archivos de c6digo fuente en JavaScript

Si ha seguido 10s capitulos desde el principio (en orden, claro) habra tratado de
comprender como trabajan juntas las variables y las funciones para que las
aplicaciones vistas hiciesen algo. Creo que merece la pena tomarse un descanso
y ver algunas cosas en este capitulo que nos faciliten la vida a la hora de crear
aplicaciones.
En este capitulo no encontrara ninguna aplicacion, sin0 unas cuantas funcio-
nes que suelen aparecer en el cddigo de JavaScript. Aunque no todas serviran la
mayoria de las veces, hay unas cuantas funciones que se podran personalizar y
utilizar con nuestras aplicaciones.
La finalidad de incluir estos archivos no es la de ponerlos en manos de 10s pro-
gramadores para que se busquen la vida. Todo lo contrario. En estas paginas in-
tentark animar a 10s lectores a crear bibliotecas de codigo para que las vuelvan
a utilizar siempre que haga falta. Asi, no tendran que reinventar la rueda cada
vez que escriban una nueva aplicacion. La lista que veremos en breve se compo-
ne de archivos .js, ordenados alfabkticamente, junto con la finalidad de cada uno
de ellos.
arrays.js. Contiene una serie de funciones especializadas en la manipula-
cion de arrays. Algunas de estas funciones le permiten desarrollar acciones
equivalentes a las de JavaScript 1.2 en exploradores que no son compatibles
con esta version del lenguaje.
cookies.js. Esta biblioteca, una de las primeras que creo Bill Dortch, facilita
bastante la vida del programador a1 crear cookies.
dhtrnZ.js. Aqui veremos algunas de las funciones de 10s capitulos 3 y 4. Es
un paquete muy interesante para crear capas DHTML.
194 Implementaci6n de archivos de c6digo fuente en JavaScript

events.js. Este archivo contiene el codigo que activara y desactivara la cap-


tura de eventos mousemove y keypress tanto en NN como en MSIE.
frames.js. Las funciones de este archivo nos ayudaran a incluir (0excluir)
nuestras paginas Web en cuadros de trabajo.
irnages.js. Codigo para secuencias de imagenes, que ya se han visto en otros
capitulos.
navbar.js. Contiene el c6digo necesario para generar barras de navegacion
sobre la marcha, basadas en el documento que cargue el usuario. Impre-
sionante. number.js. Contiene c6digo para corregir 10s errores de redondeo
de JavaScript y dar formato a 10s numeros.
objetrs.js. Contiene codigo para la creacion e inspeccion de objetos.
strings.js. Este archivo contiene una serie de funciones especializadas en la
manipulacion de cadenas.

A excepcion de navbar.js, el nombre del resto de archivos j s se corresponde con


un documento HTML (por ejemplo, arrays.htmZ se convierte en arrays.js). La
explicacion que daremos de cada funcion no serB tan detallada como la vista en
las aplicaciones. En la mayoria de 10s casos, no es necesario, aunque es cierto que
hay alguna excepcion. En cualquier caso, mientras leamos el contenido de este
capitulo, debemos intentar imaginar en quC casos nos puede resultar util cada
una de las funciones Si no nos vale el codigo impreso en estas paginas, podemos
tratar de personalizarlo para que se ajuste a nuestras necesidades.
En cada seccion se describe u n archivoj s . Se empezara por el nombre del archi-
vo, sus usos practices, la version de JavaScript con la que hay que trabajar y la
lista de funciones que contiene el archivo.

arrays.js
Us0 practico
Manipulacion de arrays.
Version necesaria
JavaScript 1.2.
Funciones
avg ( ) , high ( ) , low ( ) , jsGrep ( ) , truncate ( ) , shrink ( ) , integrate ( ) ,
reorganize ( ) .
Estas funciones toman nuestros arrays, 10s manipulan y devuelven algun tip0
de informacion de utilidad, incluyendo otros arrays. En la figura 6.1 tenemos el
contenido de arrays.htmZ. No hay nada especial, unicamente que se puede ver la
demostracion de cada funcion.
Imdernentacion de archivos de c6digo fuente en IavaScriDt 195

O n g d A m y . 1,2,3,0 1 0 9 8 ~ . 1 . 3 . 4 5 6 . 1 3 2 4 . 5 , ~ . 7 6 C I . 3 C Z Z Y p m M B
Aver= 82 43633684210526
Lowcrt -076
&hest 1324 55
T m a t e by 1 1.2.3.0 1098.5.2.3456.1324 5
:
O 76.45.3.47 234.0 0006.657.1.3.2.4
Shmk by 1 2.3.0 1098.5.2.3 456.1324 55.-076.45.3.47 2M.O 0006.65 7.1.3.2.4
Rcor-(by 4) 1.5.-076.0 0006.2,2.2.45.657.4.3.3 456.3.1 .O 1098.1324 55.47 234.3
:r onc.Onc mom.2.3456.1324 55,-

r w m M NSM

Figura 6.1.Demostracih del poder de arrays.js

Esta es la lista de funciones que utiliza arrays.js:


avg ( ) . Devuelve la media de todos 10s numeros del elemento.
high ( ) . Devuelve el valor mas alto de un array.
low ( ) . Devuelve el valor mas bajo de un array.
j sGrep ( ) . Ejecuta una busqueda y una sustitucih en todos 10s elementos
del array.
truncate ( 1 . Devuelve una copia del array sin el ultimo elemento.
shrink ( ) . Devuelve una copia del array sin el primer elemento.
integrate ( ) . Combina 10s elementos de dos arrays empezando por el
indice que se le indique.
reorganize( ) . Reordena 10s elementos del array de acuerdo con el mul-
tiplo que seleccionemos.
En el ejemplo 6.1 se muestra el c6digo de arrays.htmZ. No hay mucho mas que
comentar. Unicamente una llamada a document.writeln ( ) . La cadena que se
mostrara contendra el resultado de todas las llamadas a las funciones con 10s
arrays de ejemplo: somearray ( ) y grepExample( 1 .
Ejemplo 6.1.arrays.htm1
<HTML>
<HEAD>
<TITLE>arrays.js Example</TITLE>
<SCRIPT LANGUAGE="JavaScriptl . 2 SRC="arrays. js ></SCRIPT> 'I

< /HEAD>
<BODY>
<SCRIPT LANGUAGE="JavaScript 1.2 " >
<!--
196 lmplementacion de archivos de codigo fuente en JavaScript

10 var someArray = new Array(1,2,3,.1098,5,2,3.456.1324.55,


0.76,45,3,47.234,.00060,65.7,1,3,2,4,55);
11 var grepExample = new Array('Monday', 'Tuesday', 'Wednesday',
12 'Thursday', 'Friday');
13 document .write("<B>OriginalArray: + someArray + "</B><BR>"+
14 "Average: + avg(someArray) + "<BR>"+
15 '"Lowest: + low(someArray) + "<BR>" +
16 "Highest: + high(someArray) + "<BR>"+
17 "Truncate by 1: + truncate(s0meArray) + "<BR>" +
'I

18 "Shrink by 1: " + shrink(s0meArray) + "<BR>" +


19 "Reorganize(by 4): + reorganize(s0meArray. 4 ) + "<BR>" +
I'

20 "Integrate ('An element', 'Another one', and 'One more', " +


21 "at index 5) : integrate(someArray, new Array('An element', " +
22 "'Another one', 'One more'), 5) + "<BR><BR><B>Original
grepExample +
23 "Array: '' + grepExample + "</B>cBR> " +
24 " jsGrep (grepExample, /day/, \ 'day Night\ ) : + 'I

25 jsGrep(grepExample, /day/, 'day Night') + "<BR>");


26
27 //-->
28 </SCRIPT>
29 </BODY>
30 < /HTML>

Como se puede ver, las dos etiquetas SCRIPT han de trabajar con la version 1.2
de JavaScript. L a unica razon para ello es la existencia de la funcidn j s G r e p ( )
que utiliza las propiedades de localizacion y sustitucion de JavaScript 1.2. Vere-
mos mas sobre j s G r e p ( ) en un instante. Podemos hacer que 10s exploradores
compatibles con JavaScript 1.1 tambiCn trabajen con este c6digo. Para ello eli-
minaremos (0volveremos a escribir) j s G r e p ( ) . Ahora que ya hemos visto como
se llama a las funciones, vamos a centrarnos en el contenido de las propias fun-
ciones. En el ejemplo 6 . 2 tenemos el contenido del archivo arrayxjs.

Ejemplo 6.2. arrays.js

1 var sum = 0;
2 for (var i = 0; i < arr0bj.length; i++) {
3 sum += arrObj[i];
4 I
5 return (sum / i);
6 >
7
8 function high(arr0bj) {
9 var highest = arrObj[Ol;
10 for (var i = 1; i < arr0bj.length; i++) {
11 highest = (arrObj[il > highest ? arrObj[i] : highest);
12 1
13 return (highest);
14 I
lmplementacion de archivos de codigo fuente en JavaScript 197

15
16 function low(arr0bj) {
17 var lowest = arrObj[O];
18 for (var i = 1; i < arrObj.length; i + + ) (
19 lowest = (arrObj[il < lowest ? arrObj[il : lowest);
20 }
21 return (lowest);
22 I
23
24 function jsGrep(arrObj, regexp, subStr) (
25 for (var i = 0; i < arrObj.length; i++) (
26 arrObj [il = arrObj [il.replace(regexp,subStr);
21 }
28 return arr0bj;
29 I
30
31 function truncate(arr0bj) (
32 arrObj.length = arrObj.length - 1;
33 return arrObj;
34 I
35
36
37 function shrink(arr0bj) {
38 var tempArray = new Array();
39 for(var p = 1; p < arr0bj.length; p++) (
40 tempArray[p - 11 = arrObj [PI ;
41 1
42 return tempArray;
43 1
44
45
46 function integrate(arrObj, elemArray, startIndex) (
47 startIndex = (parseInt(Math.abs(start1ndex))< arr0bj.length ?
48 parseInt(Math.abs(start1ndex)) : arrObj.length);
49 var tempArray = new Array( ) ;
50 for( var p = 0; p < startIndex; p++) (
51 tempArray[pl = arrObj [PI ;
52 1
53 for( var q = startIndex; q < startIndex + elemArray.length;
q++) (
54 tempArray[ql = elemArray[q - startIndex1;
55 }
56 for( var r = startIndex + elemArray.length; r < (arrObj.length
57 + elemArray.length); r + + ) {
58 tempArray[r] = arrObj[r - elemArray.length1;
59 1
60 return tempArray;
61
62
63 function reorganize(formObj, stepup) (
64 stepUp = (Math.abs(parseInt(stepUp))> 0 ?
198 lmplementacidn de archivos de codigo fuente en JavaScript

65 Math.abs(parseInt(stepUp)) : 1);
66 var nextRound = 1;
67 var idx = 0;
68 var tempArray = new Array() ;
69 for (var i = 0; i < formObj.length; i++) {
70 tempArray[il = formObj[idx];
71 if (idx + stepUp >= formObj.length) (
72 idx = nextRound;
13 nextRound++;
74 )
75 else {
76 idx += stepup;
77 )
18
19 return tempArray;
80 1

Las funciones avg ( ) , high ( ) y l o w ( ) no parecen nada del otro mundo. avg ( )
suma todos 10s valores y divide el resultado entre arrObj .length. Devuelve el
cociente de la operacion. Las otras dos funciones se repiten a traves del array
que se les ha entregado, comparando cada elemento con el que esta delante suyo,
hasta que a1 final se obtiene el valor mas alto o mas bajo del grupo.
La funcion j sGrep ( ) se repite con todos 10s elementos del array y ejecuta una
cadena de localizacion o busqueda. Cualquiera que este familiarizado con Perl
posiblemente habra utilizado la subrutina grep ( ) . La version de Perl es bastan-
te mas potente que ksta, per0 siempre podremos decir que se parecen.
Las funciones truncate ( ) y shrink ( son equivalencias en JavaScript 1.1 de
las funciones pop ( ) y shift ( ) de JavaScript 1.2. En realidad,pop ( ) y shift ( )
reciben su nombre de las rutinas de Perl a las que tanto se asemejan.
La funcion integrate ( ) tambien es una equivalencia para JavaScript 1.1 del
metodo slice ( ) de JavaScript 1.2.
El nombre de slice ( ) tambien procede de Perl. Esta funci6n es muy sencilla.
Aunque contiene tres bucles for, el ndmero total de iteraciones siempre sera
arrObj.length + elemArray.length.
La funcion reorganize ( ) reordena 10s elementos del array a partir del mdltiplo
que seleccionamos. En otras palabras, si Yeorganizamosl' un array de 10 ele-
mentos (compuestos por: 0, 1, 2, 3, 4, 5 , 6, 7, 8 y 9) por 3, el nuevo orden sera
0,2,.5,8,1,4,7,3,6,9.

cookies.js
Us0 prictico
Contadores individuales, formularios y preferencias de usuarios.
Implernentacion de archivos de codigo fuente en JavaScript 199

Version necesaria
JavaScript 1.1.
Funciones
getcookieVal (), Getcookie ( ) , Deletecookie ( ) , Setcookie ( 1 .
iQueremos conocer el estado de administracion del lado del cliente? iQue hay
de esos saludos que algunos sitios Web ofrecen a 10s usuarios que vuelven a
visitar sus paginas Web? LNecesita configurar una interfaz que cambie de idio-
ma o cualquier otra preferencia del usuario? Este c6digo esta especializado en la
creacion y obtencion de la informacion almacenada en las cookies. En las figu-
ras 6.2, 6.3 y 6.4 podemos ver cookies.htm1 en accion. En la primera de ellas,
podemos ver qu6 ocurre la primera vez que se carga la pagina. Se le pide a1
usuario que facilite su nombre. En la figura 6.3 se muestra un saludo a1 usuario
que accede por primera vez a estas paginas Web. En la figura 6.4 se puede ver el
saludo que recibe un usuario que accede de nuevo a dicha pagina.

Figura 6.2. En la primera visita nos pedira nuestro nombre

Es un ejemplo muy significativo de la potencia de las cookies. En el capitulo 7


veremos como se pueden utilizar para recordar las preferencias de 10s usuarios.
Per0 de momento, bastara con que captemos el concept0 de cookie.
El archivo cookies.htrnZ funciona de la siguiente manera: busca una cookie con
el nombre user-id. Si no existe (es decir, su valor es igual a cero), le pedira a1 usua-
rio que introduzca su nombre. A continuaci6n configura user-id para que guar-
de el nombre del usuario y asigna el valor 2 a hit-count (que sera el numero de
veces que el usuario visite la pagina la proxima vez que entre en ella).
200 Implernentacion de archivos de codigo fuente en JavaScript

Figura 6.3. Mensaje de bienvenida que se muestra la primera vez que accedemos
a la pagina Web

Ilio”um- ~ ~ -.-.
..._________
f -!---r;;iiiii-------,
3

Figura 6.4. Mensaje de bienvenida que se muestra las sucesivas vece5 que arcedemos
a la phgina Wet)

Si user-id existe, toma su valor y el dc hit-count. El hecho de que exista user-id


indicara que el usuario ya ha visitado la phgina con anterioridad. Por lo que po-
demos asegurar que ya se determinci anteriormente el valor de hit-count. La
funci6n muestra en la pantalla el nombre del usuario y el numero de veces que
ha visitado las paginas Web. A continuacicin, asigna u n nuevo valor a hit-count
equivalente a hit-count+l.
En el ejemplo siguiente vamos a ver el contenido correspondiente a1 arrhivo
cookies.js.
Irnplementacion de archivos de codigo fuente en JavaScript 201

Ejemplo 6.3. cookiesjs


1 var today = new Date ( ) ;
2 var expiry = new Date(today.getTime0 + 365 * 24 60 * 60 *
1000);
3
4 function getCookieVa1 (offset) {
5 var endstr = document.cookie.indexOf ( " ; " , offset);
6 if (endstr == -1) { endstr = document.cookie.1ength; I
7 return unescape(document.cookie.substring(offset, endstr)) ;
8
9
10 function Getcookie (name) {
11 var arg = name + " = " ;
12 var alen = arg.length;
13 var clen = document.cookie.1ength;
14 var i = 0;
15 while (i < clen) {
16 var j = i + alen;
17 if (document.cookie.substring(i, j ) == arg) I
18 return getCookieVa1 ( j ) ;
19 1
20 i = document.cookie.indexOf(" " , i) + 1;
21 if ( i == 0) break;
22 1
23 return null;
24 1
25
26 function Deletecookie (name,path,domain){
27 if (GetCookie(name)) {
28 document.cookie = name + +
"=I'

29 ( (path) ? " ; path=" + path : " " ) +


30 ( (domain) ? domain=" + domain : " " ) +
' I ;

31 'I;expires=Thu, 01-Jan-70 00:00:01 GMT";


32 1
33 1
34
35 function Setcookie (name,value,expires,path,domain,secure) {
36 document.cookie = name + " = " + escape (value) +
37 ( (expires) ? '' ; expires=" + expires.toGMTString ( ) : " " ) t
38 ( (path) ? " ; path=" + path : " " ) +
39 ( (domain) ? domain=" + domain : " " ) +
' I ;

40 ( (secure) ? " ; secure" : ;


" ' I )

41 1

Hay cuatro funciones, pero so10 llamaremos a tres: Setcookie ( ) ,Getcookie( )


y Deletecookie( ) . getCookieVa1( ) es una funcion interna. Nunca la llama-
remos directamente.
Es muy sencillo crear cookies con Setcookie ( ) . Le pasaremos el nombre de la
cookie (para acceder mas adelante a ella con Getcookie( ) ), la informacion que
202 Irnplementacion de archivos de codigo fuente en JavaScript

queremos almacenar (como el nombre del usuario o el contador) y una fecha de


caducidad (todo en este orden). Nosotros nos encargaremos de suministrar 10s
dos primeros parametros. La fecha de caducidad se determinara a travCs de las
variables today y expiry. A esta segunda variable se le asignara un valor que se
correspondera con una antiguedad de un aiio. Es decir, que la caducidad de la
cookie sera un aiio a partir de la fecha en la que el usuario accede a la pagina Web.
Para ello se inicia la variable today en u n nuevo objeto Date y se utiliza el mtto-
do getTime ( ) . Funciona de la siguiente manera.
La variable today constituye un objeto Date. Asi, today.getTime ( devuelve
la hora medida en milisegundos (a partir de 1970 0O:OO:OO). Es decir, nos la
presenta en milisegundos, per0 lo que realmente queremos es una caducidad de
un aiio contado a partir de la fecha actual. Un aiio tiene 365 dias, cada dia 24
horas, cada hora 60 minutos y cada minuto 1.OOO milisegundos. Por lo tanto,
si multiplicamos obtenemos el valor de setTime ( ) , es decir, 3.153e10 mili-
segundos).
Las sintaxis de getcookie ( ) y Deletecookie( ) tambitn son muy sencillas.
Todo lo que tenemos que hacer es pasar el nombre asociado con la cookie. Enton-
ces Getcookie ( ) devolvera el valor que haya encontrado (0 cero, si no encuen-
tra nada) y Deletecookie( ) elimina la cookie asociada con el nombre que se
le pasa. Para eliminar la cookie, unicamente se trabaja con una fecha de caduci-
dad que ya se haya cumplido.

dhtml.js
Us0 prictico
Crear, ocultar y mostrar capas DHTML.
Version necesaria
JavaScript 1.2.
Funciones
genLayer ( 1, hideslide ( ) , showslide ( ) , refslide( ) .
Si hemos lefdo el contenido de esta obra en orden, ya conoceremos este codigo
porque nos lo hemos encontrado en dos de las aplicaciones anteriores (el ejem-
plo de las diapositivas y la interfaz del motor de busqueda). En las figuras 6.5 y
6.6 podemos ver el codigo que se ha utilizado para crear la capa y como nos per-
mite mostrarla u ocultarla.
En el ejemplo 6.4 tenemos el contenido correspondiente a1 archivo dhtrnl.js. No
he cambiado nada. Si quiere mas detalles sobre este codigo, consulte 10s capitu-
10s anteriores.
Imdernentacion de archivos de codiao fuente en IavaScriDt 203

l l u r is a rtylleslmt. Ain't it grand?! I

Figura 6 . 5 . Ahora lo ves. ..

I . .
[%Ids1IShowl

Y .
--_I_

rr-rwm __=6 I
Figura 6.6. ... dhora 110 lo vcs

Ejemplo 6.4. dhtml.js


var NN = (document.layers ? true : false);
var hideName = (NN ? 'hide' : 'hidden');
var showName = (NN ? 'show' : 'visible');
var zIdx = -1;
function genLayer(sName, sLeft, sTop, swdh, sHgt, sVis, copy) {
if ( N N ) I
document.writeln('<LAYER NAME="' + sName + "' LEFT=' + sLeft
+ ' TOP=' + STop +
' WIDTH=' + sWdh + ' HEIGHT=' + sHgt + ' VISIBILITY="' +
svis +
10 z-Index=' + zIdx + + copy + '</LAYER>');
' > I

11 }
12 else {
13 document.writeln('<DIV ID="' + sName +
14 "' STYLE="position:absolute; overf1ow:none;left:' + sLeft +
15 'px; top:' + sTop + 'px; width:' + sWdh + 'px; height:' +
sHgt +
16 'px; visibility:' + svis + ' z-Index=' + (++zIdx) + " ' > ' +
204 lmplementacion de archivos de codigo fuente en JavaScript

17
18
19
20
21 function hideSlide(name) I
22 refSlide(name).visibility = hideName;
23 1
24
25 function showSlide(name) t
26 refSlide(name).visibility = showName;
27 >
28
29 function refSlide(name) t
30 i f (NN) ( return document.layers[namel; >
31 else { return eval('document.al1.' + name + '.style');1
32 >
events.js
Us0 practico
Seguimiento del movimiento del raton y asignaci6n del controlador de eventos
compatible con diferentes exploradores.
Version necesaria
JavaScript 1.2.
Funciones
enableEffects0, showXY0, keepKeys0, showKeys0.
Si aun no ha probado a controlar eventos de forma que Sean compatibles con
varios exploradores Web, aqui tiene su primer ejemplo. Trabajamos con tres
controladores: ondick, onmousemove y onkeypress. La primera vez que se hace
clic en algun punto del documento, JavaScript captura las coordenadas x e y del
cursor tomando como referencia la ventana del explorador. A continuacih, la
barra de estado mostrara las coordenadas del cursor, segun se mueva por la pan-
talla. Si volvemos a hacer clic, desactivaremos el seguimiento de coordenadas y
calcularemos la distancia (en pixeles) que hay entre el primer punto en el que se
hizo clic y la posicibn actual del cursor. En las figuras 6 . 7 y 6.8 lo podemos ver
en accion.
Independientemente de la accion del raton, podemos escribir cualquier secuen-
cia de teclas a traves del teclado. En la barra de estado apareceran las teclas que
se vayan pulsando. Cuando terminemos, podremos hacer clic en el boton "Show
Keys" que se encuentra en la pantalla y la funci6n nos presentara un cuadro de
dialog0 donde mostrara la secuencia de teclas. En la figura 6.9 podemos verlo.
Para volver a empezar otra vez desde el principio, haremos clic en el b o t h "OK".
Implementation de archivos de codigo fuente en JavaScript 205

Chck your mouse button. then move tt around, chck agm to stop traclrmg

Now type some keys any keys ?hen press shout Keys

Figura 6.7. Coordenadas x e y en la barra de estado

Chck y o u mouse button. thcn move IIaround,chck agm to stop trackma

Now type some keys any keys Thm press Show Ksys

Figura 6.8. Distancia (en pfxeles) entre dos puntos

De momento bastar6 simplemente con que nos familiaricemos con las compli-
caciones que pueden tener las hojas de estilos que son compatibles con varios
exploradores.
Tenemos que saber que las etiquetas LAYER de un explorador, equivalen a las
etiquetas D I V de otro. Y en lo que se refiere a 10s tipos de eventos, las cosas tam-
poco resultan demasiado intuitivas que se diga. Si comprobamos el cddigo co-
rrespondiente a events.htmZ, nos encontraremos con las siguientes dos lineas de
JavaScript :

document.onclick = enableEffects;
document.onkeypress = keepKeys;
206 Implementaci6n de archivos de cddigo fuente en JavaScript

Chck your moue button,then move it around. chck agam to stop e a c h


I
Now type some keys my keys Then press

Figura 6.9.Teclas que ha pulsado el usuario

N o t a : podremos utilizar las etiquetas DIV para ubicar objetos en Netscape


Navigator 4.x, siempre y cuando se incluya la posicion dentro del atributo
STYLE. En cualquier caso, hasta que Netscape incluya el modelo de objetos
para documentos, la etiqueta LAYER nos permitira acceder a todas las pro-
piedades del objeto Layer.

El controlador del eventoonclick esta asociado con la funcion enableEf f e c t s ( )


y onkeypress esta ligado a la funcion keepKeys ( ) . A continuacion, veremos el
codigo de ambas funciones. ObsCrvese que ninguna de ellas tienen ningdn pa-
rCntesis. Es decir, que el codigo no es de la forma:

document.onclick = enableEffects0;
document.onkeypress = keepKeys0;

Si utilizasemos parentesis se procederia a llamar a 10s metodos en el momento


en que se ejecutase la linea de codigo. Y no es lo que andamos buscando. El con-
trolador de eventos esta asociado con las funciones a traves de una referencia.
Veamos el c6digo del ejemplo 6.5.
Ejemplo 6.5.events.js
1 var keys = ' I ;

2 var change = true;


3 var x l , x 2 , yl, y2;
4
5 function enableEffects(ev) {
6 if(change) {
I if(document.layers) (
8 x l = ev.screenX;
Irnplementacion de archivos de codigo fuente en JavaScript 207

9 yl = ev.screenY;
10 document.captureEvents(Event.MOUSEM0VE);
11 I
12 else {
13 xl = event.screenX;
14 yl = event.screenY;
15 1
16 document.onmousemove = showXY;
11 }
18 else {
19 if (document.layers){
20 x2 = ev.screenX;
21 y2 = ev.screenY;
22 document.releaseEvents(Event.MOUSEM0VE);
23 f
24 else I
25 x2 = event.screenX;
26 y2 = event.screenY;
21 document.onmousemove = null;
28 }
29 window.status = 'Start: ( ' + Xl + ' , ' + yl +
30 End:
8 )+ x2 + + y2 + ' ) Distance: ' +
( 8 I , '

31 (Math.round(Math.sqrt(Math.pow((X2 - XI), 2 ) + Math.Pow( (Y2


- Yl), 2)))) + ' pixels';
32 }
33 change = !change;
34 I
35
36 function showKeys ( ) (
31 if (keys ! = " ) (
38 alert('You have typed: ' + keys);
39 window.status = keys = I ;

40 I
41 else { alert('Y0u have to type some keys first.'); }
42 I
41
44 function showxY(ev1 (
45 if (document.al1) ( ev = event; }
46 window.status = 'x: + ev.screenX + ' Y: ' + ev.screenY;
I

41 1
48
49 function keepKeys(ev) {
50 if (document.1ayer.s) I
51 keys += String.fromCharCode(ev.which);
52 window.status : 'Key pressed: ' +
String.fromCharCode(ev.which);
53 I
54 else {
55 keys += String.fromCharCode(event.keyCode);
56 window.status = 'Key pressed: ' +
String.fromCharCode(event.keyCode);
208 lmplementacion de archivos de codigo fuente en JavaScript

57 1
58 1

La funcion enableEf fects ( ) es el epicentro de 10s eventos click y mouseover.


Me gustaria que prestasemos especial atencion a las lineas 6 , 18 y 3 3 :

if (change) { ....
else { . ...
change = !change;

La variable change empezara con el valor true y despuks lo cambiara por su


opuesto en cada llamada. Puesto que la primera vez que hagamos clic se llama-
ra a enableEf fects ( ) y la variable change sera true, nos encontraremos en
la linea 7.15:

if(docurnent.layers) (
xl = ev.screenX;
yl = ev.screenY;
document.captureEvents(Event.MOUSEM0VE);
1
else {
xl = event.screenX;
yl = event.screenY;
1

Estas lineas capturan las coordenadas x e y, y activan el controlador del evento


onmousemove. Si existe document. layers, el usuario estara trabajando con Na-
vigator. Por lo tanto, el objeto del evento que se ha creado sobre la marcha se
pasara como argument0 a la funcion. En este caso, su nombre sera ev. A las va-
riables globales x l e y l se les asigna el valor de las coordenadas x e y correspon-
dientes a1 punto donde el usuario haya hecho clic por primera vez (el valor de
estas coordenadas las tenemos dentro de las variables screefl y screenu). A con-
tinuacion se llama a1 mktodo captureEvents ( ) del documento, que se encar-
ga de capturar el evento mousemove.
Si no existe document. layer, el scipt asumira que el usuario trabaja con Mi-
crosoft Internet Explorer y tomara las acciones pertinentes para hacer lo mismo
de antes. Pero el modelo de objetos de Microsoft define un evento de objetos
como event. Esto es exactamente lo que estaban esperando las propiedadesscreefl
y screenY. No hace falta que se llame a ningun otro metodo adicional para cap-
turar el movimiento del raton en MSIE, tal y como se puede apreciar en la linea
16 de codigo:

document.onmousemove = showXY;
lmplementacidn de archivos de codigo fuente en JavaScript 209

A la funcion showXY ( ) se le asignara el controlador del evento onmousemove


a traves de una referencia. Este hecho tendra lugar con 10s dos exploradores.
Vamos a ver el contenido de showKeys ( ) :

function showXY ( ) in both browsers. Let ' s have a quick look at showXY ( 1 :
function showXY(ev1 {
if (document.al1) { ev = event; 1
window.status = 'X: + ev.screenX + ' Y: ' + ev.screenY;
1

Se llama a showKeys ( cada vez que se mueve el raton para que se muestren
las coordenadas del cursor en la pantalla. La referencia a 10s valores x e y es
identico a la vista anteriormente. Las llamadas a showKeys ( ) se repetiran cons-
tantemente hasta que el usuario vuelva a hacer clic, con lo que se invocara a la
funcion enableEf f ects ( ) . En cualquier caso, el valor de la variale change sera
false. Podemos ver la llamada entre las lineas 19-31:

if (document.layers) {
x2 = ev.screenX;
y2 = ev.screenY;
document.releaseEvents(Event.MOUSEM0VE);
1
else {
x2 = event.screenX;
y2 = event.screenY;
document.onmousemove = null;
1
window.status = 'Start: ( ' + xl + 0 '+ yl +
' ) End: ( ' + x2 + ' , ' + y2 + ' ) Distance: ' +
(Math.round(Math.sqrt(Math.pow((x2 - xl), 2)+ Math.pow((y2 - yl), 2 ) ) )
+ ' pixels';

Las variables xl e y l contienen 10s valores del punto inicial. Ahora, las varia-
bles x2 e y2 contienen 10s valores del punto final. Ya no hay ninguna necesidad
de continuar con el proceso del controlador del evento onmousemove. Asi que,
con Navigator, se llamara a1 metodo releaseEvents ( ) con objeto de que in-
tercepte el evento mousemove. Obtendremos el mismo resultado con MSIE con
document.onmousemove.
Todo lo que queda es mostrar la distancia que hay entre el punto inicial y el fi-
nal. LRecuerda la formula de la distancia? Se utilizaba en el colegio. Es la misma
que se muestra en las lineas 29-3 1 .
Se hace cargo de 10s controladores de eventos onclick y onmouseover, dejando
solo aonkeypress. No olvidemos que se configuraba document.onkeypress para
que llamase a la funcion keepKeys ( ) en la carga de evenkhtml. En las lineas
49-58 se encuentra dicha funcion:
210 Imdementacion de archivos de c6dino fuente en IavaScriDt

function keepKeys(ev) {
if (document.layers) {
keys += String.fromCharCode(ev.which);
window.status = 'Key pressed: ' + String.fromCharCode(ev.which);
1
else {
keys += String.fromCharCode(event.keyCode);
window.status = 'Key pressed: +
String.fromCharCode(event.keyCode);
}
)

Utilizando la misma tkcnica de deteccion de la navegacion, se asignara a la va-


riable keys un valor igual a si mismo mas una cadena que equivale a las teclas
que se pulsen. De ello se encarga String.f romCharCode( ) , independientemen-
te del navegador que se utilice. Per0 JavaScript 1.2 representa las pulsaciones
del teclado como caracteres I S 0 Latin-1 . Jscript utiliza representaciones Unicode.
El numero en JavaScript se guarda dentro de la propiedad which del objeto. El
numero en Jscript se guarda dentro de la propiedad event.keyCode. Asi pues,
cuando el usuario teclea una serie de teclas y pulsa el b o t h "Show Keys", se
buscara el valor de keys y se le asignara una cadena vacia.

frames.js
Us0 practico
Carga de cuadros.

Version necesaria
JavaScript 1.1.

Funciones
keepIn ( ) , keepout ( 1 .
Irnplernentacion de archivos de codigo fuente en JavaScript 21 1

Este archivo de codigo fuente unicamente contiene dos funciones. Una guarda
10s documentos en un marco de trabajo determinado. La otra 10s guarda fuera
de el. frarnes.js necesita varias paginas HTML. Por ejemplo, trate de cargarlo en
su explorador Web. El archivo tiene dos cuadros. Uno de ellos utilizafrarnes.htrnZ
como fuente. Dicho archivo utiliza frarnes.js para asegurarse de que se carga
frarnes.htrnZ en la parte superior de la ventana. Esta es la raz6n por la cual la car-
ga defrarneset.htrn2ofrece el resultado de las figuras 6.10 y 6.1 1 (el explorador
carga frarneshtrnl).

Figura 6.10. Violacihn de la politica dr cuadros

Por el contrario, para todos aquellos que quieran asegurarse de que no se car-
gan 10s archivos a menos que se encuentran en un marco de trabajo predetermi-
nado, podran utilizar frarnes.js. Veamos el contenido de la figura 6.10. Aparece
un aviso el cual nos indica que se ha tratado de cargar un archivo dentro del
marco equivocado.
En la figura 6.1 1 vemos el resultado que obtenemos si cargamos el archivo en
el cuadro correcto.
El codigo que se utiliza para acomodar esta funcionalidad es muy corto. La fun-
cion keepout ( ) compara el URL del documento que se encuentra situado en la
parte superior de la ventana con el del cuadro actual. Si no coinciden las propie-
dades l o c a t i o n . href, keepout ( ) entonces protestara y nos mostrara un aviso,
212 Irnplernentacion de archivos de cddigo fuente en JavaScript

cargando el documento en el marco superior de la ventana. La funcion keepIn ( )


efectua precisamente la comparacion opuesta y carga el URL que contiene el
argument0 que se pasa en el momento en el cual falla la comparacion. A conti-
nuacion, en el ejemplo 6.6 podemos comprobar el codigo correspondiente a1
archivo frarnexjs.

There that LB much bchcr

Figura 6.11. Carga correcta

Ejemplo 6.6. frames.js

1 function keepout0 (
2 if (top.location.href ! = self.1ocation.href) {
3 alert('This document bows to no frameset.');
4 top.location.href = self.location.href;
5 I
6 >
I
8 function keepIn(parentHREF) (
9 if (top.location.href == se1f.location.href) (
10 alert("Wheez1. . . [Gasp]. . . Must. . . load. . . ' +
11 'original. . . frameset.');
12 top.location.href = parentHREF;
13 I
14 I
lmplementacion de archivos de codigo fuente en JavaScript 213

images.js
Us0 practico
Secuencias de imagenes.
Version necesaria
JavaScript 1.1.
Funciones
imagePreLoad( ) , imageswap( ) , display ( ) .
Al igual que ocurria con las funciones de dhtmZ.js, ya hemos visto en capitulos
anteriores el codigo de images.js. De hecho, en 10s tres capitulos anteriores se han
visto distintas versiones de las lineas que aparecen en el ejemplo 6.7. Podemos
cargar las imagenes de antemano y utilizarlas como secuencias dependientes
del raton.

Ejemplo 6.7. images.js

1 var imgPath = 'images/';


2 var arrayHandles = new Array('out', 'over');
3
4 for (var i = 0; i < arrayHandles.length; i++) I
5 eval( 'var ' + arrayHandles[i] + ' = new Array() ' ;
6 1
7
8 for (var i = 0; i < imgNames.length; i++) {
9 imagePreLoad(imgNames[il, i);
10 1

12 function imagePreLoad(imgName, idx) I


13 for(var j = 0; j < arrayHandles.length; j++) {
14 eval (arrayHandles[j] + " ['I+ idx + " I = new Image ( 1 ;
'I)

15 eval (arrayHandles[j] + '' [ " + idx + " I .src = ' " + imgPath +
imgName +
16 arrayHandles [jI + " .gif'" ) ;
17 1
18 1
19
20 function imageSwap(imagePrefix, imageIndex, arrayIdx) I
21 document[imagePrefix].src = eval(arrayHandles[arrayIdxl + "[" +
22 imageIndex + " 1 .src");
23 1
24 function display(stuff) { window.status = stuff; )

Como ya conocemos el procedimiento de las secuencias de imiigenes, no vamos


a incluir ninguna imagen para demostrar las diferencias.
214 Irndernentacion de archivos de codiao fuente en IavaScriDt

navbar.js
Us0 practico
Navegacion dinamica por la phgina.
Version necesaria
JavaScript 1 . 1 .
Funciones
navbar ( 1 .
Este archivo de codigo fuente solo contiene una funcion. Supongamos que te-
nemos varias paginas de contenidos en un sitio Web, cada una de ellas con una
barra de navegacion con vinculos a otras paginas. LNo estaria bien que JavaScript
creara una barra de navegacion inteligente que incluyese vinculos a1 resto de pa-
ginas del sitio Web menos a la que aparece en pantalla? La figura 6.12 muestra
una pagina Web con una barra de contenidos con vinculos a otras paginas.

The Astronomy Page

[Other Sciences] ISijorts] [Muncians' Corner] [Cool People]

Figura 6.12. Pagina de astronomia, sin ningun vinculo dirigido a ella

En la figura 6.13 se puede ver la pagina que ha cargado a1 usar el hipervinculo


"Cool People". Si nos fijamos en la barra de navegacion veremos que ya no hay
Irnplernentacion de archivos de codigo fuente en JavaScript 21 5

ningun enlace con la pagina Cool People porque es la que aparece en pantalla.
Este sistema se puede utilizar con todas las paginas que deseemos. Lo unico que
hay que hacer es modificar el contenido de navbar.js. Nos ahorraremos mucho
tiempo.

[Astiotioniy] [Other Sciences] ISportrl [Musicians'Corner]

Figura 6.13. Pagina de gente, sin ningiin vinculo dirigido a ella

El codigo encargado de esto es bastante basico. Se basa en la publicacion de un


array, navURLs, donde se guardan 10s nombres de las paginas Web y otro array,
ZinkText, con el texto que aparecera en 10s vinculos. Se puede trabajar con URL
relativas o locales, aunque nos podemos encontrar con problemas si hay dos
archivos con el mismo nombre en dos directorios distintos. Se repetira la ejecu-
cion de la funcion navbar ( ) a travCs de todos 10s nombres de 10s archivos y
generara u n vinculo con el texto correspondiente para todas las paginas que no
aparezcan en la propiedad Zocation.href del documento. Es muy sencillo. Com-
pruCbelo en el codigo del ejemplo 6 . 8 .

Ejemplo 6.8. navbar.js

1 var navURLs = new Array ( ' astronomy.html ' , ' science.html ,


'sports.htrnl',
2 'music.htm', 'people.htm');
3 var linkText = new Array('Astronomy', 'Other Sciences', ' S p o r t s ' ,
216 Irnplementacion de archivos de c6digo fuente en JavaScript

4 'Musicians\'Corner', 'Cool People');


5
6 function navbar0 {
7 var navStr= ' I ;

8 for (var i = 0; i < navURLs.length; i + + ) {


9 if (location.href.indexOf(navURLs[il) == -1) {
10 navStr += ' <B>[</B><AHREF="' + navURLs[i] + " ' > ' +
11 linkText[il + ' < / A > < B > l < / B >' ;
12 )
13
14 document.writeln('<BR><BR>' + navstr);
15 )

Podemos ampliar mucho su funcionalidad. N o hay ninguna razon por la que


no se puedan utilizar imagenes ( o secuencias de ellas) en lugar de vinculos de
texto. Si tenemos una gran cantidad de vinculos y no queremos que aparezcan
en la pagina, ipor quk no utilizar una lista de seleccion? De esta forma podre-
mos agrupar muchos elementos ocupando la minima cantidad de espacio.

numbers.js
Us0 practico
Corregir 10s errores de redondeo y el formato de numeros para 10s programas
de compra dentro del comercio electronico (tambikn conocidos como carru de la
cumpra).
Version necesaria
JavaScript 1.1.
Funciones
twoPlaces0, round(),totals().
JavaScript trabaja con 10s numeros con coma flotante de forma distinta a la
esperada. Como el resultado se obtiene a partir de numerosos calculos, es posi-
ble que difiera del esperado (por ejemplo, a1 multiplicar 0,119 * 100 se obtiene
11,899999). Las funciones de numbers.js se han diseiiado para ayudar en am-
bas situaciones. Las tres se basan en las funciones que en su dia escribio Martin
Webb. Podemos verlas en accion en la figura 6.14.
Los numeros que aparecen bajo "Two Decimal Places" nos muestran el formato
que usa la funcion twoplaces ( ) para trabajar con Euros y ckntimos. Las otras
dos cabeceras muestran la diferencia que existe entre una expresion a1 utilizar
las funciones r o u n d ( ) y totals ( ) y a1 no utilizarlas. A1 final vemos la opcion
que se suele utilizar. En el ejemplo 6.9 se puede ver el contenido correspondiente
a numbers.js.
tmplementacion de archivos de codigo fuente en JavaScript 21 7

Turn Dermal Places


0 0 0 s 0 00
10 LE 0 10
1rr100
-530 LS -053

Rounlhng expressionsM t h o u t nmhers JS Funchow


51 0 2 - 38=47220000000000006

Roundmg expressionsWith numhsrs JS Functions


5102 3 8 = 4 7 2 2

I
gjiTU0 - r -GJ*-=-- --*/
A

Figura 6.14. Gracias a JavaScript se mejora el aspecto de 10s nlimrros


Ejemplo 6.9. number, j,
1 function twoPlaces(amount) {
2 return (amount == Math.floor(amount)) ? amount + ' . O O ' :
3 ((amount*lO == Math.floor(amount*lO)) ? amount + ' 0 ' :
amount) ;
4 1
5
6 function round(number,X) I
I x = (!X ? 2 : X ) ;
8 return Math.round(number * Math.pow(lO,X)) / Math.pow(lO,X);
9 1
10
11 function totals(num) {
12 return twoPlaces(Math.floor((num - 0 ) * 100) / 100);
13 1

La funcion twoplaces ( ) devuelve una cadena con el valor del numero, a1 cual
le afiade . 0 6 . 00, o nada si el formato utilizado es el correcto. Esta expresion
condicional se podria traducir a1 castellano de la siguiente manera:
En el caso de que el numero sea igual a1 entero inmediatamente inferior
(Math.f l o o r (amount) ), entonces devuelve una cadena a la que afiade a
continuacion . 0 0.
218 lmplementacion de archivos de codigo fuente en JavaScript

En cualquier otra circunstancia, cuando el numero multiplicado por 10 es


igual a1 entero inmediatamente inferior, tambitn tste multiplicado por 10
(Math.floor (amount) * 10) devuelve una cadena a la que afiade . 0.
En cualquier otro caso devolvera el numero como una cadena, porque se
utilizara el formato adecuado.

Para 10s errores de redondeo, la funci6n round ( ) devuelve el numero per0 con
x decimales. Por defecto, el valor inicial de x es 2. Por lo tanto, el redondeo ini-
cia1 sera . 00. L a funcion total ( ) se limita a llamar otra vez a twoplaces ( )
per0 se ha disefiado para que acepte una expresion de varios valores, por ejem-
plo, 51.02 - 3 . 8 como formato final.

objects.js
Us0 prictico
Creacion de objetos gentricos, inspeccion de objetos.
Version necesaria
JavaScript 1.1.
Funciones
makeObj ( ) , parseObj ( ) , obj Prof ile ( ) .
Ahora le toca el turno a 10s objetos de JavaScript. Con ellos se puede hacer tan-
tas cosas que apenas tenemos tiempo para probarlas todas. objects.js nos ofrece
dos utilidades. Una es u n constructor de objetos genericos y la otra un inspector
de objetos basicos. En la figura 6.15 podemos ver de qu6 estamos hablando.
Las funciones del inspector de objetos, parseObj ( ) y objProf ile ( ) revelan
las propiedades de dos objetos: uno representado por la variable someobject y el
otro es la ubicacion de la ventana de dicho objeto. Vamos a echar u n vistazo a1
contenido del ejemplo 6.10, que es el codigo de objects.htrnZ.

Ejemplo 6.10. objects.htm1

1 <HTML>
2 <HEAD>
3 <TITLE>objects.js Example</TITLE>
4 <STYLE type="text/css 'I>

5 <!--
6 td { font-family: courier new; font-size: 14)
I -->
8 < /STYLE>
9 <SCRIPT LANGUAGE="JavaScriptl .lo'SRC= "objects . js"></SCRIPT>
10 </HEAD>
lmplementacion de archivos de codigo fuente en JavaScript 219

11 <BODY>
12 <SCRIPT LANGUAGE="JavaScriptl.I " >
13 <!--
14
15 function plain01dObjectO {
16 this.name = 'some name';
17 this.numba = 1000;
18 this.objInherit = new makeObj('propertyOne', 'thisProperty'
19 ,propertyTwo', OthatProperty', 'propertyThree',
'theOtherProperty');
20 return this;
21 1
22
23 var someobject = new plain01dObjectO;
24
25 document.write(objProfile('some0bject'. 'self.location'));
26 //-->
27 </SCRIPT>
28
29
30

Object Profile

Property: POTt Type: .tring "due:

Property: pr.t.c.l Type: .-by fib:


"due;

Figura 6.15. Resultados de objects.htm1


220 lmplementacion de archivos de codigo fuente en JavaScript

Obskrvese que la variable someobject que aparece situada en la linea 2 3 es igual


a p l a i n o l d o b j e c t ( ) . Este constructor tiene varias propiedades, incluyendo
nam, numba y objlnherit. Esta ultima representa un objeto construido que se ha
creado a partir demakeob j ( ) , constructor que se encuentra dentro deobjeckjs.
Veamos el contenido de este archivo de codigo fuente.

Ejemplo 6.11. objectsjs

1 function makeObj ( ) {
2 if (arguments.length 8 2 ! = 0) {
3 arguments[arguments.length] = " " ;
4 I
5 for ( var i = 0; i < arguments.length; i += 2 ) {
6 this[arguments[ill = argumentsfi + 11 ;
7 1
8 return this;
9 I
10
11 function parseObj(obj) {
12 var objStr = " ;
13 for (prop in obj) {
14 objstr += '<TR><TD>Property:</TD><TD><B>' + prop +
15 '</B></TD><TD>Type:</TD><TD><B>' + typeof(obj[prop]) +
16 '</B></TD><TD>Value:</TD><TD><B>' + obj[prop] +
17 '</B></TD></TR>';
18 if (typeof( o b j [prop]) == "object") (
19 objStr += parseObj (obj[prop] ;
20 1
21 1
22 return objstr;
23 }
24
25 function objProfile0 (
26 var objTable = '<TABLE BORDER=2 CELLSPACING=O><TR><TD><Hl>'+
27 'Object Profile</Hl></TD></TR>';
28 for (var i = 0; i < arguments.length; i++) {
29 objTable += '<TR><TD><BR><BR><H2><TT>'+ (i + 1 ) + ' ) ' t
30 arguments[i] + '</H2></TD></TR>';
31 objTable + = '<TR><TD><TT><TABLECELLPADDING=5>' +
32 parseObj(eval(arguments[il)) + '</TABLE></TD></TR>';
33 }
34 objTable += '</TABLE><BR><BR><BR>';
35 return objTable;
36 1

Vamos a empezar viendo m a k e O b j () . Este es su codigo:


function makeObj 0 {
if (arguments.length 8 2 ! = 0 ) {
lmplementacion de archivos de codigo fuente en JavaScript 221

arguments [arguments.length] = "I' ;


I
for ( var i = 0; i < arguments.length; i += 2 ) {
this[arguments[ill = arguments[i + 11 ;
I
return this;
I

El constructor crea propiedades asignando pares de argumentos. Si el numero


de argumentos que se le entrega es extraiio (es decir, que un argument0 no se
puede emparejar), makeOb j ( ) asigna u n elemento adicional (compuesto por
una cadena vacia) a1 array arguments. Luego, makeObj ( ) se ejecuta con todas las
parejas de argumentos, asignando a1 primer0 de sus componentes el nombre de
la propiedad y a1 segundo el valor correspondiente. De esta manera, a1 llamar a
makeObj('name', 'Madonna', 'ocuppation', 'cantante/letrista'),
obtendriamos u n elemento con las siguientes propiedades:

this.name = 'Madonna'
this.ocupation = 'cantante/letrista'

Por lo tanto, la variable objInherit ahora hace referencia a un objeto que tiene
las siguientes propiedades:

objInherit.property0ne = 'thisProperty';
objInherit.propertyTwo = 'thatProperty';
obj1nherit.propertyThree = 'theOtherProperty';

Obskrvese que 10s valores de todas las propiedades es una cadena. Con ella, po-
dremos pasar numeros, objetos, etc. La funcionmakeobj ( ) se utiliza para crear
varios objetos, cada uno con propiedades diferentes y sin tener que definir un
constructor para cada uno de ellos.
El otro objeto que revisaremos sera el encargado de determinar la ubicacion.
Komo se formaliza la inspecci6n? Las funciones ob jProf ile ( ) y parseOb j ( )
trabajan juntas en las propiedades del objeto para crear una tabla de resultados.
Cada fila de la tabla identifica el nombre, tip0 y valor de la propiedad del objeto.
Empecemos con objProf ile ( ) :

function objProfile0 (
var objTable = '<TABLE BORDER=2 CELLSPACING=O><TR><TD><Hl>'+
'Object Profile</Hl></TD></TR>';
for (var i = 0; i < arguments.length; i++)
objTable += '<TR><TD><BR><BR><H2><TT>' + (i + 1 ) + ' + I )

arguments[il + '</HZ></TD></TR>';
objTable += '<TR><TD><TT><TABLECELLPADDING=5>' +
parseObj (eval(arguments[il)) + '</TABLE></TD></TR>';
222 Implementaci6n de archivos de codigo fuente en JavaScript

}
objTable += '</TABLE><BR><BR><BR>';
return objTable;
1

obj Prof ile ( ) es la funcion a la que se llama y a la que se entregan 10s para-
metros, como se puede apreciar en la linea 25 de objectxhtml:

document.write(objProfile('some0bject'.'self.location'));

Los argumentos no son objetos. Son cadenas. Per0 en breve reflejaran 10s obje-
tos. Al pasar el equivalente de la cadena, JavaScript podra mostrar estos objetos
basandose en el nombre de la pagina. Una vez que se crean las tablas TR y TD, se
entregan las cadenas de argumentos a obj Prof ile ( ) , que aplicara repetida-
mente el mCtodo eval( ) (linea 32)y pasara el resultado aparseObj ( ) .A con-
tinuacion, se ejecuta el siguiente extract0 de codigo:

function parseObj (obj) {


var objStr = ' I ;

for (prop in obj) {


objStr += '<TR><TD>Property:</TD><TD><B>'+ prop +
'</B></TD><TD>Type: </TD><TD><B>'+ typeof(obj[propl) +
'</B></TD><TD>Value: </TD><TD><B>' + obj[propl +
'</B></TD></TR>';
if (typeof(obj[prop]) == "object") (
objStr += parseObj (obj[prop]);
1
}
return objStr;
1

Las cadenas se reciben como objetos y de hecho, se las llamar6 obj. Con el bucle
for...if, parseobj ( ) actuar6 sobre todas las propiedades de ubj, creando una ca-
dena con la propiedad, tipo, valor y etiquetas adecuadas de cada una de ellas.
Para que parseObj ( ) acceda a1 tipo de objeto, utilizara el operador typeof ( ) .
Una vez que se ha aiiadido a la cadena la propiedad, tip0 y valor, parseOb j ( )
comprobara si el tipo de la propiedad hace que Csta sea un objeto. En ese caso, se
ejecutara y entregara dicha propiedad (que sera un objeto obj). Gracias a este sis-
tema, puede profundizar en una jerarquia de objetos hasta alcanzar 10s elemen-
tos de cualquier nivel.
Cuando a parseOb j ( ) no le quedan mas objetos que analizar, se copiara el con-
tenido de la cadena de propiedades, tipos, valores y etiquetas en la variable objStr,
que se le devolvera a la funcion obj Profile ( ) .Esta cadena, que se guarda en
la variable objTable, se escribe a1 final de la pagina (linea 25 de objectxhtml).
Implernentacion de archivos de codigo fuente en JavaScript 223

Nota: Estas funciones especializadas en la inspeccion de objetos se han


diseiiado para trabajar con objetos relativamente pequeiios, como 10s crea-
dos por 10s usuarios. Tanto Navigator como IE fallaran a1 trabajar con
ellas si la cantidad de informacion con la que tienen que trabajar es de-
masiado grande. Por ejemplo, podemos cambiar el contenido de la linea 25
de objects.htm1:

document.write(objProfile('someObject','self.location'));

document.write(objProfile('document')) ;

Ahora cargaremos el documento en MSIE. Nos encontraremos u n mensaje


de error. Si escribimos este otro codigo en la linea 25 y lo ejecutamos en
Navigator:

document.write(objProfile('window'));

nos encontraremos con otro mensaje de error.

strings.js
Us0 prictico
Manipulacion, ordenacion alfabktica y contador de us0 para cadenas.
Version necesaria
JavaScript 1.2.
Funciones
camelcaps ( ) , prepStr ( ) , wordcount ( ) , reorder ( ) .
Estas funciones nos permiten conocer todo lo que se puede hacer con las cade-
nas que se generan a partir de la informacion que introducen 10s usuarios. Si
abrimos el contenido de stringhtmo en el explorador, nos encontraremos con el
contenido de la figura 6.16.
Tenemos tres formularios con 10s que intentaremos demostrar el desarrollo de
estas tres funciones. La primera contiene u n Area de texto (TEXTAREA).Despuks
de que el usuario introduzca el texto que desee y pulse el b o t h Tount'l, la fun-
cion wordcount ( ) generara una nueva pagina con una tabla. En dicha tabla se
mostraran todas las palabras que ha introducido el usuario y el numero de ve-
ces que se ha repetido cada una de ellas. Podemos ver 10s resultados que se ob-
tienen en la figura 6.1 7.
224 Imdementacion de archivos de codino fuente en JavaScriDt

below Then choose Coant for a word count

below Then choose Upper or L-r to changc thr c e c

below Then choose Sortto ronthc trd

Figura 6.16. Tres formularios de recopilacion de datos

El segundo formulario contiene otra area de texto. El usuario puede seleccio-


nar "Upper" o "Lower"para convertir el primer caracter de cada palabra en ma-
yuscula o minuscula, respectivamente. La funcion camelcaps ( ) se encarga de
todo. Veremos que esta funcion es especialmente util para 10s procesos de vali-
dacion, posiblemente para 10s casos en 10s que 10s usuarios hayan introducido
nombres y direcciones. Tenemos el resultado en la figura 6 . 1 8 .
La ultima funcion tambitn cuenta con un area de texto donde el usuario, des-
puts de escribir lo que quiera, podra ordenar su contenido. Es un buen script de
ordenacion alfabktica. Podemos ver 10s resultados en la figura 6.19. Si se selec-
ciona "Sort" varias veces, la funcion pasara de un orden ascendente a otro des-
cendente. Vamos a ver el codigo de strings.js.

Ejemplo 6.12. strings.js


1 function wordCount(str, output) {
2 var wordArray = new Array();
3 str = prepStr (str);
4 var tempArray = str.split(' ').sort();
5 var count = 1;
lmplementacion de archivos de codigo fuente en JavaScript 225

6 for (var i = 0; i < tempArray.length; i++) {


7 if (wordArray[tempArray[i]]){
a wordArray [ tempArray [i1 1 ++ ;
9 }
10 else I wordArray[tempArray[ill = 1; I
11 I
12 if (output) I return wordArray; )
13 else {
14 var arrStr = ' I ;

15 for (word in wordArray) I


16 if (word ! = {
'I")

17 arrStr += '<TR><TD>'+ word + '</TD><TD>'+


ia wordArray[wordl + '</TD></TR>';
19 count++ ;
20 }
21 1
22 return '<TABLE BORDER=O><TR><TDWIDTH=300 VALIGN=TOP
ROWSPAN= +
23 count + '><B>OriginalFormatted Text</B><BR><I>' + str +
24 '</I><TD><B>Word</B><TD><~~~reqency~/B></TR>'+ arrStr +
25 '</TABLE>';
26 1
21 )
28
29 function prepStr(str) {
30 str = str.toLowerCase0;
31 str = str.replace(/["'-l/g, " " ) ;
32 str = str.replace(/\W/g, '' " ) ;
33 str = str.replace(/\s+/g, " ) ;
34 return str;
35 1
36
37 function camelCaps(str, thecase) I
38 var tempArray = str.split(' ' ) ;
39 for (var i = 0; i < tempArray.length; i++ {
40 if (thecase) {
41 tempArray[il = tempArray[il.charAt(O) touppercase ( ) +
42 tempArray[il.substring(l);
43 I
44 else (
45 tempArrayfi1 = tempArray[il.charAt(O). toLowerCase ( ) +
46 tempArray[il.substring(l);
47 I
48 }
49 return tempArray.join(' I ) ;

50 I
51
52 var order = true;
53
54 function reorder(str) I
55 str = prepStr(str) ;
226 Irnplementacion de archivos de codigo fuente en JavaScript

56 str = str.replace(/\d/g, " " ) ;


51 order = !order;
58 if(!order) { str = str.split(' ').sort().join('' 1 ; 1
59 else { str = str.split(' ').sort().reverse().join(' ' ) ; 1
60 return str.replace(/"\s+/, " " ) ;
61 1

Con objeto de generar un contador de palabras utilizando el primer formula-


rio, wordcount ( ) dara 10s siguientes pasos:

1 . Elimina cualquier carhcter que no sea letra, numero (0guiones bajos) o


espacios en blanco.
2. Crea un array con todas las palabras del texto.
3 . Cuenta el numero total de palabras.
4. Muestra 10s resultados con formato de tabla.

Para cumplir el primer paso necesitara la ayuda de otra funcion, prepstr ( ) .


Tenemos su contenido en las lineas 29-35:
228 Implernentacion de archivos de codigo fuente en JavaScript

for (var i = 0; i < tempArray.length; i++) (


if (wordArray[tempArray[ill) {
wordArray[tempArray[il I + + ;
I
else { wordArray[tempArray[i]l = 1; I
1

Enrcr ~ o m words
c m Bc TEXTARUL below Then choose S m to sort Bc fcyf

SYailQulel: altablieclcm

Figura 6.19. Ordenacion alfabktica de las palabras

En el segundo formulario, el texto que escribe el usuario se le entrega a la fun-


cion camelcaps ( ) en forma de cadena. Dicha funcion tambitn aceptara un se-
gundo argumento, un valor booleano el cual indica si la conversion se realizara
a mayusculas o a minusculas:

function camelCaps(str, thecase) (


var tempArray = str. split ( ; I )

for (var i = 0 ; i < tempArray.length; i++) (


if (thecase) {
tempArrayLi1 = tempArray[il.charAt(O).toUpperCase() 4
tempArray[i].substring(l);
I
else {
Irnplementacion de archivos de codigo fuente en JavaScript 229

tempArray[il = tempArray[il.charAt(O).toLowerCase()+
tempArray[il.substring(l);
I
1
return tempArray.join(' ' ) ;
}

El valor de la variable local tempArray sera un array que contiene todas las
palabras del texto. En este caso, una "palabra" sera cualquier porcion de texto que
se encuentre entre dos espacios en blanco. La sustitucion de las palabras no sera
otra cosa que repetir la funcion con todos 10s objetos. Cuando se complete el
cambio, la funcion devolvera una cadena con estas palabras unidas por medio
de espacios en blanco. La funcion camelcaps ( ) se limita a devolver 10s espacios
en blanco que se han eliminado durante la ejecucion de s p l i t ( 1 .
Para el ultimo formulario, la funcion reorder ( ) ejecutara un metodo s o r t ( )
ya sea normal o invertido. Aqui lo tenemos:

var order = true;

function reorder(str) {
str = prepStr(str);
str = str.replace(/\d/g, ; " ' I )

order = !order;
if(!order) ( str = str.split(' ').sort().join(' ' ) ; }
else ( str = str.split(' ').sort().reverse().join('' ) ; )
return str.replace(/"\s+f, " " ) ;
1

L a funcion p r e p s t r ( ) da formato a la cadena que pasara. En esta funcion se


han eliminado 10s digitos con una llamada a s t r .r e p l a c e ( / \d/g , " " . Asi,
la funcion se centra en las palabras y no en 10s numeros. Se puede cambiar el
orden de la variable por su inverso. Esto sucedera cada vez que el usuario vuel-
va a hacer clic en el boton "Sort". Consideremos ahora quC ocurrira cuando las
palabras se ordenen alfabeticamente o a la inversa. Con la ordenacion tradicional:

1. El texto se divide en un array.


2. Se ordenan 10s elementos del array.
3 . Se unen 10s elementos del array para formar una cadena.

En caso de ordenacion inversa, habra que dar un paso mas:

1. El texto se divide en un array.


2. Se ordenan 10s elementos del array.
3. Se invierten 10s elementos.
4. Se unen 10s elementos del array para formar una cadena.
230 Irnplernentacion de archivos de codigo fuente en JavaScript

Las lineas 58-59 de strings.js utilizan el valor del orden para determinar quC
sistema han de usar. DespuCs devolveran la cadena (menos 10s espacios en blan-
co iniciales que se puedan haber creado a1 utilizar j o i n ( 1 ).

Posibles ampliaciones
Lo unico limite que tenemos aqui es el que nos queramos poner. Obviamente,
podemos agregar nuevas funciones o mejorar las ya existentes. La verdad es
que dud0 que nadie quiera conservar las funciones exactamente igual a como se
muestran en estos ejemplos. Posiblemente, se estC trabajando en el diseiio de un
sitio Web y se quieran utilizar estos archivos en dicho proceso. Estupendo. Todo
lo que se ha de hacer es fijar las funciones que se necesiten en un nuevo archivo
.js, con lo que se tendra una caja de herramientas de inestimable valor. Lo que se
ha de tener en cuenta es que conviene trabajar con el sistema que mejor se adap-
te a cada uno. N o deje que 10s archivos .js dirijan su trabajo.
Capitulo 7
Preferencias del usuario basadas en cookies

Este capitulo contiene una aplicacion muy normalita, per0 que no conviene
saltarse. iHe dicho que el codigo que se recoge en estas paginas es el responsable
de una de las funciones mas solicitadas en la Web? Hablo de las preferencias del
usuario. Considere esto. iQue es lo que tiene que tener todos 10s usuarios en la
cabeza cuando naveguen por la Web?
"Yo.
Pues si, 10s usuarios son bastante egoistas y siempre estan pensando en sus pro-
pios intereses. No importa lo que hagan. Siempre buscan cosas que les recuer-
den a si mismos. Esta es la raz6n por la que 10s seguidores de DHTML visitan
Dynamic HTML Zone (http://www.dhtmZzone.com/),10s consumidores del co-
mercio electronic0 acuden a Shopping.com (http://www.shopping.corn/ibuy/) y
10s amantes de la astronomia (como yo, ive? ya estoy hacikndolo de nuevo) vi-
sitan las paginas Web de Sky & Telescope (http://www.skypub.rom/).Los comer-
ciantes, anunciantes y comerciales llevan ya muchos aiios trabajando en W e .
Utilizando una aplicacion ficticia, en este capitulo comprobara lo sencillo que
resulta recordar el nombre de un usuario. Por medio de las cookies de JavaScript,
10s visitantes podran personalizar sin probelemas la visita que realicen a nues-
tra pagina Web.
Supongamos que estamos trabajando en el desarrollo de un sitio Web para
usuarios de Internet que tienen algo de dinero para invertir. Obtendran una sus-
cripcion gratuita a esta Bolsa ficticia, llamada Take-A-Dive Brokerage $ervices.
Nuestras paginas Web no les permitira mover dinero. Per0 el usuario podra per-
sonalizarlas para que le muestre 10s vinculos que mas le gusten, dirigidos a otras
paginas Web dedicadas a las finanzas o bien a noticias. En la figura 7 . 1 vemos el
232 Preferencias del usuario basadas en cookies

contenido que mostrara la aplicacion la primera vez que se accede a ella. Desde
aqui, el usuario podra personalizar su contenido.

Figura 7 . 1 . La primera vez que se accede a la pagina, la aplicacion mostrara


directamente las opciones de personalizacion

En la figura 7.2 podemos ver la pagina de preferencias del usuario. Se trata de


un formulario donde el navegante tendra que introducir su nombre, edad, ocu-
pacidn y categoria de la inversion. Una serie de casillas de verificacion permiten
seleccionar 10s vinculos financieros que apareceran en la pagina personalizada.
El formulario incluye una serie de listas de seleccion a travks de las cuales el
usuario podra elegir una imagen de fondo, la fuente y el tamafio de letra. Cuan-
do se complete la configuracion, habra que hacer clic en el boton "Save" para
que la aplicacion guarde Csta dentro de un archivo cookie. A continuacion, se le
preguntara a1 usuario si quiere revisar 10s cambios, tal y como se puede ver en
la figura 7 . 3 . Si acepta, la aplicacion mostrara el contenido del archivodive.html,
una pagina donde se muestran las opciones e informacion personalizada del
usuario.
Muy bonito, ipero quk pasa si el usuario quiere introducir algun cambio? Sen-
cillo. Seleccionara el enlace "Set Preferences" y volvera a1 archivo prefs. html.
Observese que las opciones del usuario tambien se conservan aqui. Se mostra-
ran aquellas casillas de verificacion que selecciono la ultima vez que modifid la
Preferencias del usuario basadas en cookies 233

configuracion. La informacion personal seguira intacta. Junto a la lista desple-


gable de imhgenes de fondo aparecera una miniatura con el fondo seleccionado.
El usuario podra modificarla y ver que tal queda su pagina utilizando otras com-
binaciones.

Choose the seuings you like best hen choose

. Sava to keep your changes

. Clear to reset the form or


Back to returnto your links page

lnvestor Profile

Investment Links
. . - ._
r Barrons Online
P CNN lnteracbve
NOW r FOXNM
P MSNBC
G The Wall Street Journal ~

G Dow Jones Indexes


Stock lndaxar G NASDAQ
G The New York Stock Exchange

Figura 7.2. Configuracion de las preferencias del usuario

Requisitos para la ejecucion


Esta aplicacion se ha escrito con JavaScript 1.2. Las hojas de estilo y la sustitu-
ci6n de cadenas se encargan de todo. Los usuarios necesitaran disponer de la ver-
sion 4 de Navigator o de Internet Explorer. Se puede aumentar considerablemente
la funcionalidad de esta aplicacion, porque aqui unicamente trabaja con una hoja
de estilos. Las unicas limitaciones las encontraremos en nuestra capacidad de
trabajar con DHTML (es decir, que apenas tiene limites).
Se trata de una aplicacion basada en cookies, por lo que tendremos que adap-
tarnos a las especificaciones de estos elementos. Dependeran de cada navegador.
Las encontraremos en las siguientes direcciones:
Netscape Navigator, que se encuentra en: http://developerl .netscape.com:t?O/docs/
manuals/communicator/jsguide4/cookies.htm.
234 Preferencias del usuario basadas en cookies

MSIE, en: http://msdn.microsoft.com/msdn-online/workshop/author/dhtml/


reference/properties/cookie. asp.
No se preocupe. Esta aplicacion se ajusta a las exigencias de las cookies de am-
bos exploradores Web.

'I
r Banon s Online
B CNN lnteractlve

P MSNBC

Figura 7.3. DespuCs de hacer clic en "Save",la aplicaci6n darh la opcion de ver
la pagina personaliza

Analisis de la sintaxis
Esta aplicacion consta de dos paginas HTML (prefkhtml y dive.htrnZ) y de un
archivo de codigo fuente en JavaScript, cookiexjs. La siguiente lista describe cada
uno de estos elementos:

prefs.htm1. Se trata de la pagina que se utiliza para configurar las prefe-


rencias dedive.htrnZ. En ella tambikn influye la informacion que se extraiga
de la cookie, ya que reflejara las opciones que haya seleccionado anterior-
mente el usuario.
dive.htrnl. Esta pagina contiene la informacion personalizada por el usua-
rio y la extraida de la cookie.
Preferencias del usuario basadas en cookies 235

cookies.js. Este archivo contiene las funciones que se han usado para escribir
las preferencias del usuario y para extraer la informacion de la cookie.
cookies.js esta dentro de 10s dos archivos HTML .Las funcionesGetCookie ( )
y S e t c o o k i e ( ) , que veremos en estos dos archivos, proceden de aqui.
Los archivos prefs.htrnl y dive.htrnl son nuevos, per0 cookies.js ya lo vimos en
el capitulo 6 . Si tiene alguna duda sobre el, revise dicho capitulo.

Figura 7.4. Pagina personalizada, incluyendo fuentes e imagen de fondo

prefs.html
Aunque la secuencia de capturas de pantalla nos muestra la sucesion de accio-
nes (por ejemplo, el usuario inicia el proceso sin haber configurado ninguna
preferencia), en este ejemplo, vamos a asumir que eI usuario ya habia seleccio-
nado previamente alguna preferencia y quiere modificarla. Sera mas sencillo. El
ejemplo 7.1 muestra el contenido del archivo prefs.htrnl.
Ejemplo 7.1. prefs.htm1
1 <HTML>
2 <HEAD>
236 Preferencias del usuario basadas en cookies

3 <TITLE>Take-A-DiveUser Preferences</TITLE>
4 <STYLE type="text/css">
5 BODY, TD ( font-family: Arial; )
6 </STYLE>
I <SCRIPT LANGAUGE="JavaScriptl.2" SRC="cookies.js"></SCRIPT>
8 <SCRIPT LANGUAGE="JavaScriptl.2">
9
10 var imagepath = 'images/';
11 var newsNames = new Array(
12 new Array('The Wall Street Journal','http://www.wsj.com/'),
13 new Array('Barron\'s Online','http://www.barrons.com/'),
14 new Array('CNN Interactive','http://www.cnn.com/'),
15 new Array('MSNBC','http://www.msnbc.com/'),
16 new Array('Fox News','http://www.foxnews.com/')
17 ):
18
19 var indemames = new Array(
20 new Array('The New York Stock Exchange','http://www.nyse.com
'),
21 new Array('NASDAQ','http://www.nasdaq.com/'),
22 new Array('Dow Jones Indexes','http://www.dowjones.com/')
23 );
24
25 var strategy = new Array(
26 new Array('Cheap', 'I\'m Really Cheap'),
21 new Array('Stingy', 'I\'m Pretty Stingy'),
28 new Array('Conservative', 'I\'m Conservative'),
29 new Array('Moderate', 'I\'m a Moderate'),
30 new Array('Agressive', 'I\'mAggressive'),
31 new Array('Wil1ing to sell mother', 'I\'d Sell My Mother! ' )
32 ):
33
34 var background = new Array(
35 new Array(imagePath + 'goldthumb.gif', 'Gold Bars'),
36 new Array(imagePath + 'billsthumb.gif', 'Dollar Bills'),
37 new Array(imagePath + 'fistthumb.gif', 'Fist of Cash'),
38 new Array(imagePath + 'currencylthumb.gif','Currency l'),
39 new Array(imagePath + 8currency2thumb.gif','Currency 2')
40 );
41
42 var face = new Array(
43 new Array('times', 'Times Roman'),
44 new Array('arial', 'Arial'),
45 new Array('courier', 'Courier New'),
46 new Array( 'tahoma', 'Tahoma')
41 1;
48
49 var size = new Array(
50 new Array('lO', 'Small'),
51 new Array('l2', 'Medium'),
52 new Array('l4', 'Large'),
Preferencias del usuario basadas en cookies 237

53 new Array('l6', 'X-Large')


54 1;
55
56 indexNames = indexNames.sort0;
57 newsNames = newsNames.sort0;
58
59 var allImages = new Array();
60
61 var imageNames = new Array(
62 'courierlO','courierlZ','courierl4','courierl6',
63 'ariallo', 'ariallz', 'ariall4', 'ariall6',
64 'timeslo', 'timesl2', 'timesl4', 'timesl6'.
65 'tahomalo','tahomal2', 'tahomal4', 'tahomal6',
66 'goldthumb','billsthumb','fistthumb','currencylthumb',
8currency2thumb',
67 'blank'
68 );
69
70 for (var i = 0; i < imageNames.length; i++) {
71 allImages[i] = new Image();
72 allImages[i].src = imagepath + imageNames[il + '.gif';
73 1
74
75 function makePath(form0bj) {
76 var fontName = imagepath +
17 formObj.face.options[formObj.face.selectedIndexl.value +
78 formObj.size.options[formObj.size.selectedIndexl.value +
I .gif ;
79 swapImage ("fontImage",fontName);
80 I
81
82
83
84
85
86 function genSelect(name, select, onChangeStr) {
87 var optStr = " " ;
88 var arrObj = eval(name);
89 for (var i = 0; i < arrObj.length; i++) {
90 optStr += '<OPTION VALUE="' + arrObj[il [ O ] +
91 (i == select ? "' SELECTED' : + ' > ' + arrObj[i][l];
I " ' )

92 I
93 return '<SELECT NAME="' + name + + (onChangeStr ? '
I " '

onchange=" +
94 onChangeStr + : ") + + optStr + '</SELECT>';
' > I

95 1
96
97 function genBoxes(handle, arrObj) I
98 var boxStr = " ;
99 for (var i = 0; i c arrObj.length; i++) I
100 boxStr += '<INPUTTYPE=CHECKBOX NAME="' + handle + i +
VALUE=" +
238 Preferencias del usuario basadas en cookies

101 arrObj [il L O 1 + ' , ' + arrObj[i] [11 + "'> ' + arrObj[il L O 1 +
* <BR> ' ;
102 1
103 return boxStr;
104 J
105
106 function getPrefs(form0bj) {
107 var prefStr = GetCookie('userPrefs');
108 if (prefStr = = null) { return false; 1
109 var prefArray = prefStr.split('-->');
110 for (var i = 0; i < prefArray.length; i + + ) {
111 var currPref = prefArray[il.split('::'):
112 if (currPref[ll == "select") {
113 formObj[currPref[Ol].selectedIndex = currPrefL21;
114 1
115 else if (currPref[l] == "text") {
116 formObj[currPref[O]].value = currPrefL21;
117 1
118 else if (currPref[lI == "checkbox") {
119 formObj [currPref[Ol I .checked = true:
120 I
121 I
122 return true;
123
124
125 function setprefs formobj) {
126 var prefStr = '
127 var htmlStr = '
128 for (var i = 0; i < form0bj.length; i++) {
129 if (formObj[i .type == "select-one") {
130 prefStr += formObj [i].name + '::select::'+
131 formObj[il.selectedIndex + ' - - > I ;

132 htmlStr += formObj[i].narne + ' = ' +


133 formObj [il.options[formObj[i].selectedIndex].value + ' -
>';
134 1
135 else if (formObj[i].type = = "text") {
136 _-
if ( formObj [ i I .value - _ " 1 { formObj[il .value = "Not
Provided"; )
137 prefstr += formObj i].name + '::text::'+
138 safeChars ( f ormobj [ ].value) + ' - - > I ;

139 htrnlStr += formObj il .name + ' = ' + formObj [i].value + ' -


> I ;

140 1
141 else if (formobj[il .type == "checkbox" & & formObj [il .checked)
I
142 prefStr += formObj[il.name + '::checkbox::'+ ' - - > ' ;
143 htmlStr += formObj[il.narne + ' = ' + formObj[il.value + ' -
>';
144 1
Preferencias del usuario basadas en cookies 239

146 SetCookie('userPrefs',prefStr, expiry);


147 SetCookie('htmlPrefs',htmlStr, expiry);
148 if (confirm('Preferenceschanged. Go to your personalized
page?')) (
149 self.location.href = "dive.html";
150 1
151 I
152
153 function safechars (str) {
154 return str.replace(/::(=l-->/g, . , 1 ;
I . . ,

155 )
156
157 function populateForm(form0bj) {
158 if (getPrefs(form0bj) {
159 makePath(form0bj);
160 swapImage('bkgImage',
161 form0bj.background.options[formObj.background.selectedIndexl
.value);
162 1
163 else { resetImage(document.forms[Ol); 1
164 )
165
166 function resetImage(form0bj) {
167 swapImage('bkgImage',form0bj.background.options[0l.value);
168 swapImage('fontImage',imagepath +
formObj .face.options[Ol.value +
169 formObj.size.options[O].value + '.gif');
170 }
171
172 </SCRIPT>
173 < /HEAD>
174
175 <BODY BGCOLOR=FFFFFF onLoad="populateForm(document.forms[O]);">
176 < DIV ID= " sett ing " >
177 cH21Take-A-Dive User Preferences</H2>
178 Choose the settings you like best, then choose<BR>
179 <UL>
180 <LI><B>Save</B> to keep your changes,
181 <LI><B>Clear</B>to reset the form, or
182 <LI> <B>Back</B> to return to your links page.
183 </UL>
184
185 <FORM>
186 <TABLE BORDER=l CELLBORDER=O CELLPADDING=O CELLSPACING=l>
187 <TR>
188 <TD COLSPAN=2>
189 <BR>
190 <H3>Investor Profile</H3>
191 </TD>
192 </TR>
193 <TR>
240 Preferencias del usuario basadas en cookies

194 <TD>Name<lTD>
195 <TD><INPUT TYPE=TEXT NAME="investor"></TD>
196 </TR>
197 <TR>
198 <TD>Age</TD>
199 <TD><INPUT TYPE=TEXT NAME="age"></TD>
200 </TR>
201 <TR>
202 <TD>Strategy</TD>
203 <TD>
204 <SCRIPT LANGUAGE="JavaScriptl.2">
205 document.write(genSelect('strategy', 3)1 ;
206 </SCRIPT>
207 < /TD>
208 </TR>
209 <TR>
210 <TD>Occupation</TD>
211 <TD>
212 <INPUT TYPE=TEXT NAME="occupation> 'I

213 </TD>
214 <TR>
215 <TD COLSPAN=2>
216 <BR>
217 <H3>Investment Links</H3>
218 </TD>
219 </TR>
220 <TR>
221 <TD><B>News<B></TD>
222 <TD>
223 <SCRIPT LANUAGE="JavaScriptl.2">
224 document.write(genBoxes('newsNames')) ;
225 </SCRIPT>
226 </TD>
227 </TR>
228 <TR>
229 <TD><B>StockIndexes</B></TD>
230 <TD>
231 <SCRIPT LANUAGE="JavaScriptl.2">
232 document.write(genBoxes('indexNames'));
233 </SCRIPT>
234 </TD>
235 </TR>
236 <TR>
237 <TD COLSPAN=2>
238 <BR>
239 <H3>Screen Layout</H3>
240 </TD>
241 </TR>
242 <TR>
243 <TD>
244 <B>Background</B>
Preferencias del usuario basadas en cookies 241

245 <BR>
246 <SCRIPT LANGUAGE="JavaScriptl.2">
247 document.write(genSelect('background',0 ,
248 swapImage( bkgImage ' ,
249 this.options[this.selectedIndexl .value)")1 ;
250 </SCRIPT>
251 </TD>
252 <TD>
253 <IMG SRC="images/blank.gif"
254 NAME= bkgImage WIDTH=112 HEIGHT=6O >
'I 'I

255 </TD>
256 </TR>
257 <TR>
258 <TD>
259 <B>Font Face</B>
260 <BR>
261 <SCRIPT LANGUAGE="JavaScriptl.2">
262 document.write(genSelect('face', 0,
"makePath(this.form)" ) ) ;
263 </SCRIPT>
264 < /TD>
265 <TD ROWSPAN=2>
266 <IMG SRC="images/blank.gif"NAME="fontImage"
267 WIDTH=112 HEIGHT=60>
268 </TD>
269
270 </TR>
271 <TR>
272 <TD>
273 <B>Font Size</B>
274 <BR>
275 <SCRIPT L A N G U A G E = " J a v a S c r i p t 1 . 2 " >
276 document.write(genSelect('size', 0,
"makepath(this.form)" ) ) ;
277 </SCRIPT>
278 </TD>
279 </TR>
280 </TABLE>
281 < BR><BR>
282 <INPUT TYPE=BUTTON VALUE="Save" onClick="setPrefs(this.form); " >
283 <INPUT TYPE=RESET VALUE="Clear" onClick="resetImage(this.form); " >
284 <INPUT TYPE=BUTTON VALUE="Back"
onClick="location.href='dive.html';">
285 < ! - -
286 <INPUT TYPE=BUTTON VALUE="Show"
287 onClick="alert(GetCookie('userPrefs'));alert(GetCookie
('htmlPrefs'));">
288 <INPUT TYPE=BUTTON VALUE= "Erase"
289 onClick="DeleteCookie('userPrefs');
DeleteCookie('htmlPrefs');">
290 / / - - >
242 Preferencias del usuario basadas en cookies

291 </FORM>
292 </DIV>
293 </BODY>
294 </HTML>

Las lineas 10-68 son bastante curiosas. En vez de identificar la ruta donde se
encuentran las imagenes (linea lo), sus variables se dedican a configurar el di-
sefio de dive.htrnZ. A cada una de ellas, se le asigna el valor de un array de varias
dimensiones. Es decir, de un array de arrays.
Por ejemplo, a continuacion podemos ver un array que consta de una unica
dimension:

var oneDimension = new Array( "This", "That", "The Other") ;

Podemos establecer una referencia a cualquier elemento utilizando un conjun-


to de parkntesis y el indice correspondiente, como oneDimension [ 0 ] . En un
array multidimensional, cada elemento es un array compuesto por una serie de
elementos:

var twoDimension =
new Array (
new Array(1,2,3),
new Array ( 4 , 5 , 6 ) ,
new Array (7,8,9 )
);

En este momento twoDimension [ 0 ] se refiere a A r r a y ( 1,2, 3 ) en h g a r de a


una cadena como Esta. Si queremos establecer una referencia para 10s valores 1,
2 6 3, tendremos que utilizar un segundo conjunto de parkntesis, como vemos
a continuacion:

twoDimension[O] [O] // Referencia a 1


twoDimension[O] 111 // Referencia a 2
twoDimension[O] 12.1 // Referencia a 3
twoDimension[ll [O] // Referencia a 4
twoDimension[Zl [l] // Referencia a 5
twoDimension[3] [21 // Referencia a 6

La verdad es que utilizo el tkrmino multidimensional con un poco de cuidado


porque tkcnicamente, JavaScript no puede crear este tipo de arrays. Unicamen-
te 10s imita.
Volvamos ahora a1 script. A cada una de las variables que se encuentran decla-
radas en las lineas 11-68 se le asigna un array de informacion, tal y como se
describe en la tabla 7.1.
Preferencias del usuario basadas en cookies 243

Tabla 7.1. Variables e inforrnacion sobre la pagina del usuario

Nombre del array Contiene


newsNames Los nombres y 10s URL de 10s recursos financieros
online.
indexNames Los nombres y 10s URL de 10s indices bursatiles
online.
strategy Nombre y tipo de la estrategia de inversion que
clasifica a 10s usuarios.
background Los nombres y 10s URL de las imagenes de fondo.
face La imagen y 10s nombres de las familias de fuentes.
size La imagen y 10s nombres de 10s tamafios de las
fuentes.
alllmages Un array vacio que se utilizara mas tarde para
guardar las imagenes que se cargaran antici-
padamente. De esta forma se facilita su acceso.
i mageNames Un array que contiene 10s controladores de las
imagenes. Estas cadenas ayudan a cargar
anticipadamente las imagenes.

Tal y como indica el tCrmino array multidimensional, cada elemento del array
es un array de dos elementos. Basicamente, un elemento contiene la cadena que
se mostrara en la pantalla y el otro la cadena que se encargara del trabajo que se
ejecutara en el segundo plano. Por ejemplo , s i z e [ 0 I [ 0 I es igual a 10. Este va-
lor se usara para establecer el tamafio, en pixeles, de la fuente. Pero, s i z e [ 0 1 [ 1I
es igual a Small, que es la descripci6n que aparecera en la pantalla. A 10s usua-
rios les resultara mas sencillo asociar el tamafio de la letra a travks de "small"
(pequefio) que del valor 10. En el resto de arrays el funcionamiento es practica-
mente el mismo.
Las lineas 56-57 no son otra cosa que llamadas a1 metodo s o r t ( ) del objeto
Array para que ordene 10s indices bursatiles y 10s enlaces de noticias. No es ne-
cesario, pero un par de lineas mas no molesta a nadie. El archivo prefs.html uti-
liza una serie de graficos, por lo que seria conveniente cargarlos de nuevo. Las
lineas 70-73 se encargan de ello:

for (var i = 0; i < imageNames.1ength; i + + ) {


allImages[il = new Image();
allImages[i].src = imagepath + imageNames[i] + '.gif';
1
244 Preferencias del usuario basadas en cookies

Todos 10s elementos de imageNames son cadenas. Cuando se concatenan estos


controladores de imageries entre la variable imagepath y la cadena .gif, permiten
que se ejecuten todos 10s elementos de irnageNarnes y carguen todo lo que vaya
a hacer falta. Por cierto, no vayamos a pesar que a 10s controladores de las ima-
genes se les nombra de cualquier forma. Siguen u n convenio que veremos mas
adelante.
Si revisamos el resto del codigo que se encuentra dentro de las etiquetas SCRIPT,
veremos que todo se define como si fuesen funciones. Por lo tanto, cualquier
parte del c6digo que se ejecute, se invocara desde otro punto. Esto ocurre un par
de veces:
Durante la carga del contenido del HTML.
En el controlador de eventos o n b a d en la etiqueta BODY.

Vamos a centrarnos en el codigo HTML que se encuentra situado entre las li-
neas 174-294. El codigo se encuentra en el ejemplo 7.1 .

Forma de las preferencias


La interfaz es un formulario que contiene un campo de texto, una casilla de
verificacion y una lista de elementos a traves de 10s cuales 10s usuarios podran
establecer sus preferencias. La creacion de campos de texto es un problema (por
lo menos en este caso) de programacidn y de establecer 10s nombres deseados. Lo
vemos en las lineas 195, 199 y 212 que son las que estan dedicadas a1 nombre,
edad y ocupacibn.
Lo siguiente que se decidira sera el tip0 de estrategia de inversion que utilizara
el usuario. Las categorias van desde muy conservativo ("Stingy")a muy agresi-
vo ("Willing to Sell Mother"). El no tendra que rellenar ningun texto como hizo
antes, sino seleccionar la opcion deseada a traves de una lista. En vez de codifi-
car cada una de las opciones de la etiqueta OPTION, podemos usar una funcidn
de JavaScript para escribir las listas sobre la marcha. La funcion genselect ( )
resultarh especialmente util en las lineas 2 0 5 , 2 4 7 - 2 4 9 , 2 6 2 y 276. La linea 205
se encarga de la lista de estrategias de inversion, mientras que las otras llama-
das se encargaran de las listas del fondo, tip0 y tamaiio de la fuente. Veamos
genSelect ( ) en las lineas 86-95:

function genSelect(name, select, onChangeStr) {


var optStr = " " ;
var arrObj = eval(name);
for (var i = 0; i < arr0bj.length; i++) {
optStr += '<OPTION VALUE="' + arrObj[il[Ol +
(i == select ? SELECTED' : "")
I " + ' > ' + arrObj[il[ll;
1
Preferencias del usuario basadas en cookies 245

r e t u r n '<SELECT NAME="' + name + + (onChangeStr ?


"'I IonChange="' +
onChangeStr + : ")
l ; l l I + + optStr + '</SELECT>';
' > I

Ya hemos visto una funci6n genselect ( ) parecida en el capitulo 5. En aquel


caso, se encargaba de la generaci6n de listas de seleccion basandose en el us0 de
cadenas. Esta version genera etiquetas OPTION que no se basan en numeros,
sin0 en un array de elementos. Vamos a ver la llamada a genselect ( ) de la
linea 205:

document.write(genSelect('strategy', 3 ) ) ;

La funci6n recibe dos argumentos: la cadena de la estrategia y el numero 3.


Podemos pensar: "Un momento. Hay un array llamado strategy. iPor quk le
pasa una cadena con el mismo nombre?"
Si, ya hay un arraystrategy. Parece logic0 pasar una copia suya, except0 por el
hecho de que cada lista de seleccion necesita un nombre para que se puedan
establecer referencias a ella a la hora de configurar las preferencias del usuario.
Para simplificar las cosas, cada lista de seleccion toma el nombre del array que
tiene asociado.
A continuacion, a la variable arrobj se le asigna el valor de la cadena de refe-
rencia. Es decir, que eval (name)es igual a eval ( ' strategy ' ) que, a su vez,
equivale a1 arraystrategy. Ahora, genselect ( ) tiene un array (arrObj)que hace
las funciones de referencia para 10s elementos de OPTION y un nombre (name)
que asignara a la lista de selecci6n.
El segundo argumento que se pasa es un entero, que se le asigna a select. Este
valor determina la opci6n predeterminada. Si i es igual a select entonces, se aso-
ciara la etiqueta OPTION con selec,t que tendrii el atributo SELECTED. Si select es
igual a 0, se tomarii la primera opciijn como predeterminada. Si es igual a 1, se
tomara la segunda opcion, y asi sucesivamente. Lo podemos ver en la linea 91,
en la expresi6n condicional:

(i == select ? "' SELECTED' : ' " ' )

Despub de repetir esta acci6n con todos 10s elementos de strategy y de cons-
truir optStr con todas las etiquetas OPTION, una sencilla suma (que tiene lugar
en las lineas 93-94) se encarga de colocar todas las etiquetas OPTION con las res-
pectivas SELECT.
Y iqut ocurre con el tercer argumento que se deberia recibir, tal y como se ha
definido en genselect ( ) ? A estos elementos se les llama onChangeStr y no se
utilizan en esta llamada. Per0 lo verernos en otras ocasiones, por ejemplo en las
lineas 247-249:
246 Preferencias del usuario basadas en cookies

document.write(genSelect('background',0 ,
swapImage ( ' bkgImage ,
this.options[this.selectedIndexl.value)")
);

Dentro de esta llamada, se asigna el nombre name de un argumento a la cadena


background, a sekct el valor 0, y a1 argumento onChangeStr se le asigna el valor
swapImage('bkgImage',this.options[this.selectedIndex].value).
Cuando genselect ( ) recibe un argumento para onChangeStr,la cadena se
aiiadirh a1 controlador de eventos onchange en lo que en breve constituira una
lista de seleccion. En cualquier otro caso, se aiiadira el controlador de eventos
onchange. La lista de las lineas 247-249, 262 y 276 utilizan el controlador de
eventos onchange para cambiar a la imagen que se corresponda con la opcion
seleccionada.
Las listas de seleccion no son 10s unicos elementos que se crean sobre la marcha
con JavaScript. La funcion genBoxes ( ) crea dos grupos de casillas de verifica-
cion, una para 10s nuevos vinculos y otra para 10s indices cuyas llamadas tie-
nen lugar en las lineas 224 y 232.
El contenido de genBoxes ( ) lo tenemos en las lineas 97-104:

function genBoxes(name) {
var boxStr =
var arrObj = eval(name);
for (var i = 0; i < arr0bj.length; i++) {
boxStr += '<INPUT TYPE=CHECKBOX NAME="' + handle + i + VALUE="' +
' I '

arrObj[i] [ O ] + ' , ' + arrObj [il 111 + "'> ' + arrObj[il [O] + '<BR>'
1
return boxStr;
1

Aqui, todo se desarrolla de forma similar a como ocurria en genselect ( ) . La


cadena equivalente a1 array deseado se pasa como una referencia a dicho array.
La repeticion que tiene lugar con 10s distintos elementos, genera una cadena de
casillas de verificacion, que se escribiran en el documento final.

Carga de las preferencias almacenadas


Una vez que se ha cargado todo el codigo HTML, se podran presentar las op-
ciones del usuario (si tiene alguna). El controlador de eventos onLoad que se
encuentra en la etiqueta BODY invoca a la funcion populateForm ( ) y la entre-
ga en forma de una copia vacia del formulario. Lo podemos ver en las lineas
157-164:

function populateForm(form0bj) {
if (getPrefs(form0bj)){
Preferencias del usuario basadas en cookies 247

makePath(form0bj):
swapImage('bkgImage',form0bj.background.options
[formObj.background.selectedIndex].value):
1
else { resetImage(document.forms[O]): }

PopulateForm ( ) es un administrador de funciones que se encarga de llamar


a las que haran el trabajo. Su funcionamiento es el siguiente: si se ha configu-
rado anteriormente alguna preferencia, rellena 10s campos apropiados del for-
mulario basandose en la informacion que se encuentra dentro del archivo cookie.
Luego determina el fondo y el tip0 de letra. Utilizara las que se determinan en la
etiqueta OPTION tags de la seccion correspondiente a la configuracion de la pan-
talla. Si no se ha establecido ninguna preferencia, entonces no hace nada. La fun-
ci6n PopulateForm ( ) comprueba que haya alguna preferencia dentro del archivo
cookie. Para ello usa la funcion getprefs ( ) , que vemos en las lineas 106-123:

function getPrefs(form0bj) {
var prefStr = GetCookie('userPrefs'):
if (prefstr == null) { return false: )
var prefArray = prefStr.split('-->'):
for (var i = 0 ; i < prefArray.1ength: i++) {
var currPref = prefArray[il.split('::'):
if (currpref[l] == "select") {
formObj[currPref[O]].selectedIndex = currPref[21:
}
else if (currpreflll == "text") {
formObj[currPref[Oll.value = currPref[21:
1
else if (currpref[l] == "checkbox") {
formObj[currPref[0ll.checked = true:
1
1
return true:
1

getprefs ( ) tambien tiene que tomar una decision. Funciona asi: obtiene la
informacion que se encuentra dentro del archivo cookie que estC asociado con el
nombre userprefs. Si el valor es cero, devolvera false.Es decir, que aun no se
habra establecido la propiedad document.cookie de userPrefs. Per0 si userPrefs
no es igual a cero, entonces habra alguna configuracion. En nuestro ejemplo con-
creto, userPrefs es igual a cero, per0 conviene repasar quC ocurre cuando userPrefs
contiene informacion.
Si userPrefs contiene la informacion deseada, getprefs ( ) crea u n array divi-
dendo el valor de prefStr utilizando el separador que se haya acordado para unir
cada uno de 10s elementos de la configuracion de setprefs ( ) . L a cadena es - ->.
248 Preferencias del usuario basadas en cookies

Ahora, 10s elementos de prefsArray contienen una cadena separada por : : que
indica el tip0 del elemento y el valor que tiene asociado. Para asignar 10s valores
asociados a 10s elementos no hay mas que repetir la ejecuci6n de la funcion a
traves de 10s elementos prefsArray y asignar cada uno de ellos de acuerdo con la
clase del elemento del formulario. La verdad es que las lineas 110-12 1 lo expli-
can bastante mejor:

for (var i = 0; i < prefArray.length; i++) {


var currPref = prefArray[i] .split ( ' : : ' ) ;
i f (currPref[l] == "select") {
formObj[currPref[O]].selectedIndex = currPrefL21;
1
else if (currpref [l] - --_ "text") {
f ormOb j [ currPref [ 0 I .value = currPref [21 ;
1
- _ "checkbox") {
else i f (currpref [l] --
formObj [currPref[O I .checked = true;
}
1

No olvidemos que existen tres formas diferentes de establecer las preferencias


de 10s usuarios:

Seleccionar una opci6n de una lista.


Introducir una cadena en u n campo de texto.
Activar una casilla de verificacion.

Por lo tanto, 10s valores de prefsArray contienen u n identificador de tipo para


10s elementos del formulario text, checkbox, o select-one y otra cadena que repre-
senta el valor del elemento del formulario que tiene : : como separador. En un
momento lo veremos mas claro. La siguiente lista muestra algunos ejemplos de
elementospref sArray.

s t r a t e g y : : s e l e c t : : 0: Indica que el elemento del formulario llamado


strategy es una lista de seleccion con O P T I O N 0.
ElelementodenominadonewsNamesO: :checkbox: :Barron ' s O n l i n e ,
h t t p : / /www. b a r r o n s . corn/, nos indica que el elemento del formulario
newsNames0 es una casilla de verificacih cuyo valor esBarron ' s On1i n e ,
http://www.barrons.com/.
i n v e s t o r : : t e x t : : J e r r y Bradenbaugh: Nos indica que el elemento del
formularioinvestor es un campo de texto con el ValorJerry Bradenbaugh.

Del mismo mod0 que el buclefor de la linea 110 se ejecuta con todos 10s ele-
mentos de prefsArray, el valor de la variable local c u r r p r e f sera un array que
Preferencias del usuario basadas en cookies 249

estara dividido por pref sArray [ i 1 cada vez que aparezcan 10s simbolos : :. Es
decir, que currPref tendra tres elementos (dos para las casillas de verificacion).
Como currpref [ 1I contiene el identificador de tip0 para el elemento del formu-
lario, a1 activarlo se determinara quC hace la getpref s ( ) con curpref [ 0 I y
currpref [ 2 1 .
Si currPref [ 11 es igual a select, getpref s ( ) utiliza la linea 113 para asignar
la lista de seleccion nombrada en currpref [ 0 I , a la opcion que esta asociada
con selectedlndex en currpref [ 2 1 . En realidad es parseInt (currpref[ 2 3 ) ,
per0 como JavaScript lo conoce, convierte la cadena en un numero.
Si currPref [ 1 3 es igual a t e x t , getpref s ( ) utilizara la linea 116 para asig-
nar a1 campo de texto a quiCn se hace referencia en currpref [ 0 I , el valor de
currPref [2I .
Y por ultimo, si currpref [ 1 1 es igual a checkbox, getpref s ( ) trabajara con
la linea 119 para asignar a la propiedad checked de checkbox de currpref [ 0 I ,
a true. En este caso no habra currpref [ 2 ] . Si la informacion que se encuen-
tra en el archivo cookie contiene la casilla de verificacion, Csta sera la unica in-
dicacion que se utilizara.
Esto que hemos visto se repite con cada elemento de prefshray. En el momento
en que se termina, el usuario tendra un formulario que reflejara la informacion
de su ultima configuracion. Por lo tanto, getpref s ( ) habra completado su tra-
bajo y devolvera el valor true para indicar apopulateForm ( ) que lo completo
satisfactoriamente.

Preparar las imagenes


Ahora tendremos que ajustar el fondo y las fuentes de acuerdo a las opciones
que se han seleccionado en el formulario. ObsCrvese que 10s atributos SRC de 10s
graficos que se encuentran en el codigo HTML son images/bZank.g$ Se trata de
un separador transparente que se utilizara hasta que se amolde la imagen con
la informacion que tenga el archivo cookie sobre el formulario. La funcion lla-
mada populateForm( ) se encarga de todo. Lo vemos en las lineas 159-161:

makePath(form0bj);
swapImage('bkgImage',
formObj.background.options[formObj.background.selectedIndexl .value);

Aparte de su reputacion como controlador de funciones, populateForm ( ) lla-


ma a makepath ( ) y a swapImage ( ) para que se encarguen de la secuencia de
imagenes. En realidad, swapImage ( ) es la unica funcion que tiene algo que ver
con las secuencias de imagenes; makepath ( ) se limita a manilpular un par de
cadenas con las que construye la ruta que enviara a swapImage ( 1 . Vamos a1
250 Preferencias del usuario basadas en cookies

mas sencillo de 10s dos, la llamada a swapImage ( 1 . Una vez que sabemos que
swapImage ( ) espera un par de argumentos, que se le entregaran en las lineas
160-161, miraremos el c6digo de las lineas :

function swapImage(imageName, imageBase) I


document[imageNamel.src = imageBase;
1

El argumento imageName representa el nombre del objeto Image cuya fuente se


utilizara en la secuencia. El argumento imageBase constituye un URL de la ima-
gen que se quiere utilizar en la secuencia. Vamos a ver a continuaci6n el argu-
mento que se esta pasando:

formObj.background.options[formObj.background.selectedIndexl.value

Se trata de u n argumento realmente grande, per0 no es otra cosa que el valor


de la etiqueta OPTION seleccionada. Como getPref s ( ) acaba de asignar a las
listas de seleccion las opciones utilizadas en la ultima configuracion del usuario,
tendra que haber alguna imagen. Para hacernos una idea mejor, vamos a obser-
var el contenido de la tabla 7 . 2 . Tiene el valor de la etiqueta OPTION, su texto (el
que ve el usuario en la pantalla) y el argumento que pasara a swapImage ( ) .

Tabla 7.2.Opciones de Background

Valor de OPTION Texto de Argument0 para


OPTION swapImage( )
images/goldthumb.gif Gold Bars images/gold t humb.gif
irnages/billsthumb.gif Dollar Bills images/billsthumb.gif
images/'stthumb.gif Fist of Cash irnages/fist thumb.gif
images/currency 1 th urnb.gif Currency 1 i mages/cu rrency 1t hurnbgif
irnages/currency2 thumb.gif Currency 2 images/currency2thumb.gif

Parece muy sencillo. swapImage ( ) recibe el valor de OPTION. Como es un URL,


nos encontraremos con una secuencia. Es el mismo codigo que se utiliza cuando
el usuario modifica la opci6n de la lista correspondiente a la imagen del fondo.
Las secuencias son identicas. Este es el codigo HTML que se genera cuando se
carga la pagina. Estos dos argumentos van a parar a swapImage ( 1, tal y como
vimos en la linea 16 1 :

<SELECT NAME="background" onChange="swapImage( bkgImage ,


this.options[this.selectedIndexl.value);"~
Preferencias del usuario basadas en cookies 251

Ya tenemos la imagen del fondo. iQuC hay del grafico? Es algo mas complica-
do. Con ella tendremos una imagen de la secuencia que sera la que se utilice
para reflejar la opcion seleccionada. Con la imagen de la fuente, aun teniamos
una imagen que se podia utilizar en la secuencia, per0 Csta se basaba en las eti-
quetas OPTION de las dos listas de seleccion. Vamos a ver como se crea el URL de
las imagenes. La tabla 7 . 3 muestra 10s valores de OPTION y el texto correspon-
diente de las dos listas de seleccion.
Tabla 7.3. Opciones correspondientesa la familia y tamaiio de la fuente

Valor de OPTION Valor de OPTION Valor de OPTION Valor de OPTION


correspondiente correspondiente correspondiente correspondiente
a1 tip0 de la a1 tamaiio de la a1 tip0 de la a1 tamaiio de la
fuente fuente fuente fuente
Timesroman 10 Times Roman Small
Aria1 12 Aria1 Medium
Courier 14 Courier Large
Tahoma 16 Tahoma X-Large

Vamos a ver quC ocurre cuando se combinan 10s valores de OPTION. Veamos
todas las combinaciones posibles.

timesromanlo aria110 courierl 0 tahoma10


timesroman12 arial 12 courierl 2 tahomal2
timesroman 14 arial 14 courierl 4 tahoma14
timesroman 16 arial 16 courierl 6 tahomal6
iNo se parecen a 10s elementos utilizados para cargar de antemano las image-
nes en el array irnageNames de la linea 61? Pues si. Parece que despuks de todo la
aplicacion va a estar bien. Ahora tenemos que construir el URL. Estara com-
puesto de varias cosas. Aun tendremos que llamar a la funcion swapImage ( ) ,
per0 antes de hacerlo, tendremos que utilizar las combinaciones de la tabla an-
terior para crear el URL. En este punto entra en juego la funcion makepath ( ) .
populateForm ( ) establece la llamada en la linea 159. Per0 el meollo de la cues-
tion lo tenemos en las lineas 75-80:

function makePath(form0bj) (
var fontName = imagepath +
formObj.face.options[formObj.face.selectedIndex~.value +
formObj.size.options[formObj.size.selectedIndexl.value + '.gif';
swapImage ("fontIrnage", fontName) ;
1
252 Preferencias del usuario basadas en cookies

makepath ( ) acepta una copia del objeto del formulario como argumento. Se
trata de f ormObj cuyas referencias hemos visto ya en las dos etiquetas OPTION
seleccionadas. A continuacion se afiade .gv. Ahora, la variable f ontName sera
una cadena que apunta a la imagen correcta. La llamada a swapImage ( 1 que
tiene lugar en la linea 79 se encarga de todo. Obviamente, estamos dando por su-
puesto que el usuario ya ha establecido algun tip0 de preferencias con anterio-
ridad. SigetPrefs ( devuelve false,en la linea 163populateForm() llamara
a la funcion resetImage ( ) para que se haga cargo de la secuencia de las ima-
genes de fondo, que estan asociadas con la etiqueta O P T I O N 0 de las listas de se-
leccion de fondo y de fuente. En la proxima seccion veremos mas detalles.
Vamos a recapitular lo que hemos visto:

Las funciones genselect ( ) y genBoxes ( ) se han encargado de escribir


en la pagina 10s elementos del formulario.
Con todos 10s elementos de getPrefs() se ha utilizado la informacion alma-
cenada en el archivo cookie.
Se ha ajustado el valor de las imagenes del fondo y de las fuentes con el
contenido de las etiquetas OPTION.Para ello se han utilizado las funciones
swapImage ( ) y makepath ( 1 .

Efectuar cambios
Ahora el usuario tiene en pantalla las opciones que selecciono la ultima vez.
Vamos a ver quC pasa si quiere establecer algun cambio.
Al usuario le resultara muy sencillo modificar el contenido de la pagina. Se li-
mitara a introducir informacion en 10s campos de texto o seleccionar alguna
opcion de las listas. Cuando termine, hara clic en el boton "Save".En este momen-
to sera cuando empiece nuestro trabajo. Vamos a ver el cbdigo que se esconde
detras del b o t h "Save" en la linea 2 8 2 :

<INPUT TYPE=BUTTON VALUE="Save" onClick="setPrefs (this.form) ; " >

Pare muy tipico. Llama a la funcion setpref s ( ) y pasa una copia del formu-
lario en forma de argumento. L o divertido comienza en las lineas 125-151:

function setPrefs(form0bj)
var prefStr = " ;
var htmlstr = " ;
for (var i = 0; i < form0bj.length; i++) (
if (formObj[il .type == "select-one") I
prefStr += formObj[i].name + '::select::' + formObj[i].
selectedIndex + ' - - > ' ;
htmlStr += formObj[i].name + ' = ' + formObj[il.options[formObj[i].
Preferencias del usuario basadas en cookies 253

selectedIndex1.value + ' - - > I ;

1
else if (formObj[i].type == "text") {
if (formObj[il.value == " ) { formObj[il.value = "Not Provided";
1
prefStr += formObj[il.name + '::text::' +
safeChars(formObj[i].value) + I - - > ' ;

htmlstr += formObj[i].name + + formObj[il.value +


I = ' ' - - > I ;

1
else if (formObj[i].type == "checkbox" && formObj [i].checked) {
prefStr += formObj[i].name + '::checkbox::' + ' - - > I ;

htmlStr += formObj[i].name + ' = ' + formObj[i].value + ' - - > I ;

}
1
SetCookie('userPrefs',prefStr, expiry);
SetCookie('htmlPrefs', htmlstr, expiry);
if (confirm('Preferenceschanged. Go to your personalized page?')) {
self.location.href = "dive.htm1";
1
1

setPref s ( ) genera dos cadenas. A una le asigna la variable 1ocalprefStry a la


otra la variable htrnZStr. En realidad necesitaremos dos cookies. Una para publi-
car la informacion de la configuration del usuario y otra para generar el disefio
de 10s vinculos de dive.htm1, la pagina personalizada. La informacion que se ha
guardado es practicamente idCntica a la ya existente, con la unica salvedad de
que cada una de ellas tiene su propia forma de representar 10s datos. Lo veremos
dentro de u n momento. En 10s siguientes puntos veremos la forma de trabajar de
setPrefs0:

1. Se ejecuta a traves deformobj, construye dos cookies basandose en 10s ele-


mentos FORM.
2. Se escriben las dos cookies con la informacion del usuario.
3. Se le ofrece a1 usuario la posibilidad de revisar el contenido de dive.htrnZ y
ver 10s cambios introducidos en accion.

Paso 1: repeticion a travCs de formObj


N o supone ningun problema. Desde el principio del libro hemos estado traba-
jando con repeticiones deformObj similares a esta. Vamos a usar una vez mas lo
mismo que hemos visto en capitulos anteriores, a exception de setpref s ( ) a1
que dedicaremos algo mas de tiempo.
Vamos a centrarnos de nuevo en las preferencias. ObsCrvese que cada uno de
10s elementos (ademas de 10s botones que aparecen en la parte inferior de la
pantalla) es un campo de texto, una lista de seleccion o una casilla de verifica-
ci6n. Por lo tanto, setpref s ( ) tan solo necesitara conocer el tiempo de accion
254 Preferencias del usuario basadas en cookies

que ha de desarrollar cuando f ormObj [ i ] sea uno de estos elementos. El codi-


go de las lineas 1 2 9 - 1 4 4 sigue esta forma de trabajar.
if (formObj[il .type == "select-one") {
prefStr + = formObj[il.name + '::select::' +
formObj[il.selectedIndex+ ' - - > I ;

htmlStr += formObj[il.name + ' = ' +


+
formObj~il.options~formObj[il.selectedIndexl.value I - - > ' ;

1
else if (formObj [il .type == "text") (
i f (formObj[il.value == " ) { formObj[i].value = "Not Provided"; 1
prefStr += formObj[i].name + '::text::' +
safeChars(form0bj [i].value) + ' - - > I ;

htmlStr += formObj[i].name + ' = ' + formObj[il.value + ' - - > I ;

1
else if (formObj [il .type == "checkbox" & & formObj [i].checked) {
prefStr += formObj[i].name + '::checkbox::' + ' - - > ' ;
htmlStr += formObj[i].name + ' = ' + formObj[i].value + ' - - > I ;

Una de las propiedades de 10s elementos del formulario con la que se esta tra-
bajando es type, que contiene una cadena la cual identifica el tip0 de elemento.
setpref s ( ) solo tendra que localizar a uno de ellos: texto, casilla de verifica-
cion u opcion de una lista. La composicion de la declaracion i;f que hemos visto
en el codigo anterior desarrolla una acci6n distinta para cada tip0 de accion.
Independientemente del tipo, se ejecutara setpref s ( ) de una de estas formas:
Suma el nombre del elemento del formulario, la cadena equivalente a1 tip0
del elemento y posiblemente, el valor o el indice seleccionado. Entre ellos se
colocara un separador.
A dicha cadena le suma un valor existente de prefstr y de htmlstr.

El tip0 del elemento es una lista de selection, se aiiadira el indice de la lista a la


cadena. Si el elemento es una casilla de verificacion, se aiiadira el nombre de
dicha casilla. Si el elemento es un campo de texto, su nombre y valor se aiiadi-
rAn a las cadenas. ObsCrvese que la funcion safechars ( ) trabaja con el valor
de todos 10s campos de texto. Esto es asi, porque 10s valores asociados a las
listas y a las casillas de verificacion estan predeterminados. Como el usuario
puede introducir cualquier cosa, es posible que escriba como parte de la cadena,
u n caracter que se utilizara como delimitador (es este caso, : :, -->, y =). Si la
aplicacion intenta analizar la informacion de la cookie, generara resultados
imprevistos y erroneos. L a funcion se encuentra en las lineas 153-155:

function safeChars(str) {
return str.replace(/::I=l-->/g, .,
# . . I 1;
1
Preferencias del usuario basadas en cookies 255

La funcion saf eChars ( ) se limita a eliminar las cadenas reservadas de la in-


formation que introduce el usuario. Cada cadena nombre/tipo o valor o indice
seleccionado, se acotara con el simbolo - ->. Cada seccion de la cadena de la va-
riable prefStr vendra delimitada por : : . htmZStr utiliza =. No se dice en ningun
sitio que estos simbolos tengan que ser delimitadores, per0 10s utilizamos fun-
damentalmente por su sencillez. A continuacion, podemos ver u n ejemplo del
aspecto que mostraran cuando se construyan las cadenas dentro del mismo
formulario.
prefStr mostrara el siguiente aspecto:

invest0r::text::Not Provided-->age::text::Not Provided-->strategy::


select::3-->occupation::text::NotProvided-->newsNamesO::checkbox::-->
newsNamesl::checkbox::--~newsNames2::checkbox::-->newsNames4::checkbox::
-->indexNamesO::checkbox::-->indexNamesZ::checkbox::--
>background::select
::2-->face::select::3-->size::select::2-->

htmZStr sera asi:

investor=Not Provided-->age=Not
Provided-->strategy=Moderate-->occupation
=Not Provided-->newsNamesO=Barron's Online,http://www.barrons.com/-->
newsNamesl=CNN Interactive,http://www.cnn.com/--~newsNames2=FoxNews,
http://www.foxnews.com/-->newsNamesl=The Wall Street Journal,
http://www.wsj.com/-->indexNamesO=DowJones Indexes,
http://www.dowjones.com/-->indexNamesa=TheNew York Stock Exchange,
http://www.nyse.com/-->background=images/fistthumb.gif-->face=tahoma-->
size=14-->

No olvidemos que - -> se encarga de separar unas entradas de 10s elementos de


otras, tanto en prefStr como en htmlStr, mientras que :: y = separa las dos
partes de cada elemento en dos variables respectivamente. En nuestro codigo,
las lineas 286-289 eran las encargadas de esta separation. Los botones adicio-
nales nos permiten mostrar 10s valores de las variables cuando se utiliza "Show"
y eliminar la information de la cookie cuando se hace clic en "Erase."A 10s usua-
rios no les haran falta, per0 a nosotros nos seran de gran ayuda cuando tenga-
mos que depurar la aplicacibn.
No nos asustemos ante el aspecto del codigo anterior. Analizaremos el conteni-
do de la variable htrnlStr cuando veamos el archivo dive.htmZ. Afortunadamen-
te, ya hemos analizado el valor deprefStr con la funcion getpref s ( ) . Conviene
revisar el contenido de dicha funcion. Nos ayudara a comprender mejor como
se desarrolla todo aqui si repasamos como guarda la funcion setpref s ( ) la in-
formation dentro de la cookie y la manera en que getpref s ( ) analiza dichos
datos.
256 Preferencias del usuario basadas en cookies

Paso 2: escribir la informacion en 10s archivos cookie


Una vez que se han cargado las preferencias del usuario en prefStr y htrnZStr, se
ejecuta el c6digo de las lineas 146-147 que es el encargado de invocar a la fun-
ci6n Setcookie ( ) para que guarde dicha informacion en la cookie.
Netscape Navigator guarda la informacion relativa a la cookie en u n archivo
llamado cookies.txt. Este es u n extracto del archivo que tengo en mi sistema:

.hotwired.com TRUE / FALSE 2145917529 p-uniqid


2sfurM4NNMfDKAqQBA
.hotbot.com TRUE / FALSE 946739221 p-uniqid
3MarneJsXGwNqxWbFA
www.allaire.com FALSE / FALSE 2137622729 CFTOKEN 97611446

Por otro lado, MSIE 4.x15.x, guarda la informacion de cada cookie dentro de
un archivo independiente. Los nombres de dichos ficheros se corresponden con
el del dominio de donde procede la informacion y con el nombre del usuario que
determino las preferencias. Este es un extracto del contenido de 10s archivos
cookie que tengo en mi sistema WinNT . Estoy registrado como administrador.

Cookie:administrator@altavista.com
Cookie:administrator@amazon.com
Cookie:administrator@builder.com
Cookie:administrator@cnn.com
Cookie:administrator@dejanews.com
Cookie:administrator@hotbot.com
Cookie:administrator@infoseek.com

Paso 3: ofrecer a 10s usuarios la posibilidad de ver 10s nuevos cambios


La ultima tarea que desarrolla la funcion setprefs ( ) es dirigir a1 usuario a1
archivo dive.htrnl para que vea el efecto que han tenido sus modificaciones so-
bre el diseiio de la pAgina. El codigo se encuentra en las lineas 148-150.

if (confirm('Preferences changed. Go to your personalized page?')) {


self .location.href = "dive.htm1";
1

Es parte de la finalidad de prefxhtrnl. Per0 aun hay u n elemento mas que casi
siempre se suele olvidar: borrar el formulario.

Borrar el formulario
LNo podriamos tener u n boton <INPUT TYPE=RESET> que se encargase de esto?
Pues si. Un boton que se encargue de asignar a 10s campos de texto una cadena
Preferencias del usuario basadas en cookies 257

vacia, de desactivar todas las casillas de verificacion y de seleccionar el boton de


opcion OPTION 0 de cada lista. Todo esto esta muy bien, pero si nos fijamos, no
se han tocado ni la imagen de fondo ni las fuentes. En ambos casos se ha de
devolver la imagen y fuente asociada a OPTION 0. Esta es la explicacion de por
quk el contenido de la linea 283 es asi:

<INPUT TYPE=RESET VALUE="Clear" onClick="resetImage (this.form) ; " >

No se limita solo a "limpiar" el formulario, sino que tambikn llama a la funcion


resetImage ( ) . Aqui tenemos el contenido de las lineas 166-170.

function resetImage(form0bj) {
swapImage('bkg1mage'. form0bj.background.options[01 .value);
swapImage('fontImage', imagepath + formObj.face.options[Ol .value +
formObj.size.options[Ol.value + '.gif');
}

Se trata de una funcion que invoca a otra funcion. En realidad, llama dos veces
a swapImage ( ) . Con la primera llamada cambia la imagen del fondo y utiliza
la imagen que se encuentra asociada a OPTION 0, la cual tambikn es conocida
como f ormObj .background. opt ions [ 0 I .value.En la siguiente llamada hace
otra vez lo mismo, per0 crea la ruta de la imagen asociada a OPTION 0. Se pa-
rece a makepath ( ) , resetImages ( ) establece el URL de la imagen valitndose
de la variable imagepath, 10s valores OPTION correspondientes a las dos listas
relacionadas con las fuentes (solo que en esta ocasidn se utiliza el 0 en lugar de
sekctedlndex), seguido de la cadena .g$ Con estas dos llamadas reiniciamos el
valor de las dos imageries.
Y con esto terminados de repasar todas las funciones de prefs.htm2. Vamos a
pasar ahora a dive.htm1.

dive.html
Las preferencias del usuario han cambiado. Ha llegado el momento de conver-
tir esos cambios en una realidad visual. El proceso no es demasiado largo, per0
lo detalles nos pueden dar algun dolor de cabeza. Parece obvio que la informa-
cion que se utilice procedera del archivo cookie. Esta informacion se utilizara de
tres formas distintas.

Como un URL para la imagen que se utilizara como fondo.


Como una serie de URL para el texto y 10s vinculos.
Como parte de la declaracion de la hoja de estilos donde se declara el tamaiio
y el tipo de fuente.
258 Preferencias del usuario basadas en cookies

Vamos a revisar a continuacion el contenido correspondiente a1 ejemplo 7.2, el


codigo de dive.h tml.

Ejemplo 7.2.dive.htm1
1 <HTML>
2 <HEAD>
3 <TITLE>
4 Your Take-A-Dive Links Page
5 </TITLE>
6 <SCRIPT LANGAUGE='' JavaScript1.2 SRC= cookies.js >< /SCRIPT>
I' 'I

7 <SCRIPT LANGUAGE="JavaScript1.2">
8 <!--
9
10 var newsNames = new Array ( ) ;
11 var indexNames = new Array();
12
13 function getAttributes0 (
14 var htmlStr = GetCookie('htm1Prefs');
15 if (htmlStr == null) {
16 alert('We1come. You must first set your user preferences.' +
17 'Please choose OK to load the User Settings page.');
18 self.location.href = 'prefs.htm1';
19 1
20 var htmlArray = htmlStr.split('-->') ;
21 for (var i = 0; i < htmlArray.length; i++) {
22 var tagInfo = htmlArray[il .split('=');
23 if (tagInfo[Ol ! = '"') {
24 if (tagInfo[O].indexOf('newsNames')== 0 ) {
25 newsNames[newsNames.lengthl = tagInfo[ll;
26 1
27 else if (tagInfo[O].indexOf('indexNames')== 0 ) {
28 indexNames[indexNames.lengthl = tagInfo[ll;
29 }
30 else eval(tagInfo[Ol + ' = " ' + tagInfoLl1 + ""); 1
31 1
32 }
33 1
34
35 getAttributes0;
36
37 function genLinks(1inkArr) {
38 var linkStr = " ;
39 for (var i = 0; i < 1inkArr.length; i++) {
40 var linkparts = linkArr[il.split(',' )
41 linkStr += '&nbsp; &nbsp; - <A HREF="' + linkParts[ll + "'> '
42 + linkParts[O] + '</A><BR>'
43 1
44 return linkstr;
45 1
46
Preferencias del usuario basadas en cookies 259

47 //-->
48 </SCRIPT>
49 <SCRIPT LANGUAGE="JavaScriptl.2">
50 document.write('<STYLEtype="text/css"> TD ' +
51 { font-family: ' + face + ' ; font-size: ' + size + 'pt; )
</STYLE>');
52 </SCRIPT>
53 </HEAD>
54 <SCRIPT LANGUAGE="JavaScript">
55 document.write('<BODYBACKGROUND="' +
56 background.replace(/thumb/, " " ) + "'>' ) ;
57 </SCRIPT>
58 <TABLE BORDER=O>
59 <TR>
60 <TD VALIGN=TOP COLSPAN=I>
61 <H2>Take-A-Dive Brokerage $ervices</H2>
62 </TD>
63 </TR>
64 <TR>
65 <TD VALIGN=TOP COLSPAN=4>
66 Take-A-Dive Brokerage Services is dedicated to helping YOU
part with
67 your MONEY. <BR> Our motto is "<I>We go for broke.</I>"
68 Here is your user profile
69 and your customized links.
70 <BR><BR>
71 </TD>
12 </TR>
73 <TR>
74 <TD VALIGN=TOP>
75 Name:</TD>
76 <TD VALIGN=TOP>
77 <SCRIPT LANGUAGE="JavaScriptl.2">document.write(investor)
</SCRIPT>
78 <TD VALIGN=TOP>
79 </TD>
80 Age :
81 <TD VALIGN=TOP>
82 <SCRIPT LANGUAGE="JavaScriptl.2"~document.write(age)
</SCRIPT>
83 </TD>
84 </TR>
85 <TR>
86 <TD VALIGN=TOP>
87 Strategy:
88 </TD>
89 <TD VALIGN=TOP>
90 <SCRIPT LANGUAGE="JavaScriptl.2">
91 document.write(strategy);
92 </SCRIPT>
93 </TD>
260 Preferencias del usuario basadas en cookies

94 <TD VALIGN=TOP>
95 Occupation:
96 </TD>
97 < T D VALIGN=TOP>
98 <SCRIPT LANGUAGE="JavaScript1.2">
99 document.write(occupation);
100 </SCRIPT>
101 < / TD>
102 </TR>
103 <TR>
104 <TD VALIGN=TOP COLSPAN=2>
105 <BR><BR>
106 News Links<BR>
107 <SCRIPT LANGUAGE="JavaScriptl.2" >
108 document.writeln(genLinks(news));
109 </SCRIPT>
110 <TD VALIGN=TOP COLSPAN=2>
111 <BR><BR>
112 Stock Index Links <BR>
113 <SCRIPT LANGUAGE="JavaScript1.2">
114 document.writeln(genLinks(indexes) ) ;
115 </SCRIPT>
116 </TD>
117 </TR>
118 <TR>
119 <TD VALIGN=TOP COLSPAN=2>
120 <BR><BR>
121 [ <A HREF="prefs.html">Set Preferences</A> I
122 </TD>
123 </TR>
124 </TABLE>
125 </BODY>
126 < / n T m >

Analisis del archivo cookie


Vamos a revisar todas estas etiquetas S C R I P T . A travCs de ellas podemos deter-
minar que esta pagina no es mas que un shell que diseiia el usuario sobre la
marcha. El primer paso que hay que dar es analizar el archivo cookie. La funcion
getAttributes ( ) efectua la llamada. Si queremos crear este diseiio sobre la
marcha, necesitaremos acceder rapidamente a esta informacion (antes de que se
cargue la pagina). Esta es la raz6n por la que se llama a getAttributes ( ) en
la linea 3 5 , dos lineas despues de su definicion. A continuacion vemos el conte-
nido de las lineas 13-33:

function getAttributes0 {
var htmlstr = GetCookie('htm1Prefs');
if (htmlstr == null) {
Preferencias del usuario basadas en cookies 261

alert('We1come. You must first set your user preferences.' +


'Please choose OK to load the User Settings page.');
self.location.href = 'prefs.htm1';
)
var htmlArray = htmlStr.split('-->I);
for (var i = 0; i < htmlArray.length; i++) (
var tagInfo = htmlArray[i].split('=');
if (tagInfo[Ol ! = " " ) {
if (tagInfo[O].indexOf('newsNames')== 0 ) {
newsNames[newsNames.length] = tagInfo[l];
1
else if (tagInfo[Ol.indexOf('indexNames')== 0 ) {
indexNames[indexNames.lengthl = tagInfo[ll;
)
else ( eval(tag~nfo[O] + ' = " ' + tagInfo[ll + ""); 1
1
}
J

La variable local htrnZStr se ajustara a1 valor que devuelva la funcion denomi-


nada Getcookie ( ) . En prefs.htrnZ, la cookie prefStr contenia la informacion
necesaria del formulario, per0 dive.htrnZ necesitaba 10s datos que se encontra-
ban dentro de la cookie htrnZStr. Si se ve que htrnZStr es igual a cero, el usuario
todavia no habra establecido sus preferencias. Se le avisara y pedira que se dirija
a prefs.htrnl con objeto de efectuar todas aquellas modificaciones que estime
oportunas.
Por otro lado, a htrnZStr se le aplicara la funcion split ( ) siempre que aparez-
ca el separador -->, con lo que obtendremos el array que se le haya asignado a
la variable local htrnurray. Se utiliza u n buclefor para ejecutar esta funcion con
todos 10s elementos, asignando 10s valores que van obteniendo. Esta funcion se
parece mucho a getprefs ( ) la cual ya vimos en prefs.htrnZ. Comparemos las
lineas 110-120 correspondientes a1 listado de prefs.htrnZ con las 20-32 que tene-
mos ahora:

var htmlArray = htmlStr.split('-->');


for (var i = 0; i < htmlArray.length; i++) (
var tagInfo = htmlArray[il.split('=');
if (tagInfo[Ol ! = { "'I)

if (tagInfo[O].indexOf('news')== 0 ) {
newsNames[newsNames.lengthl = tagInfo[ll;
1
else if (tagInfo[O].indexOf('indexes')== 0 ) (
indexes[indexNames.lengthl = tagInfo[ll;
}
else ( eval(tagInfo[O] + ' = " ' + tagInfo[ll + ""); 1
1
1
262 Preferencias del usuario basadas en cookies

Trabajar con lo desconocido


Muy interesante. La funcion getpref s ( ) correspondiente a1 archivoprejkhtml
sabe que va a trabajar con el formulario representado enformObj, y asigna va-
lores, comprobaciones y selecciona opciones de acuerdo con dicho objeto. La
funcion getAt tributes ( ) desconoce esta information. Asi que sera necesario
hacerse con una idea de 10s datos que contiene la cookie. Por ejemplo, sabemos
que habra alguna informacion sobre 10s recursos de noticias que apareceran en
10s vinculos. Y lo mismo ocurre con 10s indices bursatiles. No sabemos el nume-
ro de vinculos que tendremos en total. LO? i l O ? i50 quizas? Como no lo sabemos,
podremos separar la informacion de 10s vinculos en dos arrays independientes.
Tal y como se puede ver en las lineas 10-11:

var newsNames = new Array ( ) ;


var indexNames = new A r r a y O ;

L a variable newsNames contendra la informacion de las fuentes de noticias,


mientras que indexNames contendra la informacion de 10s indices bursatiles.
Vamos a revisar las cookie de la seccion anterior. Observese el texto en negrita:

investor=Not Provided-->age=Not Provided-->strategy=Moderate-->


occupation=Not Provided-->newsNamesO=Barron's Online,
http://www.barrons.com/-->newsNamesl=CNNInteractive,
http://www.cnn.com/-->newsNames2=FoxNews,
http://www.foxnew~.com/--~newsNames4=The Wall Street Journal,
http://www.wsj.com/-->indexNamesO=Dow Jones Indexes,
http://www.dowjones.com/-->indexN-s2=The New York Stock Exchange,
http://www.nyse.com/-->background=images/fistthumb.gif-->
face=tahoma-->size=l4-->

Los nombres que aparecen en negrita se corresponden con el de 10s marcadores


de 10s vinculos de informacion que van precedidos por las variables array que
acabamos de declarar. Ahora ya sabemos que tendremos alguna variable pero,
icomo las bautizamos? iCu6ntas seran? El codigo de dive.htmZ no revela estos
secretos. Per0 tampoco importa. Mientras sepamos cuales son 10s nombres de
las variables, no necesitaremos especificarlas en el codigo de dive.htmZ. Vamos a
echar otro vktazo a Getcookies ( ' htmlPref s ' 1 . Nos volvemos a fuar en el
texto en negrita:

investor=Not Provided-->age=Not Provided-->strategy=Moderate-->


OCCupatiOn=NOt Provided-->newsNamesO=Barron's Online,
http://www.barrons.com/-->newsNamesl=CNN Interactive,
http://www.cnn.com/--~newsNames2=FoxNews,http://www.foxnews.com/--~
newsNames4=The Wall Street Journal,http://www.wsj.com/-->
Preferencias del usuario basadas en cookies 263

indexNamesO=Dow Jones Indexes,http://www.dowjones.com/-->


indexNames2=The New York Stock Exchange,http://www.nyse.com/-->
background=images/fistthumb.gif--~face=tahoma--~size=14--~

El texto que aparece en negrita representa el nombre de las variables que defi-
niremos en un momento. El buclefor de getAttributes ( ) acomoda la asigna-
ci6n a 10s elementos del array y la declaracion de las variables que se desconocen.
Lo podemos ver en 1as lineas 22-3 1:

var tagInfo = htmlArray[il.split('=');


if (tagInfo[Ol ! = " " ) {
if (tagInfo[O].indexOf('newsNames')== 0 ) {
newsNames[newsNames.~engthl= tagInfoLl1;
}
else if (tagInfo[O].indexOf('indexNames') == 0 ) {
indexNames[indexNames.lengthl = tagInfo[l];
1
else ( eval(tagInfo[Ol + ' = + tagInfoLl1 +
'I' I " ' ) ; 1
}

Cada elemento de htrnZilrray contiene un signo igual (=) que separa el iden-
tificador de su valor. Con cada repetition del buclefor, se ejecuta la funcion split ( )
para que divida a htmlArray [ i 3 por cada signo igual, y sus dos sub-array de
dos elementos se asignan a la variable local taglnfo. Si tagInf o [ 0 I no es igual
a una cadena vacia, tendremos un par identificador-valor valido. La comproba-
ci6n de si la cadena esta vacia se debe a la forma en que JavaScript devuelve el
resultado de split ( 1 .
Cada uno de 10s pares identificador-valor pertenecera a una de estas dos cate-
gorias: un elemento array o una variable ordinaria. Si nos encontramos con el
primer caso, tambiCn tendremos dos posibilidades: contiene un nuevo enlace o
un vinculo a1 indice bursatil. La siguiente declaracion if-else determina la accion
que se desarrollara en dichas circunstancias:

if (tagInfo[O].indexOf('newsNames') == 0 ) I
newsNames[newsNames.length] = tagInfo[ll;
1
else if (tagInfo[O].indexOf('indexNames') == 0 ) (
indexNames[indexNames.~ength]= tagInfo[ll;
1
else { eval(tag~nfo[o] + ' = " ' + tagInfo[ll + ""); 1

Gracias a1 convenio de nombres que se ha utilizado enprefshtml, si tagInfo [ 0 I


contiene la cadena newNames, se tendra que asociar con 10s nuevos vinculos.
Por lo tanto, el valor de tagInf o [ 1 I se asociara con el siguiente elemento dispo-
nible de newsNames. Si tagInf o [ 0 3 contiene la cadena indexNames, se tendra
264 Preferencias del usuario basadas en cookies

que asociar con 10s indices bursatiles. Por lo tanto, tagInf o [ 1] pasara a1 siguien-
te elemento de indexNarnes. Si tagInfo [ 01 tampoco contiene una cadena, en-
tonces tagInfo [ 0 I y tagInfo [ 1] habran de contener el nombre de la variable
que se va a declarar y el valor que se le asignar8. El c6digo de la linea 30 sabe quC
ha de hacer:
eval(tagInfo[o] + ' = ' I ' + tagInfo[ll + " " ) ;

Cuando se complete este buche, se generare el codigo equivalente:

newsNames[O] = 'Barron's Online,http://www.barrons.com/';


newsNames[l] = 'CNN Interactive,http://www.cnn.Com/';
newsNames[2] = 'Fox News,http://www.foxnews.com/';
newsNames[3] = 'The Wall Street Journal,http://www.wsj.com/';

Estos son 10s nuevos vinculos:

indexNames[O] = 'DOW Jones Indexes,http://www.dowjones.com/';


indexNames[l] = 'The New York Stock Exchange,http://www.nyse.com/';

Y estos 10s enlaces a 10s indices de bolsa:

var investor = 'Not Provided';


var age = 'Not Provided';
var strategy = 'Moderate';
var occupation = 'Not Provided';
var background = 'images/fistthumb.gif';
var face = 'tahoma';
var size = '14';

Y estas son las variables encargadas del diseiio.


Preferencias del usuario basadas en cookies 265

Tenemos toda la informacion que necesitamos para construir la phgina que


quiere el usuario. En el momento en que lo hagamos, habra terminado nuestro
trabajo. Usaremos document.write ( ) para recopilar toda la informacion que
se usara en la construccion de la pagina. Hay ocho llamadas a document.write ( ) .
La tabla 7.4 nos describe cada una de ellas.
Tabla 7.4.Us0 de document.write( 1 para crear el codigo HTML

Lineas Codigo Descripcion


50-5 1 document.write('<STYLE Crea la hoja de estilo sobre la
type="text/css"?TD { marcha.
font-family: ' + face + I ;

font-size: + size + 'pt;


} </STYLE>');
55-56 document.write('<BODY Define un URL para la imagen
BACKGROUND= " ' + del fondo.
background.replace(/thumb/,
I,") + r l v > l ) ;

77 document.write investor); Aiiade el nombre del inversor.


82 document.write age); Aiiade la edad del inversor.
91 document.write strategy); Aiiade la estrategia del
inversor.
99 document.write occupation); Aiiade la ocupacion del
inversor.
108 document.write(genLinks Aiiade 10s nuevos vinculos.
news)) ;
114 document.write(genLinks Aiiade 10s enlaces a 10s indices
indexes)) ; bursatiles.
266 Preferencias del usuario basadas en cookies

Aunque las llamadas 3-6 se explican por si solas, no ocurre lo mismo con las
dos primeras. Vamos a empezar por las lineas 50-51:

document.write('<STYLE type="text/css">TD { font-family: ' + face +


' ; font-size: + size + 'pt; } </STYLE>');

Esta llamada escribe una hoja de estilo en la pagina, pero insertara las variables
de estilo que se encargan del tipo y tamaiio de las fuentes.

Una vez que tenemos la hoja de estilo que acabamos de crear sobre la marcha,
trataremos de conseguir la imagen que se utilizara como fondo. Lo tenemos en
la linea 55-56:

document.write ( ' <BODY BACKGROUND="' +


background.replace(/thumb/, " " ) + ' " > ' ) ;

N o olvidemos que el valor de la variable de fondo es irnages/fistthurnb.gif. Bien,


solo que se trata de la version reducida de la imagen que queremos aplicar como
fondo. No hay ningun problema. El nombre de las miniaturas es el mismo que
el de la imagen completa, con la salvedad de que afiade la palabra "thumb". Si la
Preferencias del usuario basadas en cookies 267

eliminamos, tendremos el nombre de la imagen del fondo. En este caso concreto


sera irnages/'st.gq. El metodo replace ( ) se encarga del cambio.
Las dos ultimas llamadas a document. write ( ) utilizan la unica funcion que
se ha definido en la pagina: genLinks ( ) . Esta funcion es parecida a las funcio-
nes genBoxes ( ) y genSelect ( ) de prefs.htrnZ ya que se repite con todos 10s
elementos del array para crear una cadena personalizada de HTML. La unica di-
ferencia es que esta funcion devuelve una cadena de vinculos, nada de casilla de
verificacion ni de etiquetas OPTION. Todo tiene h g a r en las lineas 37-45, como
vemos a continuacion:

function genLinks(1inkArr) {
var linkStr = " ;
for (var i = 0; i < 1inkArr.length; i++) {
var linkparts = linkArr[i].split(', ' )
linkStr += '&nbsp; &nbsp; - <A HREF="' + linkParts[ll + "'> ' +
linkParts[O] + ' < / A > < B R > '
1
return linkStr;
1

Se ha diseiiado genLinks ( ) para que reciba, como unico argumento, un array


de cadenas limitadas. El primer elemento de cada cadena se mostrara como un
vinculo de texto. El segundo elemento sera el URL del atributo HREF. Cada uno
de ellos esta separado por una coma, por lo que a1 utilizar el mCtodo split ( )
con estas comas y a1 asignar el resultado a la variable local ZinkParts, podremos
obtener las distintas partes de forma independiente. El buclefor funcionara como
siempre, creando una cadena de vinculos que entregara cuando complete su
ejecucih.

Posibles aplicaciones
Con un poco de creatividad se puede sacar mucho provecho de esta aplicacion.
Aqui tenemos unas cuantas posibilidades:

Agregar campos para manipular el color de fondo de las celdas de una tabla
y otros elementos de la composici6n.
Permitir que 10s usuarios seleccionen u n tema para las paginas.
Aiiadir un par de campos de texto adicionales con la finalidad de que el
usuario pueda agregar la direcci6n de sus paginas Web favoritas (con
nombres).
Publicar anuncios de acuerdo a las preferencias del usuario que se han re-
gistrado en las cookies.
268 Preferencias del usuario basadas en cookies

Mas opciones para modificar el aspecto de la composicion


A 10s usuarios les encanta disponer de muchas opciones. Les gusta poder cam-
biar y controlarlo todo. Tambitn 10s graficos, cuadros y ventanas remotas.

Aiiadir temas
Esta idea procede de 10s temas del escritorio de Windows 95. En vez de dejar
que 10s usuarios seleccionen un tema para cada elemento de la composicion,
ipor qut no dejar que seleccione un par de temas que se encarguen de la confi-
guracion de todos 10s elementos de la composicion? Supongamos que tenemos
un sitio Web relacionado con el mundo de la musica. Podriamos trabajar con la
siguiente lista:
<SELECTNAME="themes"onChange="swapImage('theImage',
this.options[this.selectedIndexl.value);"~
<OPTION ~ ~ ~ ~ ~ = " n o n e " > N o n e
<OPTION VALUE="bigband">BigBand
<OPTION VALUE="rocknroll">Rock ' n Roll
<OPTION VALUE="rap">Rap
<OPTION VALUE= " country">Country
<OPTION VALUE="reggae">Reggae
<OPTION VALUE="grunge">Grunge
<OPTION VALUE="jazz">Jazz
<OPTION VALUE="club">Club Music
< f SELECT>

Cada uno de estos valores de OPTION podria estar relacionado con una minia-
tura de una imagen. Podemos utilizar genselect ( ) y swapImage ( ) deprefs.htmZ
para crear la lista y encargarnos de las secuencias de imagenes. No olvidemos
que a1 seleccionar uno de estos temas, tendremos que permitir que se puedan
desactivar 10s elementos de la composicion individualmente, como por ejemplo,
la imagen del fondo o el aspecto de la fuente. Obstrvese que la primera OPTION
muestra "None" en pantalla. Conviene incluir esta opci6n para que el usuario
pueda seleccionar las opciones que desee.

Permitir que 10s usuarios creen sus propios vinculos


El formulario de preferencias que hemos visto en este capitulo permitia que 10s
usuarios seleccionasen 10s vinculos con 10s que quiere trabajar de entre una se-
rie de enlaces predeterminados. Siempre podremos agregar un par de campos de
texto para que el usuario introduzca a mano la direccion y nombre de su sitio
Web preferido. La siguiente tabla nos muestra un buen punto de inicio:
<TABLE>
<TR>
Preferencias del usuario basadas en cookies 269

<TD><B>Extra Links</B></TD>
<TD>
<INPUT TYPE=BUTTON VALUE=" Add " onClick="addOpt(this.form);">
<INPUT TYPE=BUTTON VALUE="Delete"
onClick="deleteOpt(this.form); " >
</TD>
</TR>
<TR>
<TD>Link Name</TD>
<TD><INPUT TYPE=TEXT NAME="linkname" SIZE=20></TD>
</TR>
<TR>
<TD>Link URL</TD>
<TD><INPUT TYPE=TEXT NAME="linkURL" SIZE=20></TD>
</TR>
</TABLE>

Los usuarios pueden agregar o eliminar vinculos escribiendo el nombre y el


URL y haciendo clic en el b o t h "Addl o "Delete."Estas variables se pueden guar-
dar en un array y para agregar o eliminar elementos se puede utilizar la fun-
cion addopt ( ) y delOpt ( ) que se ha visto en el c6digo anterior. Si se quiere
mAs, tambiCn se puede crear una lista de seleccih donde se muestren 10s enlaces
que se pueden agregar o eliminar.

Marketing a traves de anuncios directos


iPor quC no crear una campaiia publicitaria que coincida con 10s intereses del
usuario? En el caso de un sitio Web especializado en inversiones, podrfamos uti-
lizar un c6digo que mostrase una serie de anuncios a 10s inversores m8s conser-
vadores de ofertas y nuevas inversiones. Por otro lado, para 10s inversores mAs
ambiciosos podrfamos ofrecer anuncios de inversiones de riesgo pero con una
gran rentabilidad.
Capitulo 8
Shopping Bag: un carro de la compra en JavaScript

Si en este libro hay una aplicacion que sea realmente robusta, es la que vere-
mos en este capitulo. Con Shopping Bag unicamente necesitaremos las ilustra-
ciones y descripciones de 10s elementos que apareceran en nuestro sitio Web. No
habra que crear ningun archivo adicional. Shopping Bag lo hara por nosotros
sobre la marcha. Tampoco necesitaremos 10s servicios de un servidor que se en-
cargue de calcular 10s impuestos y el valor total de la compra. De esto tambien
se encargara Shopping Bag. El usuario podra agregar y eliminar productos de
su carro de la compra con un sencillo clic. A diferencia de lo que ocurre en el su-
permercado, con este carro de la compra no tendremos que esperar colas.

Revision de Shopping Bag


Todo lo que suele ser sencillo e intuitivo para el usuario online, implica bastan-
te trabajo para el programador. Y no nos encontramos ante una excepcion. Pero
10s resultados hacen que el esfuerzo merezca la pena. La descripcion de la funcio-
nalidad y el codigo de Shopping Bag se basa en el siguiente ejemplo.
Tenemos un proceso de cuatro pasos:

1 . Se carga la aplicacion.
2. El comprador revisa las categorias de 10s productos o busca uno determi-
nado. Mientras, puede agregar a su carro de la compra todos 10s que desee.
3 . Segun el contenido del carro, el comprador podra revisar 10s articulos se-
leccionados y modificar su contenido o cantidad.
4. Cuando este de acuerdo con su seleccion, podra pagar lo que ha comprado.
2 72 Shopping Bag: un carro de la compra en JavaScript

Esta aplicaci6n tambitn establece una serie de reglas que ha de cumplir Shopping
Bag. Las iremos viendo en las secciones donde se tengan que aplicar. Vamos a
ver 10s pasos del proceso.

Paso 1: cargar la aplicacion


En el momento en que se abra el archivo index.htmZ se cargara la pantalla que
nos muestra la figura 8.1. El inicio no puede ser mas sencillo: una pantalla de
presentacion. Cuando el usuario haga clic en el vinculo "Begin" pasara a la pan-
talla de la figura 8.2.

Welcome to Shopping Bag!!!


ES&z

I'
~-~__----.. -__ .-_____I_
i-r-"~m----~-'
Figura 8 . 1 . Paritalla de inicio de Shopping Bag

L o que tenemos aqui es la pantalla de inicio ( y ayuda) que incluye un cuadro


inferior con 10s vinculos de navegacion. Antes de seguir, conviene clue nos mo-
vamos por la aplicacicin para acostumbrarnos a clla
i t b r que dos ventanas de navegacihn? N o hay ninguna raz6n que inipida tra-
bajar con una iinica vcntana, pero de esta f'ornia rrservaremos u n marco cxclu-
sivamente para 10s productos. Ademas, no conviene distraer a 10s usuarios con
botones como "Bookmarks" o "Search." Es importante que presten toda la aten-
ci6n posible cn 10s productos. Si lo deseamos, podenios utilizar la ventana prin-
cipal conio una phgina de registro a travCs dc la cual podamos distinguir cntre
10s compradores habituales y 10s casuales.
274 Shopping Bag: un carro de la compra en JavaScript

Shopping Bag: Buildings

Figura 8.3. Hemos incluido el iglu en nuestro carro de la compra

Regla 2 : El vinculo "Gimme One" unicamente aiiade un product0 a1 carro


de la compra. Para modificar la cantidad de productos que se desea adquirir,
el usuario tendra que utilizar el vinculo "View/Change Bag" (ver/cambiar
bolsa).

Aun admirado por nuestras ofertas, nuestro cliente decide utilizar la propie-
dad de busqueda. Selecciona el vinculo "Product Search" y accede a la pantalla
que recoge la figura 8.4. Nuestro cazador de ofertas introduce la cantidad "1.15"
en el campo de busqueda para localizar productos con ese precio. La aplicacion
revisa el precio de todos 10s productos de su base de datos y muestra una lista
con cinco de ellos, tal y como se puede observar en la figura 8.5. Un secador de
pelo (hairdryer), una corbata (necktie) y otros productos, todos con un precio
de 1.15. Revisa las patatas fritas (fries),le gustan y las aiiade tambikn a la bolsa
de la compra.
Nuestro comprador decide regresar a la pantalla donde se muestran todas las
categorias, asi que hace clic en el vinculo "Show All Categories". Esta vez, entra
en la seccion de ropa (clothing). Sigue 10s vinculos y encontrara que la corbata
tiene un precio increible, 1.15, asi que la incluye en la bolsa de la compra.
Figura 8.4. Aqui empieza la bhsqueda de productos

Figura 8.5. Una hnica busqueda, varias ofertas


2 76 Shopping Bag: un carro de la compra en JavaScript

Paso 3: revisar y modificar el pedido


Bien, el comprador esta satisfecho. Decide revisar el contenido de su bolsa de la
compra. Para ello hace clic sobre "ViewKhange Bag". Shopping Bag genera una
pagina con 10s articulos que ha comprado el cliente (figura 8.6). En la tabla que se
ha generado se ha tenido en cuenta el precio de 10s productos, su suma, las tasas
y el precio total a pagar.

Your Shopping Bagf!'

Figura 8.6.Contenido de la bolsa de la compra del cliente, incluyendo el precio

Aun encantado con 10s iglus, nuestro cliente cambia la cantidad de estos produc-
tos que quiere adquirir. Pasa de 1 a 6. Vive en un clima muy caluroso y este tipo
de productos no dura demasiado. Mejor tener de sobra. Las patatas fritas tienen
un aspect0 muy apetitoso, asi que opta por comprar dos paquetes. Pero no tiene
tanto dinero como desearia, asi que opta por olvidarse de la corbata. Siempre
habra una proxima vez.

Regla 3: Los compradores tendran que hacer clic sobre "Change Bag" para
guardar 10s cambios de la bolsa de la compra. Es decir, no pasa nada por
modificar la cantidad de productos o por eliminar alguno de ellos de la bol-
sa de la compra y pasar a otra pantalla.
Shopping Bag: un carro de la compra en JavaScript 277

Es muy sencillo modificar un pedido. Bastara con modificar las cantidades de 10s
objetos solicitados y eliminar 10s que no que quieran, activando la casilla de ve-
rificaci6n que se encuentra en la columna Remove de la tabla. Todos 10s produc-
tos cuentan con una lista desplegable desde donde se podra seleccionar la cantidad
de unidades que se compraran y de una casilla de verificacih "Remove" para
eliminar el product0 del carro de la compra. Despues de modificar el contenido
del pedido, el cliente hara clic sobre "ChangeBag" para ver las cantidades corres-
pondientes a su nuevo pedido (figura 8.7). Ahora su bolsa de la compra mostra-
ra 6 iglus, dos paquetes de patatas y una corbata. Con 10s impuestos, el precio
de la compra asciende a $6,190.5 7.

Your Shopping Bag!!!

Figura 8.7. Aspect0 de la bolsa de la compra despuCs de introducir 10s cambios

Regla 4: En el momento en que se envia el pedido de Shopping Bag se vacia


el carro de la compra. Si queremos mas productos, tendremos que empezar
de cero.

Paso 4: comprobacion
Cuando el cliente est6 de acuerdo con la compra, hara clic en el vfnculo "Check
Out" con lo que la aplicaci6n abrira el formulario de la figura 8.8. Rellenara la
2 78 Shopping Bag: un carro de la compra en JavaScript

informacion del pedido y lo enviara a la tienda, con lo que unicamente le tocar&


esperar a que llegue por correo
Y ya est5. Otro cliente satisfecho. Vamos a pasar a las siguientes secciones para
ver 10s requisitos que necesita el cliente para poder trabajar con esta aplicacih
y estudiaremos el c6digo responsable de esta maravilla.

Figura 8.8. Formulario de pedido

Requisitos para la ejecucion


Shopping Bag utiliza JavaScript 1.2 y CSS, asi que el usuario no podra utilizar
exploradores de la generacion 3.x.Per0 no hemos de olvidar que cierta parte de
10s usuarios que accederan a esta aplicacion para comprar no tendran la ultima
version de 10s navegadores. Podremos eliminar 10s componentes CSS para hacer
que nuestra aplicacidn sea compatible con MSIE y NN 3.x.
En lo referente a la escalabilidad, podremos disponer de hasta 500 articulos dis-
tintos en el almacen de Shopping Bag. DespuCs de todo, cada product0 unica-
mente ocupa una linea de codigo. Detuve las pruebas con 700 elementos. Estas
pruebas las hice en mi PC a 120 MHz con 128 MB de RAM.Asi que, a no ser que
quiera competir con Pryca, las propiedades de esta aplicacion deberian ser sufi-
cientes para sus propositos.
Shopping Bag: un carro de la compra en JavaScript 2 79

Analisis de la sintaxis
Vamos a echar u n vistazo a1 diagrama de flujo de Shopping Bag. L a figura 8 . 9 .
nos muestra como inicia el usuario las compras, como navega a t r a v b de todos
10s productos, como efectua 10s ultimos cambios, rellena el formulario y 10s en-
via a1 servidor de pedidos.
Esta aplicacion se compone de ocho archivos. En esta lista podemos ver la fi-
nalidad de cada uno de ellos:
index.htmZ. Pagina de inicio de Shopping Bag que controla la ventana de
trabajo.
shopset.htmZ. Marco de trabajo para la ventana remota que se abre a pan-
talla completa. Contiene dos archivos intro.htmZ y rnanager.htmZ.
intro.htmZ. Pagina predeterminada para el marco de trabajo mas grande y
para el documento de ayuda que muestra las funciones de cada una de las
propiedades de la barra de navegacion.
manager.htmZ. Epicentro de Shopping Bag. Toda la funcionalidad procede
de este archivo y sera el centro de estudio de este capitulo.
inventory.js. Contiene funciones, constructores y array para construir el
inventario de Shopping Bag. Gran parte de su trabajo tiene lugar durante
la carga de la aplicacion.
search/index.htmZ. Marco de trabajo donde se carga la aplicacion de bus-
queda de productos. Si hemos leido el contenido del primer capitulo del
libro veremos que se trata de una version de la maquina de busqueda que
se trato entonces.
search/main.htmZ. Pagina de ayuda para el buscador. Se completa con una
serie de ejemplos.
search/nav. html. El cerebro de la aplicacion de busqueda.
Debido a1 tamaAo de la aplicacion y a1 hecho de que ya hemos visto en 10s capi-
tulos anteriores extractos del c6digo de JavaScript que se utiliza para manipu-
lar aplicaciones, en este capitulo vamos a estudiar la aplicacion desde otro punto
de vista.
En vez de avanzar de un archivo a otro, nos moveremos por ellos siguiendo las
cinco operaciones que desarrolla la aplicacion:
1. Cargar Shopping Bag: construye el inventario y carga la pantalla.
2. Mostrar productos: salta de una categoria a otra y de un product0 a otro.
3 . Agregar productos a1 carro de la compra: guarda un registro con todo lo
que se incluye en el carro de la compra.
4. Buscar por productos: establece busquedas basadas en 10s productos del
almackn.
280 Shopping Bag: u n carro de la compra en JavaScript

5. Modificar el contenido del carro de la compra/comprobacion final: ultimos


cambios y pago.

la aplicacion
a pantalla
completa

I c[>;
Categorias
/as categorias
oroductos
buscar Buscar

Seleccionar
a partir de una
lista de
1 lntroducir
la cadena c
debusqueda

Mostrar (otro)

o categoria

T Navegar

Agregar product0
de revision
de la compra de productos

Figura 8.9.Revision de Shopping Bag


Shaming Baa: un carro d e la comma en IavaScript 281

Si comparamos estas descripciones con las que hemos visto anteriormente con
10s archivos, nos haremos una idea de quk archivos se encargan de quk tarea.
Aun sin estar las cosas muy perfiladas, todo apunta a que la operacion 1 utiliza
codigo de index.htrn1, shopset.htm1 y inventoryjs; en las operaciones 2, 3 y 5 in-
terviene managerhtml; y la operacion 4 es exclusiva de 10s archivos de busqueda.
Aun tenemos el codigo basado en archivos, per0 en breve cubriremos las fun-
ciones de JavaScript que se encargan de cada operacion. Cada una de estas cinco
secciones se describira desde el punto de vista del usuario y de las acciones que
realiza, modificando la cantidad de 10s articulos adquiridos, solicitando ayuda,
etc. Tambikn veremos tkcnicas relevantes. Per0 vamos a empezar por cargar la
aplicacion.

Paso 1:cargar Shopping Bag


JavaScript y el explorador residente se encargan de la mayor parte del trabajo,
aunque el usuario tambikn tiene un pequeiio papel. Vamos a centrarnos en la
primera pagina de indexhtml. En el ejemplo 8.1 tenemos su codigo.
Ejemplo 8.1.index.htm1
1 <HTML>
2 <HEAD>
3 <TITLE>Shopping Bag</TITLE>
4 <STYLE TYPE= " text / css " >
5 <!--
6 #welcome ( text-align: center; margin-top: 150)
7 //-->
8 </STYLE>
9 <SCRIPT LANGUAGE= JavaScr ipt >
'I

10 <!--
11 var shopwin = null;
12 var positionstr = ; I

13 function whichBrowser0 {
14 if(navigator.appVersion < 4) {
15 alert("You need MSIE 4.x or Netscape Navigator 4.x to use +
16 " Shopping Bag. 'I )

17 return false;
18 I
19 return true;
20
21
22 function launch0 {
23 if(!whichBrowser()) ( return; >
24 if(navigator.appName == "Netscape")
25 { positionstr = ",screenX=O,screenY=O";1
26 else { positionStr = " ,fullscreen=yes"; 1
27 if(shopWin == null) I
282 Shopping Bag: un carro de la compra en JavaScript

28 shopwin = open ( " shopset.html , " , "width= + screen.width +


'I 'I

29 ",height=" + screen.height + positionstr) ;


30 I
31 1
32 function closeUpShop0 {
33 if (shopwin ! = null) I
34 if (typeof(shopwin) == "object") I
35 shopWin.close0;
36 1
37 1
38 1
39 window.onunload = closeUpShop;
40 //-->
41 </SCRIPT>
42 < f HEAD>
43 <BODY>
44 < D IV ID= we 1come >
45 <Hl>Welcome to Shopping Bag!! !</H1>
46 <A H R E F = " javascript: launch(); ">Begin</A>
47 </DIV>
48 </BODY>
49 < f HTML>

Puede parecer u n conjunto de drdenes en JavaScript que se encargaran de que


aparezcan cinco palabras en una pagina; per0 el codigo adicional mejorara con-
siderablemente la aplicacion. JavaScript define e inicia un miembro de alto nivel
que se utilizara para administrar el contenido de la ventana y de identificar el
tip0 de explorador para proporcionarle el codigo correct0 en el momento en que
se abra la ventana.

Miembros de alto nivel


Las variables y funciones de las lineas 11 y 32-38 se encargan de que se cum-
pla la primera regla: si se cierra la ventana principal, t a m b i h se cerrara la se-
cundaria. Si no se hace asi, la aplicacidn se encontrara con un error si el usuario
intenta actualizar el contenido de la ventana remota.
Este es el contenido de la linea 11:

var shopwin = null;

Yeste es el de las lineas 32-38:

function closeupshop ( ) {
if (shopwin ! = null) I
if (typeof(shopwin) == "object") I
shopWin.close0;
1
Shopping Bag: u n carro de la compra en JavaScript 283

1
1
window.onunload = closeUpShop;

A la variable shopWin, cuyo valor inicial es cero, se le asignara el objeto corres-


pondiente a la ventana remota (acabo de saltar a la linea 27). La funcidn deno-
minada closeUpShop ( ) se llamara para que se encargue de cerrar la ventana.
Esta funcidn determina si el usuario tiene abierta la ventana remota de la apli-
cacion. En caso afirmativo, la cerrar6. Si shopwin es distinto de cero, se abrirh la
ventana remota. La funcion closeUpShop ( ) cierra la ventana remota antes de
actualizarla.
Llegados a este punto, lo unico que debe hacer el usuario es clic en el vinculo
"Begin" para abrir la ventana remota. De esta forma, se abre shopset.htm2, un
marco de trabajo. El ejemplo 8.2 nos muestra su c6digo.
Ejemplo 8.2. shopset.htm1
1 <HTML>
2 <HEAD>
3 <TITLE>Shopping Bag Frameset</TITLE>
4 <SCRIPT LANGUAGE="JavaScriptl.2">
5 <!--
6 function resetopener0 {
7 opener.shopWin = null;
8 1
9 //-->
10 </SCRIPT>
11 </HEAD>
12 <FRAMESET ROWS="80%,2 0 % " FRAMEBORDER=O BORDER=O
onLoad="self.focus ( 1 ;"
13 onUnLoad="resetOpenerO;">
14 <FRAME SRC="intro.html" NORESIZE>
15 <FRAME SRC="manager.html" NORESIZE>
16 </FRAMESET>
17 </HTML>

Este es el marco de trabajo bAsico. Contiene dos filas. A una se le asigna el co-
dig0 fuente de intro.htrnZ; y la otra la controla rnanager.htm2. Aqui no hay casi
nada de c6digo JavaScript:
function resetopener0 {
opener.shopWin = null;
1

En las lineas 6-8 se encuentra una funci6n llamada resetopener ( 1 a la que


se llama siempre que se descarga el documento que se encuentra en la ventana
principal (en este caso, el marco de trabajo). Como el valor de opener.shopWin es
284 Shopping Bag: un carro de la compra en JavaScript

cero, resetopener ( ) permitira entonces que el usuario cierre la ventana re-


mota de Shopping Bag y la vuelva a abrir haciendo clic en el vinculo "Begin".
Esto puede parecer algo completamente trivial, incluso innecesario. Pero obskr-
vese que en el archivo index.htm2 (linea 27), solo se abrira una ventana adicio-
nal si shopwin es igual a cero. A cerrar la ventana no se devuelve shopwin a
dicho valor, asi que resetopener ( ) entrara en la ayuda. El valor del controla-
dor de eventos onLoad de la etiqueta FRAMESET sera self. f o c u s ( ) . De esta
forma se asegura que la ventana remota se abrira y cargara por delante de la
ventana principal.
Esto, basicamente, se encarga de la carga del marco de trabajo. Todavia quedan
otras tres paginas por cargar: intro.htrn1, manager.html y inventory.js. intro.htm1
es un archivo estatico de ayuda. Puesto que se carga rnanager.htmZ, el codigo
fuente del archivo inventoryjs tambitn lo hara con 61. Merece la pena que deje-
mos manager.htrnZ para mas tarde y lo estudiemos con detalle. En cualquier
caso, inventoryjs tiene el codigo que vamos a examinar a continuacion. Es algo
largo, pero nos dara una idea sobre la construccih del inventario.
Shopping Bag: un carro de la compra en JavaScript 285

inventory.js
inventory.js contiene tres funciones. Las dos primeras son funciones del cons-
tructor. Una define un producto; la otra define su categoria. L a iiltima funcion
crea un array de objetos 10s cuales crean 10s constructores. Lo podemos ver en el
ejemplo 8 . 3 .
Ejemplo 8.3. inventory.js
1 function product(name, description, price, unit) I
2 this.name = name;
3 this.description = description;
4 this.price = price;
5 this.unit = unit;
6 this.plu = name.substring(0, 3).toUpperCase() +
7 parseInt (price). tostring ( ) ;
8 this.icon = new Image();
9 return this;
10 I
11 function category(name, description) I
12 this.name = name;
13 this.description = description;
14 this.prodLine = eval(name1;
15 var imgDir = images/" + name.toLowerCase( ) + " / " ;
16 for (var i = 0; i < this.prodLine.length; i++) {
17 this.prodLine[i].icon.src= imgDir +
18 this.prodLine[i].name.toLowerCase() + ".gif";
19 1
20 return this;
21 I
22 function makeproducts0 I
23 Appliances = new Array(
24 new product ( "Dryer",
25 "Stylish pastel design, contemporary two-button
engineering.*' ,
26 263.31 ,
27 "each"),
28 new product ( "Hairdryer",
29 "Fancy yellowish blast, and durable cord. No expense
spared. ,
I'

30 1.15,
31 "pair") ,
32 new product ( "Oven",
33 "Made in the 1850's, this coal-powered unit quickly
blackens any" +
34 "favorite dish.",
35 865.78,
36 "each") ,
37 new product ("Radio",
38 "Revolutionary one-channel technology. White noise and
static" +
286 Shopping Bag: un carro de la compra en JavaScript

39 "included." ,
40 15.43,
41 "each") ,
42 new product ("Toaster",
43 "BBQ-style toaster. Only a moderate shock hazard.",
44 25.78,
45 "each") ,
46 new product ( "Washer",
47 "Does a great job on partially everything.",
48 345.61,
49 "each")
50 );
51
52 Buildings = new Array(
53 new product ( "Barn",
54 "Complete with rusty silo and rotting doors. Pig sty sold" +
55 "separately.*I,

56 6350.57,
57 "each") ,
58 new product ( "Lighthouse",
59 "Made of cement. Assorted light bulbs. Three AA batteries '
60 + "not included. 'I,

61 12351.15,
62 "each") ,
63 new product ("Igloo",
64 "Made from top grade snow blocks, and includes a chimney
and +
65 "5-tOn air conditioning unit.",
66 954.76,
67 "each") ,
68 new product ( "City",
69 "Buildings, streets, lights, skyline. Excellent volume
purchase. *' ,
70 334165.95,
71 "each") ,
72 new product ("Castle",
73 "Sturdy medieval design, complete with alligators in moat,
and " +
14 "remote control drawbridge." ,
75 93245.59,
76 "each") ,
77 new product ( "Tower",
78 "Really tall. Ideal for winning friends and spotting forest
I9 " + f ires . " ,
I'

80 24345.87,
81 "pair")
82 );
81
84 Clothing = new Array(
85 new product ( "Bowtie",
86 "Swell red fabric. Doubles a bow for Christmas wreaths or 'I
Shopping Bag: un carro de la compra en JavaScript 287

87 + "birthday gifts." ,
88 5.41,
89 "five"),
90 new product ( "Necktie",
91 "Be the first (and probably only) one (ever) on your block.
92 + "Made of genuine burlap.",
93 1.15,
94 "each"),
95 new product ( "Purse",
96 "Attractive green material. Wards off most mammals.",
97 18.97,
98 "each") ,
99 new product ( "Jacket",
100 "Plush fake fur with fiberglass lining. Washer safe.",
101 180.72,
102 "each") ,
103 new product ( "Glove",
104 "Covers all four fingers and one thumb. Fancy latex
design. " ,
105 6.59,
106 '"three"),
107 new product ("Dress",
108 "Found at a garage sale. Also doubles as a picnic table
cover.'I,

109 7.99,
110 "each") ,
111 new product ( "Watch",
112 "Geuine replica. Doesn't tell time. You have to look at
it.",
113 6.19,
114 "each")
115 );
116
117 Electronics = new Array(
118 new product ( "Camcorder",
119 "Solar-powered. Free microphone. Custom-built for
blackmailing " +
120 "close relatives." ,
121 60.45,
122 each") ,
'I

123 new product ( "Stereo",


124 "Quadraphonic,pre &track sound. Leisure suit and roach
killer " +
125 "shoes are optional",
126 54.91,
127 "each") ,
128 new product ( "Speaker",
129 "Extra piece of hi-fi junk. Works best if discarded.",
130 1.90,
131 "each") ,
132 new product ("Remote",
288 Shopping Bag: un carro de la compra en JavaScript

133 "Dozens of buttons. Controls everything- TV, VCR, stereo,


134 + "pets, local government. ,
135 465.51,
136 "each") ,
137 new product ( "Cellphone",
138 "Product of tin can technology. 35-ft calling area. Dandy
139 + "lavender plastic. " ,
140 64.33,
141 "each") ,
142 new product ( "Camera",
143 "Takes brilliant one-color photos. Landfill safe.",
144 2.95,
145 "each") ,
146 new product ( "Television",
147 "Two-channel UHF only model. Wow. " ,
148 22.57,
149 "each")
150 );
151
152 Food = new Array(
153 new product ( "Cheese",
154 "Wait 'ti1 you get a wiff. Puts bleu cheese to shame.",
155 3.05,
156 "chunk") ,
157 new product ( "Fries",
158 "More grease than the local car garage. You can't beat the
159 " + "taste, though.",
160 1.15,
161 "box") ,
162 new product ( "Eggs'' ,
163 "The standard breakfast staple.",
164 1.07,
165 "dozen") ,
166 new product ( "Drumstick",
167 "This leg of pterodactyl is a sure crowd pleaser.",
168 100.00,
169 "half ton") ,
170 new product ( "Chips" ,
171 "Opened-bag flavor. Guaranteed stale, or your money back.",
172 1.59,
173 "bag"),
174 new product ( "Shrimp",
175 "Great raw, served above room temperature.',
176 2.95,
177 "each")
178 );
179
180 Hardware = new Array(
181 new product ( "Chainsaw",
182 "Be your own eager beaver with this tree-cutting machine.",
183 226.41,
Shopping Bag: un carro de la compra en JavaScript 289

184 "each"),
185 new product ( "Cycle",
186 "Mow down the wheat field with a few swipes. Just like the
187 + "Grim Reaper ' s . , I'

188 11.15,
189 "each") ,
190 new product ( "Hammer",
191 "Tempered steel head, fiberglass handle. Perfect for
hitting +
192 things. ,
'I

193 9.87,
194 "each"),
195 new product ( "Lawnmower",
196 "Self-propelled (you propel it yourself). " ,
197 165.95,
198 "each") ,
199 new product ( "Pliers",
200 "Perfect for eye brows and nose hairs.",
201 6.59,
202 "each") ,
203 new product ( "Stake",
204 "This 2-in-1 miracle secures tents or gets rid of
vampires." ,
205 3.95,
206 "pair")
207 );
208
209 Music = new Array(
210 new product ( "Bongos",
211 "Great little noise makers for even the most sophisticated
212 '* + "occasions. I , ,

213 35.50,
214 "bongo") ,
215 new product ("Piano",
216 "It ain't grand, but this baby will make you sound like
tavern " +
217 "material in no time." ,
218 1001.40,
219 "each") ,
220 new product ( "Notes",
221 "Choose from A, B, C , D , E, F , or G. Can be reused in any
song.'I,

222 2.97,
223 "note"),
224 new product ( "Guitar",
225 "Strum, strum. This one is your fast track to fame and
fortune. , 'I

226 241.11,
227 "each") ,
228 new product ( "Trumpet",
229 "Solid copper body, and not many dents. Extra spit valve
290 Shopping Bag: un carro de la compra en JavaScript

230 + included. ,
231 683.59,
232 "each")
233 );
234
235 categoryset = new Array(
236 new category "Appliances", "Kitchen machines to make life
eas er") ,
237 new category "Buildings", "Architectural structures your
can t " +
238 "resist"),
239 new category "Clothing", "Fashionably questionable apparel
for +
240 "the 21st century"),
241 new category ( "Electronics", "Nifty gizmos that drain your
wallet"),
242 new category ( " F o o d " , "The best product to order over the
Net"),
243 new category ( "Hardware", "All kinds of general purpose " +
244 "construction tools"),
245 new category("Music", "The hottest new instruments from
places + I'

246 "you've never heard of")


247 );
248 1

Propiedades del producto


LRecuerda 10s objetos JavaScript que utilizamos en 10s primeros capitulos? Han
vuelto. A cada producto se le trata como si fuera u n objeto con las siguientes
propiedades:
name. El nombre del producto.
description. Una descripcion basica del producto.
price. Precio del producto.
unit. Unidad que se utiliza para vender el producto: por decenas, pares,
unidad, etc.
p h . Numero del producto: numero arbitrario que se asigna a1 producto
para guardarlo en el almackn y poder referirse a 61.
icon. Una imagen de cada producto.

Para conseguir 10s resultados deseados, la funcion encargada de la construc-


cion de 10s productos tendra el siguiente codigo (tal y como se describe en las li-
neas 1-10):

function product(name, description, price, unit) {


this.name = name;
this.description = description;
Shopping Bag: un carro de la compra en JavaScript 291

this.price = price;
this.unit = unit;
this.plu = name.substring(0, 3).toUpperCaseO +
parseInt(price).toStringO;
this.icon = new Image();
return this;

Obskrvese que se han creado seis propiedades, per0 s610 se esperan cuatro ar-
gumentos. El numero de propiedades y el de argumentos que se han de recibir
no coinciden. Vamos a revisar c6mo se asigna a cada propiedad su respectivo
valor. Las cuatro primeras con bastante obvias. A las propiedades name, des-
cription, price y unit se les asigna su correspondiente argumento (aquellos que
coincidan con su nombre).
Per0 plu es otra historia. Se compone de las propiedades name y price. Se utili-
zan las tres primeras letras en mayusculas del nombre del producto y se suma
el precio del producto. Asi, un barco (boat) que cueste $5501.00 tendra una
propiedadplu igual a BOA550 1.No olvidemos que se trata de algo completamen-
te arbitrario. Los productos que se suelen vender ya cuentan con un numero de
referencia. Lo hemos hecho de esta forma para simplificar las cosas. La ultima
propiedad es icon y, de momento, su valor se le ha asignado a a1 objeto Image.
No necesita ningun argumento.

Propiedades de la categoria del producto


Sabemos que 10s productos son, en realidad, un objeto product. Sabemos que
cada categoria de 10s productos es en realidad un objeto category. Con las cate-
gorias nos encontraremos con una situacion similar a la vista con las propieda-
des de 10s productos. Vamos a ver las propiedades de 10s objetos:

name. Nombre de la categoria.


description. Description basica de la categoria.
prodline. Toda la linea de productos que se encuentran dentro de dicha
categoria.

El constructor de categoria se encuentra en las lineas 11-21 :

function category(name, description) {


this.name = name;
this.description = description;
this.prodLine = eval(name);
var imgDir = "images/" + name.toLowerCase0 + " / " ;
for (var i = 0; i < this.prodLine.1ength; i++) {
this.prodLine[i].icon.src = imgDir +
this.prodLine[il.name.toLowerCaseO + ".gif";
292 Shopping Bag: u n carro de la compra en JavaScript

}
return this;
}

Cada una de estas categorias tendra tres propiedades: una cadena llamada name,
otra llamada description y un array llamado prodline. Las propiedades name y
description parecen bastante claras pero, ide donde procede el array y como se
puede utilizar con eval ( ) ? En un momento responderemos ambas preguntas.
De momento vamos a centrarnos en la estrategia basica: siempre que nombre-
mos algo como categoria, se creara un array en la linea de productos con el mis-
mo nombre. Por ejemplo, si creamos una categoria llamada stereos, se creara un
array llamada stereos en donde se guardaran todos 10s productos pretenecientes
a dicha categoria.
No olvidemos que 10s productos tienen una propiedad llamada icon, que es un
objeto Image que no se ha asignado como fuente. Vamos a ver algo mas sobre el
nombre de la categoria. Ya sabemos que todas las categorias guardan su linea
de productos dentro de un array que tiene su mismo nombre. Pues tambien se
guardan dichos productos dentro de un directorio que tiene el mismo nombre
que la categoria.
Todos 10s productos de la categoria Music se guardaran dentro del directorio
music/. Las imagenes correspondientes a la categoria Hardware se encontraran
en el directorio hardware/, etc. Parece un razonamiento logico. Con este sistema,
podremos cargar de antemano 10s productos de la categoria que se est6 constru-
yendo. El codigo de las lineas 16-19 se encarga de ello:

var imgDir = "images/" + name.toLowerCase0 + " / " ;


for (var i = 0; i c this.prodLine.length; i++) {
this.prodLine[i].icon.src = imgDir +
this.prodLine[i].name.toLowerCaseO + " . g i f " ;
1

Si examinasemos la estructura de 10s directorios de esta aplicacion nos encon-


trariamos con 10s siguientes:
images/
appliances/
buildings/
cloth ing/
electronics/
food/
hardware/
music/
Shopping Bag: u n carro de la compra en JavaScript 293

L a linea 1 7 asigna a la propiedad SRC de cada icono (Image)el valor images/ +


el nombre del producto en minusculas /+ + ".gif'. Se sigue el convenio de
nombres que hemos visto en 10s capitulos anteriores. Cada producto tiene una
imagen con el mismo nombre de la carpeta en la que se encuentra, que t a m b i h
coincide con el nombre de la categoria. Esta es la formula:

URL de cada imagen de 10s productos = imagenes/categoria


nombregroducto.gif

Si nos pudiCsemos dirigir a1 directorio images\ de esta aplicacion, veriamos que


10s nombres de las imagenes se corresponden con el de 10s productos de Shopping
Bag que estan en el directorio correspondiente a su categoria Shopping Bag. Asi
se simplifican las cosas y podremos agregar, eliminar y registrar productos con
una gran facilidad.

Nota: Si tenemos imagenes demasiado grandes, podriamos prescindir de


la carga anticipada. Siempre es conveniente que estas imagenes se en-
cuentren en la maquina del cliente para que no sufra ningun retraso a la
hora de ejecutar la aplicacion. Per0 si el tamaiio de las imagenes es demasiado
grande, no podemos tener a1 cliente esperando media hora hasta que se
complete la carga de todas.

Crear productos y categorias


Ya hemos visto las funciones del constructor. Ahora las vamos a poner a tra-
bajar. Lo primero de todo sera crear 10s productos. DespuCs cargaremos las ca-
tegorias. La funcion makeproducts ( ) se encarga de todo. Este es el contenido
de las lineas 22-248. Como gran parte del codigo es el mismo constructor a1 que
se le llama una y otra vez, veremos una version abreviada:

function makeproducts ( ) {
Appliances = new Array(
new prdduct ( "Dryer",
"Stylish pastel design, contemporary two-button engineering.",
263.37 ,
"each") ,
new product ( "Hairdryer",
"Fancy yellowish blast, and durable cord. No expense spared.",
1.15,
"pair") ,
new product ( "Oven",
"Made in the 1 8 5 0 ' s . this coal-powered unit quickly blackens any"
+ "favorite dish.",
865.78,
"each"),
294 Shopping Bag: un carro de la cornpra en JavaScript

new product ("Radio",


"Revolutionary one-channel technology. White noise and static" t
" included. ,
cq

15.43,
"each") ,
new product ( "Toaster",
"BBQ-style toaster. Only a moderate shock hazard.",
25.78,
"each"),
new product ("Washer",
"Does a great job on partially everything.",
345.61,
"each")
);
...
... etc ...
...
categoryset = new Array(
new category( '"Appliances","Kitchen machines to make life easier"),
new category("Buildings", "Architectural structures your can't " +
"resist"),
new category("Clothing", "Fashionably questionable apparel for +
"the 21st century"),
new category("E1ectronics", "Nifty gizmos that drain your wallet"),
new category("Food", "The best product to order over the Net"),
new category( "Hardware", "All kinds of general purpose +
"construction tools") ,
new category("Music", "The hottest new instruments from places +
"you've never heard of")
);
1

Primero tenemos 10s productos. El valor de la VariableAppZiances sera un array.


Cada uno de 10s elementos de este array es un producto. Cada llamada a1 pro-
ducto incluye 10s argumentos necesarios para construir el producto: nombre,
descripcion, precio y unidad. Y esto se cumple con todas las categorias: Buildings,
Clothing, Electronics, Food, Hardware y Music.
A continuacion vamos con las categorias. Realmente, ya conocemos sus nom-
bres (Appliances, Buildings, Clothing, etc.); todo lo que tenemos que hacer es in-
dicarselos a Shopping Bag. esto ocurre en las lineas 235-248:

categoryset = new Array(


new category("Appliances", '"Kitchenmachines to make life easier") ,
new category ( "Buildings", "Architectural structures your can't + I'

"resist"),
new category ( '"Clothing","Fashionably questionable apparel for + 'I

"the 21st century"),


new category("E1ectronics". "Nifty gizmos that drain your wallet"),
new category ( "Food", "The best product to order over the Net" ) ,
Shopping Bag: un carro de la cornpra en JavaScript 295

new category( "Hardware", "All kinds of general purpose + 'I

"construction tools" ) ,
new category( "Music", "The hottest new instruments from places 'I +
"you've never heard of")
);

El valor de la variable categorySet sera un array. Cada uno de 10s elementos del
array sera un objeto category construido a partir de dos argumentos, un nom-
bre y una descripcion. Al primer argument0 se le asigna el nombre de la propie-
dad y a1 segundo su descripcidn. Vamos a revisar de nuevo el contenido de la
linea 14 del constructor de categorias:

this.prodLine = eval(name);

El valor deprodline sera eval (name). Asi pues, la llamada a category ( ) que
tiene lugar en la linea 249 implica queprodLineequivale a eval ( "Appliances ) , 'I

es decir, a Appliances. Ahora, la categoria Appliances lo sabe todo sobre sus pro-
ductos (esta informacion se encuentra dentro del array prodline). Cada uno de
10s elementos de categorySet representa otra categoria de Shopping Bag. Es decir,
que la inclusion y eliminacidn de categorias es realmente sencilla.

Crear la bolsa de la compra


Ya hemos creado todos 10s productos. Lo unico que queda por hacer es cargar
la bolsa de la compra. Todo lo que necesitaremos sera unas cuantas propiedades
que se encarguen de controlar el pago y de almacenar 10s productos que selec-
cione el usuario. El constructor Bag ( ) de rnanager.htrnZ es quien se encarga de
definir la unica bolsa de la compra que se utilizara en esta aplicacion. A conti-
nuacidn tenemos el contenido de las lineas 21-3 1 de rnanager.htrnl, que veremos
en el ejemplo 8.4 de este mismo capitulo:

function Bag() {
this.taxRate = .06;
this.taxTota1 = 0;
this.shipRate = .02;
this.shipTota1 = 0;
this.subTota1 = 0;
this.bagTota1 = 0;
this.things = new Array ( ) ;
1
shoppingBag = new Bag();

Hay dos variables con valores arbitrarios: taxRate y shipRate. Una es un multiplo
de 10s impuestos estatales. La otra se utiliza para calcular 10s precios del transpor-
te. Quiza la estructura de impuestos con la que debamos trabajar no se parezca
296 Shotming Bag: un carro de la comma e n IavaScriDt

en nada a Csta. Aun hay otras tres variables, taxTota1, subTotal y shipTotaZ, que
son la suma de 10s impuestos de todos 10s productos, la suma del importe total
que ha de pagar el usuario y 10s portes. La ultima variable es un array. things
contendra todos 10s productos y la cantidad de ellos que seleccionara el usuario.
A la variable ShoppingBag se le asigna el valor de Bag ( 1 . Vamos de compras.

Paso 2: mostrar 10s productos


Una vez que se ha cargado la aplicacion, una de las primeras cosas que querra
el usuario sera ver el contenido del inventario. El usuario puede navegar por las
distintas categorias a travCs de 10s vinculos "Previous Category" (categoria an-
terior) y "Next Category" (siguiente categoria) o de un producto a otro a traves
de 10s enlaces "Previous Product" (producto anterior) y "Next Product" (siguiente
producto). Funciona asi. Recordemos las lineas 235-248 del archivo inventory../s:

categoryset = new Array(


new category("Appliances", "Kitchen machines to make life easier"),
new category("Buildings", "Architectural structures your can't +
"resist"),
new category ( "Clothing", "Fashionably questionable apparel for +
I'

"the 21st century" ) ,


new category( "Electronics", "Nifty gizmos that drain your wallet"),
new category("Food", "The best product to order over the N e t " ) ,
new category ( "Hardware", "All kinds of general purpose +
"construction tools"),
new category("Music", "The hottest new instruments from places +
" y o u ' v e never heard o f " )
);

categoryset tiene siete objetos category. El primer0 se le conoce como category-


Set [ 01, a1 siguiente categoryset [ 11,etc. No importa el producto que estC
revisando el usuario, Shopping Bag conoce el numero (en este caso, 0-6) de la
categoria a la que pertenece. Si el usuario decide pasar a la siguiente categoria,
Shopping Bag resta 1 del numero de la categoria y muestra el primer producto
Shoming Bag: u n carro de la comma en IavaScriDt 297

del nuevo grupo. Si Shopping Bag se encuentra en la categoria 0 y el usuario


quiere acceder a la categoria anterior, Shopping Bag mostrara el primer elemen-
to de la ultima categoria (en este caso, la numero 6 ) .
Si el usuario quiere ver la siguiente categoria, Shopping Bag afiadira 1 a1 nume-
ro de la categoria. Si esta en la ultima, Shopping Bag volvera a la numero 0.
Y ocurre igual con 10s productos. Cada categoria tiene un numero de produc-
tos. Shopping Bag conoce el numero de referencia del producto que esta en la
pantalla, asi que a1 seleccionar "Previous Product" o "Next Product" hara que se
muestre el siguiente producto o el anterior, segun lo que desee el usuario.
Cuando el usuario accede a1 ultimo producto de la categoria y quiere avanzar
a1 siguiente, la aplicacion volvera a mostrar el primer producto de la categoria
siguiente. Y si tiene el primer elemento de la categoria en la pantalla y quiere ac-
ceder a1 anterior, la aplicacion le mostrara el ultimo de la categoria anterior.
Quiz&la explicacion sea algo confusa. Por eso el siguiente grafico puede aclarar
las cosas. La figura 8.10 muestra como lleva Shopping Bag a1 usuario a travks
de las distintas categorias. La navegacion sera igual que la de 10s productos. Al
llegar a1 ultimo producto de una categoria, pasamos a1 primero de la siguiente.

I
I
I
I
I
I
I I
I
I
I
I
I
I

Categoria j
siguiente
I
I

Figura 8.10. Navegar a travCs de las categorias


298 Shopping Bag: un carro de la cornpra en JavaScript

manager.html
Toda esta funcionalidad la tenemos en el archivo rnanager.htrnZ.

Ejemplo 8.4. manager.htm1


1 <HTML>
2 <HEAD>
3 <TITLE>Shopping Bag Manager</TITLE>
4 < STYLE TYPE= '' text / css " >
5 <!--
6 TD (font-weight: bold; margin-left: 20; margin-right: 20;
padding: 10)
7 //-->
8 </STYLE>
9 </HEAD>
10 <BODY onLoad="freshStart( ) ; makeproducts( 1 ; "
11 LINK=BLUE ALINK=BLUE VLINK=BLUE>
12 <SCRIPT LANGUAGE='I JavaScriptl.2 SRC="inventory.js "></SCRIPT>
13 <SCRIPT LANGUAGE="JavaScriptl. 2">
14 <!--
15 var gimmecontrol = false;
16 var browseContro1 = false;
11 var curCLoc = -1;
18 var curPLoc = -1;
19 var infostr = " ;
20 var shoppingBag;
21 function B a g 0 I
22 this.taxRate = .06;
23 this.taxTota1 = 0;
24 this.shipRate = .02;
25 this.shipTota1 = 0;
26 this.subTota1 = 0;
27 this.bagTota1 = 0;
28 this.things = new Array ( ) ;
29
30
31 shoppingBag = new B a g 0 ;
32
33 function showstore0 {
34 gimmecontrol = false;
35 var header = '<HTML><TITLE>Category</TITLE><BODY
BGCOLOR=FFFFFF>';
36 var intro = '<H2>ShoppingBag Product Categories</H2><B>';
37 var footer = '</DL></BLOCKQUOTE></BODY></HTML>';
38 var storeStr = '<BLOCKQUOTE><DL>';
39 for (var i = 0; i < categorySet.length; i++) {
40 storeStr += '<DT><AHREF="javascript :
parent.frames[ll .recall('+
41 i + ' , O ) ; " > ' + categorySet[i].name + '</A><DD>'+
42 categorySet[il.description + '<BR><BR>';
Shopping Bag: un carro de la compra en JavaScript 299

43 1
44 infoStr = header + intro + storeStr + footer;
45 parent.frames[O].location.replace('javascript:
46 parent.frames[ll.infoStr');
47 I
48
49 function portal ( ) {
50 gimmeControl = false;
51 parent.frames[O].location.href = "search/index.html";
52 1
53 function display(cOffset, p0ffset) {
54 if(!browseControl) {
55 alert("Start shopping by selecting a product category from
56 + "Show All Categories or searching products from Product
Search. ) ;
'I

57 return;
58 I
59 gimmeControl = true;
60 if (curPLoc + poffset < 0 I I curPLoc + poffset ==
61 categorySet[curCLocl.prodLine.length) {
62 if (curPLoc + pOffset < 0 ) I
63 if (curCLoc - 1 < 0) { curCLoc = categorySet.length - 1; 1
64 else { curCLoc--; 1
65 curPLoc = categorySet[curCLocl.prodLine.length - 1;
66 1
67 else if (curPLoc + pOffset ==
categorySet[curCLoc].prodLine.length) {
68 if (curCLoc + 1 == categorySet.length) { curCLoc = 0; 1
69 else { curCLoc++; 1
70 curPLoc = 0;
71 1
72 1
73 else {
74 if (curCLoc + coffset < 0 I I curCLoc + coffset ==
75 categorySet.length) {
76 curCLoc = (curCLoc + coffset < 0 ? categorySet.length - 1 :
0) ;
77 1
78 else { curCLoc += c0ffset; 1
79 if (coffset == -1 1 1 coffset == 1) { curPLoc = 0; }
80 else if (pOffset == 0 ) {
81 curPLoc = (curPLoc >= categorySet[curCLoc].prodLine.length
? O :
82 curPLoc)
83 1
84 else { curPLoc = curPLoc + pOffset; 1
85 1
86 infoStr = '<HTML><HEAD><TITLE>ProductName</TITLE></HEAD>' +
87 '<BODY><TABLECELLPADDING=3><TR><TDVALIGN=TOP COLSPAN=2>' +
88 '<FONT FACE=Tahoma><H2>Shopping Bag: <I>' +
89 categorySet[curCLocl.name + '</I></H2><TR>'+
300 Shopping Bag: un carro de la compra en JavaScript

90 '<TD VALIGN=TOP><IMG SRC="' +


91 categorySet[curCLocl.prodLine[curPLocl.icon.src +
92 '"></TD><TDVALIGN=TOP><FONT FACE=Tahoma>' +
93 '<B>Name: </B>' +
categorySet[curCLoc].prodLine[curPLoc].name +
94 '<BR><B>Description:</B>' +
95 categorySet[curCLocl .prodLine[curPLocl.description + '<BR>'+
96 '<B>Price: </B> $ ' +
97 numberFormat(categorySet[curCLoc].prodLine[curPLocl.pr~ce~ +
'/, +
98 categorySet[curCLocl.prodLine[curPLocl.unit + '<BR>' +
99 '<B>PLU: < / B > ' + categorySet[curCLocl.prodLine[curPLocl.plu +
100 '</TD></TR></TABLE></BODY></~T~~>~;
101 parent.frames[O].location.href = 'javascript:
1 0 2 parent.frames[ll.infoStr';
103 I
104
1 0 5 Eunction reCall(cReset, pReset) {
106 browseContro1 = true;
107 curCLoc = cReset
108 curPLoc = pReset
109 display ( 0, 0 ) ;
110 I
111
1 1 2 Eunction gimmeone (
113 if ( !gimmeContro
114 alert("Nothing on this screen to give you.");
115 return;
116 I
117 for (var i = 0; i < shoppingBag.things.1ength; i++) I
118 if (categorySet[curCLocl.prodLine[curPLocl.plu ==
119 shoppingBag.things[il.plu) {
1 20 alert("That's already in your bag. You can change the
quantity + 'I

121 "by choosing View/Change Bag. " ) ;


122 return;
123 I
124 I
125 shoppingBag.things[shoppingBag.things.lengthl =
126 categorySet[curCLocl.prodLine[curPLocl;
127 shoppingBag.things[shoppingBag.things.length - 11.itemQty = 1;
128 shoppingBag.things[shoppingBag.things.length - 11.category =
129 categorySet[curCLocl.name;
130 alert("0K. You put the " +
131 shoppingBag.things[shoppingBag.things.length - 11.name +
132 in your bag. " ) ;
133 I
134
135 function showBag0 {
136 if (shoppingBag.things.length == 0 ) (
137 alert("Your bag is currently empty. Put some stuff in.");
Shopping Bag: un carro de la compra en JavaScript 301

138 return;
139 1
140 gimmeContro1 = false;
141 var header = '<HTML><HEAD><TITLE>YourShopping Bag</TITLE>'+
142 '</HEAD><BODYBGCOLOR=FFFFFF +
143 'onLoad="parent.frames[ll.runningTab(document.forms[Ol);"~';
144 var intro = '<H2>YourShopping Bag!! !</H2>'+
145 '<FORM onReset="' +
146 'setT~meout(\'parent.frames[ll.runningTab(document.forms[Ol)
147 \ ' , ' + '25);">';
148 var tableTop = '<TABLEBORDER=l CELLSPACING=O CELLPADDING=5>'+
149 '<TR><TH><B>Index' +
150 '<TH><B>Product<TH><B>Category'+
151 '<TH><B>PLU<TH><B>Unit Price' +
152 '<TH><B>Quantity<TH><B>ProductTotal' +
153 '<TH><B>Remove'+
154 ' </TR>' ;
155 var itemStr = " ;
156 for (var i = 0; i < shoppingBag.things.1ength; i++) {
157 itemStr += '<TR>' +
158 '<TDALIGN=CENTER>' + (i + 1) + '</TD>' +
159 '<TD>'+ shoppingBag.things[il.name + '</TD>'+
160 '<TD>'+ shoppingBag.things[i].category + '</TD>'+
161 '<TD>'+ shoppingBag.things[il.plu + '</TD>'+
162 '<TD ALIGN=RIGHT>$' +
163 parent.frames[1l.numberFormat(shoppingBag.things[il
164 .price) + '</TD>'+
165 '<TD ALIGN=CENTER>' +
166 parent.frames[ll.genSelect(shoppingBag.things[il.price,
167 shoppingBag.things[il.itemQty, i) + '</TD>'+
168 '<TD ALIGN=CENTER><INPUT TYPE=TEXT SIZE=10 VALUE="' +
169 parent.frames[ll.numberFormat(shoppingBag.things[il.price *
170 shoppingBag.things[i].itemQty) +
171 "' onFocus="this.blur() ;"></TD>'+
172 '<TDALIGN=CENTER><INPUT TYPE=CHECKBOX></TD>'+
173 '</TR>';
174 1
175 var tableBottom = '<TR>'+
176 Y T D ALIGN=RIGHT COLSPAN=b>SubTotal:</TD>' +
177 '<TDALIGN=CENTER><INPUTTYPE=TEXT SIZE=10 NAME="subtotal" ' +
178 onFocus="this.blur(); "></TD></TR>'+
179 '<TR><TDALIGN=RIGHT COLSPAN=6> + 6% Tax:</TD>' +
180 '<TD ALIGN=CENTER><INPUT TYPE=TEXT SIZE=10 NAME="tax" ' +
181 'onFocus="this.blur();"></TD></TR><TR><TD ALIGN=RIGHT
COLSPAN=6> +
182 '2% Shipping:</TD><TDALIGN=CENTER><INPUT TYPE=TEXT +
183 'SIZE=10NAME="ship"onFocus="this.blur() ; "></TD></TR>'+
184 ' <TR>' +
185 '<TDALIGN=RIGHT COLSPAN=3><INPUTTYPE=BUTTON VALUE="Check
Out" +
186 'onClick="parent.frames[ll.checkOut(this.form);"></TD>'+
302 Shopping Bag: un carro de la compra en JavaScript

187 ' <TD ALIGN=RIGHT><INPUTTYPE=RESET VALUE="Reset Qtys"></TD>


188 + '<TDALIGN=RIGHT><INPUTTYPE=BUTTON VALUE="Change Bag" ' +
189 'onClick="parent.frames[ll.changeBag(this.form, true);">
</TD>' +
190 '<TD ALIGN=RIGHT>Total:</TD><TDALIGN=CENTER>' +
191 '<INPUTTYPE=TEXT NAME="total" SIZE=10
;">' +
onFocus="this.blur()
192 '</TD></TR>';
193
194 var footer = '</TABLE></FORM></BODY></HTML>';
195 infoStr = header + intro + tableTop + itemStr + tableBottom t
footer;
196 parent.frames[O].location.replace('javascript:
197 parent.frames[l].infoStr');
198 1
199
200 function genSelect(priceAgr, qty, idx) I
201 var selStr = '<SELECTonChange="this.form.elements[' + (idx * 3
+ 1) +
202 '].value = this.options[this.selectedIndexl.value;
203 parent.frames[l].runningTablthis.form);">';
204 for (var i = 1; i <= 10; i++) {
205 selStr += '<OPTIONVALUE="' + numberFormat(i * priceAgr) +
I , * , +

206 (i == qty ? * SELECTED' : " ) + ' > ' + i;


207 1
208 selStr += '</SELECT>';
209 return selStr;
210 1
211
212 function runningTab(form0bj) {
213 var subTotal = 0;
214 for (var i = 0; i < shoppingBag.things.1ength; i++)
215 subTotal += parseFloat(formObj.elements[(i * 3 ) + 1l.value);
216 1
211 form0bj.subtotal.value = numberFormat(subTota1);
218 formObj.tax.value = numberFormat(subTota1 *
shoppingBag.taxRate);
219 formObj.ship.value = numberFormat(subTota1 *
shoppingBag.shipRate);
220 formObj.tota1.value = numberFormat(subTota1 +
221 round(subTota1 * shoppingBag.taxRate) +
222 round(subTota1 * shoppingBag.shipRate)) ;
223 shoppingBag.subTota1 = formObj.subtotal.value;
224 shoppingBag.taxTota1 = form0bj.tax.value;
225 shoppingBag.shipTota1 = formObj.ship.va1ue;
226 shoppingBag.bagTota1 = form0bj.total.value;
221
228
229 function numberFormat(am0unt) {
230 var rawNumStr = round(amount) + " ;
Shopping Bag: un carro de la cornpra en JavaScript 303

231 rawNumStr = (rawNumStr.charAt(0)== ? ' 0 ' + rawNumStr


I . ' :
rawNumStr);
232 if (rawNumStr.charAt(rawNumStr.1ength - 3) == ' . ' ) {
233 return rawNumStr
234 1
235 else if (rawNumStr.charAt(rawNumStr.1ength - 2) == ' . ' ) I
236 return rawNumStr + ' 0 ' ;
237 1
238 else { return rawNumStr + ' . O O ' ; )
239 1
240 function round(number,decPlace){
241 decPlace = (!decPlace ? 2 : decPlace);
242 return Math.round(number * Math.pow(lO,decPlace)) /
243 Math.pow(lO,decPlace);
244 1
245
246 function changeBag(formObj, showAgain) {
247 var tempBagArray = new Array ( ) ;
248 for (var i = 0; i < shoppingBag.things.1ength; i++) I
249 if (!formObj.elements[(i * 3 ) + 21.checked) (
250 tempBagArray[tempBagArray.length] = shoppingBag.things[il;
251 tempBagArray[tempBagArray.length - 11 .itemQty =
252 formObj.elements[i * 31.selectedIndex + 1;
253 1
254 1
255 shoppingBag.things = tempBagArray;
256 if(shoppingBag.things.1ength == 0 ) {
257 alert("You've emptied your bag. Put some stuff in.");
258 parent.frames[ll.showStoreO;
259 1
260 else ( showBag0; )
261 1
2 62
263 function checkOut(form0bj) I
264 gimmeControl = false;
265 if(!confirm("Do you have every product in the right quantity ''
266 + "you need? Remember that you have to choose Change Bag to
267 + "remove products or change quantities. If so, choose OK to
check " +
268 "out. {
' 1 ) )

269 return;
270 1
271 if(shoppingBag.things.1ength == 0 ) {
272 showstore( ) ;
213 return;
274 1
275 var header = '<HTML><TITLE>ShoppingBag Check Out</TITLE>' +
276 '<BODY BGCOLOR=FFFFFF>':
277
278 var intro = '<H2>ShoppingBag Check Out</H2><FORM METHOD=POST '
279 + 'ACTION="http://your.webserver.com/cgi-bin/bag.cqi~'+
304 Shopping Bag: un carro de la compra en JavaScript

280 'onSubmit="returnparent.frames[ll.cheapCheck(this);">';
281
282 var shipInfo = '<TABLE BORDER=O CELLSPACING=O CELLPADDING=5>' +
283 '<TR><TD><B>ShippingInformation</TD></TR>'+
284 '<TR><TD>FirstName</TD><TD><INPUT TYPE=TEXT NAME="fname">
</TD>' +
285 '</TRxTRXTD>LastName</TD><TD>' +
286 '<INPUT TYPE=TEXT NAME="lname"></TD></TR><TR><TD>Company
Name</TD>' +
287 '<TD><INPUT TYPE=TEXT NAME="cname">.c/TD></TR><TR>' +
288 '<TD>StreetAddressl</TD><TD><INPUTTYPE=TEXT
NAME= " saddressl > * +
'I

289 '</TD></TR><TR><TD>Street Address2</TD>' +


290 '<TD><INPUTTYPE=TEXT NAME="saddress2"></TD></TR><TR>' +
291 ' < T D > C i t y c / T D > < T D > < I N P U T TYPE=TEXT NAME="city"></TD></TR>'+
292 '<TR><TD>State/Province</TD>'+
293 '<TD><INPUT TYPE=TEXT NAME="stpro"></TD></TR><TR>' +
294 ' < T D > C o u n t r y < / T D z < T D > < I N P U T TYPE=TEXT NAME="country"></TD>
</TR>' +
295 '<TR><TD>Zip/MailCode</TD><TD><INPUTTYPE=TEXT NAME="zip">
</TD>' +
296 'C/TR>cTR><TD><BR><BR>I/TD></TD><lTR></TABLE>~ ;
297
298 var payInfo = '<TABLE BORDER=O CELLSPACING=O CELLPADDING=5>' +
299 '<TR><TD><B>PaymentInformation</TD></TR>'+
300 '<TR><TD>CreditCard Type: &nbsp; &nbsp; &nbsp; </TD>' +
301 ' <TD>Vis a <INPUT TYPE=RADIO NAME= '' ctype VALUE= "visa 'I

CHECKED> ' +
302 '&nbsp; &nbsp; &nbsp; ' +
303 ' Amex <INPUT TYPE=RADIO NAME= "ctype" VALUE= "amex"> ' +
304 '&nbsp; &nbsp; &nbsp; ' +
305 'Discover <INPUT TYPE=RADIO NAME="ctype" VALUE="disc"> ' +
306 '&nbsp; &nbsp; &nbsp; </TD></TR>'+
307 '<TR><TD>CreditCard Number</TD>' +
308 '<TD><INPUTTYPE=TEXT NAME="cnumb"></TD></TR><TR>' +
309 '<TD>ExpirationDate</TD><TD><INPUTTYPE=TEXT NAME="edate">
</TD>' +
310 'C/TR><TR><TD><INPUTTYPE=SUBMIT VALUE="Send Order"></TD>'+
311 '<TD><INPUT TYPE=RESET VALUE="Clear Info"></TD></TR>'+
312 '</TABLE>';
313
314 var itemInfo = " ;
315 for (var i = 0; i < shoppingBag.things.1ength; i++) {
316 itemInfo += '<INPUT TYPE=HIDDEN NAME="prod' + i +
317 "' VALUE="' + shoppingBag.things[il .plu + + I - '

318 shoppingBag.things[il .itemQty + " ' > ' ;


319
320 var totalInfo = '<INPUT TYPE=HIDDEN NAME="subtotal" VALUE=" ' +
321 shoppingBag.subTota1 + " ' > ' +
322 '<INPUT TYPE=HIDDEN NAME="taxtotal" VALUE="' +
323 shoppingBag.taxTota1 + " ' > ' +
Shopping Bag: un carro de la compra en JavaScript 305

324 '<INPUT TYPE=HIDDEN NAME="shiptotal" VALUE=" i


325 shoppingBag.shipTota1 + " ' > ' +
326 '<INPUT TYPE=HIDDEN NAME="bagtotal" VALUE=" +
327 shoppingBag.bagTota1 + " ' > ' ;
328
329 var footer = '</FORM></BODY></HTML>';
330
331 infoStr = header + intro + shipInfo + payInfo + itemInfo i

332 totalInfo + footer;


333 parent.frames[O].location.replace('javascript:
334 parent.frames[ll.infoStr');
335 1
336
337 function cheapCheck(form0bj) {
338 for (var i = 0; i < formObj.length; i++) {
339 if (formObj[i].type == "text" && formObj.elements[il.value ==
'I ) I
340 alert ("Youmust complete all fields.");
341 return false;
342 }
343 1
344 if(!confirm("If all your information is correct, " i
345 "choose OK to send your order, or choose Cancel to make
changes." ) ) {
346 return false;
347 1
348 alert("Thank you. We'll be living off your hard-earned money
soon.");
349 shoppingBag = new Bag();
350 showstore( ) ;
351 return true;
352 I
353
354 function help0 {
355 gimmeContro1 = false;
356 parent.frames[O] .location.href = "intro.htm1";
357 }
358
359 function freshstarto {
360 if (parent.frames[O].location.href ! = "intro.htm1"){ help0 ; }
361 }
3 62
363 //-->
364 </SCRIPT>
365 <TABLE ALIGN=CENTER BORDER=O>
366 <TR>
367 <TD>
368 <A HREF="javascript:gimmeOne ( 1 ; ">Gimme One<A>
369 </TD>
370 <TD>
371 <A HREF="javascript: showBag ( ) ; ">View/ChangeBag<A>
306 Shopping Bag: u n carro de la compra en JavaScript

372 < /TD>


313 <TD>
374 < A HREF="javascript: showStore();">Show All Categories<A>
375 </TD>
316 <TD>
377 <A HREF="javascript: portal ( ) ; ">Product Search<A>
378 </TD>
379 <TD>
380 < A HREF="javascript: help ( ) ; ">Help<A>
381 </TD>
382 < / TR>
383 </TABLE>
384 <TABLE ALIGN=CENTER BORDER=O>
385 <TR>
386 <TD>
387 <A HREF="javascript: display(-l,O);">PreviousCategory<A>
388 </TD>
389 <TD>
390 <A HREF="javascript: display(O,-l);">PreviousProduct<A>
391 </TD>
392 <TD>
393 <A HREF="javascript: display(O,l);">Next Product<A>
394 < /TD>
395 <TD>
396 < A HREF="javascript: display(l,O);">NextCategory<A>
397 </TD>
398 < /TR>
399 < /TABLE>
400 </BODY>
401 </HTML>

Una nota rapida. iSe ha fijado que todo el c6digo de JavaScript se encuentra
dentro de una etiqueta BODY? Como a1 principio se cargan las imagenes y se
crean 10s objetos, Netscape Navigator mostrara un fondo gris en la ventana (En
este caso, en el marco) hasta que se complete todo el proceso. El explorador ana-
lizara el contenido de la etiqueta BODY y se encontrara con el atributo BGCOLOR.
Asi corregira el color antes de proceder con el resto del trabajo que tiene que
realizar.

Variables
A continuacion tenemos el codigo que hace que se muestren 10s productos en
la pantalla. E n las lineas 15-18 se configuran cuatro variables, y en las lineas
53-103 se define la funcion display ( ) . L a variable girnrneControZ le indica a la
aplicacion si hay algo en la pantalla (un producto) que se pueda agregar a la bol-
sa de la compra. La variable browsecontrol se encarga de que se cumpla la regla
que dice que el usuario ha de empezar la compra haciendo clic sobre el vinculo
"Show All Categories" o sobre "Product Search" (regla 1 ) . Vamos a ver estas dos
Shopping Bag: un carro de la cornpra en JavaScript 307

variables varias veces a lo largo de la aplicacion, per0 display ( ) es la primera


que trabaja con ellas:
var gimmecontrol = false;
var browseControl = false;
var curCLoc = -1;
var curPLoc = -1;

Las variables curCLoc y curPLoc albergan 10s respectivos numeros de indice de


la categoria y producto del objeto que aparece en pantalla. Son 10s numeros de 10s
que hemos hablado en la seccion anterior. A pesar de que el valor arbitrario que
se les ha asignado a ambos es -1, se les puede cambiar en cualquier momento,
ya que tendran que mostrar el numero de referencia de la categoria y producto.
En breve, veremos mas sobre ellos. Vamos a centrarnos de momento en lo que
esta pasando. Aqui tenemos el contenido de las lineas 53-103:
function display(c0ffset. p0ffset) {
if(!browseControl) {
alert("Start shopping by selecting a product category from Show +
"All Categories or searching products from Product Search.");
return;
1
gimmecontrol = true;
if (curPLoc + poffset < 0 1 1 curPLoc + poffset ==
categorySet[curCLocl .prodLine.length) {
if (curPLoc + poffset < 0 ) {
if (curCLoc - 1 < 0) { curCLoc = categorySet.length - 1; 1
else { curCLoc--; 1
curPLoc = categorySet[curCLoc].prodLine.length - 1;
1
else if (curPLoc + poffset == categorySet[curCLocl.prodLine.length)
{
if (curCLoc + 1 == categorySet.length) { curCLoc = 0; 1
else { curCLoc++; 1
curPLoc = 0;
1
}
else {
if (curCLoc + coffset < 0 1 1 curCLoc + coffset ==
categorySet.length) {
curCLoc = (curCLoc + coffset < 0 ? categorySet.length - 1 : 0 ) ;
1
else { curCLoc += cOffset; 1
if (coffset == -1 I I coffset == 1) { curPLoc = 0; 1
else if (pOffset == 0 ) {
curPLoc = (curPLoc >= categorySet[curCLocl.prodLine.length ? 0 :
curPLoc)
1
else { curPLoc = curPLoc + p0ffset; 1
308 Shopping Bag: u n carro de la cornpra en JavaScript

1
infoStr = '<HTML><HEAD><TITLE>ProductName</TITLE></HEAD>'+
'<BODY><TABLE CELLPADDING=3><TR><TD VALIGN=TOP COLSPAN=2>' +
'<FONT FACE=Tahoma><H2>Shopping Bag: < I > ' +
categorySet[curCLocl.name + ' < / I > < / H 2 > < T R >+'
' < T D VALIGN=TOP><IMG SRC="' +
categorySet[curCLocl .prodLine[curPLocl.icon.src +
' " > < / T D > < T DVALIGN=TOP><FONT FACE=Tahoma>' +
'<B>Name: < / B > ' + categorySet[curCLoc].prodLine[curPLoc].name +
'<BR><B>Description:< / B > ' +
categorySet[curCLocl.prodLine[curPLoc].description + ' < B R > '+
'<B>Price: < / B > $ ' +
numberFormat~categorySet~curCLocl.prodLine[curPLocl.price)+ ' / ' +
categorySet[curCLoc].prodLine[curPLoc].unit + ' < B R > ' +
'<B>PLU: < / B > ' + categorySet[curCLoc].prodLine[curPLocl.plu +
'c/TD></TR></TABLE></BODY>~/HTM~>';

parent.frames[O].location.href =
'javascript:parent.frames[ll.infoStr';
1

display( )
La funcion display ( ) ha de desarrollar tres acciones:
Determinar si puede mostrar un producto.
Determinar la categoria/producto que quiere ver el usuario.
Mostrar el producto.

El primer trabajo es muy sencillo. Si el valor de browseControZ es true, la res-


puesta sera si. El valor actual debrowseControZ es false. Una vez que el usuario
seleccione un producto de "Product Search'' o una categoria a travCs de "Show
A1 Categories", el valor de browsecontrol se cambiara a true.Ahora display ( )
puede encargarse de 10s trabajos 2 y 3 . Como se puede mostrar un producto en
la pantalla, el valor de gimmeControZ sera true.
Obskrvese que la funcion display ( ) espera dos argumentos: cOffset y pOffset.
Uno contiene un valor que determina hasta donde se desplazara tomando como
referencia el numero de la categoria en la que se encuentra en ese momento el
usuario. El otro hace igual, per0 con el numero del producto. cOffset y pOffset
pueden ser positivos, negativos o cero. Para simplificar las cosas, supongamos
que nuestro comprador cumple la primera regla y que ahora puede utilizar 10s
enlaces "Next" Y "Previous" para moverse a travCs del inventario. El codigo de
estos vinculos se encuentra en las lineas 386-397:

<TD>
<A HREF="javascript: display(-1,O); ">Previous Category<A>
Shopping Bag: un carro de la compra en JavaScript 309

</TD>
<TD>
<A HREF="javascript: display( 0, -1) ; ">Previous Product<A>
</TD>
<TD>
< A HREF="javascript: display(0,l); " > N e x t Product<A>
</TD>
<TD>
< A HREF="javascript : display ( 1 , O ); " > N e x t Category<A>
</TD>

Cada uno de estos enlaces llaman a la funci6n d i s p l a y ( ) y le pasan un par de


enteros. En la tabla 8.1 vemos la explicacion de lo que representa cada una de es-
tas llamadas. No olvidemos que curCLoc es el numero de la categoria y curPLoc
el numero del producto.
Tabla 8.1. Determinar el valor de curCLoc y curCPloc

Vinculo Argument0 Explicacion


que se pasa
Previous Category -1, 0 Agrega -1 a curCLoc; agrega 0 a
curPLoc.
Previous Product 0, -1 Agrega 0 a curCLoc; agrega -1 a
curPLoc.
Next Product 0,1 Agrega 0 a curCLoc; agrega 1 a
curPLoc.
Next Category 1, 0 Agrega 1 a curCLoc; agrega 0 a
curPLoc.

Excepciones a la regla
Tiene sentido. Si queremos retroceder una categoria, restamos 1 a1 numero de
la categoria en la que nos encontramos. Si queremos ver la siguiente categoria,
entonces sumaremos 1. Per0 existen tres excepciones a esta regla que requieren
cierta explicacih:

1. No hay ninguna categoria cuyo numero sea -1. Si el numero de la categoria


o el del producto es 0 y el usuario selecciona el vinculo "Previous Category"
o "Previous Product" respectivamente, Shopping Bag se encontrara con un
error.
2. Nohaycategoriasconelnurnerocategoryset[ c a t e g o r y s e t . l e n g t h ] .
Como solamente h a y c a t e g o r y s e t .l e n g t h categorias, el numero de Csta
nunca podra ser mayor que c a t e g o r y s e t . l e n g t h -1. Si dicho numero
310 Shopping Bag: u n carro de la compra en JavaScript

es categoryset.length -1,y el usuario selecciona el vinculo "Next


Product" o "Next Category", nos encontraremos con un error de JavaScript.
Y lo mismo ocurrira con 10s productos.
3 . Al pasar de una categoria a otra hace que siempre se muestre el primer
producto de Csta, independientemente del numero que haya aparecido en
la pantalla.

Las lineas 60-85 dan la funcionalidad deseada y se acomodan a estas nuevas


imposiciones. La verdad es que el us0 de declaraciones $-else anidadas hace que
el codigo sea muy largo, por eso conviene dedicar unos minutos a su analisis.

if (curPLoc + poffset < 0 I I curPLoc + poffset ==


categorySet[curCLocl.prodLine.length) {
if (curPLoc + pOffset < 0 ) I
if (curCLoc - 1 < 0) ( curCLoc = categorySet.length - 1; )
else ( curCLoc--; )
curPLoc = categorySet[c~rCLocl.prodLine.length - 1;
)
else if (curPLoc + poffset == categorySet[curCLocl.prodLine.length) {
if (curCLoc + 1 == categorySet.length) ( curCLoc = 0; )
else { curCLoc++; )
curPLoc = 0;
1
1
else I
if (curCLoc + coffset < 0 11 curCLoc + cOffset == categorySet.length)
{
curCLoc = (curCLoc + cOffset < 0 ? categorySet.length - 1 : 0);
1
else ( curCLoc += cOffset; 1
if (coffset == -1 I I coffset == 1 ) { curPLoc = 0 ; )
else if (poffset == 0 ) {
curPLoc = (curPLoc >= categorySet[curCLocl.prodLine.length ? 0 :
curPLoc)
1
else { curPLoc = curPLoc + pOffset; )
1

Mediante el siguiente pseudo-codigo vamos a intentar aclarar un poco el fun-


cionamiento de este bloque de sentencias. Los numeros de cada linea del codigo
coinciden con 10s de nuestra traduccion:

1 IF el numero del producto sera demasiado pequeAo o demasiado


grande THEN (73)
2 IF el numero del producto sera demasiado pequeiio THEN ( 7 4 )
3 IF el numero de la categoria sera demasiado pequeiio THEN
el ndmero de la categoria serd igual a1 ndmero de la
categoria menos 1 ( 7 5 )
Shoming Bag: un carro de la comDra en IavaScrbt 31 1

4 ELSE el ndmero de la categoria es igual a si mismo menos


1 (76)
5 el ndmero del producto es igual a1 ndmero de 10s
productos que hay en el ndmero de la categoria menos
1 (77)
6 ELSE IF el ndmero del producto sera demasiado grande THEN (79)
7 IF el ndmero del producto sera demasiado grande THEN el
ndmero de la categoria sera igual a 0 (80)
8 ELSE el ndmero de la categoria es igual a si mismo m b s 1
(81)
9 el ndmero del producto es igual a 0 ( 8 2 )
10 ELSE (85)
11 IF el ndmero de la categoria sera demasiado pequeiio OR
demasiado grande THEN (86)
12 IF el ndmero de la categoria es demasiado pequeiio THEN el
ndmero de la categoria sera igual a1 ndmero de la
categoria menos 1 (87)
13 ELSE el ndmero de la categoria es igual a 0 (88)
14 ELSE el ndmero de la categoria es igual a si mismo mas la
compensacih de la categoria (89)
15 IF la compensacih de la categoria es igual a -1 OR 1 THEN el
ndmero del producto sera igual a 0 (90)
16 ELSE IF la compensacih del producto es igual a 0 THEN (91)
17 IF el ndmero del producto es mayor o igual que el ndmero
de 10s productos que tiene el ndmero de la categoria
THEN el ndmero del producto sera igual a 0 (92)
18 ELSE el n6mero del producto es igual a si mismo mas el numero
de compensaci6n del producto (94)

El bloque if externo controla las variables si no se obtiene el numero del pro-


ducto despues de 10s dos primeros intentos. El bloque else externo controla las
variables si no se consigue determinar el numero de la categoria en 10s dos
primeros intentos. Para que se cumpla la tercera excepcion, la linea 80 hara que
el numero del producto sea 0 si el numero de compensacion de la categoria sube
o baja una unidad.

Construccion de la pagina
Una vez que la aplicacion conoce el numero de la categoria y del producto,
puede proceder a crear el c6digo HTML de la pagina en la que se mostrara el
producto. Casi todo el codigo restante de la funcion display[) se ha dedicado a
mostrar el producto en la pantalla. Vamos a ver las lineas 86-102:

infoStr = '<HTML><HEAD><TITLE>Product Name</TITLE></HEAD>' +


'<BODY><TABLECELLPADDING=3><TR><TDVALIGN=TOP COLSPAN=2>' +
'<FONT FACE=TahomaxH2>Shopping Bag: < I > ' + categorySet[curCLocl.name
+ '</I></H2><TR><TD VALIGN=TOP><IMG SRC="' +
categorySet[curCLocl.prodLine[curPLoc].icon.src +
'"></TD><TDVALIGN=TOP><FONT FACE=Tahoma><B>Name:< / B > ' +
312 Shopping Bag: un carro de la compra en JavaScript

categorySet[curCLoc].prodLine[curPLocl .name + '<BR>'+


'<B>Description: </B>' +
categorySet[curCLoc].prodLine[curPLoc].description+ '<BR>'+
'<B>Price: </B> $ ' +
numberFormat(categorySet[curCLocl.prodLine[curP~ocl.price) + '/' +
categorySet[curCLoc].prodLine[curPLoc].unit + '<BR>' +
'<B>PLU: </B>' + categorySet[curCLocl.prodLine[curPLocl .plu +
'</TD></TR></TABLE></BODY></HT~L>~;
parent.frames[Ol.location.href= 'javascript:parent.frames[l].infoStr';

Como se puede ver, todo se basa en una gran concatenacion de codigo HTML
para hacerse con el valor de la variable infoStr. Vea que 10s valores de curPLoc y
curCLoc son de vital importancia para establecer la referencia con la informacion
del producto. categoryset [curCLocI es la encargada de referirse a la catego-
ria correcta, mientras categoryset[curCLocI .prodLine [curPLocI se refie-
re a1 producto correcto. Una vez que se han determinado 10s valores de curCLoc
y curPLoc, podremos mostrar la informacibn del producto deseado.
DespuCs de que infoStr tenga todo el codigo HTML que necesita para mostrar el
producto, la propiedad href que se encuentra en el marco superior de trabajo
mostrara este valor. Para ello se utiliza el protocolo javascript : . No olvide-
mos que debido a1 alcance de este protocolo, tendremos que establecerle una
referencia absoluta (por ejemplo, parent.frames [ 1 ] .infostr en vez de infoStr).
Esta tecnica se ha visto en el segundo capitulo de este libro.

Pass 3: mostrar todas las categorias


Cuando el usuario selecciona el vinculo "Show All Categories" tiene otra forma
de navegar a travks de 10s productos. La funcion showstore ( ) se hace cargo de
esta tarea, como se puede ver en las lineas 33-47:

function showStore0 (
gimmeContro1 = false;
var header = '<HTML><TITLE>Category</TITLE><BODYBGCOLOR=FFFFFF>';
var intro = '<H2>Shopping Bag Product Categories</H2><B>';
var footer = '</DL></BLOCKQUOTE></BODY></H~~L>~;
var storeStr = '<BLOCKQUOTE><DL>';
for (var i = 0; i < categorySet.length; i++) (
storeStr += '<DT><A HREF="javascript: parent.frames [ 11 .recall( ' + i
+ ' , O);">' + categorySet[i].name + '</A><DD>'+
categorySet[i].description + '<BR><BR>';
1
infoStr = header + intro + storeStr + footer;
parent.frames[O].location.replace('javascript:
parent.frames[ll.infoStr');
1
Shopping Bag: un carro de la compra en JavaScript 313

Mostrar el primer producto


Obviamente, la primera regla obliga a que la primera vez que el usuario accede
a un producto, tenga que hacerlo desde el vinculo "ShowAll Categories"o "Product
Search". La propiedad "Product Search'' la veremos en breve. Ahora nos vamos a
centrar en "Show All Categories". Es muy sencillo mostrar todas las categorias.
showstore ( ) se limita a ejecutarse con todos 10s elementos de categoryset, ge-
nerando una lista con vinculos donde se puede encontrar el nombre y la descrip-
cion de cada uno de 10s productos. Tras la ultima categoria, este vinculo sera una
lista encadenada (a la que tambiCn se conoce como infoStr) cuyo valor sera la
propiedad href correspondiente a1 marco superior de trabajo. El codigo que apare-
ce en cada etiqueta HREF sera igual a:

javascript: parent.frames[ll.reCall(' + i + ' , 0 )

A1 hacer clic sobre cualquiera de 10s vinculos se invocara a la funcion recall ( )


de rnanager.htmZ. Este es el contenido de las lineas 105-110:

function reCall(cReset, pReset) {


browseContro1 = true;
curCLoc = cReset;
curPLoc = pReset;
display(0, 0 ) ;
1

recall ( ) espera dos argumentos, el numero de la categoria, representado por


el valor de i en la linea 42, y el numero 0. El valor de i se asigna a curCLoc. De
esta forma se determina la categoria que quiere ver el usuario. El numero 0 se
asigna a curPLoc. iRecuerda la tercera excepcion 3? Cuando el usuario acceda a
una categoria, la aplicacion le mostrara el primer producto, que es ProdLine [ 0 I .
A continuacion, recall ( ) llama a la funcion display ( ) y le entrega dos ceros
como argumentos. La primera vez que se analiza la declaracion if (lineas 60-85)
se asume que el usuario puede ver u n producto o una categoria con un valor
inferior o superior que curCLoc o curPLoc. En realidad, la funcion recall ( ) po-
see el conjunto de valores de esas variables, por lo que no tendra que "ir a nin-
gun sitio". El usuario quiere ver el producto asociado con 10s valores de curCLoc
y curPLoc. Esto es lo que se obtiene a1 pasar dos ceros y el codigo que se encarga
de ello se encuentra en las lineas 60-85.

iDonde esta el codigo DHTML?


ObsCrvese que las paginas que se han producido no contienen ningun c6digo
DHTML. Ni capas. iPor quC no? L a mayoria de 10s exploradores pueden trabajar
314 Shopping Bag: un carro de la cornpra en JavaScript

con JavaScript 1.2. Hemos dedicado un par de capitulos a trabajar el tema de la


portabilidad del codigo SHTML entre plataformas. iPor quk razon vamos a dar
u n paso atras ahora? Cuando se carga por primera vez la aplicacion, ino po-
driamos crear una capa para cada producto e ir mostrando y ocultando capas?
Si pero.. .
Si la carga anticipada de imagenes se convierte en un proceso demasiado largo,
puede ser algo contraproducentr. Como hemos comentado anteriormente, si
tenemos demasiados grhficos, la carga anticipada puede agotar la paciencia del
comprador. Cuando se crea una capa para cada producto, hace que se carguen
esas imagenes. Utilizaremos HTML normal y corriente para hacer el trabajo,
con o sin carga anticipada de imagenes.
En este momento el usuario podra navegar a travks de 10s productos y catego-
rias que desee. Ha llegado el momento de ver quk pasara cuando el usuario se
encuentre con un producto que quiera adquirir y decida incluirlo en el carro de
la compra.

Paso 4: agregar productos a la bolsa de la compra


Es muy sencillo incluir productos en la cesta de la compra. Lo unico que el
usuario tendra que hacer sera clic en el vinculo "Gimme One". Asi se invoca a la
funcion gimmeone ( ) correspondiente. Las lineas 112-1 33 se encargan de mos-
trar 10s detalles:

function gimmeone ( ) {
if (!gimmeControl) {
alert ("Nothing on this screen to give you. " ) ;
return;
>
for (var i = 0; i < shoppingBag.things.length; i++) {
if (categorySet[curCLocl.prodLine[curPLocl.plu==
shoppingBag.things[i].plu) {
alert("That's already in your bag. You can change the quantity +
"by choosing View/Change Bag. ;
I")

return;
1
1
shoppingBag.things[shoppingBag.things.lengthl =
categorySet[curCLocl.prodLine[curPLocl;
shoppingBag.things[shoppingBag.things.length - 11.itemQty = 1;
shoppingBag.things[shoppingBag.things.length - 11.category =
categorySet[curCLoc].name;
alert("0K. You put the " +
shoppingBag.things[shoppingBag.things.length - 11.name +
" in your bag. " ) ;
1
Shopping Bag: un carro de la compra en JavaScript 31 5

Lo primero que hace la funcion gimmeone ( ) es asegurarse de que hay algo en


la pantalla. El valor de la variable gimmeControZ sera true justo antes de que se
muestre el producto en la pantalla. Si no, otra funcion cualquiera se encargarh
de dibujar el contenido en la pantalla y el valor degimmeControZ sera false.Por
lo tanto, sigirnrneControZ es false,no habra ningun producto en la pantalla. La
aplicacion mostrara un avisto a1 usuario y devolvera gimmeone ( 1 . En caso con-
trario, se ejecuta gimmeone ( ) con todos 10s elementos del array things, que es
una propiedad del objeto ShoppingBagdel usuario y se utiliza fundamentalmen-
te para comprobar que el producto de la pantalla ya se encuentra en la bolsa de
la compra.
La funcion gimmeone ( no espera ningun argumento. Se basa generalmente en
10s valores correspondientes a curCLoc y curPLoc. Suponiendo que cada producto
cuente con un numero PLU unico, gimmeone ( ) tratara de establecer una con-
cordancia con cualquiera de 10s numeros PLU pertenecientes a 10s productos que
se encuentran en la bolsa. Si lo encuentra, entonces mostrara un aviso a1 usua-
rio con la finalidad de indicarle que dicho producto ya se encuentra incluido
dentro de la bolsa.
Si el producto no se encontrase en la bolsa, la funcion gimmeone ( ) lo guarda-
ria en ella. Per0 esto nos lleva a una situacion bastante interesante. Pensemos en
la siguiente pregunta: el objeto que se encuentre en la bolsa, isera true o false?
Si optamos por true,estaremos en lo correcto. En cualquier caso, 10s productos
que tenga el usuario en su bolsa se tendran que ampliar, ser mas complejos.
Todavia conservan su nombre, su descripcion, PLU y el precio, per0 necesitaran
una nueva propiedad que represente la cantidad solicitada y la categoria a la
que pertenece.
Por lo tanto, cada elemento de things, ha de disponer de una serie de propieda-
des que se le aiiadan dinhmicamente. Las lineas 125-129 muestran como la fun-
cion gimmeone ( ) aiiade estos productos especializadosa things y las propiedades
a cada objeto:
shoppingBag.things[shoppingBag.things.length] =
categorySet[curCLocl.prodLine[curPLocl;
shoppingBag.things[shoppingBag.things.length - 1l.itemQty = 1;
shoppingBag.things[shoppingBag.things.length - 11.category =
categorySet[curCLocl .name;

shoppingBag . things [ shoppingBag .things.length] crea una referencia


para el objeto product en categoryset [ curCLoc I .prodLine [ curPLoc I . Asi
se aiiade un objeto product "normalll a la cesta de la compra. Las dos lineas que
aparecen a continuacion se encargan de aiiadir las propiedades itemQty, cuyo
valor inicial era 1, y category a1 nombre de la categoria a la cual pertenece el
objeto.
31 6 Shopping Bag: un carro de la compra en JavaScript

Buscar por productos


Posiblemente se haya dado cuenta que el vinculo "Product Search" es una copia
de una de las propiedades vistas en el capitulo 1. Hemos modificado el motor de
busqueda del cliente para ajustarlo a las necesidades de 10s usuarios de Shopping
Bag. El resto es igual. Per0 las capacidades de busqueda se han reducido unica-
mente a la busqueda del booleano OR. En otras palabras, si cualquier parte de la
cadena de texto que introduce el usuario se encuentra dentro de la informacion
de un producto, se considera un resultado. N o se utiliza el booleano AND ni 10s
URL. De cualquier forma, con esta condicion nos basta para nuestros proposi-
tos. Hay una propiedad adicional de la cual carece el motor de busqueda del
capitulo 1. Los usuarios pueden introducir una cadena vacia, para lo cual bas-
tara con que pulsen la tecla Intro. La busqueda sera nula y la aplicacion mos-
trara todos 10s productos que tenga en su base de datos.
Shopping Bag: un carro de la compra en JavaScript 31 7

No vamos a entrar en el mismo nivel de detalle que en el capitulo 1 , per0 va-


mos a dedicar unos cuantos parrafos a mostrar lo sencillo que resulta ampliar
aplicaciones de JavaScript. Desputs de todo, a1 igual que con la mayoria de 10s
motores de busqueda, el usuario unicamente querra introducir algo de texto para
que la aplicacion se encargue de generar una serie de vinculos con 10s resulta-
dos. Con objeto de que todo esto funcione con nuestra aplicacion sin necesidad
de modificar demasiado el codigo, habra que efectuar 10s siguientes cambios en
el buscador:

Mostrar vinculos que puedan trabajar con el sistema de navegacion basado


en categorias/productos que explicamos en el paso 2.
Ser capaces de localizar cualquier producto de la base de datos.
Devolver vinculos para todos 10s elementos de la base de datos.

Afortunadamente, todos estos cambios se pueden efectuar en el archivo nav.htmZ.


Asi que no tendremos que vernos las caras con otros cientos de lineas de codigo.
Nos limitaremos a ver las lineas de nav.htmZ que nos interesan.

Convertir productos y categorias


Tenemos que cambiar un poco las cosas para que funcionen con suavidad. Los
cambios vienen de la mano de dos nuevas variables y de una funcion new, tal y
como se puede ver a continuacion:

var ref = top.frames[ll;


var prodProfiles = new Array();
function genProfile0 (
for (var i = 0; i < ref.categorySet.length; i + + ) I
for (var j = 0; j < ref.categorySet[il.prodLine.length; j + + ) (
prodProfiles[prodProfiles.lengthl = new Array(i, j ) ;
I

L a variable ref se utiliza como un alias de t o p . frames [ 1 I . Como la mayoria


de objetos y variables de las que se hablan en este motor de busqueda se encuen-
tran dentro de manager.htmZ, las referencias de 10s objetos que se encuentren
dentro de otros objetos, seran especialmente largas. Por eso, a1 utilizar ref en vez
de t o p . frames [ 1 I se acorta bastante la escritura del codigo. prodProfiZes inicia
un array vacio, per0 que se rellenara en breve por medio de una llamada a la fun-
cion genprofile ( ) .
L a funcion genprof ile ( ) unicamente tiene que hacer un trabajo para el cual
usara un sistema de referencia con todos 10s productos de las categorias. Para
ello utilizaremos el numero de la categoria y el del producto. Formara un par
318 Shopping Bag: un carro de la compra en JavaScript

con ambos, donde primer0 aparecera el numero de la categoria y luego el del


producto.
Por ejemplo, supongamos que i, el numero de la categoria, es igual a 1 y que j,
el numero del producto, es 2. Si revisamos el contenido de inventory.js, veremos
que categoryset [ i] .prodLine [ j ] se refiere a1 producto "Iglu' que se encuen-
tra dentro de la categoria "Buildings". Es algo asi como trabajar con las coorde-
nadas de un mapa.
Los buclesfor anidados de genprof ile ( ) se repetiran con todas las categorias
(i) y productos cj). Si lo deseamos, genprof ile ( ) puede tomar nota de la ubi-
caci6n de 10s productos dentro de la categoria. Para ello guardaria el par i,j en
u n array de su propiedad. Cuando termine, cada elemento de prodProfiZes repre-
sentara u n par categoria/producto que sefiala a un producto determinado den-
tro de una categoria.
Es posible que se pregunte: ise establece alguna referencia para 10s productos?
La respuesta es si. Per0 la funcion del motor de busqueda conoce todas las com-
binaciones posibles. Cada par se guarda como un elemento de prodProfiZes. Con
esto se facilita considerablemente el establecimiento de referencias (asi como las
busquedas e impresion en pantalla) de la informacion que exista en la base de
datos sobre un producto.

Buscar en la base de datos ya existente


L a version original del motor de busqueda se encargara de localizar el nombre,
descripcion y URL de una pagina Web. El que usaremos en Shopping Bag debe
localizar unos elementos parecidos. El problema es que hay que hacerlo de acuerdo
con la base de datos existente. Afortunadamente, podemos establecer una serie
de cambios en la funcion allowAny ( ) . Este es el contenido de nav.htrnZ:

function allowAny(t) [
var findings = new Array();
for (var i = 0 ; i < prodProfiles.1ength; i + + ) {
var compareElement = ref.categorySet[prodProfiles[il [Oll.
p r o d L i n e [ p r o d P r o f i l e s [ i l [ l ] I .name + ' ' +
ref.categorySet[prodProfiles[il [Oll.prodLine[prodProfiles[i1[111.
description + ' +
ref.categorySet[prodProfiles[i][Oll.prodLine[prodProfiles[il~lll.
price.toString0 + ' ' +
ref.categorySet[prodProfiles[il[Oll.prodLine[prodProfiles[il~lll.
plu;
compareElement = compareElement.toUpperCase0;
for (var j = 0; j i t.length; j + + ) {
var comparestring = t[jl.toUpperCaseO;
if (compareElement.indexOf(compareString) ! = -1) {
findings[findings.lengthl = new Array(prodProfiles[il L O ] ,
Shopping Bag: un carro de la compra en JavaScript 319

N o se han cambiado demasiadas cosas. Lo unico que nos interesa es el fin de la


busqueda. El usuario puede buscar el nombre, la descripcion, precio o PLU de un
producto. La funcion allowAny ( ) junta estos cuatro datos de 10s productos. h i ,
conseguimos una cadena bastante mas larga que la que forman las cadenas que
se han introducido en el campo de busqueda. Si aparece algun resultado, a1 si-
guiente elemento localizado se le asignara a un array conprodProf i les [ i I [ 0 I
y prodprof iles [ iI [ 1I como sus elementos. No olvidemos que estos dos ele-
mentos son enteros que se utilizaran para imprimir en breve el resultado en la
pantalla.

Navegacion por categorias/productos


Supongamos que efectuamos una busqueda y nos encontramos con un con-
junto de resultados. Los tendremos que imprimir en pantalla. No tendremos
ningun txito si preparamos un motor de bdsqueda donde se ignore la navega-
ci6n por productos/categorias. Es decir, aunque en la pantalla aparezcan todos
10s resultados, 10s usuarios han de ser capaces de ver 10s resultados como siem-
pre y moverse por ellos a travCs de 10s botones "Next"y "Prev".Vamos a hacer un
par de cambios en la funci6n f o r m a t R e s u l t s ( ) :

function formatResults(results, reference, offset) {


docObj.open0;
docObj.writeln('<HTML>\n<HEAD>\n<TITLE>Search Results</TITLE></HEAD>'
+ '<BODY BGCOLOR=WHITE TEXT=BLACK>'+
'<TABLEWIDTH=780 BORDER=O ALIGN=CENTER CELLPADDING=3><TR><TD>'+
'<HR NOSHADE WIDTH=lOO%></TD></TR><TR><TD VALIGN=TOP><B>'+
'Search Query: <I>' +
parent.frames[0].document.forms[0].query.value + '</I><BR>\n'+
'Search Results: <I>' + (reference + 1) + ' - ' +
(reference + offset > results.length ? results.length :
reference + offset) +
' of ' + results.length + '</I><BR><BR>'+ '<B>' +
'\n\n<!-Begin result set //-->\n\n\t<DL>');
var currentRecord = (results.length < reference + offset ?
results.length : reference + offset);
for (var i = reference; i < currentRecord; i++) {
docObj.writeln('\n\n\t<DT>' + '<FONT SIZE=4>' +
'<A HREF="javascript: top.frames[l].recall(' + results[i] [O] +
320 Shopping Bag: un carro de la compra en JavaScript

' , ' + results[i] [l] + ' ) " > ' +


ref.categorySet[resuIts[il[011.prodLine[results[i1 [ll].name +
'</A></FONT>\t<DD>' +
ref.categorySet~results[il[Oll.prodLine[results[il[lll.description
+ '\t<DD>'+ 'Price: <I>$' +
ref.numberFormat(ref.categorySet[results[ilLO] I .
prodLine[results[il [ll].price) +
'</I> &nbsp; &nbsp; &nbsp; + ' P L U Number: <I>' +
ref.categorySet[resuIts[il[0l1.prodLine[results[il [l]].plu +
'</I><P>');
I
docObj.writeln('\n\t</DL>\n\n<!-End result set //-->\n\n');
prevNextResults(results.length, reference, offset);
docObj.writeln('<HRNOSHADE WIDTH=100%>' +
'</TD>\n</TR>\nl/TARLE>\n~/~OD~>\n~/~T~~>~);
docObj.close0;
document.forms[O].query.selectO;
1

Cada resultado mostrara el nombre, descripcion, precio y numero PLU del pro-
ducto. Esta funci6n se repite con todos 10s elementos de results y utiliza 10s en-
teros results [ i ] [ 0 ] y results [ i ] [ 1] con objeto de acceder a la informacion
de prodline. En otras palabras, si el resultado tiene el siguiente aspecto:

results = new Array(


new Array(0, l), / / El elemento 0 representa el ndmero de l a
new Array(2, 21, / / categoria y el 1 el del producto
new Array(4, 1)
);

Entonces, 10s resultados de la busqueda contendran el secador de pel0 (catego-


ria 0, producto l), el monedero (categoria 2, producto 2) y las patatas fritas (ca-
tegoria 4, producto 1 ) . Con estos pares de numeros se facilita el almacenamiento
de pequefias cantidades de informacion y su posterior referencia.
Shopping Bag: u n carro de la compra en JavaScript 321

El codigo del vinculo


Ya tenemos 10s resultados en la pantalla pero, icomo lo podemos codificar para
que utilice el sistema de navegacion del que hemos hablado?
La solucion la tenemos en formatResults ( ) :

'<A ~ ~ ~ ~ = " j a v a s c r itop.frames[ll


pt: .recall(' + results[il [ O l + ' , ' +
results[il [ l l + ' ) " > '

Cada enlace usa un protocolo j avascript : para llamar a la funcion recall ( ) ,


que es la misma que utilizamos para ver la lista de productos a travks del enlace
"Show All Categories".recall ( ) esperaba dos argumentos: el numero de la ca-
tegoria y el del producto. Esto es lo que se ha estado utilizando en el motor de
busqueda. Todo lo que tenemos que hacer es incluir cada uno de 10s componen-
tes de 10s pares de numeros en la llamada. Asi, siguiendo con el ejemplo anterior
del secador, tendremos el siguiente vinculo:

<A HREF="javascript: top.frames[l] .reCall(O, 1)">Hairdryer</A>

Observese a continuaci6n lo que ocurre entre 0 y 1 cuando se acude a la lla-


mada recall ( ) :
322 Shopping Bag: un carro de la compra en JavaScript

function reCall(cReset, pReset) {


browseControl = true;
curCLoc = cReset;
curPLoc = pReset;
display(0, 0 ) ;
1

A la variable curCLoc se le asigna el valor del numero de la categoria de cReset;


lo mismo ocurre con curPLoc y pReset. El motor de busqueda coexiste con el res-
to de Shopping Bag.

Paso 5: cambiar el orden/registro


Cuando el usuario se quede sin dinero o cuando no le guste ninguno de 10s ar-
ticulos, querra salir de la aplicacion. Al hacer clic sobre "ViewKhange Bag" se
abre una puerta similar a la de la figura 8.8. L a bolsa de la compra tendra que
hacer algo mas que mostrar 10s productos seleccionados. Consideremos 10s si-
guientes requisitos:
Mostrar cada product0 y su categoria, numero PLU y precio unitario.
Proporcionar una forma interactiva de cambiar la cantidad de productos,
eliminar selecciones y volver a calcular 10s precios.
Mostrar 10s totales, incluyendo 10s correspondientes a 10s productos y ca-
tegorias, el subtotal y 10s impuestos.

Posiblemente no le sorprenda saber que hay una serie de funciones listas para
adaptarse a las necesidades de Shopping Bag. Son las siguientes:
showBag ( ) . Muestra el contenido de la bolsa de la compra.
genselect ( 1 . Genera listas de seleccion dinhmicas para cambiar la can-
tidad de productos.
runningTab ( ) . Efectua 10s calculos correspondientes y muestra 10s pre-
cios de 10s productos.
numberFormat ( ) . Se asegura de haber utilizado el formato correct0 en las
operaciones matematicas
round ( ) . Ajusta 10s calculos.
changeBag ( ) .Elimina las selecciones de 10s productos y cambia el numero
de estos que tenga el usuario en su bolsa de la compra.

La funcion showBag ( ) se encarga de la llamada en el mismo momento en que


el usuario hace clic sobre el vinculo. Veamos el contenido de las lineas 135-198:

function showBag ( ) {
if (shoppingBag.things.1ength == 0 ) I
Shopping Bag: un carro de la compra en JavaScript 323

alert("Your bag is currently empty. Put some stuff in.");


return;
)
gimmeControl = false;

var header = '<HTML><HEAD><TITLE>YourShopping Bag</TITLE>' t


'</HEAD><BODYBGCOLOR=FFFFFF ' +
'onLoad="parent.frames[ll.runningTab(document.forms[Ol);"~';

var intro = '<H2>YourShopping Bag!! !</H2><FORMonReset="'+


' +
'setTimeout(\'parent.frames[l].runningTab(document.forms[Ol)\',
'25);">';

var tableTop = '<TABLEBORDER=l CELLSPACING=O CELLPADDING=5>' t


'<TR><TH><B>Index'+
'<TH><B>Product<TH><B>Category'+
'<TH><B>PLU<TH><B>UnitPrice' +
'<TH><B>Quantity<TH><B>Product Total' +
'<TH>cB>Remove'+
' </TR>';

var itemstr = I n ;

for (var i = 0; i < shoppingBag.things.1ength; i++) {


itemstr += '<TR>'+
'<TD ALIGN=CENTER>' + (i + 1) + '</TD>'+
'<TD>'+ shoppingBag.things[i].name + '</TD>'+
'<TD>'+ shoppingBag.things[il.category + '</TD>'+
'<TD>'+ shoppingBag.things[il.plu + '</TD>'+
'<TDALIGN=RIGHT>$' +
parent.frames[l].round(shoppingBag.things[i].price) + '</TD>'+
'<TD ALIGN=CENTER>' +
parent.frames[l].genSelect(shoppingBag.things[il .price,
shoppingBag.things[i].itemQty, i) + '</TD>'+
'<TD ALIGN=CENTER><INPUT TYPE=TEXT SIZE=10 VALUE="' +
parent.frames[l].numberFormat(shoppingBag.things[il.price *
shoppingBag.things[i].itemQty) + ' " onFocus="this.blur();"></TD>'
+ 'ITD ALIGN=CENTER><INPUT TYPE=CHECKBOX></TD></TR>';
)

var tableBottom = '<TR>' +


'<TDALIGN=RIGHT COLSPAN=6>SubTotal:</TD>' +
'<TDALIGN=CENTER><INPUT TYPE=TEXT SIZE=10 NAME="subtotal" ' +
'onFocus="this.blur();"></TD></TR><TR>' +
'<TD ALIGN=RIGHT COLSPAN=6> + 6% Tax:</TD>' +
'<TD ALIGN=CENTER><INPUT TYPE=TEXT SIZE=10 NAME="tax" ' +
'onFocus="this.blur +
( ) :"></TD></TR><TR>'
'<TD ALIGN=RIGHT COLSPAN=6> + 2% Shipping:</TD>'+
'<TD ALIGN=CENTER><INPUT TYPE=TEXT SIZE=10 NAME="ship" ' +
'onFocus="this.blur();"></TD></TR>' +
'<TR><TDALIGN=RIGHT COLSPAN=3>' +
'<INPUTTYPE=BUTTON VALUE="Check Out" ' +
324 Shopping Bag: un carro de la compra en JavaScript

'onClick="parent.frames[l].checkOut(this.form):"~</TD>'+
'<TD ALIGN=RIGHT><INPUT TYPE=RESET VALUE="Reset Qtys"></TD>'+
'<TD ALIGN=RIGHT><INPUT TYPE=BUTTON VALUE="Change Bag" +
'onClick="parent.frames[l].changeBag(this.form,true):"></TD>'+
'<TD ALIGN=RIGHT>Total:</TD><TDALIGN=CENTER>' +
'<INPUT TYPE=TEXT NAME="total" SIZE=10 onFocus="this.blur(); " > ' +
'</TD></TR>'; ?u

var footer = '</TABLE></FORM></BODY></HTML>';


infoStr = header + intro + tableTop + itemStr + tableBottom + footer;
parent.frames[Ol.location.replace('javascript:
parent.frames[l].infoStr');
1

Veremos que showBag ( ) no hace otra cosa que generar la tabla de la figura 8.8.
Pero, showBag ( tendra que comprobar antes de nada que la bolsa de la com-
pra contiene algo:

if (shoppingBag.things.length == 0) I
alert("Your bag is currently empty. Put some stuff in.");
return:
1

Si things.length es igual a 0, entonces la bolsa del usuario estara vacia. No


tiene ningun sentido continuar. Si la bolsa del usuario contiene por lo menos un
producto, el proceso continuara. En las lineas 140-154 se ajustan la variables
header, intro y tabZeTop y se ajustan a la parte superior de la tabla, con el encabe-
zamiento de las columnas. Veamos como showBag ( ) llama a varias funciones:

gimmecontrol = false;
var header = '<HTML><HEAD><TITLE>YourShopping Bag</TITLE>' +
'</HEAD><BODYBGCOLOR=FFFFFF ' +
'onLoad="parent.frames[ll .runningTab(document.forms[O]);">';

var intro = '<H2>Your Shopping Bag!! !</H2><FORM onReset=' +


' +
'"setT~meout(\'parent.frames[l].runningTab~document.forms[Ol)\',
' 25) ; " > ' ;

var tableTop = '<TABLE BORDER=l CELLSPACING=O CELLPADDING=5>' +


'<TR><TH><B>Index'+
'<TH><B>Product<TH><B>Category' +
'<TH><B>PLU<TH><B>UnitPrice' +
'<TH><B>Quantity<TH><B>Product Total' +
'<TH><B>Remove'+
' </TR> ;

Todo lo que se ha generado es bastante estatico, a excepcion de la llamada del


controlador de eventos o n b a d a parent.frames [ 1I .runningTab ( ) . Veremos
Shopping Bag: un carro de la compra en JavaScript 325

como funciona en u n momento. Cuando se ha completado la informacion que


se utilizara en la cabecera de la tabla, se tendra que ejecutar la funcion con to-
dos y cada uno de 10s elementos de la bolsa de la compra. Como se puede supo-
ner, la funcion showBag ( ) se encarga de las repeticiones things. length, y
genera 10s datos que apareceran dentro de una de las filas de la tabla. El codigo
lo tenemos en las lineas 155-174:

var itemStr = * I ;

for (var i = 0; i < shoppingBag.things.1ength; i++) {


itemStr += ' < T R > ' +
' < T D ALIGN=CENTER>' + (i + 1) + ' < / T D > ' +
' < T D > '+ shoppingBag.things[i].name + ' < / T D > '+
' < T D > '+ shoppingBag.things[i].category + ' < / T D > '+
' < T D > ' + shoppingBag.things[i].plu + ' < / T D > '+
' < T D ALIGN=RIGHT>$' +
parent.frames[ll.round(shoppingBag.things[il.price) + ' < / T D > '+
' < T D ALIGN=CENTER>' +
parent.frames[ll.genSelect~shoppingBag.things[il.price,
shoppingBag.things[i].itemQty, i) + ' < / T D > '+
' < T D ALIGN=CENTER><INPUT TYPE=TEXT SIZE=10 VALUE="' +
parent.frames[l].numberFormat(shoppingBag.things[i].price *
shoppingBag.things[i].itemQty) + "' onFocus="this.blur();"></TD>' +
' < T D ALIGN=CENTER><INPUT TYPE=CHECKBOX></TD>' +
'</TR>' ;
1

Construccion de las listas de seleccion


Para ajustar las cabeceras de la tabla que acabamos de crear, el buclefor genera
una columna con el indice de productos (para que podamos contar 10s produc-
tos uno a uno), u n nombre, la categoria, el numero PLU, el precio por unidad,
una lista de seleccion para la cantidad, el coste total del numero de productos
seleccionados y una casilla de verificacion para eliminar el producto. Cada una
de estas propiedades contiene su propia etiqueta TD. L a creacion de la lista de
seleccion para la cantidad de elementos es mas complicada de lo que parece, por
eso nos vamos a centrar en ella con mas detalle. La lista se crea con la funcion
genselect ( ) . En este libro se han visto y veran otras variantes de esta misma
funcion, por lo que es posible que le resulte familiar. Este es el contenido de las
lineas 200-2 10:

function genSelect(priceAgr, qty, idx) I


var selStr = '<SELECT onChange="this.form.elements[' + (idx * 3 + 1) +
'].value = this.options[this.selectedIndexl.value; ' +
'parent.frames[l].runningTab(this.form);">';
for (var i = 1; i <= 10; i++) (
selStr += '<OPTION VALUE="' + numberFormat(i * priceAgr) + +' ' I '
326 Shopping Bag: un carro de la compra en JavaScript

(i == qty ? ' SELECTED' : ") + ' > ' + i;


1
selStr += '</SELECT>';
return selStr;
1

Esta funcion acepta tres argumentos: el precio, la cantidad y el numero del


producto (que es el valor de la variable i que aparece en el buclefor de showBag ( ) ).
Estos tres argumentos se utilizan para acceder a1 campo donde aparecera junto
a la lista de seleccion. He establecido que el usuario no pueda pedir mas de diez
unidades de cada producto. En cualquier caso, esta cantidad se puede elevar sin
ningun problema. Para crear esta lista de seleccion, se repite la ejecucion de la fun-
cion genselect ( ) de 1-10 veces y crea una etiqueta OPTION fuera de la si-
guiente sintaxis, que se suma a selstr:

selStr += '<OPTION VALUE="' + numberFormat(i * priceAgr) + "" +


(i == qty ? ' SELECTED' : " ) + ' > ' + i;

Las etiquetas OPTION creadas son muy sencillas. A su atributo VALUE se le asig-
nara el precio por unidad del producto multiplicado por i, que es la cantidad
asociada con esta opcion. Por ejemplo, u n producto que cueste $1.OO la unidad,
generara las siguientes etiquetas OPTION:

<OPTION VALUE="l.0 0 " SELECTEDsl


<OPTION VALUE="2.00">2
<OPTION VALUE="3.00">3
<OPTION VALUE="4.00">4
<OPTION VALUE="5.00">5
<OPTION VALUE="6.00">6
<OPTION VALUE="7.00">7
<OPTION VALUE="8.00">8
<OPTION VALUE="9.00">9
<OPTION VALUE="10.00">10

Si i es igual a la cantidad (qty)de productos seleccionados, la etiqueta OPTION


de la misma cantidad se marcara como (SELECTED).Como el valor predetermi-
nado de todos 10s elementos que se encuentran en la bolsa de la compra, inicial-
mente era 1, siempre se seleccionaran las etiquetas OPTION con el texto 1. Esta
forma de trabajar resulta especialmente util cuando el usuario quiere modificar
la cantidad de objetos seleccionados. Lo veremos en breve.
Vamos a saltar a1 valor original de seZStr. Vamos a verlo:

var selStr = '<SELECT onChange="this.form.elements[" + (idx * 3 + 1) +


'].value = this.options[this.selectedIndexl.value; ' +
'parent.frames[ll.runningTab(this.form);">"
Shopping Bag: un carro de la cornpra e n javascript 327

Cada lista de selecci6n tiene asociado un controlador de eventos onchange que


cambia el valor de elements [ ( i d x * 3 ) + 1 ] por el valor de la opci6n selec-
cionada. No olvidemos que todos 10s valores de OPTION proceden de multiplicar
el precio unitario del producto por 1-10. Siguiendo con el ejemplo anterior de
$1.OO, si el usuario selecciona 4 de la lista, el valor de elements [ ( idx * 3 ) +
1 I cambiara a 4.00.Es mas complicado de lo que parece. iDe quk elemento del
formulario estamos hablando? Para contestar esta pregunta tendremos que re-
visar el codigo de showBag ( ) que se encuentra en las lineas 166-167:

parent.frames[l].genSelect(shoppingBag.things[il.price,
shoppingBag.things[i].itemQty, i )

En la linea 200 podemos ver 10s argumentos que espera genselect ( ) :

function genSelect(priceAgr, qty, idx) {

Se puede observar que el valor de idx siempre coincidira con el de i, que se ini-
cia en la linea 156 y se incrementa de uno en uno. Si el usuario tiene 10 produc-
tos en la bolsa de la compra, el rango de idx sera 1-10. Por lo tanto, las etiquetas
de seleccion que creara the showBag ( ) ser5n asi:

<!-- Para el primer producto / / - - >


<SELECT onChange='this.form.elements[ll.value =
this.options[this.selectedIndexl.value;
parent.frames[l].runningTab(this.form);'>
< ! - - Para el segundo producto / / - - >
<SELECT onChange='this.form.elements[4l.value =
this.options[this.selectedIndexl.value;
parent.frames[l].runningTab(this.form);'>
< ! - - Para el tercer producto / / - - >
<SELECT onChange='this.form.elements[l0l.value =
this.options[this.selectedIndexl .value;
parent.frames[l].runningTab(this.form);'>

Vamos a pensar en esto un momento. f o r m . elements [ 13 constituye el cam-


PO de texto que se encuentra a continuacion de la primera lista de seleccion. En
el momento en que se crea el controlador de eventos onchange, el campo de
texto no existe adn. f o r m . elements [ 4 I hace referencia a1 campo de texto que
se encuentra a continuacih de la lista de seleccion de la siguiente columna.
Para establecer una referencia con el campo de texto tendremos que calcular el
indice que tendra el elemento despuks de crear el formulario. Asi he obtenido
( i d x * 3 ) + 1.
Cada producto seleccionado aparecerh en una fila de la tabla. Estas filas con-
tendran 10s tres elementos del formulario y 10s mostrara en el mismo orden:
328 Shopping Bag: un carro de la compra en JavaScript

Una lista de seleccion para la cantidad


Un campo de texto donde se mostrara el total del producto
Una casilla de verificacion para eliminar el producto

Es decir, que el primer campo de texto sera elements [ 1I ; el siguiente, corres-


pondera a elements [ 4 I . For lo tanto, el campo de texto sera el segundo ele-
mento de cada grupo de tres. La funcion genselect ( ) crea el codigo adecuado
multiplicand0 constantemente el resultado por 3 y surnando 1 .

Guardar el registro de la factura


iQut pasa con el resto del codigo del controlador de eventos onchange? Este
controlador de eventos, ademas de publicar el total de 10s productos en el campo
correspondiente, llama a la funcion runningTab( ) con objeto de que calcule el
importe total de la compra. El codigo de runningTab( ) lo tenemos en las lineas
212-227:

function runningTab(form0bj) {
var subTotal = 0;
for (var i = 0; i < shoppingBag.things.1ength; i++) {
subTotal += parseFloat(formObj.elements[(i * 3 ) + 11.value);
1
form0bj.subtotal.value = numberFormat(subTota1);
formObj.tax.value = numberFormat(subTota1 * shoppingBag.taxRate);
form0bj.ship.value = numberFormat(subTota1 * shoppingBag.shipRate);
form0bj.total.value = numberFormat(subTota1 +
round(subTota1 * shoppingBag.taxRate) + round(subTota1 *
shoppingBag.shipRate)) ;
shoppingBag.subTota1 = form0bj.subtotal.value;
shoppingBag.taxTota1 = form0bj.tax.value;
shoppingBag.shipTota1 = form0bj.ship.value;
shoppingBag.bagTota1 = formObj.total.value;
1

Esta funcion es muy sencilla. Desarrolla tres operaciones basicas:

1 . Calcula y muestra el subtotal, que representa la suma de 10s totales (lineas


213-217).
2 . Calcula y muestra 10s impuestos (lineas 218-222).
3. Guarda finalmente 10s totales en las propiedades del objeto ShoppingBag
(lineas 223-226).

Las funciones numberFormat ( ) y round ( ) se aseguran de que todas las funcio-


nes matematicas se desarrollen completamente y de que se muestran en forma-
to 0.00 6 .OO. Este es el contenido de las lineas 229-239:
Shopping Bag: un carro de la compra en javascript 329

function numberFormat(amount) {
var rawNumStr = round(amount) + " ;
rawNumStr = (rawNumStr.charAt(0)== ' . ' ? ' 0 ' + rawNumStr :
rawNumStr);
if (rawNumStr.charAt(rawNumStr.1ength - 3 ) == ' . ' ) {
return rawNumStr
}
else if (rawNumStr.charAt(rawNumStr.1ength - 2 ) == ' . ' ) {
return rawNumStr + ' 0 ' ;
1
else { return rawNumStr + ' . G O ' ; 1
}

numberFormat ( ) se limita a devolver la cantidad calculada, redondeada y en


formato 0.00. Para ello, llama a round ( ) y analiza la cantidad que se le entrega
como argumento. La funcion round ( ) redondea el numero tomando por defec-
to dos decimales, tal y como se puede ver:

function round(number,decPlace) {
decplace = (!decPlace ? 2 : decPlace);
return Math.round(number *
Math.pow(lO,decPlace)) / Math.pow(lO,decPlace);
}

Tecnica en JavaScript:redondeo de numeros y conversion


de cadenas
Se puede pensar que pedir a JavaScript que utilice a l e r t ( ) con el produc-
to de 1.15 * 3, el precio de cada b o b de patatas ffitas por tres unidades, no
es muy complicado. Todos sabemos que el resultado sera 3,45.Si lo hace-
mos, nos encontraremos con que el resultado es 3.4499999999999997.
iDe donde sale este resultado? JavaScript representa 10s n h e r o s con coma
flotante con valores IEEE-754de 64-bit. Los bit de precision de 10s valores
con coma flotante pueden hacer que nos encontremos con este tip0 de
resultados. Si desea mlls informcibn, consulte el contenido de estas pllgi-
nas Web:

http://help.netscape.com/kb/client/970930-1.html
h ttp://www.psc.edu/general/software/packages/ieee/ieee.html

Independientementede la raz6n, necesitaremos una soluci6n. LQut tal si


le pedimos a JavaScript que se haga la multiplicaci6n 115 * 31 El resulta-
do sera cien veces mayor que la cantidad obtenida en la multiplicacion
330 Shopping Bag: un carro de la compra e n JavaScript

Ajustar showgag(): mostrar 10s totales


Ahora, cada uno de 10s productos seleccionados tendra su propia fila en la tabla
con sus propios elementos para calcular las cantidades y eliminarlos de la bolsa
de la compra. Ha llegado el momento de incluir las dos ultimas filas que conten-
dran 10s campos donde se mostrara el subtotal, el total de las tasas y el total de
la compra. TambiCn contendran 10s botones de accion "Check Out", "Reset Qtys"
y "Change Bag." Este es el contenido de las lineas 175-1 94:

var tableBottom = '<TR>'+


'<TDALIGN=RIGHT COLSPAN=6>SubTotal:</TD>'+
'<TDALIGN=CENTER><INPUTTYPE=TEXT SIZE=10 NAME="subtotal" ' +
'onFocus="this.blur( ) "></TD></TR><TR>' +
'cTD ALIGN=RIGHT COLSPAN=6> + 6% Tax:</TD>cTDALIGN=CENTER>' +
'<INPUTTYPE=TEXT SIZE=10 NAME="tax" onFocus="this.blur ( ) ;" > ' +
'</TD></TR><TR><TD ALIGN=RIGHT COLSPAN=6> + 2% Shipping:</TD>'+
'<TDALIGN=CENTER><INPUTTYPE=TEXT SIZE=10 NAME="ship" ' +
'onFocus="this.blur +
( ) ; "></TD></TR><TR>'
'ITD ALIGN=RIGHT COLSPAN=3><INPUTTYPE=BUTTON VALUE="Check Out" +
'onClick="parent.frames[ll.checkOut(this.form);">I/TD~' +
'<TD ALIGN=RIGHT><INPUTTYPE=RESET VALUE="Reset Qtys"></TD>'+
'<TDALIGN=RIGHT><INPUTTYPE=BUTTON VALUE="Change Bag" ' +
'onClick="parent.frames[ll.changeBag(this.form,true);"></TD>' +
'<TD ALIGN=RIGHT>Total:</TD><TDALIGN=CENTER>' +
'<INPUTTYPE=TEXT NAME="total"SIZE=10 onFocus="this.blur() ;">' +
'</TD></TR>';
var footer = '</TABLE></FORM></BODY></HTML>';
Shopping Bag: u n carro de la compra en JavaScript 33 1

A1 revisar este codigo HTML vemos que inicialmente, 10s campos donde se
mostrara el total estan vacios. La llamada a la funcion runningTab ( ) en el con-
trolador de eventos onLoad de este documento se encargar6 de publicar 10s valo-
res de dichos campos. Cada uno de 10s campos tiene el siguiente c6digo:

onFocus='this.blurO;'

Como no hay ninguna necesidad de modificar su contenido, a1 hacer clic en el


campo se desactiva su contenido. De esta forma se evita que 10s usuarios modi-
fiquen el contenido de 10s campos.
Tenemos tres botones: "Check Out", "Reset Qtys" y "Change Bag." Los vemos
por separado.

"Check Out"
Cuando el usuario ya ha terminado de comprar, tendra que introducir la in-
formation necesaria para efectuar el pago y confirmar el pedido. Al hacer clic en
este boton, la aplicaci6n llama a la funcion checkout ( ) que se encarga de ha-
cer dos cosas:

Genera un formulario de pedido donde se introducira la informacion


necesaria para efectuar el pago.
Se genera una serie de camposHIDDEN adicionales que representan a todos
10s productos seleccionados.

Esta funcion es larga, asi que la dividiremos en dos partes. Aqui tenemos el
contenido de las lineas 263-312:

function checkOut(form0bj) {
gimrneContro1 = false;
if(!confirm("Do you have every product in the right quantity + 'I

"you need? Remember that you have to choose Change Bag to remove + 'I

"products or change quantities. If so, choose OK to check out.")) {


return;
}
if(shoppingBag.things.length == 0 ) {
showstore ( ) ;
return;
I
var header = '<HTML><TITLE>ShoppingBag Check Out</TITLE>' +
'<BODY BGCOLOR=FFFFFF>';
var intro = '<H2>ShoppingBag Check Out</H2><FORM METHOD=POST " +
' +
'ACTION="http://your_web_serverver/cgi-~in/bag.cgi"
'onSubmit="returnparent.Frames[l].cheapCheck(this);">';

var shipInfo = '<TABLE BORDER=O CELLSPACING=O CELLPADDING=5>' +


332 Shopping Bag: un carro de la compra en JavaScript

'<TR><TD><B>Shipping Informationc/TD></TR><TR>'+
'<TD>FirstName</TD>iTD><INPUTTYPE=TEXT NAME="fname"></TD></TR>' +
'<TR><TD>LastName</TD><TD><INPUTTYPE=TEXT NAME="lname">'+
'</TD></TR><TR><TD>Company Name</TD>' +
'<TD><INPUTTYPE=TEXT NAME="cname"></TD>i/TR><TR>' +
'<TD>StreetAddreSsl</TD><TD><INPUTTYPE=TEXT NAME="saddressl">'+
'</TD></TR><TR><TD>Street Address2</TD><TD>'+
'<INPUTTYPE=TEXT NAME="saddress2"></TD></TR><TR>' +
'<TD>City</TD><TD><INPUT TYPE=TEXT NAME="city"></TD></TR>'+
'<TR><TD>State/Province</TD><TD><INPUT TYPE=TEXT NAME="stpro">'+
'</TD></TR><TR><TD>Country</TD><TD>' +
'<INPUT TYPE=TEXT NAME="country"></TD></TR><TR>' +
'<TD>Zip/MailCode</TD><TD><INPUTTYPE=TEXT NAME="zip"></TD>'+
'</TR><TR>ITD><BR><BR></TD></TR~</TA~LE>';

var payInfo = '<TABLEBORDER=O CELLSPACING=O CELLPADDING=5><TR>'+


'<TD><B>PaymentInformation</TD></TR><TR>' +
'<TD>CreditCard Type: &nbsp; &nbsp; &nbsp; </TD>' +
'<TD>Visa<INPUT TYPE=RADIO NAME="ctype"VALUE="visa"CHECKED> ' -t
'&nbsp; &nbsp; &nbsp; +
'Amex <INPUT TYPE=RADIO NAME='ctype'VALUE="amex"> +
'&nbsp; &nbsp; &nbsp; +
'Discover <INPUT TYPE=RADIO NAME="ctype"VALUE="disc"> +
'&nbsp; &nbsp; &nbsp; </TD></TR><TR>'+
'<TD>CreditCard Number</TD><TD><INPUTTYPE=TEXT NAME="cnumb">'+
'</TD></TR><TR><TD>Expiration Date</TD>' +
'<TD><INPUTTYPE=TEXT NAME="edate"></TD></TR><TR>'+
'<TD><INPUTTYPE=SUBMIT VALUE="Send Order"></TDz'+
'<TD><INPUTTYPE=RESET VALUE="Clear Info"></TD></TRz</TABLE>';

Es muy larga, per0 completamente estatica. El codigo que tenemos aqui se en-
carga de generar el formulario de la figura 8.8. El formulario contiene una serie
de campos con objeto de que el usuario introduzca la informacion solicitada. El
nombre de cada uno de estos campos es unico para que el script del lado del ser-
vidor pueda identificar toda la informacion que recibe.
La ultima parte de la funcion checkout ( ) prepara 10s campos HIDDEN para que
registre todos 10s productos que haya seleccionado el usuario. Aqui tenemos el
contenido de las lineas 3 14-319:

for (var i = 0; i < shoppingBag.things.length; i++) {


itemInfo += '<INPUT TYPE=HIDDEN NAME="prod' + i +
"' VALUE="' + shoppingBag.things[i].plu + +
' - I

shoppingBag.things[i].itemQty + ' " > ' ;

De esta forma se genera un campo HIDDEN cuyo nombre sera prod + el valor
de i. El valor se ajustara a la sintaxis de la cantidad determinada por PLU. Asi,
si el usuario pide dos bolsas de patatas fritas, el valor de este campo HIDDEN se-
ria VALUE= '' FRI1- 2 " . DespuCs de crear un campo HIDDEN para cada uno de 10s
Shopping Bag: u n carro de la cornpra en JavaScript 333

productos, se empieza a trabajar en la cantidad. La funcion checkout ( ) suma


todos 10s totales y asigna el resultado a un campo H I D D E N .
Las lineas 320-327 nos enseiian como se hace:

var totalInfo = '<INPUTTYPE=HIDDEN NAME="subtotal"VALUE=" t


shoppingBag.subTota1 + "I>' +
'<INPUT TYPE=HIDDEN NAME="taxtotal"VALUE="' +
shoppingBag.taxTota1 + "'>' +
'<INPUTTYPE=HIDDEN NAME="shiptotal"VALUE=" +
shoppingBag. shipTotal + " > +
'<INPUTTYPE=HIDDEN NAME="bagtotal"VALUE=" +
shoppingBag.bagTota1 + " ' > ' ;

Se afiade un boton "Send Order" para enviar la informacion y otro Tlear Info"
para borrar el contenido del formulario. Antes de seguir, conviene observar que
el controlador de eventos onsubmit es el que se encarga de llamar sobre la mar-
cha a cheapcheck( ) . Esta funcion se limita a asegurarse que, cuando se envia
el formulario, no queda ningun campo vacio.
Este es el contenido de las lineas 337-352:

function cheapCheck(form0bj) {
for (var i = 0; i < formObj.length; i++) {
if (formObj[i].type == "text" && formObj.elements[il.value == " ' I ) {
alert ( "You must complete all fields. " ) ;
return false;
>
I
if(!confirm("If all your information is correct, choose OK to send " +
"your order, or choose Cancel to make changes.")) {
return false;
1
alert("Thankyou. We'll be living off your hard-earned money soon.");
shoppingBag = new Bag();
showstore( 1 ;
return true;
I

Si alguno de 10s campos se dejase en blanco, cheapcheck( ) avisaria a1 usua-


rio y devolveria false para evitar que se enviase el formulario. Es posible que
se quiera diseiiar una funcion de validacion que se adapte a las necesidades de
cada caso, per0 a1 menos, esta funci6n que tenemos aqui representa un punto
de inicio. Observese tambikn que, si el usuario ha cumplimentado correctamen-
te el formulario, a la variableshoppingBag se le asignara un Bag ( ) nuevo y se Ila-
mara a la funcion Store ( ) con el fin de que se encargue de ordenar 10s productos
seleccionados por categorias.
334 Shopping Bag: un carro de la cornpra en lavascript

Completar la pantalla
Una vez que la funcion showBag ( ) header, intro, tableTop, itemStr, totallnfo,
tabZeBottom yfooter ha cambiado 10s valores que se necesitaran para crear una
pantalla con sentido, la funcion colocara toda esta informacion en la pantalla.
Veamos el contenido de las lineas 195-197:

infoStr = header + intro + tableTop + itemStr + tableBottom + footer;


parent.frames[0l.location.replace('javascript:parent.frames[11
.infoStr');

iQue ocurre en el servidor?


Nuestra compra esta tocando a su fin, por lo menos en lo que a1 cliente se re-
fiere. Pero, icomo se desarrolla el pago y como se reciben 10s productos? Hay que
preparar a1 servidor para que se encargue de 10s pedidos, es decir, debemos crear
una base de datos. He incluido una serie de botones CGI escritos en Perl que se
encargaran de crear un archivo ASCII en el servidor para cada una de las tran-
sacciones que tengan lugar y escribiran toda la informacion que se tenga sobre
el product0 y el pago del cliente. El siguiente procedimiento nos muestra como
podemos adaptar estos botones a Shopping Bag. Se asume que el servidor Web
con el que trabajamos tiene instalado Perl y que el directorio donde se encuentra
el archivo que contiene 10s botones, bag.pZ, tiene activos 10s permisos de escritu-
ra y lectura.

1. Copiamos el archivo bag.cgi en dicha carpeta (por ejemplo, en cgi-bin)


donde podremos ejecutar 10s scipts en CGI.
2. En la linea 279, cambiamos el atributo ACTION por la ruta del archivo
bag.cgi.

Cuando 10s compradores seleccionen el boton "Check Out" se llamara a1 archi-


vo bag.cgi, que se encargara de procesar la informacion y de mostrar un acuse
de recibo para que el usuario sepa que el servidor ha recibido la informacion
solicitada. Por cierto, para las transacciones seguras tendremos que trabajar con
un servidor SSL (Secure Socket Layer) o con algun tip0 de encriptacion. De esta
forma se podra intercambiar la informacion del pedido y 10s datos de la tarjeta
de crkdito del usuario.

"ResetQtys"
Este boton se limita a borrar 10s cambios que se han efectuado en el formula-
rio. Per0 si se selecciona "Change Bag", podremos guardar permanentemente 10s
cambios efectuados en la bolsa de la compra.
Shopping Bag: un carro de la compra en JavaScript 335

"Change Bag"
Supongamos que el usuario ha efectuado una serie de cambios en la cantidad
de productos y que ha seleccionado la casilla de verificacion "Remove"de un par
de productos. Al seleccionar "Change Bag" la aplicacion guarda estos registros y
vuelve a mostrar la bolsa de la compra, per0 reflejando ahora 10s nuevos conte-
nidos y cantidades de la bolsa de la compra. Esta es la funcion changeBag ( )
que se encuentra en las lineas 246-261 :

function changeBag(formObj, showhgain) {


var tempBagArray = new Array();
for (var i = 0; i < shoppingBag.things.length; i++l {
if (!formObj.elements[(i* 3 ) + 21.checked) {
tempBagArray[tempBagArray.lengthl = shoppingBag.things[il;
tempBagArray[tempBagArray.length - 11 .itemQty =
formObj.elements[i * 31.selectedIndex + 1;
J
>
shoppingBag.things = tempBagArray;
if(shoppingBag.things.1ength == 0) {
alert("You've emptied your bag. Put some stuff in.");
parent.frames [l]. showstore ( ) ;
I
else ( showBag0 ; )

Esta tknica es muy sencilla:

1. Crea un array vacio llamado tempBagArray.


2. Se ejecuta con todos 10s elementos (10s productos) del array.
3 . Si no se activa la casilla de verificacion del elemento seleccionado, se
aiiadira el elemento a tempBagArray.
4. Se ajusta a continuacion la cantidad del product0 que se haya seleccionado
en tempBagArray a la cantidad seleccionada por el usuario a travCs de la
lista de seleccion.
5. Hace que 10s productos Sean iguales afempBagArrayy muestra el contenido
de la bolsa de la compra.

La referencia a la correspondiente casilla de verificacion se efectuara de la misma


forma en que la funcion runningTab ( ) establece el valor total de 10s produc-
+
tos. El elemento del indice (i * 3 ) 2 se encargara de acceder a todas las casillas
de verificacion. Si no hay ningun elemento dentro de tempBagArray despuCs de
que se repita las ejecuciones, entonces el usuario habra eliminado todos 10s pro-
ductos de la bolsa de la compra. Se le avisara y se le permitira que acceda de nue-
vo a la seccion de compras.
336 Shopping Bag: un carro de la compra en JavaScript

Las funciones olvidadas


Ya lo hemos revisado practicamente todo a exception de tres funciones. La ver-
dad es que juegan un papel muy pequeiio, per0 hay que mentarlas. Se trata de
portal ( ) , help ( ) y freshstart ( ) . En las lineas 49-52 tenemos el contenido
de portal ( :

function portal0 I
gimmeContro1 = false;
parent.frames[O].location.href = "search/index.html";
I

Puesto que el motor de busqueda no va a mostrar cualquier producto, se asig-


nara el valor false a la VariablegimmeControZ antes de que se cargue el archivo
search/index.html. Se trata de la misma rutina que se vio con help ( ) en las li-
neas 354-357:

function h e l p 0 {
gimmeContro1 = false;
parent.frames[Ol.location.href = "intro.htm1";
I

L a unica diferencia es que parent.frames[ 0 1 carga intro.htmZ. Por ultimo,


tenemos la funcion freshstart ( ) :

function freshstart0 {
if (parent.frames[O].location.href != "intro.htm1") { help(); }
1

La funci6n f reshstart ( ) se ejecuta cada vez que el usuario cargue el archivo


shopset.html, parent.frames [ 0 I siempre empieza con intro.htrn2. Lo tenemos
en el controlador de eventos onLoad de la linea 10.

Posibles ampliaciones
Tambien podemos aplicar un poco de creatividad. Estas son algunas de ellas:

I. Crear productos "mas inteligentes".


2. Agregar funciones de busqueda mas refinadas.
3 . Agregar propiedades de cookies para 10s compradores mas frecuentes.

Crear productos mas inteligentes


Supongamos que aiiadimos una propiedad, un array, a1 constructor del produc-
to que se encarga de 10s nombres y de 10s pares de numeros categoria/producto
Shopping Bag: un carro de la compra en JavaScript 337

relacionados con la vista que aparece en pantalla. El constructor del producto


podria tener el siguiente aspecto:

function product(name, description, price, unit, related) I


this.name = name;
this.description = description;
this.price = price;
this.unit = unit;
this.related = related;
this.plu = name.substring(0, 3).toUpperCaseO +
parseInt(price).toStringO;
this.icon = new Image();
return this;
1

El argument0 relacionado representa un array que podemos asignar a una pro-


piedad relacionada. Cuando el usuario revisa un producto, podra ejecutarlo con
todos 10s elementos relacionados, generando una serie de enlaces a ellos. De esta
forma se crearia un mercado cruzado.
Si todo esto nos parece demasiado nuevo, conviene que revisemos el sitio Web
de amazon.com. Buscaremos un libro de cualquier tema que nos interese. A con-
tinuacion, haremos clic sobre cualquiera de 10s vinculos que apareceran en la
lista de resultados, con lo que la aplicacion nos mostrara la revision del libro se-
leccionado y una lista con 10s libros que han seleccionado 10s usuarios que han
comprado dicha obra.

Agregar capacidades de busqueda mas refinadas


Podemos tratar de ampliar las capacidades de nuestro motor de busqueda de
Shopping Bag. En primer lugar, podriamos incluir la posibilidad de trabajar con
el booleano AND. No es nada complicado. Todo lo que habria que hacer seria co-
piar la funcion requireAll( ) de la aplicacion que se vio en el capitulo 1 y modi-
ficarla como se vio con a1lowAny ( ) . TambiCn tendremos que ajustar la funcion
validate ( ) para indicar quC funcion habra que utilizar.
En vez de buscar el producto en toda la base de datos, el usuario unicamente
tendra que buscar una o mas categorias. Consideremos la posibilidad de agregar
una lista de seleccion a nav.htrn2:

<SELECT MULTIPLE SIZE=5>


<OPTION VALUE= " Appl iances" >Appliances
<OPTION VALUE= "Building" >Building
<OPTION VALUE="Clothing">Clothing
<OPTION VALUE="Electronics">Electronics
< 0PTION VALUE= Food > F ood
I' 'I

<OPTION VALUE= "Hardware">Hardware


338 Shopping Bag: u n carro de la compra en JavaScript

<OPTION VALUE= Music '' >Music


I'

</SELECT>

Aunque se trata de codigo puro y duro, podremos utilizar una logica similar a
la utilizada en g e n s e l e c t ( ) para obtener un resultado mas dinamico. Cuando
se completa la busqueda, podremos buscar 10s productos cuya categoria haya
seleccionado el usuario, por lo tanto, estaremos refinando la busqueda.
Otra de las propiedades que podemos agregar es la capacidad de localizar pro-
ductos a partir de un rango de precios. Si un comprador busca productos con
precios inferiores a $50, mayores de $100 o entre ambas cantidades, podremos
trabajar con etiquetas > , < , >= y <=. Tendremos que modificar las funciones
v a l i d a t e ( ) y f o r m a t R e s u l t s ( ) para lograrlo.

lncluir cookies
Supongamos que nuestro sitio Web tiene productos que cambian con cierta
velocidad. Quiza, de esta forma 10s compradores nos visiten con mas frecuen-
cia. Seria conveniente hacer que no tuviesen que rellenar la informacion del pago
cada vez que quieran adquirir un producto. iPor qut no incluir las cookies que
se vieron en el capitulo 7? De esta forma, la informacion del usuario se guarda-
ria en su propio explorador, de tal manera que se podria utilizar para rellenar
automaticamente el formulario de pago de este usuario. Esta funcionalidad pue-
de ser algo desafiante, per0 cuando la complete seguro que se siente como una
especie de htroe de JavaScript. Con las funcionesGetCookie ( ) y S e t c o o k i e ( )
podremos escribir y extraer informacion de las cookies. Tambitn necesitaremos
una funcion que se encargue de extraer la informaci6n correspondiente a cada
uno de 10s campos del formulario y otra que la publique.
Capitulo 9
Cifrado e n JavaScript

Si acaba de completar el capitulo anterior, no hay ninguna duda de que este le


parecera un descanso. Es bastante mas sencillo y en 61 se analizan las tkcnicas de
cifrado que se pueden conseguir con JavaScript. L a aplicacion mezcla mensajes
de texto de tal forma que cualquiera que no posea la llave de lectura se encontra-
ra con un caos.
L a interfaz que se puede ver en la figura 9.1 es muy sencilla. Cuando se selec-
ciona Caesar, entonces la aplicaci6n mostrara un parrafo en el que se describe la
tecnica de cifrado que se ha seleccionado, una lista de seleccion a traves de la que
se puede seleccionar un numero el cual hara las veces de llave y tambitn un
campo de texto dentro del cual se introducira el codigo que se quiere encriptar/
desencriptar.
Se introduce el texto "JavaScript es el mejor lenguaje de encriptacion de todo el
planeta" en el area de texto. Se selecciona el 6 de la lista de seleccion y hacemos
clic en el boton "Encipher"para que la aplicacion cifre el contenido del cuadro de
texto, tal y como se puede ver en la figura 9.2. A continuacion vemos el resul-
tad0 obtenido:

pglgyixovz ky kr skpux rktmOgpk jk ktixovzgiot j k z u j u kr vrgtkzg

Si hacemos clic en "Decipher"recuperaremos el texto en su forma original. Ob-


servese que ahora todo el texto aparece en minusculas. El sistema de cifrado
Vigenere funciona de la misma forma. Seleccionamosla opcion "VigenereCipher"
de la lista de seleccion que se encuentra en la parte superior de la pantalla y ob-
tendremos el resultado de la figura 9 . 3 .
Cifrado en JavaScript 341

Made famous by mathematician Blaise de Vigenere. the


Vigenera cipher can be considered a 'dynamic' version of
the Caesar cipher Instead ofshdmg each plaintext
character by a fixed number, this cipher shifts characters
according lo rhe character index of a keyword you choose
such as dog

Since d. 0 . and gare letters 4, 15. and 7 oflhe alphabet,


each three plaintext characters are shded by 4.15, and 7.
respeclnely This application includes digrts 0-9 So your
keyword can h a e letters and numbers

Note This ciphet has many versions. one of h c h was


oensed D)I 2 ~ 1 Canol
5 author of/\l,ce In Wonoedand

Figura 9 . 3 . La interfaz del cifrado Vigenere

En esta ocasi6n no tenemos lista de selecci6n. Ahora aparece un cuadro de cam-


PO donde escribiremos la palabra o frase que utilizaremos como llave de cifrado.
En el ejemplo de la figura hemos escrito "codejunky" y a1 hace clic en el b o t h
"Encipher" obtenemos el resultado que muestra la figura 9.4:

loyelwlsdv hw yy agxrv 5rx4womi xr 2pqumydnm6p gi dlnc so y5nx2vo

Obviamente, a1 seleccionar el b o t h "Decipher" utilizando la llave ''code junky"


volveremos a obtener el texto original. Como quiz6 esta sea la primera vez que
se encuentra con el concept0 de cifrado, conviene que dediquemos un momento
para ver c6mo funcionan 10s dos sistemas utilizados en esta aplicaci6n.

Como funcionan 10s cifrados


Pero, iquk es un sistema de cifrado? Se trata de un algoritmo o conjunto de ellos,
que convierten el contenido de u n mensaje de texto en un conjunto de caracteres
que no tienen ningun sentido. Por medio de las autorizaciones pertinentes, se
puede devolver a este conjunto de simbolos el aspect0 de la frase original. Los si-
guientes tkrminos y definiciones nos ayudar6n a comprender el cifrado, desci-
frado y el c6digo que se encuentra detr6s de ellos.
342 Cifrado en JavaScript

IVigenere Q p h e r d

Made famous by mathematician Blase de V!genere the


Vigenere cipher can be considered a 'dynamic" version of
the Caesar cipher Instead of shifting each plaintext
character by a fixed number this cipher shins characters
according to the character index of a keyword you choose
such as dog

Since d 0 , and g are letters 4. 15, and? ofthe alphabet


each three plaintext characters are shined by 4. 15, and 7.
respectively This application includes digns 0-9 So your
keyword can have letters and numbers

Note This cipher has many versions, one ofwhich was


densed by L e w s Carroll. author ofAlice in Wonderland

Figura 9.4. Resultado de cifrado con Vigenere

El termino plaintext (texto plano) se refiere a1 mensaje original. Es el mensaje


que envia el remitente a1 destinatario.
El termino ciphertext (texto cifrado) se refiere a1 texto que se genera despues de la
encriptacion, es decir, el que aparece despues de aplicar el algoritmo. Este texto
se puede volver a descifrar.
Los sistemas de cifrado pueden utilizar una o varias llaves. Una llave (key) es
una cadena de texto o un conjunto de bits que se pueden utilizar para encriptar
o desencriptar datos. RSA Data Security, Inc. (http://www. rsa.com/), empresa
lider en la criptografia, asegura que la llave determina la conversion del texto
plano en texto cifrado. Como llave se puede utilizar cualquier cosa, desde la pa-
labra "Badajoz,"a la frase "10s ganadores no se rinden nunca", el numero binario
10011011 o cualquier conjunto de simbolos, como %--.;,(<<*&A.
Los sistemas de cifrado donde el remitente y el destinatario utilizan la misma
llave para encriptar y desencriptar el mensaje se conoce comosistemas de cifrado
de ZZave simetrica. Aquellos en 10s que 10s datos se cifran o descifran con una lla-
ve de distribucion publica per0 la operacion inversa tecnicamente se puede llevar
a cab0 con una llave privada se conocen como sistemas de cifrado de llave publi-
ca. Los sistemas que veremos en esta aplicacion utilizan la llave simktrica.
Hay cientos de sistemas de encriptacion documentados. Algunos cuentan con
cientos de afios de antiguedad y salieron a la luz gracias a 10s grandes cientificos
Cifrado en IavaScriDt 343

que han poblado la historia de la humanidad. Otros sistemas no tienen mas que
unas semanas de antigiiedad y proceden de las mentes inquietas de jovenes
adolescentes que llegan a ellas en momentos de euforia despues de batir el record
de su video juego favorito. Independientemente de cual sea la procedencia del sis-
tema, todos ellos se pueden clasificar en tres grupos: ocultacion, transposicion
y sustitucion.
Los sistemas de cifrado de ocultacion incluyen texto plano dentro del texto
cifrado. El destinatario sera quien ha de conocer que letras o simbolos ha de eli-
minar del texto cifrado para hacerse con el mensaje. Por ejemplo, aqui tenemos
u n texto cifrado:

Si quitamos todos 10s numeros, nos encontraremos con la frase "me gusta el
chocolate". Y ique hay de esta otra frase?

Ves el terreno esteril? Y ahora?

La primera letra de cada palabra revela el mensaje: Vete ya. Es cierto que ambos
sistemas son muy sencillos, pero hay gente que tiene mucha mas imaginacion a
la hora de ocultar mensajes. Por cierto, este sistema de ocultacion no necesita
texto cifrado, con 10s ejemplos que hemos visto anteriormente. Supongamos que
tenemos un bote de tinta invisible, como la que se utiliza en 10s articulos de bro-
ma. Con ella podemos enviar mensajes de texto. Per0 en un caso mas extremo,
encontramos a un hombre llamado Histiaeus, que vivi6 en el siglo V a.C., el cual
rap6 la cabeza de un esclavo de su confianza y tatuo un mensaje en su cabeza.
Cuando a1 esclavo le volvio a crecer el pelo, Histiaeus lo envio a encontrarse con
Aristagoros, quien volvio a rapar la caheza del esclavo para leer el mensaje que
le incitaba a la revolucion.
La encriptacion por transposicion tarnbien conserva 10s caracteres del texto
plano dentro del texto cifrado. Para encriptar el texto basta con cambiar la po-
sicion de 10s caracteres. Por ejemplo, mire esta frase:

le er aq cu efi ta um sp oe ta e

Es un conjunto de letras a las que se les ha cambiado su ubicacion. Si las colo-


casemos como estaban, encontrariamos el mensaje "que la suerte te acompaiie".
La encriptacion por sustitucion cambia cada caracter del texto plano por otro
simbolo. Veamos la siguiente frase:

8-16-12-1 1 21-16-4-16-20
344 Cifrado en JavaScript

Si sustituimos cada numero por su letra correspondiente, obtendremos la frase


"Hola a todos'' (Por ejemplo, la h es la octava letra del abecedario, la "0"la 16 ...)
El cifrado por sustitucion se puede utilizar con cualquier caracter, tanto para la
encriptacion como para la desencriptacion. Los dos sistemas de cifrado que ve-
remos en este capitulo utilizan el sistema de sustitucion.

Piratear el codigo
El texto cifrado que genera esta aplicacion puede parecer complejo a primera
vista. En realidad, cualquier experto en cifrado lo podra piratear en cuestion de
minutos con un lapiz y un papel como etnicas armas. Afortunadamente, las
medidas de seguridad de la actualidad utilizan algoritmos mucho mas seguros
como RSA, IDEA y el triple DES. No puedo ensefiar c6mo se rompen estos siste-
mas, pero dare una pista a traves de la cual se puede ver la vulnerabilidad de 10s
sistemas de cifrado por transposicion.
El arma principal que se usa contra estos es la distribucion basada en la frecuen-
cia de letras. Es decir, en cualquier conversacion hay unas letras que aparecen
mas que otras. Las letras mas comunes son E-T-N-R-0-A-I-S y las menos co-
munes J, K, Q X y Z.
Otra forma de comprometer un sistema de cifrado sencillo es analizando las
cadenas de dos y tres caracteres (digraphs y trigraphs, respectivamente). Estos
elementos tambikn se repiten con cierta frecuencia en las conversaciones coti-
dianas. Por ejemplo, el ejkrcito de 10s EE.UU. considera que 10s siguientes elemen-
tos son 10s que mas se repiten en su idioma: en, er, re, nt, th, on, in, ent, ion, and,
ing, ive, tho yfor. Los menos frecuentes son: df, hu, ia, It, rnp, eri, hir, iet, der y dre.
Las letras mas frecuentes no solo indican cuales pueden ser el resto de letras de
la palabra, sino que tambien dan pistas sobre las letras que se encuentran en las
palabras colindantes. Pensemos por u n momento la cantidad de estos elementos
que usamos a diario: es, si, no, yo, tu, el, la ... Y la lista sigue. Aunque 10s siste-
mas de cifrado que utilizamos en esta aplicacion no son 10s mas seguros, si re-
sulta divertido trabajar con ellos. Y seguro que en ocasiones evitara que algun
intruso lea nuestros mensajes.

Caesar
Este sistema tiene este nombre porque es el que utilizaba Julio Cksar para co-
municarse con 10s generales de sus ejercitos. Es uno de 10s primeros sistemas de
cifrado utilizados para encriptar mensajes. El algoritmo usado es bastante sim-
ple. Se limita a mover las letras del alfabeto entre 1 y 25 lugares (de la b a la z).
De esta forma, si se trabaja con un nivel 3 , la letra a del texto plano se conver-
tira en la d, y viceversa. Las letras que sobrepasen la posicih de la z, seguiran
Cifrado en IavaScriDt 345

contando desde el principio. Es decir, que con un nivel 3 , la z del texto plano
pasara a ser la c del texto cifrado. El numero es la llave que ha de poseer el des-
tinatario y el remitente del mensaje.
Observese que, una vez que se ha seleccionado una llave, todos 10s caracteres
del texto plano estaran relacionados con el mismo caracter del texto cifrado. Por
ejemplo, u n nivel 3 indica que la a siempre se convertira en la d. Es decir, que
tecnicamente habra un alfabeto cifrado. Por eso se dice que este sistema es mono-
alfabetico.

Vigenere
Este sistema lo ideo el matematico Blaise de Vigenere en el siglo XVI. Se trata de
un sistema de cifrado poli-alfabetico porque utiliza mas de un alfabeto cifrado.
Es decir, la letra a del texto plano no se correspondera siempre con la letra d del
texto cifrado.
En vez de numeros, este sistema de encriptacion utiliza una palabra. Suponga-
mos que queremos cifrar el texto meet a t midnight y que seleccionamos como
llave la palabra vinegar. Las letras de la llave se alinean en sucesion con las letras
del texto plano:

vine ga rvinegar
meet at midnight
Bien. La V es la letra numero 22a del alfabeto. La I es la 9a . Las letras n, e, g, a
y r son las 14a, 5a, 7a, 1” y lSa, respectivamente. Asi que la letra m del texto
plano se movera 22 posiciones, la primera e se desplazara 9 y la segunda 14, etc.
De esta forma obtendremos:

hmrx gt ddlammhk
Si nos fijamos bien, este sistema de cifrado es igual que Caesar, con la diferen-
cia de que se aplica sobre la marcha. En cada caracter se aplica un nuevo sistema
Caesar.

Nota: Si quiere aprender mas sobre 10s sistemas de encriptacion, puede


bajarse de la Web documentos PDF clasificados por el ejCrcito de 10s EE.UU.
Los encontrarBen la direccionhttp://www und.nodak.edu/org/crypto/crypto/
army.j3eld.manual/separate.chaps/.
Esta copia se encuentra en un sitio Web llamado Crypto Drop Box. La pagina
principal se encuentra en http://www.und. nodak.edu/org/crypto/crypto/.
Encontrara suficiente documentacion como para estar entretenido durante
unos cuantos dias.
346 Cifrado en JavaScript

Requisitos para la ejecucion


Esta aplicacidn utiliza JavaScript 1.2 y DHTML, por solamente se podra ejecu-
tar con exploradores 4.x o posteriores. Hay que sustituir muchas cadenas, por
lo que se trabaja con JavaScript 1.2.

Analisis de la sintaxis
Afortunadamente, esta aplicacion tecnicamente necesita dos archivos. Mejor
aun, solo nos fijaremos en el codigo de uno de ellos. Estos archivos sonindexhtml
y dhtrnl.js (dhtrnZ.js se vio en el capitulo 6). Antes de iniciar el analisis del codi-
go, merece la pena que nos detengamos a considerar el aspect0 que presentara
la aplicacion. Su construccion se har6 desde un punto de vista muy basico. En el
carro de la compra, el cual hemos tratado en el capitulo anterior, ya vimos otra
aplicacion que utilizaba la orientacion de objetos, per0 ahora iremos u n poco mas
alla.
Esta aplicacion cuenta con dos sistemas de cifrado. Cada uno de ellos tiene una
serie de elementos en comun con el otro, independientemente del tip0 de en-
criptacion que aplique. No olvidemos que hay tres tipos de sistemas de cifrado:
ocultacion, trasposicion y sustitucion. Aqui veremos dos sistemas de sustitu-
ci6n: Caesar y Vigenere. La figura 9.5 nos muestra la estructura que acabamos
de describir.

Figura 9.5. Estructura de cifrado


Cifrado en JavaScript 347

En la figura se puede ver que 10s objetos ConceaZrnentCipher,TranspositionCipher


y Substitutioncipher pertenecen a1 objeto Cipher, como si fuesen subclases. Por lo
tanto, 10s sistemas de cifrado Vigentre y Caesar son ejemplos deSubstitutionCipher
y contienen todos sus mttodos y propiedades.
Para acallar la curiosidad de 10s mas intelectuales, vamos a ver u n modelo am-
pliado. La figura 9.6 muestra otros tipos de sistemas de cifrado que se han in-
cluido dentro de su correspondiente categoria. La parte en negrita de la estructura
identifica la parte que utilizaremos en esta aplicacidn.

Figura 9.6.Ampliacidn de la estructura de cifrado

Como podemos ver, el numero de tipos y sistemas de cifrado que se puede aiia-
dir a la estructura sin modificar nada es enorme. Tambitn podemos tener "sub-
clases" de una subclase. De nuevo nos podemos aprovechar del diseiio orientado
a objetos. No lo olvidemos mientras analicemos el cddigo de las siguientes pagi-
nas. Veremos la facilidad con la que se puede agregar mas sistemas de cifrado a
la aplicacidn sin tener que modificar nada.
En el ejemplo 9.1 podemos ver el contenido del archivo indexhtrnl.

Ejemplo 9.1. indexhtrnl

1 <HTML>
2 <HEAD>
3 <TITLE>Cipher</TITLE>
4 <STYLE TYPE= " text / cs s " >
5 <!--
348 Cifrado en JavaScript

6 BODY { margin-left: 50 px; font-family: arial; 1


7 I ( font-weight: bold; }
8 //-->
9 </STYLE>
10 <SCRIPT LANGUAGE="JavaScriptl.2 SRC="dhtml. j s"></SCRIPT>
'I

11 <SCRIPT LANGUAGE="JavaScriptl.2">
12 <!--
13
14 var caesar = '<FONT SIZE=2>Made famous by Julius Caesar, this
cipher * +
15 'performs character shifting (substitution). Plaintext is ' +
16 'enciphered by shifting forward each character of the alphabet
a ' +
17 'fixed number of characters.<BR><BR>Forexample, shifting by 1
I +

18 'changes plaintext <I>a</I> to <I>b</I>, <I>b</I> to <I>c</I>,


' +
19 'and so on. Plaintext characters at the end of, say, the
alphabet, ' +
20 'are enciphered by starting at the beginning. In other words,
21 + '<I>z</I>becomes <I>a</I>. This application also includes
digits ' +
22 '0-9. So a plaintext <I>z</I> becomes <I>O</I>,and a plaintext
' +
23 '<I>9</I>becomes <I>a</I>. The process is reversed for
deciphering. +
24 '<BR><FORM>Shift: +
25 genSelect('Shift', 35, 0, 0 ) +
26 '</FORM><BR>Note:Caesar was rumored to prefer a shift of 3.';
27
28 var vigenere = '<FONT SIZE=2>Made famous by mathematician Blaise
de ' +
29 'Vigenere, the Vigenere cipher can be considered a "dynamic" '
30 + 'version of the Caesar cipher. Instead of shifting each
plaintext ' +
31 'character by a fixed number, this cipher shifts characters ' +
32 'according to the character index of a keyword you choose such
as +
33 '<I>dog</I>.<BR><BR>Since <I>d</I>, <I>o</I>,and <I>g</I> are
# +
34 'letters 4, 15, and 7 of the alphabet, each three plaintext ' +
35 'characters are shifted by 4, 15, and 7, respectively. This ' +
36 'application includes digits 0-9. So your keyword can have
letters ' +
31 'and numbers.<BR><BR><FORM>Keyword:<INPUT TYPE=TEXT
NAME="KeyWord" +
38 'SIZE=25></FORM><BR>Note:This cipher has many versions, one of
t
39 'which was devised by Lewis Carroll, author of Alice in
Wonderland.';
40
Cifrado en JavaScript 349

41 var curcipher = "caesar";


42
43 function Cipher0 I
44 this.purify = purify;
45 this.chars = ~abcdefghijklmnopqrstuvwxyz0123456789';
46 1
45
48 function purify(rawText) (
49 if (!raw"ext) { return false; 1
50 var cleanText = rawText.toLowerCase();
51 cleanText = cleanText.replace(/\s+/g,' ' ) ;
52 cleanText = c l e a n T e x t . r e p l a c e ( / [ ^ a - z 0 - 9 \ s ] / g . " ) ;
53 if(cleanText.length == 0 I I cleanText.match(/^\s+$/) ! = null) {
54 return false;
55 }
56 return cleanText
57 1
58
59 function SubstitutionCipher(name, description, algorithm) (
60 this.name = name;
61 this.description = description;
62 this.substitute = substitute;
63 this.algorithm = algorithm;
64 }
65 SubstitutionCipher.prototype = new Cipher;
66
67 function substitute(baseChar, shiftIdx, action) I
68 if (basechar == ' ' ) { return basechar; 1
69 if(action) {
70 var shiftsum = shiftIdx + this.chars.indexOf(baseChar);
71 return (this.chars.charAt((shiftSum < this.chars.length) ?
72 shiftsum : (shiftsum % this.chars.length)));
73 1
74 else {
75 var shiftDiff = this.chars.indexOf(baseChar) - shiftIdx;
76 return (this.chars.charAt((shiftDiff < 0 ) ?
77 shiftDiff + this.chars.length : shiftDiff));
78 1
79 1
80
81 function caesarAlgorithm (data, action) {
82 data = this.purify(data);
83 if (!data) {
84 alert('N0 valid text to ' + (action ? 'cipher.' :
'decipher.'));
85 return false;
86 1
87 var shiftIdx =
88 (NN ? refSlide("caesar") .document.forms[Ol.Shift.
selectedIndex : document.forms[l].Shift.selectedIndex);
89 var cipherData = " ;
350 Cifrado en JavaScript

90 for (var i = 0; i < data.length; i++) (


91 cipherData += this.substitute(data.charAt(i),shiftIdx,
action);
92
93 return cipherData;
94 )
95
96 function vigenereAlgorithm (data, action) (
97 data = this.purify(data);
98 if(!data) {
99 alert('No valid text to ' + (action ? 'cipher.' :
'decipher.')) ;
100 return false;
101 1
102 var keyword = this.purify((NN ?
103 refSlide("vigenere").document.forms[Ol.KeyWord.value:
104 document.forms[2l.KeyWord.value) 1 ;
105 if(!keyword I I keyword.match(/\^s+$/) ! = null) (
106 alert('No valid keyword for ' + (action ? 'ciphering.' :
107 'deciphering.')) ;
108 return false;
109 1
110 keyword = keyword.replace(/\s+/g, ' , ) ;
111 var keywordIdx = 0;
112 var cipherData = ' I ;

113 for (var i = 0; i < data.length; i++) (


114 shiftIdx = this.chars.indexOf(keyword.charAt(keyword1dx)) ;
115 cipherData += this.substitute(data.charAt(i), shiftIdx,
action);
116 keywordIdx = (keywordIdx == keyword.length - 1 ? 0 :
keywordIdx + 1);
117 1
118 return cipherData;
119 )
120
121 var cipherArray = [
122 new SubstitutionCipher("caesar", caesar, caesarlllgorithm),
123 new SubstitutionCipher("vigenere",vigenere, vigenereAlgorithm)
124 I;
123
126 function showCipher(name) (
127 hideSlide(curCipher);
128 showSlide(name);
129 curcipher = name:
130 1
131
132 function routeCipher(cipherIdx, data, action) (
133 var response = cipherArray[cipherIdxl.algorithm(data, action);
134 if (response) {
135 document.forms[0l.Data.value = response;
136 )
Cifrado en IavaScriDt 351

137
138
139 //-->
140 </SCRIPT>
141 < /HEAD>
142 <BODY BGCOLOR=#FFFFFF>
141
144 <DIV>
145 <TABLE BORDER=O>
146 <TR>
147 <TD ALIGN=CENTER COLSPAN=3>
148 <IMG SRC= images/cipher.jpg " >
I'

149 </TD>
150 </TR>
151 <TR>
152 <TD VALIGN=TOP WIDTH=350>
153 <FORM>
154 <SELECT NAME="Ciphers"
155 onChange="showCipher(this.options[this.selectedIndexl
.value); " >
156 <OPTION VALUE="caesar">CaesarCipher
157 <OPTION VALUE="vigenere">Vigenere Cipher
158 </SELECT>
159 </TD>
160 <TD ALIGN=CENTER>
161 <TEXTAREA NAME= "Data" ROWS= 15 COLS=" 40 ''
WRAP= PHYSICAL '' >< / TEXTAREA>
I'

162 <BR><BR>
163 <INPUT TYPE=BUTTON VALUE="Encipher"
164 onClick="routeCipher(this.form.Ciphers.selectedIndex,
165 this.form.Data.value, true);">
166 <INPUT TYPE=BUTTON VALUE="Decipher"
167 onClick="routeCipher(this.form.Ciphers.selectedIndex,
168 this.form.Data.value, false);">
169 <INPUT TYPE=BUTTON VALUE=" Reset "
170 onclick=" this.form . Data.value= ' ' ; " >
171 < /FORM>
172 </TD>
173 </TR>
174 < /TABLE>
175 < / DIV>
176
177 <SCRIPT LANGUAGE="JavaScriptl.2">
178 <!--
179 document.forms[O].Ciphers.selectedIndex = 0;
180 genLayer("caesar",50, 125, 350, 200, showName, caesar);
181 genLayer("vigenere",50, 125, 350, 200, hideName, vigenere);
182 //-->
183 </SCRIPT>
184 i/BODY>

185 </HTML>
352 Cifrado en JavaScript

El archivo fuente dhtml.js en JavaScript es el primero que se interprets. Su co-


dig0 utiliza DHTML para configurar las capas y generar las listas de seleccion
sobre la marcha. Lo veremos en breve. El siguiente c6digo que nos interesa se
encuentra en las lineas 14-39. Las variables caesar y vigenere se han diseiiado
para configurar el valor de las cadenas HTML. Cada una de ellas define la capa
de la interfaz correspondiente a cada sistema de encriptacion. Todo lo que tene-
mos es estAtico, a excepcion de la llamada a la funcion genselect ( ) que tiene
lugar en caesar. Es M a :

genSelect('Shift', 35, 0 , 0 )

Crea una lista de seleccion llamada Shift, que empieza en 0 y termina en 35 y


que por defect0 se inicia con la opci6n 0 seleccionada. Los atributos VALUE y TEXT
se ajustan a 10s numeros que se han utilizado en la cuenta. El c6digo procede
directamente del capitulo 5 . Si creamos la biblioteca de codigo que sugerimos en
el capitulo 6, seguramente habra incluido este codigo. L a definicion de esta fun-
cion se encuentra a1 final de dhtmLjs.

Definir un sistema de cifrado


Las siguientes lineas de c6digo definen todos 10s sistemas de cifrado existentes
posibles. En las lineas 43-46 tenemos el constructor Cipher ( ) :

function C i p h e r 0 {
this.purify = purify:
this.chars = ~abcdefghijklmnopqrstuvwxyz0123456789';
1

Es muy pequeiio. Si esperaba encontrarse con una definicibn muy compleja


llena de ecuaciones diferenciales y geometria esferica que convirtiese 10s resulta-
dos a la cuarta dimension, sentimos haberle decepcionado. Cipher ( 1 define 10s
sistemas de cifrado a u n nivel bastante alto. Unicamente suponen dos principios
relacionados con 10s sistemas de cifrado que se verAn en esta aplicacion:
Todos saben como dar formato a 10s datos del usuario, tanto si estan co-
dificados como si no.
Cada sistema conoce 10s caracteres que incluirh en el cifrado.
Cifrado en JavaScript 353

nstructor Cipher ( ) ti
ene un mCtodo llamad
signaci6n de las propiedades es fAcil. Basta con asignar el valor que se
sea a una variable utilizando la sintaxis this.variable-name. La asig-
i6n de mCtodos es algo distinta. Lo primer0 que se ha de hacer es de-
r una funcion. Luego se utilizarA la sintaxis this.variable-name
a establecer una referencia que apunte a dicha funci6n. Esto es exada-
ente lo que ocurre en el constructor Cipher ( ) . El script contiene la
nci6n purify ( ) que acabamos de definir. cipher ( ) tiene una variable
amada this.purif y que hace referencia a1 mttodo purify. Obskrvese
que no hay parentesis. Asi se sabe que se trata de una referencia. Como el
valordethis.purifyespurify0,sepodriallamaralafunci6npurify() ,
on lo que se asignaria a this.purify el valor que se devolviese, fuese
cual fues
r, n .

No importa si 10s datos se encuentran encriptados o no. Han de cumplir las


siguientes reglas:
Cada carhcter tendr6 que ser a-z 6 0-9.El resto se tendran que omitir. No
se distinguen entre mayusculas y minusculas.
Tampoco se encriptaran ni desencriptaran 10s espacios en blanco. Cuando
haya varios espacios en blanco en una cadena, se reduciritn 8 uno solo.
Los caracteres correspbndientes a1 salto de linea se convertir6n en espacios
en blanco.

No est6n mal. Por lo menos son sencillas. Todo lo que tenemos que hacer es
obligar a que se cuhplan. En las lineas 48-5 7 tenemos la funci6n purify ( ) :

function purify(rawText) {
if (!rawText) { return false; 1
var cleanText = rawText.toLowerCase0;
cleanText = cleanText.replace(/\s+/g,' I ) ;

cleanText = cleanText.repla~e(/[~a-zO-9\sI/g,");
if(cleanText.length == 0 1 1 cleanText.match(/"\s+$/) ! = null) {
return false;
I
return cleanText
I
354 Cifrado en JavaScript

Esta funcion devuelve: f a l s e o un texto formateado para las acciones de ci-


frado. Con f a l s e se cancelarh la operation de encriptacion. Si rawText contiene
cualquier cosa a la que se pueda dar formato, p u r i f y ( ) empezarh por conver-
tir todas las letras a minusculas de la siguiente manera:

cleanText = cleanText.replace(/\s+/g,' I ) ;

Al buscar expresiones regulares, el mCtodo replace ( ) del objetostring busca-


ra todos 10s espacios en blanco de la cadena y sustituira cada grupo de ellos por
un unico espacio en blanco, sin que importe el numero que espacios que confor-
men cada grupo. A continuacion, p u r i f y ( ) sustituirh el resto de caracteres
distintos de a-z 6 0-9 o 10s espacios en blanco, por un carhcter vacio. Asi elimina
todos 10s caracteres no vhlidos. El mCtodo r e p l a c e ( ) funciona asi:

cleanText = cleanText.replace(/["a-z0-9\s]/g,");

acteres. En ot er cosa que no se encuen

caracteres que se incluira (0


uiran). Supongamos que ten
Cifrado en IavaScriDt 355

I Esta expresion buscar5 todas las letras minusculas del abecedario, 10s digitos

ICfono, c6digos posta

Ya hemos completado el formato, Ha llegado la hora de comprobar si lo que


queda de la cadena es vAlido para proceder con el cifrado. Mientras que la cade-
na formateada contenga por lo menos uno de 10s caracteres a-z 6 0-9, todo ir6
bien. Per0 habr6 dos excepciones:

DespuCs de eliminar todos aquellos caracteres que no son vBlidos, no queda


ninguno.
DespuCs de eliminar todos 10s caracteres no v6lidos, tinicamente quedan
espacios en blanco.

En cualquier caso, sera el momento de llamar a la operaci6n que se encargar6


de comprobar estos datos. El codigo encargado de esta operacih ser6 el que
aparezca en las lineas 53-55. Esto hace q u e p u r i f y ( ) devuelva f a l s e si ocurre
cuaIquiera de estas excepciones:

if(cleanText.length == 0 1I cleanText.match(/"\s+$/) ! = null) {


return false;
}

Con objeto de saber quC caracteres tiene que valorar, Cipher utilizarfi la siguien-
te cadena:

this.chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
356 Cifrado en JavaScript

Definicion del sistema de cifrado por sustitucion


Una vez que se ha definido la madre de todos 10s objetos Cipher, Cipher ( ) , va-
mos a crear una version mas concreta, la encargada de trabajar con el sistema
de cifrado por sustitucion: SubstitutionCipher ( ) . Su c6digo se encuentra en
las lineas 59-65:

function SubstitutionCipher(name, description, algorithm) {


this.name = name;
this.description = description;
this.substitute = substitute;
this.algorithm = algorithm;
1
SubstitutionCipher.prototype = new Cipher;

Se da por supuesto que todos 10s objetos Cipher conocen el formato de 10s datos
del usuario. Los sistemas de cifrado por sustitucion aun dan suponen mas cosas:
Cada uno tiene un nombre y una descripcion.
Cada uno utiliza un metodo general para sustituir 10s caracteres de las
cadenas de cifrado y descifrado.
Cada uno contiene informacion sobre el mCtodo general de sustitucion.
Esto es lo que diferencia un sistema de cifrado de otro.
Cada objeto Substitutioncipher tambien sera Cipher.

Asignar un nombre y una descripcion a 10s objetos es una tarea facil. Cualquie-
ra de las dos cadenas que se pasan durante la llamada a SubstitutionCipher ( )
sera completamente valida. Las variables caesar y vigenere que se iniciaron con
anterioridad con el c6digo HTML haran las funciones de descripciones. Asi nos
encargamos de la primera suposicion. iQue hay de la definition del metodo
general de sustitucion? Este mCtodo puede sustituir un caracter por otro. Cada
llamada a este metodo devolvera un caracter, que se sustituira por otro.

Sustitucion basica
Cada Substitutioncipher utiliza el mismo metodo para sustituir un caracter de
la cadena por otro. La funcion substitute ( ) que veremos a continuacion, se
definira como un mCtodo para cada ejemplo de Substitutioncipher:

function substitute(baseChar, shiftIdx, action) {


if (basechar == ' ' ) { return basechar; 1
if(action) {
var shiftsum = shiftIdx + this.chars.indexOf(baseCbar);
return (this.chars.charAt((shiftSum< this.chars.length) ?
shiftsum : (shiftsum % this.chars.length)));
Cifrado en IavaScrbt 357

else {
var shiftDiff = this.chars.indexOf(baseChar) - shiftIdx;
return (this.chars.charAt((shiftDiff< 0 ) ?
shiftDiff + this.chars.length : shiftDiff));
1
1

Este metodo espera tres argumentos. basechar es el caracter que se sustituira.


shiftldx es un entero que determina el nivel que hay que aplicar para localiaar el
caracter por el que se sustituira. action es un booleano que especifica si se trata-
ra a basechar como texto plano o como texto cifrado. Para dejar el espacio en
blanco tal cual, sin modificar, la primera linea devolvera basechar como si fuese
en realidad u n espacio en blanco. En cualquier otro caso, este metodo utilizara
una accion para determinar c6mo calculara el nivel de la sustitucion. Si action
es true, se utilizara el algoritmo de encriptacion. Si action es false, se utiliza-
ra el algoritmo de desencriptacion.
No olvidemos que chars contiene una cadena con todos 10s caracteres que se
pueden utilizar. El algoritmo de encriptacion valorara dichos caracteres. Se li-
mitara a determinar el indice de basechar dentro de dichos caracteres y a conti-
nuacion seleccionara el caracter de chars correspondiente a1 indice mas el valor
de shiftldx.
Vamos a verlo con un ejemplo. Supongamos que basechar es la letra d,shiftldx
es 8 y chars. indexof ( ' d ' ) es 3. Esto nos lleva a la linea 70:

var shiftsum = shiftIdx + this.chars.indexOf(baseChar);

+
L a variable shiftsum es igual a 11 (8 3 ) .Asi, chars. charAt ( 11 sera la le-
tra 1.En este caso, sera el valor que devolvera substitute ( ) . Parece muy sen-
cillo. Y lo es, per0 supongamos que basechar es la letra o y que shiftldx es 30.
Revisemos las matematicas. Ahora shiftsum sera igual a 45. El problema es que
chars solo tiene 36 caracteres (a-z y 0-9). Por lo tanto, chars.charAt ( 4 5 ) no
existira.
Cuando el algoritmo llega a1 ultimo caracter de chars, debe continuar por el
principio, desde 0 y proseguir desde ahi con la suma. Podemos utilizar el opera-
dor de modulos para obtener el efecto deseado. Recordemos que este operador
devolvera un resto entero partiendo de dos operadores. Aqui tenemos varios
ejemplos :

4 % 3 = 1. Al dividir 4 entre 3 se obtiene un resto de 1.


5 % 3 = 2. Al dividir 5 entre 3 se obtiene u n resto de 2.
6 % 3 = 0. Al dividir 6 entre 3 no hay resto.
358 Cifrado en javascript

Todo lo que tenemos que hacer es utilizar el operador de modulos. Asi, en vez
de utilizar u n shiftsum de 45, podremos utilizar shif tSum % chars.length,
que es igual a 9. chars. charAt ( 9 ) sera la letra j . Esto explica el codigo que uti-
liza el algoritmo de cifrado como comprobacion:

return (this.chars.charAt( (shiftsum < this.chars.length) ? shiftsum :


(shiftsum 8 this.chars.length)));

En este caso concreto, substitute ( ) devuelve chars.charAt (shiftSum) o


bienchars .charAt (shiftSum % this.chars.length),dependiendo del ta-
maiio de shiftsum y de la longitud de chars. LQuC tienen que ver las llaves con
todo esto? Es posible que nos preguntemos quC esta pasando aqui. No olvide-
mos que substitute ( ) no es una funcion; es el mCtodo de todas las variables
que se inician como un caso de Substitutioncipher. Utilizando esto, dentro de
este metodo encontraremos referencias a las propiedades de la variable. Como
Substitutioncipher hereda todas las propiedades de Cipher, dicha variable sera la
propietaria de la propiedad chars.
El procedimiento se parece a1 utilizado con el algoritmo de desencriptacion. L a
unica diferencia es que para obtener el caracter correct0 de chars resta shiffldx.
En este caso, la variable shiftDifl sera igual a la diferencia del indice de basechar
y shiftldx:

var shiftDiff = this.chars.indexOf(baseChar) - shiftIdx;

Muy sencillo. Sin cmbargo, si shiftDifles menor que 0, nos encontraremos con
el mismo problema que vimos cuando shiftsum era mayor que el resultado de
chars.length - 1. L a solucion sera sumarshiftDiffa chars.length. Perfec-
to. shiftDifl es negativo, es decir, que a1 sumar 10s dos campos obtendremos un
resultado (shiftDi;ff)menor que chars. length que es el indice que se anda bus-
cando con el fin de proceder con la desencriptacion.
El codigo que podemos ver a continuacion determina si substitute ( ) utili-
zara shiftDifl o shiftDifl+ chars. length como indice para proceder con la
desencriptacion:

return (this.chars.charAt((shiftDiff< 0 ) ?
shiftDiff + this.chars.length : shiftDiff));

Diferentes soluciones para distintos cifrados


Ya hemos visto que todos 10s elementos Substitutionciphers tienen el mCtodo
substitute ( ) . Vamos a ver ahora cada uno de ellos por separado. El cons-
tructor Substitutioncipher espera un argument0 llamado algorithm. No sera una
Cifrado e n IavaScriut 359

cadena, sino un booleano, un numero o incluso un objeto. Este argumento es


una referencia que apunta a la funcion que llamara a substitute ( ) .
En el caso del sistema de cifrado Caesar, el argumento se pasa como una refe-
rencia a la funcion caesarAlgorithm ( ) . Con Vigenere, recibe una referencia a
la funci6n vigenereAlgorithm ( ) . Vamos a verlo.
Algoritmo Caesar
El algoritmo Caesar es el mas sencillo de 10s dos. El c6digo se encuentra en las
lineas 81-94:
function caesarAlgorithm (data, action) I
data = this.purify(data);
if(!data) {
alert('No valid text to ' + (action ? 'cipher.' : 'decipher.')):
return false;
1
var shiftIdx =
(NN ? refSlide("caesar").document.forms[Ol .Shift.selectedIndex :
document.forms[ll.Shift.selectedIndex);
var cipherData = ' I ;

for (var i = 0; i s: data.length; i++) {


cipherData += this.substitute(data.charAt(i), shiftIdx, action);
1
return cipherData;
I

Las primeras lineas se encargaran de dar formato a 10s datos y de comprobar


si 10s caracteres de la cadena de texto son validos para la operacion. El conteni-
do de la cadena del argumento data se formateara con purify ( ) , que entrega-
ra 10s datos en forma de argumento. Mientras que la llamada a puri f y ( ) no de
c6mo resultado false, continuara el proceso de encriptacion. Si desea mas in-
formation sobre el mttodo purify ( ) , consulte la seccion anterior.
Lo siguiente que se ha de determinar sera el numero de caracteres que se salta-
ra el usuario en la conversion. Es muy facil. El numero procedera de la lista de
seleccion que hay en la capa caesar. Aun no hemos dicho nada sobre ella. En cual-
quier caso, si siente curiosidad, salte a las lineas 180-18 1 y reviselas. La diferen-
cia entre el DOM de Navigator y el de Internet Explorer es patente a1 acceder a
elementos de distintas capas. El nombre de la lista de selection sera Shift.
En Navigator, sera asi:

document.layers['caesar'].document.forms[Ol.Shift.selectedIndex

Pero en MSIE, sera:

document.forms[ll.Shift.selectedIndex
360 Cifrado en IavaScriDt

N o t a : Como se puede ver, para acceder a 10s formularios (y a sus elementos)


que se encuentran en las capas, hay que utilizar distintas sintaxis. El modelo
para objetos del documento en NN es distinto del de MSIE. No es la primera
vez que lo vemos en esta misma obra. De hecho, la mayor parte del codigo
de dhtrnZ.js se utiliza para crear y manipular las capas en 10s exploradores.
En cualquier caso, conviene que sepamos cuando habra que trabajar con
ambos exploradores y cuando no. Hasta que no veamos DOM, convendra
tener a mano 10s siguientes recursos:

Objetos DHTML de Microsoft:

h t t p : / / w w w .microsoft.com/workshop/au thor/dhtmZ/reference/
objects.asp
Referencia para las hojas de estilos de Netscape y JavaScript para el cliente:

http://deveZoperl.netscape. com:8O/docs/manuals/communicator/
dynhtml/jss34.htm y http://deveZoper.netscape.com/docs/manuaZs/
js/cZien t/jsref/index.h tm

La variable shiftldx se encarga de dicha diferencia. Para ello utiliza la variable


N N con objeto de determinar a cual de 10s dos tiene que acceder. La llamada a
ref Slide ( ) que se encuentra en la linea 88 se utiliza para establecer una refe-
rencia a document. layers [ caesar ] . Ahora que ya se ha asignadoshiftldx,
'I I'

caesarAlgorithm ( ) se ejecutara data. length veces, llamando para ello a


substitute ( ) cada vez y sumando despuks su resultado a una variable local
vacia, cipherData. El argument0 action se pasara todas las veces con el fin de in-
dicar a substitute ( ) si tiene que encriptar o desencriptar. DespuCs de la ultima
repeticion, caesarAlgorithm ( ) devolvera cipherData, que contendra la cade-
na cifrada.

Algoritmo Vigenere
Es el mas sencillo de 10s dos. Vamos a ver VigenereAlgorithm ( ) . L a princi-
pal diferencia que hay es que el argumentoshiftldx que se pasa a substitute (
en caesarAlgorithm ( ) permanece constante. Con esta funcion, shiftldx podra
(y generalmente lo hace) cambiar con cada llamada a substitute ( ) . La otra
diferencia es que el usuario utilizara una palabra llave como llave en vez de un
numero. Este es el contenido de las lineas 96-1 19:

function VigenereAlgorithm (data, action


data = this.purify(data);
if(!data) {
alert('No valid text to ' + (action ? 'cipher.' : 'decipher.'));
return falsk:
Cifrado en JavaScript 361

1
var keyword =
this.purify( (NN ?
refslide( "vigenere").document .forms [O] .KeyWord.value :
document.forms[2l.KeyWord.value));
if(!keyword 1 1 keyword.match(/\^s+$/) ! = null) {
alert('No valid keyword for ' +
(action ? 'ciphering.' : 'deciphering.'));
return false;
1
keyword = keyword.replace(/\s+/g, " ) ;
var keywordIdx = 0 ;
var cipherData = " ;
for (var i = 0; i < data.length; i++) {
shiftIdx = this.chars.indexOf(keyword.charAt(keyword1dx) ) ;
cipherData += this.substitute(data.charAt(i), shiftIdx, action);
keywordIdx = (keywordIdx == keyword.length - 1 ? 0 : keywordIdx +
1);
1
return cipherData;
1

Las cinco primeras lineas son iguales que las de caesarAlgori thm ( ) . Se en-
cargan del formato y validacion. Las siguientes lineas desarrollan una tarea
similar con la palabra que se usa como llave. Esta procede del campo del formu-
lario que se encuentra en la capa vigenere. No olvidemos que tenemos que adap-
tar la aplicacion para que trabaje con 10s DOM de Navigator y MSIE.
En Navigator, sera asi:

document.layer ['vigenere'l.document.forms[O].KeyWord.value

Y en MSIE, asi:

document.forms 21 .KeyWord.value

La asignacion dc la palabra clave sera:

var keyword = this.purify((NN ?


refSlide("vigenere").document.forms[Ol .KeyWord.value :
document.forms[2l.KeyWord.value) ) ;

ObsCrvese que volvemos a utilizar el mCtodo purify ( ) . Se ha disefiado para


que trabaje con el texto plano y con el cifrado, per0 10s requisitos de la palabra
clave son tan parecidos que tambiCn 10 podemos utilizar con ella. Como el mC-
todo substitute ( ) unicamente puede sustituir 10s caracteres de chars, la pa-
labra clave tendra que contener este tip0 de caracteres. Por ejemplo, las siguientes
362 Cifrado en IavaScriDt

son ejemplos de palabras clave aceptables: gente, cielo, init2wnit o 1or2or.3. Per0
no se puede utilizar caracteres que no se encuentren en chars. No olvidemos que
purify ( elimina todos 10s caracteres distintos de a-z 6 0-9 y sustituye 10s
retornos de carro y grupos de espacios en blanco por un espacio en blanco. Aun-
que el usuario puede introducir l@@#derf t como una cadena, purify ( ) dara
formato y devolvera lderft que unicamente contiene caracteres validos. Va-
mos a considerar ahora las claves con espacios en blanco. De hecho, todos 10s
caracteres seran validos, a exception de 10s espacios en blanco. El contenido de
la linea 1 1 0 se encarga de eliminarlos:

keyword = keyword.replace(/\s+/g, " ) ;

No olvidemos que mientras quede un caracter valido de una cadena, se utiliza-


ra en vigenereAlgorithm( ) .

Como se modifica shiftldx


Ya se ha dado formato a1 texto plano ( 0 a1 cifrado) y a la palabra clave. Todo lo
que queda sera sustituir cada uno de 10s caracteres de acuerdo con la llave. Por
definicion, en el sistema de cifrado Vigenkre, cada caracter del texto se encriptara
o desencriptara de acuerdo con el indice del siguiente caracter de la palabra
clave. Vamos a las lineas 111-118:

var keywordIdx = 0;
var cipherData = ' I ;

for (var i = 0; i < data.length; i++) (


shiftldx = this.chars.indexOf(keyword.charAt(keyword1dx));
cipherData += this.substitute(data.charAt(i), shiftIdx, action);
keywordIdx = (keywordIdx == keyword.length - 1 ? 0 : keywordIdx + 1);
}
return cipherData;

Con la variable keywordldx empezando en 0, utilizaremos la siguiente senten-


cia para conseguir el indice de cada caracter de la palabra clave:

Para cada caracter de 10s datos (ya estk el texto codificado o sin codificar), el
valor de shiftldx sera el indice de chars de keyword.charAt (keywordIdx). La
variable cipherData sera igual a si misma mas el valor que devuelva el metodo
substitute( ) , que recibe una copia dedata.charAt (i)yshi,ftldx,junto con
action.Al aumentar keyword& una unidad, se prepara el conjunto para la
siguiente repeticion.
Cifrado en JavaScript 363

Cada Substitutioncipher tambien es un objeto Cipher


Del mismo mod0 que todos 10s sistemas de cifrado, independientemente del tipo
a1 que pertenezcan, tienen que tener las mismas caracteristicas basicas, el cons-
tructor SubstitutionCipher ha de heredar todas las propiedades de Cipher. Todo
esto ocurre dentro de una unica linea:

SubstitutionCipher.prototype = new Cipher;

Ahora, cada uno de 10s objetos Substitutioncipher que se ha iniciado tiene una
propiedad llamada chars y un metodo purify[). Es decir, que todos 10s objetos
Substitutioncipher serfin una versi6n mas especifica de Cipher.

lenguajes de programaci6n como Java. En el capitulo anterior aprendi-


0s a agregar cadenas o numeros a 10s objetos ya existentes. Tambitn
demos utilizar la propiedad prototype de las funciones constructoras
para crear una jerarquia de herencias. Esto mismo es lo que ocurre en la

Hasta ahora hemos visto la manera en la que trabajan 10s dos sistemas de en-
criptacion. A continuacion vamos a ver como crear 10s objetos que 10s represen-
tan y como se puede construir la interfaz por medio de la cual se interactua con
ellos. La creacion de 10s objetos no nos llevara mas que cuatro lineas, desde la
121 hasta la124:

var cipherArray = [
new SubstitutionCipher("caesar",caesar, CaesarAlgorithm),
new SubstitutionCipher("vigenere",vigenere, VigenereAlgorithm)
I;
364 Cifrado en JavaScript

A la variable cipherArray se le asigna un array. Cada uno de sus elementos serh


un Substitutioncipher. iPor qu6 10s coloreamos dentro de un array? La razon es
que la aplicacion sabe que sistema utilizarh. Para ello se basa en la OPTION esco-
gida en la primera lista de seleccih. Lo veremos en un momento.

De momento, nos vamos a centrar en cada una de las llamadas que se efectlian
a1 constructor Substitutioncipher( ) ,donde se entregan las cadenas adecua-
das, un nombre, una descripcion y una referencia a la funcion, a la que se asig-
narh la propiedad dgorithrn de cada objeto Substitutioncipher que se ha creado.
Asi se crean 10s objetos. Vamos a echar un vistazo a la interfaz. Es la parte del
codigo que se encontrarh entre las etiquetas BODY:

<DIV>
<TABLE BORDER=O>
<TR>
<TD ALIGN=CENTER COLSPAN=3>
<IMG SRC="images/cipher.j p g " >
Cifrado en JavaScript 365

</TD>
/ TR>
<TR>
<TD VALIGN=TOP WIDTH=350>
<FORM>
<SELECT NAME="Ciphers"
onChange="showCipher(this.options[this.selectedIndexl.value); " >
<OPTION VALUE="caesar">CaesarCipher
<OPTION VALUE="vigenere">Vigenere Cipher
< /SELECT>
</TD>
<TD ALIGN=CENTER>
<TEXTAREA NAME="Data" ROWS="15" COLS="40"
WRAP= PHYSICAL">< /TEXTAREA>
<BR><BR>
<INPUT TYPE=BUTTON VALUE="Encipher"
onClick="routeCipher(this.form.Ciphers.selectedIndex,
this.form.Data.value, true);">
<INPUT TYPE=BUTTON VALUE="Decipher"
onClick="routeCipher(this.form.Ciphers.selectedIndex,
this.form.Data.value, false); " >
<INPUT TYPE=BUTTON VALUE=" Reset
onClick="this. form.Data.value= ' ' ; " >
< I FORM>
< I TD>
< /TR>
</TABLE>
</DIV>

Este codigo crea una tabla de dos filas. En la superior colocara un grafico en
TD, utilizando COLSPAN con un valor de 2. La fila inferior contendra dos celdas.
La de la izquierda contiene una lista de selection como esta:

<SELECT NAME="Ciphers"
onChange="showCipher(this.options[this.selectedIndexl .value);">
<OPTIONVALUE="caesar">CaesarCipher
<OPTION VALUE="vigenere">Vigenere Cipher
</SELECT>

Esta lista determina la interfaz que se mostrara en pantalla. Como s610 hay
dos, sera una de ellas. El controlador de eventos onchange llamara a la funcion
showcipher ( ) , entregandole un valor que reflejara la opcion seleccionada. Esta
funcion es muy corta. La tenemos en las lineas 126-130:

function showCipher(name) {
hideSlide(curCipher);
showSlide(name1;
curcipher = name;
>
366 Cifrado en IavaScriDt

Este codigo nos puede resultar bastante familiar. Lo hemos visto en 10s capitu-
10s 3 y 6. Las funciones hideslide ( ) y showslide ( ) estan dentro dedhtrnljs.
Obskrvese que la celda de datos tendra u n ancho de 3 5 0 pixeles. Al contrario de
lo que ocurre con la lista de seleccion, la celda de datos aparecera vacia. Afortu-
nadamente, las dos capas se encargaran de rellenarla. En las lineas 180-181 te-
nemos las llamadas que se encargan de la creation. La funcion genLayer ( ) crea
las capas de 10s sistemas de encriptacion y tambikn la podemos encontrar den-
tro de dhtmLjs. Tambikn es una funcion que hemos visto en capitulos anterio-
res, por eso no le dedicaremos mhs tiempo:

genLayer("caesar",50, 125, 350, 200, showName, caesar);


genLayer("vigenere",50, 125, 350, 200, hideName, vigenere);

Con estas dos sentencias se crea el texto que mostrara cada sistema de cifrado,
junto con la lista de seleccion de Caesar y el campo de texto de Vigenere. Como
ya hemos comentado, podemos cambiar el sistema de encriptacion (Caesar o
Vigenkre) a travks de la lista de seleccion que se encuentra en la parte superior de
la pantalla, y que mostrara el sistema con el que se trabajara.
Al igual que ocurre con la otra celda de la fila inferior de la tabla, contendra
una zona de texto y tres botones. Se encuentran en las lineas 161-1 70:

<TEXTAREA NAME= Data '' ROWS= 15 " COLS= 40 WRAP= " PHYSICAL'' >< /TEXTAREA>
I'

<BR><BR>
<INPUT TYPE=BUTTON VALUE="Encipher"
onClick="routeCipher(this.form.Ciphers.selectedIndex,
this.form.Data.value,true); " >
<INPUT TYPE=BUTTON VALUE="Decipher"
onClick="routeCipher(this.form.Ciphers.selectedIndex,
this.form.Data.value, false);">
<INPUT TYPE=BUTTON VALUE=" Reset onClick="this.form.Data.value=' ; " >
'I

El campo de texto contiene el texto plano ( o el encriptado). El boton "Encipher"


hace que se encripte el texto que se encuentra en dicho campo. Es el inverso a1
boton "Decipher".Ambos llaman a la misma funcion, routecipher ( ) . Ambos
entregan como valor el contenido del campo de texto. La diferencia es que el ul-
timo argument0 sera true en un caso y false en el otro.

Seleccionar un sistema de cifrado


La seleccion del sistema de cifrado no tiene ninguna dificultad. El sistema co-
rrecto se correspondera con el indice de la lista de seleccion que se encuentra en
la parte superior del formulario y con el indice de cipheri4rray. Este es el conteni-
do de routecipher ( ) :
Cifrado en JavaScript 367

function routeCipher(cipher1dx. data, action) (


var response = cipherArrayIcipherIdx].algorithm(data, action);
if (response) {
document.formst0l.Data.value = response;
)
1

Esta funcion acepta tres argumentos. Ya hemos hablado de 10s dos ultimos.
data es el contenido del campo de texto y action sera true o false.El primero,
cipherIdx, procede de document. forms [ 0 1 .Ciphers.selectedIndex. Tendra
que ser 0 6 1. Independientemente de su valor, el mCtodo algorithm ( ) del ob-
jetoSubstitutionCipher encipherArray se hace con una llamada. Si algorithm ( )
devuelve otro valor distinto de false,se tendra que preparar el texto para que
se encripte o desencripte.

Una nota final


Posiblemente se haya dado cuenta, per0 el codigo de la linea 179:

docurnent.forrns[O].Ciphers.selectedIndex = 0 ;

se limita a reiniciar el valor de OPTION correspondiente a la lista de seleccion que


se encuentra en la parte superior de la pantalla. De esta forma se obliga a que la
opcion (OPTION)seleccionada coincida con la capa del sistema de encriptacion
que se muestra en la pantalla, aun en el caso en que el usuario vuelva a recargar
su contenido.

Posibles ampliaciones
Aunque esta aplicacion es muy divertida para jugar con ella, el siguiente nivel
de aplicacion sera el envio de mensajes de correo. Podemos hacerlo en tres pasos.
En primer lugar, copiaremos Ia siguiente funcion y la pegaremos entre las eti-
quetas SCRIPT:

function sendText(data) {
parawidth = 70;
var iterate = parseInt(data.1ength / parawidth);
var border = '\n-------\n' ;
var breakData = " ;
for (var i = 1; i <= iterate; i + + ) (
breakData += data.substring((i - 1) * parawidth, i parawidth) +
'\r';
1
breakData += data.substring((i - 1) * parawidth, data.length);
docurnent.CipherMail.Message.va1ue = border + breakData + border;
368 Cifrado en IavaScriDt

document.CipherMail.action =
'"mailto:someone@somewhere.com\?subject=TheSecret Message";
return true;
I

Desarrolla un formqto (en cuestion de milisegundos) a1 texto del mensaje antes


de proceder con su envio. Aqui se insertaran retornos de carro en todos 10s carac-
teres paraWidth. Asi se asegura de que las lineas de texto del mensaje que recibi-
ra el destinatario no mediran un kil6metro. Despues, se aiiadira otro formulario.
Se insertara este codigo dentro de las eti-quetas FORM del documento:

FORM NAME= " CipherMail ACTION= '' METHOD= POST" ENCTYPE= text /plain"
I' 'I

onSubmit="return sendText(document.forms[0l.Data.value);"~
<INPUT TYPE=HIDDEN NAME="Message">
<INPUT TYPE=SUBMIT VALUE=" Send ">
< /FORM>

Este formulario, llamado CipherMaiZ, unicamente contiene un campo HIDDEN.


Y, por ultimo, cambiaremos las referencias del formulario que seiialan a las fun-
ciones de 10s algoritmos de cifrado.
Cambie las lineas 87-89:

var shiftIdx = (NN ?


refSlide("caesar").document.forms [O].Shift.selectedIndex :
document.forms[ll .Shift.selectedIndex);

por esto:

var shiftIdx = (NN ?


refSlide("caesar").document.forms[O].Shift.selectedIndex :
document.forms[21 .Shift.selectedIndex);

Y las lineas 102-104:

var keyword = this.purify((NN ?


refSlide("vigenere").document.forms[ O I .KeyWord.value :
document.forms[2l.KeyWord.value));

por eso:

var keyword = this.purify((NN ?


refslide( "vigenere").document.forms [O].KeyWord.value :
document.forms[31.KeyWord.value)) ;

Tendremos que efectuar todos estos cambios puesto que, en el paso anterior,
hemos aiiadido otro formulario a lajerarquia. sendText ( ) determinara el valor
Cifrado en JavaScript 369

del campo oculto, asignandole el contenido del campo de texto. A continuacion,


sendText ( ) envia este formulario, cuyo atributo ACTION s e r a m a i l t o : su-e-
mail@su_servidor-de-correo. es. La figura 9.7 nos muestra el aspect0 que
tendra el mensaje en el momento de su recepcibn. El destinatario podra cortar y
pegar el contenido que se encuentra entre las lineas discontinuas y aplicar la lla-
ve de codificacidn para proceder con la desencriptacion (esta llave a de ser la mis-
ma que se ha utilizado para cifrar el mensaje)

I U ! ~
scha
RV The Top Secret Message
Sat 20 May Lm0 11 3Y 21 10200

tt b n a C l e p d r r m u e u l r p l naqlumz b y t i z i p 2 01 i l y YIOLUOI
4e we Oaspef2 v y e p l d l n t l t 3 c l s u l n u p n3 LpndClml I eo 1 3e lnbrloln V
-
e5dO d t WP c
--

a t f d y l d l i oe n i g 2 a e r r5e 3c O U f o e O c p y s t r u j 2 d z n ua.vlsd2iq4 ml boljna


d c n xeOnlb mfysbuet d i t f 8 t p df t b l w r n l r5e nUbrqYteSl r5e YO OOtPa
va wlb6e oc wed4usl t p i y c p y t s l e noo uo ebrs a. t n u p r g l z lut s t p w d t
vrz f y ml grpvza kl pa xuz s t y c 3 w l b am s t r e d n i p p a s c b P b 2 ml b o l j n a d t n
x o t 4 r b 2 v y 92rbq0 o o m c 3c o t c n r j m va 4eoccb d t c j q r b o o 3empcdtooldh
u o l mtsul e p t p l f n c j y b CnIvt d t 1b cvll 3 c Outoe 3cmpcdCmIr 5n mi2
D 1uf M 2 m l s l e d p s oe ilb6c 9 v y d l w e e p upxur c z n e p c p ~ y t $ z d v n l a
m c c t g p qvp t p r51f2e p n d 2 I q l m S O e t p n d Z l C I 4 ~
.____--

I(Mover ala carpeta selemonada) A

Figura 9.7. Correo encriptado

PD. Esta aplicacidn s610 funcionarh si el usuario tiene la funcidn de correo elec-
tronico de NN o MSIE correctamente configurado, que suele ser en la mayoria
de 10s casos.
CaPitulo 10
m

Cyber Greetings: arrastrar y soltar sobre el correo electr6nico

El fin de esta aplicacihes s6lo divertirse. Los usuarios podran enviar felicitacio-
nes personalizadas a sus familiares y amigos con simpaticos simbolos y un men-
saje. La figura 10.1 muestra la pantalla principal de la interfaz de la aplicaci6n.

Cyber Greetings

To I
Message 2.l

Figura 10.1. Pantalla predeterminada de Cyber Greetings


3 72 Cyber Greetings: arrastrar y soltar sobre el correo electronico

A la izquierda aparece u n formulario de entrada. Los usuarios rellenaran 10s


campos adecuados, entre 10s que se encuentra la direcci6n del destinatario, el
mensaje y la felicitacibn. Los usuarios tambien podran seleccionar u n fondo
haciendo clic en el b o t h "Backgrounds - ->" varias veces, hasta que aparezca el
deseado. Y lo mismo ocurre con 10s iconos. El b o t h "Icons - ->" tiene el mismo
efecto.
En la parte derecha se encuentra la pantalla. Aqui, el usuario podrh ver 10s fon-
dos y 10s iconos con 10s que puede trabajar. TambiCn podrii seleccionar u n icono
y arrastrarlo hasta la zona donde se muestra el fondo del mensaje. Estos iconos
se incluiran en el mensaje. En la figura 10.2 tenemos u n bonito ejemplo.

Cyber Greetings

o,"anelamlllD es

Images

Greenng

Sending

Figura 10.2. LReconoce a alguien en esta foto?

Cuando se completa la tarjeta, 10s usuarios podran ver el resultado haciendo


clic en el b o t h "Test". Se abre una ventana remota donde se Vera el aspect0 de la
tarjeta que recibira el destinatario. Vease figura 10.3.
Cuando estC satisfecho con 10s resultados obtenidos, el usuario harh clic sobre
el bot6n ''Send con objeto de enviar la tarjeta. La aplicaci6n enviarh toda la in-
formacibn del formulario y u n script del servidor para que se encargue de crear
la tarjeta y muestre una ultima pantalla de confirmacion con otro bot6n "Send
Cyber Greetings: arrastrar y soltar sobre el correo electr6nico 373

(figura 10.4).Si se hace clic en el, entonces el script enviara la tarjeta a la direc-
ci6n especificada.

Your Cyber Greeting


To bojuan@ramllla es

I Hola ti0 Juan

SO o q ero recoraarte que en un par ae swnana5 t~nemo5a reun on lam ar


POI favor trap a camara a6 ride0 y 0 18 a la na J. $aqJe prepare una ae 51.5
maim ~ s a ianaq
q

Figura 10.3. Aspect0 que presentara la felicitacion que recibira el destinatario

Congratulations!
YO" hsvr PucccsrrWhr created a Cybn GRermaca
Isrls.h&p@olae 'IP ram M y o u h r ro do II s a d Imn or her m e-mal to
armomcc the p e t m g Just push &e buaoo below. md the c-md d be on the

You m&t txpmmct a delay d e you e mml r o b contam your d

R c m To Cytc_jie_cta%

Figura 10.4. Perfecto, so10 hay que enviar el formulario

Ahora el usuario puede leer el mensaje. (No tengo ningun tio Juan, por lo que
me he enviado el mensaje a mi propia cuenta de hotmail.) La figura 10.5 muestra
374 Cyber Greetings: arrastrar y soltar sobre el correo electronico

el aspect0 que presentari4 el mensaje. No es mas que un mensaje y un vinculo.


Una vez que el destinatario recibe el mensaje, entonces se procede con la carga
del documento que ha sido creado por el usuario. Lo podemos comprobar en la
figura 10.6.

Figura 10.5. Anuncio de Cyber Greeting

Figura 10.6. Mensaje correspondiente a la aplicacion denominada


CyberGreetings
Cyber Greetings: arrastrar y soltar sobre el correo electronico 375

Requisitos para la ejecucion


Para ejecutar esta aplicacion necesitara MSIE o Navigator 4.x (0superior) de-
bido a DHTML y la gran cantidad de datos de las hojas de estilos. El programa
se ha diseiiado para trabajar con un monitor con una resolucion de 1024x768,
aunque se puede modificar para que trabaje con resoluciones de 800x600.
Este programa tambien necesita trabajar con 10s script del servidor. Si no esta
familiarizado con estos elementos, no se preocupe. Ya veremos mas adelante uno
de estos scripts en 10s apCndices del libro.

Analisis de la sintaxis
Con esta aplicacidn tambien empezaremos con el diagrama de flujo y despuCs
pasaremos a ver el codigo. En la figura 1 0 . 7 podemos ver que el usuario intro-
duce la direccidn del destinatario, el contenido del mensaje, selecciona un fondo
y coloca 10s iconos sobre la tarjeta. Luego podra ver el aspect0 de su trabajo. Si
esta satisfecho con 10s resultados, se lo envia a1 servidor.
Esta aplicacion trabaja a dos niveles: en el explorador del cliente y en el servi-
dor Web. El explorador es la herramienta que utiliza el usuario para crear el fon-
do, 10s iconos y el mensaje de la tarjeta. Cuando envia el formulario HTML, la
informacion la recibira el servidor Web, que se encargara de la creacion de un
archivo que coincida con la tarjeta. El servidor Web devolvera un formulario
HTML a1 usuario para que pueda enviar el mensaje a1 destinatario. Este mensaje
no contendra nada mas que un anuncio y un vinculo para que se pueda recoger
la tarjeta de Cyber Greeting.
Primero repasaremos la aplicacion desde el punto de vista del cliente y luego
desde el servidor. Hay cuatro archivos. Los vemos en la siguiente lista:
indexhtrnl. Nivel superior. Contiene 10s marcos de trabajo.
back.htmZ. Contiene el espacio de trabajo donde se selecciona la tarjeta, el
fondo y 10s iconos.
front.htmZ. Contiene la interfaz para crear y enviar el mensaje.
greet.pZ. Script del servidor que se utiliza para crear y almacenar las tarjetas
en un archivo y crear, a continuacion, un formulario HTML que se enviara
a1 destinatario.

Como se puede ver a traves de las capturas de pantalla, esta interfaz tiene dos
caras. La secundaria (backhtrnl) es la pantalla de la tarjeta donde el usuario
puede ver la tarjeta seleccionada y jugar con 10s elementos graficos. La principal
(front.html)contiene el formulario de entrada y es la que se encarga de la inser-
cion de la direccion de correo electronico y del cuerpo del mensaje y tambien de
3 76 Cyber Greetings: arrastrar y soltar sobre el correo electr6nico

la producci6n de 10s grhficos a 10s que se les conoce como iconos. Ambos docu-
mentos se encuentran dentro del archivo index.htrnZ. En el ejemplo 10.1 tene-
mos 10s detalles.

Selecciona el fond0
y coloca 10s iconos
selecciona una tau

I
Creacih del archivo Envlo
en el servidor de la tarjeta
a/ servidor
1 I

de la confimaci6n
y del formulano
del correo electrdnico

de un mensaje El destineiah
de correo electrdnico mbe el mensaje
a/ destinatano de correo electnsnrcu

I hXn.1

' €1 desbnaterio carga


' el URL donde se
encuentra la taqeta

Figura 10.7. Logica de Cyber Greeting: pasos hasta que el destinatario lee el mensaje

Ejemplo 10.1. index.htm1


1 <HTML>
2 <HEAD>
3 <TITLE>Cyber Greetings</TITLE>
4 <SCRIPT L A N G U A G E = " J a v a S c r i p t 1 . 2 " >
5 <!--
6
Cyber Greetings: arrastrar y soltar sobre el correo electronico 377

7 var greetings = [
8 'Choose One', 'Family Reunion! I ,

9 'Get Well Soon','ThinkingOf You',


10 'Big Party!', 'Psst. . . You\'re Invited.',
11 'Happy Eirthday! ' , 'Congratulations!' ,
12 'We\'reGonna Miss U ' , 'Just A Reminder',
13 'Don\'tForget'
14 I;
15
16 var baseURL = " . '' ;
17
18 //-->
19 < / SCRIPT>
20 </HEAD>
21 <FRAMESET COLS="450,* " FRAMEEORDER="2"BORDER="O">
22 <FRAME SRC= front .h tml " NAME= Front '' NORESIZE>
'I

23 <FRAME SRC= bac k .html NAME= " Back NORESIZE SCROLLING= NO


'I I' I' >
24 </FRAMESET>
25 < /HTML>

index.html contiene un array llamadogreetings y esta en las lineas 7-14. Se tra-


ta de las tarjetas que puede utilizar el usuario a travks de la lista de seleccion.
baseURL contiene el directorio base del servidor Web de la aplicacion. L o contiene
todo: 10s cuatro archivos, todas las imagenes y el directorio que contiene cada
una de las tarjetas creadas por el usuario. baseLIRL tambikn se incluye dentro de
la propia tarjeta. Cuando se modifica este valor, se cambiara en el lado del clien-
te y en el del servidor.
Entonces, ipor qu6 declarar una variable y un array dentro de este archivo?
Ambos archivos se definen dentro de 10s marcos de index.htmZ y necesitaran estas
tarjetas para crear las respetivas paginas durante la carga del codigo JavaScript.
Y lo mismo ocurre con baseLIRL. De esta manera se evitan errores durante el pro-
ceso de carga.

10s otros dos documentos


El concept0 que se esconde detras de las dos caras de la aplicacion es el que rige
las tarjetas postales tradicionales. La cara principal contiene la direccion y el men-
saje, mientras que la cara posterior contiene una foto de la playa. E n este caso,
back.htmZ contiene la zona grafica que aparecera en la cara secundaria. Este
archivo es el responsable de la configuracion inicial del documento. front.html
facilita las cosas despuCs de la carga del documento, como, por ejemplo, la in-
sercion del mensaje y el envio de la tarjeta. Por ese motivo parece 16gico empezar
por back.htmZ. Afortunadamente, ya hemos visto gran parte de este codigo en
10s capitulos anteriores. Vamos a ver a continuacion el contenido correspon-
diente a1 ejemplo 10.2.
3 78 Cyber Greetings: arrastrar y soltar sobre el correo electr6nico

Ejemplo 10.2. back.htm1

1 <HTML>
2 <HEAD>
3 <TITLE>Cyber Greetings</TITLE>
4 < STYLE TYPE= text / cs s >
5 <!--
6
7 .Greeting
8 {
9 font-family: Arial;
10 font-size: 48px;
11 font-weight: bold;
12 1
11
14 //-->
15 </STYLE>
16 <SCRIPT LANGUAGE="JavaScript1.2">
17 <!--
18
19 var NN = (document.layers ? true : false);
20 var hideName = (NN ? 'hide' : 'hidden');
21 var showName = (NN ? 'show' : 'visible');
22 var zIdx = -1;
23
24 var iconNum = 4;
25 var startWdh = 25;
26 var imgIdx = 0;
27 var activate = false;
28 var activeLayer = null;
29
30 var backImgs = [ I :
31 var icons = [
32 'bear', 'cowprod', 'dragon', 'judo',
33 'robot', 'seniorexec', 'dude',' juicemoose',
34 'logol', 'logos', 'logo3','tree',
35 'sun', 'gator','tornado', 'cactus'
36 I;
37
38 function genLayout0 {
39
40 for (var i = 0; i <= 7; i++) {
41 backImgs[il = new Image();
42 backImgs[i].src = top.baseURL + '/images/background'+ i +
43 ,.jpg';
44 1
45
46 genLayer("Back", 10, 250, backImgs[l] .width,
backImgs [ 1I .height,
47 showName, <IMG NAME="background" SRC=" + top.baseURL +
48 '/images/backgroundO.jpg">');
Cyber Greetings: arrastrar y soltar sobre el correo electronico 3 79

49
50 for (var j = 0; j < parent.greetings.1ength; j++) (
51 genLayer("greeting" + j, 5 0 , 2 7 5 , 5 0 0 , 1 0 0 , hideName,
52 '<SPAN greeting">' + parent.greetings[jl + ' <
SPAN>') ;
53 1
54
55 for (var i = 0; i < icons.length; i++) {
56 if (i B iconNum == 0 ) ( startWdh = 2 5 ; )
57 else ( startWdh += 110; )
58 genLayer(icons[i], startWdh, 1 5 , 100, 100, (i < iconNum ?
showName :
59 hideName), <A HREF="javascript: changeAction(\' ' +
icons[il +
60 + (i + 1) + ' ) ; " x I M GSRC="' + top.baseURL +
I \ ' , '

61 '/images/' + icons[i] + '.gif" BORDER="O"></A>');


62 1
63 startwdh = 2 5 ;
64 1
65
66 function genLayer(sName, sLeft, sTop, sWdh, sHgt, sVis, copy) (
67 if ( N N ) {
68 document.writeln('<LAYER NAME="' + SName + "' LEFT=' + sLeft
69 + ' TOP=' + sTop + WIDTH=' + sWdh + HEIGHT=' + sHgt +
70 ' VISIBILITY="' + sVis + ' " z-Index=' + (++zIdx) + ' > ' + copy
71 + '</LAYER>');
72 I
73 else (
74 document.writeln('<DIVID="' + sName +
75 ' " STYLE="position:absolute; overf1ow:none; left:' +
76 sLeft + 'px; top:' + sTop + 'px; width:' + sWdh + 'px;
height: ' +
77 sHgt + 'px; visibility:' + sVis + ' ; z-Index=' + (++zIdx) +
"'>' +

78 copy + '</DIV>'
I9 );
80 1
81 1
82
83 function hideSlide(name) {
84 refSlide(name).visibility = hideName;
85
86
87 function showSlide(name) {
88 refSlide(name).visibility = showName;
89 I
90 function refSlide(name) I
92 if ( N N ) ( return document.layers[namel; )
93 else ( return eval('document.al1.'+ name + '.style'); 1
94 1
95
380 Cvber Greetings: arrastrar v soltar sobre el correo electronico

96 function motionlistener0 {
97 if (NN) I
98 window.captureEvents(Event.MOUSEM0VE);
99 window.onmousemove = grabXY;
100 1
101 else {
102 document.onmousemove = grabXY;
103 }
104
105
106 function grabXY(ev) {
107 if (activate) {
108 if(”) {
109 var itemwdh =
refSlide(activeLayer).document.images[Ol.width;
110 var itemHgt =
refSlide(activeLayer) .document.images[Ol .height;
111 refSlide(activeLayer).left = ev.pageX - parseInt(itemWdh /
2):
112 refSlide(activeLayer).top = ev.pageY - parseInt(itemHgt /
2) :
113 1
114 else {
115 var itemwdh = document.images[imgIdx].width;
116 var itemHgt = document.images[imgIdx].height;
117 refSlide(activeLayer).left = event.x - parseInt(itemWdh /
2):
118 refSlide(activeLayer).top = event.y - parseInt(itemHgt /
2):
119 1
120 }
1 21 1
122
123 function changeAction(name, MSIERef) {
124 activate = !activate;
125 activeLayer = name;
126 imgIdx = MSIERef;
127 )
128
129 //-->
130 </SCRIPT>
131 < /HEAD>
132 <BODY onLoad=”motionListener( ) : ” >
133
134 <SCRIPT L A N G U A G E = “ J a v a S c r i p t 1 . 2 ” >
135 <!--
136
137 genLayout () ;
138
139 //-->
140 < /SCRIPT>
Cvber Greetings: arrastrar v soltar sobre el correo electronico 381

Antes de que el remitente pueda crear la felicitation, una serie de funciones ten-
dran que generar una serie de capas y determinar la ubicacion del cursor del
raton, con respecto a1 documento. Es muy parecido a lo que se vio en 10s capi-
tulos 3 y 8. De hecho, varias funcionen proceden directamente de dichos capitu-
10s. Lo volveremos a comentar cuando las analicemos. De momento, vamos a
echar un vistazo a la declaracion de variables que tiene lugar en las lineas 19-36:

var NN = (document.layers ? true : false);


var hideName = (NN ? 'hide' : 'hidden');
var showName = (NN ? 'show' : 'visible');
var zIdx = -1;

var iconNum = 4;
var startWdh = 25;
var imgIdx = 0;
var activate = false;
var activeLayer = null;

var backImgs = [ I ;
var icons = [
'bear', 3cowprod', 'dragon', 'judo',
'robot', 'seniorexec', 'dude', 'juicernoose',
'logol', 'log02'. 'logo3','tree',
'sun', 'gator', 'tornado', 'cactus'
I;

Las cuatro primeras variables proceden de 10s script anteriores. N N ayuda a


determinar el explorador que se esta utilizando. showName y hideName son dis-
tintas cadenas que se usan para mostrar u ocultar capas, dependiendo del mos-
trador, y zldx representa un entero que se utilizara para determinar el valor del
indice z de las distintas capas que se van creando. La variable iconNum es un en-
tero que se utiliza para determinar la cantidad de iconos que se mostraran a la
vez en la pantalla. Empezamos con 4. startWdh se usa para determinar la posi-
ci6n iniciar de 10s iconos. Lo veremos en breve, con la funcion genLayout ( 1 .
imgldx registra las imagenes. activate es una variable booleana que determina
si la capa se arrastra o suelta . activeLayer determina la capa en la que se encuen-
tra el cursor del usuario o d6nde ha hecho clic.
Por si no fuese suficiente, tambih hay dos variables array. backlmgs, que ini-
cialmente su valor es un array vacio. Se publicara en breve, junto con 10s obje-
tos Image, cada uno de 10s cuales contendra una imagen de fondo. Los nombres
de estas imagenes son backgroundO.jpg, background1 .jpg, backgroundZ.jpg, etc.
382 Cvber Greetings: arrastrar v soltar sobre el correo electronico

icons es un array de cadenas que identifican el nombre de cada icono. Es decir,


que cada icono se creara en una capa del icono con el nombre del elemento. L a
imagen que se usa con icono tendra el mismo nombre. Por ejemplo, la capa bear
contendra la imagen bear.@. Por cierto, todos 10s iconos son GIF transparentes.
El color que tiene esta propiedad es el blanco. Como las imagenes del fondo son
principalmente blancas, podremos colocar una imagen sobre otra sin que la de
arriba cubra a la de abajo.

Moverse por un terreno conocido


Si ha revisado 10s capitulos anteriores del libro, le gustara saber que ya ha he-
cho gran parte del trabajo. L a tabla 10.1 le ayudara a determinar las funciones
que ya se han visto en otros capitulos.
Tabla 10.1. Funciones para la rnanipulacion de capas

Funcion Finalidad Capitulo(s)


~ ~~ ~ ~ ~ ~ ~ ~ ~

genLayer ( ) Crear las capas en NN o MSIE 3, 4, 6 , 9 , 1 1 .


hides 1 ide ( ) Ocultar las capas basandose 3, 4, 6 , 9 , 11.
en el nombre
showslide ( ) Mostrar las capas basandose 3, 4, 6 , 9 , 1 1 .
en el nombre
refslide ( ) Establecer referencias a las capas 3, 4, 6, 9 , 11.
basandose en el nombre
motionListener () Registrar el movimiento del cursor 11.
grabXY ( ) Obtener las posiciones x e y de 11
u n elemento

Las cuatro primeras funciones de la tabla son las mismas que las vistas en 10s
capitulos anteriores. Si no esta familiarizado con ellas, revise 10s capitulos espe-
cificados. Sin embargo, m o t ionListener ( ) se ha modificado ligeramente, por
lo que tendremos que analizarla. La funcion grabXY ( ) que se Vera en el siguien-
te capitulo tambiin ha sufrido algun cambio. A continuacidn veremos el resto
de funciones.

iHay sitio para todos!


Mientras se carga la aplicacidn, backhtrnl se estarh encargando de la carga de las
imagenes, de la creacion y ubicacidn de las capas y de mostrarlas u ocultarlas
segun sea necesario. L a funcion genLayout ( ) se encarga de toda la coordina-
cion. Vamos a ver el contenido de las lineas 38-64:
Cyber Greetings: arrastrar y soltar sobre el correo electronico 383

function genLayout0 {

for (var i = 0; i <= 7; i++) {


backImgs[i] = new Image();
backImgs[i].src = top.baseURL + '/images/background'+ i t
' .jpg';
1
genLayer("Back", 10, 250, backImgs[ll.width, backImgs[ll.height,
showName, <IMG N?+ME="background" SRC=" + top .baseURL +
'/images/backgroundO.jpg">');

for (var j = 0; j < parent.greetings.1ength; j++) {


genLayer("greeting"+ j, 50, 275, 500, 100. hideName,
'<SPAN CLASS="Greeting">' + parent.greetings[jl + '</SPAN>');
1

for (var i = 0; i < icons.length; it+) {


if (i % iconNum == 0 ) { startWdh = 25; 1
else { startwdh += 110; )
genLayer(icons[i], startWdh, 15, 100, 100, (i c iconNum ? showName :
hideName), '<A HREF="javaSCript: changeAction(\" + icons[i] +
+ (i + 1) + ');"z<IMGSRC="' + top.baseURL +
' \ I , '

'/images/' + icons[i] + '.gif" BORDER="O"></A>');


1
startWdh = 25;
1

Lo primero que hace genLayout ( ) es cargar por anticipado las imagenes del
fondo. Como el usuario querra verlas antes de decidirse por una de ellas, convie-
ne cargarlas antes. Al usar backlmgs, la funcion crea un objeto Image para cada
elemento, asigna su origen de acuerdo con t o p . baseURL (ya la hemos declara-
do en indexhtml), crea la cadena background, el valor de i y la cadena .jpg:

for (var i = 0; i <= 7 ; i++) (


backImgs[il = new Image();
backImgs[il.src = top.baseURL + '/images/background'+ i + I.jpg'; 1

Despuks de cargar todas las imhgenes, lo primero que se ha de determinar es la


imagen de fondo que se utilizara como predeterminada. Podemos seleccionar
cualquiera de ellas, per0 para simplificar, he seleccionado background0.jpg y la he
guardado en la capa Back. Las dimensiones de la capa seran las mismas que las
de la imagen de fondo. Este hecho cobrara importancia cuando mas tarde colo-
quemos 10s iconos:

genLayer("Back",10, 250, backImgs[ll .width, backImgs[ll .height,


showName, '<IMG NAME="background"SRC="' + top.baseURL +
'/images/backgroundO.jpg">');
384 Cyber Greetings: arrastrar y soltar sobre el correo electronico

Ya tenemos la capa del fondo y la imagen predeterminada en su sitio. Lo siguien-


te que tenemos que hacer es configurar la tarjeta. No es mas que capas con algo
de texto, como "Family Reunion" (reunion familiar) o "Thinking Of You" (pen-
sando en ti). Estas tarjetas se obtienen del array greetings de index.htrnZ. Lo apli-
camos en las lineas 50-53:

for (var j = 0; j < parent.greetings.1ength; j + + ) (


genLayer("greeting" + j, 50, 275, 500, 100, hideName,
'<SPAN CLASS="Greeting">' + parent.greetings[jl + '</SPAN>');

Es decir que habra tarjetas parent.greetings.length con las mismas posi-


ciones izquierda y superior (50 y 275, respectivamente). El usuario no las podra
mover, sino que se colocaran suavemente en la parte superior izquierda de la
zona de visualizacion de la pantalla. Cada tarjeta se encontrara dentro de su pro-
pia capa. La capa contiene un conjunto de etiquetas SPANcon las que se utiliza-
ra la definicion de la clase para las hojas de estilo, llamada Greeting, que se ha
definido en la parte superior del documento.
Una vez que tenemos en su sitio el fondo y la tarjeta, todo lo que nos queda es
colocar 10s iconos. Veamos las lineas 55-62:

for (var i = 0 ; i < icons.length; i + + ) (


i f (i 8 iconNum == 0 ) ( startWdh = 25; }
else { startWdh += 110; }
genLayer(icons[i], startwdh, 15, 100, 100, (i < iconNum ? showName :
hideName), ' < A HREF="javascript: changeAction(\" + icons[i] +
' \ ' , ' + ( i + 1) + ' ) ; " > < I M GS R C = " ' + top.baseURL +
' / i m a g e s / ' + icons[i] + ' .gif" B O R D E R = " O " > < / A > ' ) ;
)

Cada elemento de 10s iconos representa una capa. La variable iconNurn deter-
mina que se mostraran cuatro iconos a la vez. Ademas, las imagenes tendran
100 pixeles de ancho. Los tamafios pueden variar. La variablestartWdh empieza
en 25. Este valor determinara la posicion del pixel izquierdo de las capas que se
van creando. He dejado una separaci6n de 10 pixeles entre 10s iconos. Es decir,
que si empezamos a 25 pixeles del margen derecho de la capa, colocaremos un
icono nuevo cada 110 pixeles (100 pixeles de ancho de la capa y 10 de separa-
cion entre ellas). Despues de crear y colocar el iconNurn de 10s iconos, el proceso
se inicia con el mismo punto de referencia de 10s 25 pixeles. Esto es posible gra-
cias a las propiedades de programacion. Una de ellas es la declaracion if-else que
se ejecuta antes de que la funcion genLayer ( ) se encargue de generar la capa;
la otra es la utilizacion del operador de modulos (%). Vamos a verlas con mas
detalle:
Cvber Greetinas: arrastrar v soltar sobre el correo electronic0 385

if ( i % i c o m u m == 0 ) { startWdh = 25; }
else { startWdh += 110; 1

Segun se ejecuta el bucle, i crece cada vez mas. Cada vez que i es multiplo de
iconNum (4, en este caso), se iniciara un nuevo grupo de iconos, colocando el pri-
mero a 25 pixeles de distancia del borde izquierdo de la capa. startWdh tendra el
valor 25. Por ejemplo, se iniciara un nuevo grupo de iconos cuando el valor de
i sea 4, 8, 12, 16 y 20. Si i es cualquier otro valor, se indicara que el siguiente
icono se colocara a 110 pixeles de distancia del icono anterior. Esta es la razon
por la que se aiiade 110 a1 valor de startWdh. El operador de modulo devolvera
un entero que indicara el resto del cociente. Si el resto es 0, i sera multiplo de
iconNum.
La parte mas complicada sera conocer donde se encuentra el borde izquierdo
de cada capa. Ahora genLayout ( ) completa sus tareas creando una capa para
cada icono junto con una llamada a genLayer ( ) :

genLayer(icons[i], startWdh, 15, 100, 100, (i < iconNum ? showName :


hideName), ' < A HREF="javascript: changeAction(\" + icons[i] + ' \ ' , ' +
(i + 1 ) + ' ) ; " > < I M GS R C = " ' + top.baseURL + '/images/' +
icons[i] + '.gif" B O R D E R = " O " > < / A > ' ) ;

Cada icono contiene una unica etiqueta IMG rodeada por otra etiqueta de refe-
rencia. ObsCrvese que el segundo y tercer argumento que se le pasa a genLayer ( )
seran 10s valores de las propiedades izquierda y superior de la capa. startWdh
siempre representa la izquierda; la parte superior sera siempre 15 pixeles. El sex-
to argumento que se pasa determina si el icono sera visible o no. Por defecto,
unicamente se mostrara el primer conjunto creado de iconos. En este caso, las
cuatro primeras capas. Por lo tanto, el operador condicional del sexto argumen-
to determinara que si i es menor que iconNum (por ejemplo, 0, 1, 2 6 3), la capa
sera visible. En cualquier otro caso, se ocultara. Si el icono ha de ser visible, se
pasara la variable showNarne. Si no, hideName se hara cargo de la llamada.
Los dos ultimos puntos que se han de tener en cuenta antes de seguir seran la
naturaleza de la etiqueta de referencia. iQu6 hace? Supongarnos lo siguiente.
Siempre que el usuario pase la raz6n sobre u n icono y haga clic una vez, la apli-
caci6n entendera que quiere moverlo. Para ello, el protocolo javascript : que
se encuentra dentro del atributo HREF se encarga de enviar una llamada a la
funcion changeAction ( ) , que veremos en breve. Todos 10s atributos HREF lla-
man a esta funcion, per0 cada enlace entregara a changeAction ( ) informacion
sobre si mismo.
En primer lugar, changeAction ( ) tendra que conocer el nombre del icono
con el que tiene que trabajar. Es sencillo. Se le entrega en icons [ i 1 , que contie-
ne la cadena correcta. A continuacion, tendremos que entregar u n entero que
386 Cyber Greetings: arrastrar y soltar sobre el correo electronico

represente el icono del modelo de objetos para el documento de MSIE. Es necesa-


rio para que funcione la propiedad "arrastrar y soltar", ya que MSIE necesita co-
nocer la imagen con la que trabajara. No olvidemos que la primera imagen que
se crea en la pagina es la del fondo de la tarjeta, es decir, document. images [ 0 I .
El primer icono del documento sera document. images [ 1] . La representacion
del resto de iconos sera document. images [ i + 1 ] . Esta es la razon por la que
se pasa el valor de ( i + 1) . Lo veremos mas claro cuando analicemos las fun-
ciones changeAction ( ) y g r a b X Y ( ) .
Esta es la explicacion de la funcion que se encuentra en la linea 2 7 . Vamos a
empezar por asignar a startWdh el valor 2 5 y seguiremos avanzando.

Registrar el movimiento del raton


m o t i o n L i s t e n e r ( ) permite que JavaScript capture las actividades del raton
a traves del controlador de eventos onmousemove. Su configuracion es muy sen-
cilla. La unica diferencia entre 10s dos exploradores es que Navigator necesita
que se llame a1 metodo c a p t u r e E v e n t s ( ) y que implementa el evetornousemove
desde la ventana. MSIE lo hace desde el documento. Este es el contenido de las
lineas 96-104:

function motionlistener0 {
if ( N N ) {
window.captureEvents(Event.MOUSEM0VE);
window.onmousemove = grabXY;
1
else {
document.onmousemove = grabXY;
1
1

Cuando el usuario mueve el raton, la funcion llama a la funcion g r a b X Y ( ) .


Recordemos que para establecer la llamada no se necesitara ningun parametro.
onmousemove establece una referencia dirigida a g r a b X Y , no la llama. El evento
onLoad llama a la funcionmotionListener ( ) en la linea 132. Esta llamada uni-
camente tendra lugar una vez, por lo que el registro del movimiento del raton
tendra lugar durante toda la aplicacion.

Llamar a todos 10s iconos


Cuando el usuario hace clic sobre un icono, la llamada a changeAc t i o n ( ) ac-
tiva el icono y permite su movimiento. Las lineas 123-127 tienen 10s detalles:

function changeAction(name, MSIERef) {


activate = !activate;
Cyber Greetings: arrastrar y soltar sobre el correo electronico 387

activeLayer = name;
imgIdx = MSIERef;
)

iSe acuerda de las variables activate y activelayer? Las declaramos hace bastan-
te tiempo, a1 principio del documento. activate se inicia con el valor f a l s e que
(como pronto veremos con g r a b X Y ( ) ) no movera ninguna capa a1 desplazar el
cursor del ratbn. La primera vez que se llama a changeAction ( 1, activate pa-
Sara a t r u e , con lo que g r a b X Y ( ) entrara en acci6n. La capa seguira a1 cursor.
L a unica forma de detenerlo sera haciendo clic de nuevo. Esta vez cambiara el
valor de la variable activate por supuesto, por f a l s e . Se detendra la operaci6n
de arrastre.
LRecuerda como se disefio c h a n g e a c t i o n ( ) para pasar su valor en dos argu-
mentos? Uno es el nombre de la capa con la que se esta trabajando, que se asig-
nara a name. El otro es el indice de la imagen, a traves del cual se establecera la
referencia a la imagen que se encuentra en el document. images (accion valida
para MSIE). Este valor se asignara aMSIERef. El valor de activelayer sera el mis-
mo de name e imgIdx se configurara con el valor de MSIERef. Es todo lo que ne-
cesitamos para arrastrar 10s iconos, independientemente del explorador que se
estk utilizando.

Mover 10s iconos


La funci6nmotionListener ( ) se configura de tal forma que llamara a g r a b X Y ( )
cada vez que el usuario mueva el rat6n. Vemos que ocurre en las lineas 106-121 :

func t on grabXY(ev) {
if (activate) {
f(") I
var itemWdh = refSlide(activeLayer).document.images[Ol.width;
var itemHgt = refSlide(activeLayer) .document.images[Ol .height;
refSlide(activeLayer).left = ev.pageX - parseInt(itemWdh / 2 ) ;
refSlide(activeLayer).top = ev.pageY - parseInt(itemHgt / 2 ) ;
1
else {
var itemWdh = document.images[imgIdxl.width;
var itemHgt = document.images[imgIdxl.height;
refSlide(activeLayer).left = event.x - parseInt(itemWdh / 2 ) ;
refSlide(activeLayer).top = event.y - parseInt(itemHgt / 2 ) ;
)
)

Se llama a la funci6n g r a b X Y ( ) pero solo se ejecutara el c6digo si el valor de la


variable activate es t r u e . La primera vez que el usuario haga clic en u n icono,
388 Cvber Greetings: arrastrar v soltar sobre el correo electronic0

activate sera true. Se ejecutara la declaracidn anidada if-else. Si el usuario esta


trabajando con Navigator, se ejecutara el bloque if. En cualquier otro caso, se eje-
cutara el bloque else. Ambos realizan la misma funcion, per0 cada uno se adap-
ta a u n explorador distinto.
Ambos bloques declaran las variables locales iternWdh e iternffgt. Determina-
ran las posiciones izquierda y superior del icono sobre el que se hace clic. Enton-
ces, ipor quk no se les asigna las coordenadas del cursor del raton? DespuCs de
todo, para eso estamos registrando el movimiento del raton.
Podriamos, per0 hay un inconveniente. Si lo hacemos tendremos que el cursor
se encuentra en la esquina superior izquierda del icono durante la accion del
movimiento. Parece un poco raro, per0 el usuario puede mover el raton dema-
siado rapido con lo que no se encontraria sobre el punto adecuado en el momen-
to de hacer clic y no activaria el icono. El usuario tendra que hacer clic un par de
veces para "liberar" el icono.
La soluci6n es colocar el icono de tal forma que el cursor se encuentre siempre
en su centro. Independientemente del explorador, iternWdh e iternHgt represen-
tan el ancho y el alto de la imagen del icono que ha seleccionado el usuario. Te-
nemos que conseguir estos valores y esta accion si que dependera el explorador.
Con Navigator sera asi:

var itemWdh = refSlide(activeLayer).document.images[O].width;


var itemHgt = refSlide(activeLayer).document.images[Ol.height;

Para obtener la imagen tendremos que establecer una referencia que apunte,
primero a la capa adecuada, luego a1 documento y por ultimo a images [ 0 I (es
la unica imagen de la capa). En MSIE las cosas cambian:

var itemWdh = document.images[imgIdx].width;


var itemHgt = document.images[imgIdx].height;

MSIE no tiene u n array de capas. Podemos acceder directamente a las image-


nes que se encentran dentro del array. Per0 tendremos que dirigirnos a la ima-
gen correcta, para lo cual utilizaremos la referencia irngldx. No olvidemos que
determinamos su valor cada vez que se llama a changeAct ion ( ) . Revise esta
funcion si tiene alguna duda.
Ahora tenemos un controlador sobre el ancho y alto de la imagen deseada.
Todo lo que tenemos que hacer es algo de matematicas para colocar el cursor en
su centro.
Lo veremos con u n ejemplo. Supongamos que el usuario hace clic sobre un
icono y empieza a arrastrarlo En el momento en que hace clic, el cursor se en-
cuentra a 100 pixeles del borde izquierdo del documento y a otros 100 del borde
Cyber Greetings: arrastrar y soltar sobre el correo electronic0 389

superior (del documento, no de la pantalla). Supongamos que la imagen del ico-


no tiene 100 pixeles de ancho y 150 pixeles de alto. Si ajustamos el valor de las
propiedades izquierda y derecha del icono a 100, 100, colocaremos el cursor di-
rectamente sobre la esquina superior izquierda. No esta mal, per0 lo que quere-
mos es colocarlo en el centro del icono.
Para ello, restaremos la mitad del ancho de la imagen del valor de la posicion iz-
quierda y la mitad del alto a1 valor de la posicion superior. Sabemos que el valor
de itemWdh es 100 y que itemHgt es igual a 150. Asi obtendremos las nuevas
posiciones:

Icon left = pointer arrow horizontal ( x ) location-(100/2)=100-(50)=50


Icon top = pointer arrow vertical ( y ) location-(150/2)=100-(75)=25

En vez de asignar 10s valores 100 y 100 a las posiciones izquierda y superior,
aplicaremos 10s valores 50, 25, con lo que tendremos el punter0 en el centro del
icono. Para asegurarse de que el resultado de la division entre 2 es u n numero
entero, utilizaremos parseInt ( ) para quedarnos unicamente con la parte en-
tera de dicho resultado. Vamos a volver a mirar el codigo de grabXY ( ) . L a im-
plementacion en Navigator sera la siguiente:

refSlide(activeLayer).left = ev.pageX - parseInt(itemWdh / 2);


refSlide(activeLayer).top = ev.pageY - parseInt(itemHgt / 2);

El modelo de eventos en Navigator utiliza un objeto evento que se ha creado


sobre la marcha, que podemos ver en la variable local ev. Las propiedades pageX
y pageY de cada uno de estos objetos contienen 10s valores de las coordenadas
x,y de la capa activa. Por otro lado, MSIE tiene un objeto evento global a travCs
del cual se puede acceder a las coordenadas:

refSlide(activeLayer).left = event.x - parseInt(itemWdh / 2);


refSlide(activeLayer).top = event.y - parseInt(itemHgt / 2 ) ;

Las propiedades x e y contienen 10s valores equivalentes. Ahora, el icono se


puede arrastrar y soltar.

Despues de la carga de 10s documentos


La accion empieza enfront.htrnZ. En el ejemplo 10.3 podemos ver su codigo. La
primera decena de lineas contiene las propiedades de la hoja de estilos. Las dos-
cientas siguientes definen las variables y funciones de JavaScript responsables
de capturar la informacion que se utilizara con objeto de crear, probar y enviar
la tarjeta.
390 Cvber Greetings: arrastrar v soltar sobre el correo electronico

Ejemplo 10.3. front.htm1

1 <HTML>
2 <HEAD>
3 <TITLE></TITLE>
4 <STYLE TYPE="text/css">
5 <!--
6
7 TD
a (
9 font-family: Arial;
10 >
11
12 .Front
13 I
14 position: absolute;
15 left: 25;
16 top: 25;
17 width: 325;
ia border: lpx solid;
19 background: #ffffee;
20 J
21
22 //-->
23 < 1 STYLE>
24 <SCRIPT LANGUAGE="JavaScriptl.2">
25 <!--
26
27
28 var curGreet = iconIdx = 0;
29 var backgroundIdx = 0;
30 var bRef = parent.Back
31
32 function showGreeting(sel1dx) {
33 if (selIdx > 0 ) (
34 bRef.hideSlide("greeting" + curGreet);
35 bRef . showslide ( "greeting" + selIdx);
36 curGreet = selIdx;
31 )
38 }
39
40 function nextBackground0 {
41 backgroundIdx = (backgroundIdx == bRef.back1mgs.length - l ?
42 backgroundIdx = 0 : backgroundIdx + 1);
43 if(document.al1) {
44 bRef.document.background.src =
bRef.backImgs[backgroundIdxl.src;
45 }
46 else {
41 bRef . document.layers [ "Back"] . document.images [ 0 I . src =
48 bRef.backImgs[backgroundIdxl .src;
Cyber Greetings: arrastrar y soltar sobre el correo electronico 391

49 1
50 I
51
52 function nextIcons0 {
53 for (var i = bRef.iconNum * iconIdx; i < (bRef.iconNum *
iconIdx) +
54 bRef.iconNum; i++) {
55 if (i < bRef.icons.length && !onCard(i)) {
56 bRef.hideSlide(bRef.icons[il);
57 1
58 1
59 iconIdx = (iconIdx >= (bRef.icons.length / bRef.iconNum) - 1 ?
0 :
60 iconIdx + 1);
61 for (var i = bRef.iconNum * iconIdx; i < (bRef.iconNum *
iconIdx) +
62 bRef.iconNum; i++) {
63 if (i < bRef.icons.length)
64 bRef.showSlide(bRef.icons[il);
65 1
66 else { break; 1
67 1
68 )
69
70 function resetForm0 {
71 if (document.al1) {
72 bRef .hideslide( "greeting" +
73 document.EntryForm.Greetings.selected1ndex);
74 document.EntryForm.reset0;
75
76 else {
77 bRef .hideslide("greeting" +
78 document.layers["SetupForm"].document.EntryForm.Greetings
.selectedIndex);
79 document.layers["SetupForm"].document.EntryForm.reset~~:
80 I
81 1
82
83 function onCard(iconRef) {
84 var ref = bRef.refSlide(bRef.icons[iconRef]
85 var ref2 = bRef .refSlide("Back");
86 if(document.al1) {
87 if((parseInt(ref.left) >= parseInt(ref2.lef
88 (parseInt(ref.top) >= parseInt(ref2.top))
89 (parseInt(ref.left1 + parseInt(ref.width)
parseInt(ref2.left) +
90 parseInt(ref2.width)) & &
91 (parseInt(ref.top)+ parseInt(ref.height)
parseInt(ref2.top) +
92 parseInt(ref2.height))) {
93 return true;
392 Cyber Greetings: arrastrar y soltar sobre el correo electronico

94 1
95 )
96 else {
97 if((ref.left >= ref2.left) &&
98 (ref.top > = ref2.top) & &
99 (ref.left + ref.docurnent.images[O].width < = ref2.left +
100 ref2.document.images[O].width) & &
101 (ref.top + ref.document.images[Ol.height <= ref2.top +
102 ref2.document.images[0l.height)) {
103 return true;
104 I
105 1
106 ref.left = ((iconRef 8 bRef.iconNum) * 110) + bRef.startWdh;
107 ref.top = 15;
108 return false;
109 1
110
111 function shipGreeting(f0bj) {
112 if (fObj.Recipient.value == " " ) I
113 alert('You need an email address in the To: field');
114 return false;
115 I
116 else if (fObj.Message.value == " " ) {
117 alert ( "You need to type a Message. " ) ;
118 return false;
119 I
120 else if (fObj.Greetings.selected1ndex = = 0 ) {
121 alert('You need to choose a Greeting.');
122 return false;
123 }
124
125 f0bj.EntireMessage.value = genGreeting(f0bj);
126
127 fObj.BaseURL.value = top.baseURL;
128 return true;
129 1
130
131 f:unction testGreeting(f0bj) {
132 var msgStr = '<HTML><TITLE>Cyber Greeting Test Page</TITLE>' +
133 genGreeting(f0bj) + '<TABLE ALIGN="CENTER"><TR><TD><FORM>' +
134 '<INPUT TYPE=BUTTON VALUE=" OK
onClick="self.close(); " > ' +
135 '</FORM></TD></TR></TABLE></HTML>';
136 newwin = open(", I *'width=' + (
,

137 bRef.backImgs[backgroundIdx].width + 5 0 ) +
138 ',height=600,scrollbars=yes');
139 with(newWin.document) (
140 open ( ) ;
141 writeln(msgStr);
142 close();
143 )
Cyber Greetings: arrastrar y soltar sobre el correo electronico 393

144
145
146
147 function genGreeting(f0bj) {
148 var greetingIdx = fObj.Greetings.selected1ndex;
149 var msg = f0bj.Message.value;
150
151 msg = msg.replace(/\r+/g, " " ) ;
152 msg = msg.replace(/\n+/g, "<BR><BR>") ;
153
154 var msgStr = '<TABLE BORDER=O><TR><TD COLSPAN=2><FONT
FACE=Arial>' +
155 '<H2>Your Cyber Greeting</H2>To: ' + f0bj.Recipient.value +
156 '<BR><BR></TD></TR><TR><TD VALIGN=TOP><IMG SRC="' +
157 top.baseURL + /images/background'+ backgroundIdx + ' . jpg">
158 + '<DIV STYLE="position:relative;left:4O;top:-255;font
fami1y:Arial;' +
159 'font-size:48px;font-weight:bold;">' +
parent.greetings[greetingIdxl +
160 ' </DIV>' ;
161
162 var iconStr = " ;
163 for (var i = 0; i < bRef.icons.length; i++) {
164 if (onCard(i) {
165 iconStr += '<DIV STYLE="position:absolute;left:' +
166 bRef.refSlide(bRef.icons[i]).left + ';top:'+
167 (parseInt(bRef.refSlide(bRef.icons[il).top) -
168 (document.al1 ? 140 : 150)) + ';"><IMGSRC="' +
169 top.baseURL + '/images/'+ bRef.icons[i] + '.gif"></DIV>';
170 1
171
172
173 msgStr += iconStr + '</TD></TR><TR><TDWIDTH=' +
174 bRef.backImgs[backgroundIdxl .width + '><FONT FACE=Arial>' +
msg +
175 '</TD></TR></TABLE>';
176 return msgStr;
177 )
178
179 //-->
180 </SCRIPT>
181
182 < /HEAD>
183 <BODY onLoad="resetForm(); " >
184 <DIV ID="SetupForm" CLASS="Front">
185 <FORM NAME= " EntryForm"
186 ACTION="http://www.your_domain.com/cgi-bin/greetings/greet.pl"
187 METHOD="POST" TARGET="-top " Onsubmit=" return
shipGreeting(this);">
188 <INPUT TYPE=HIDDEN NAME='' Ent ireMeS sage >
189
394 Cyber Greetings: arrastrar y soltar sobre el correo electronico

190 <INPUT TYPE=HIDDEN NAME="BaseURL">


191 <TABLE CELLSPACING="0 CELLPADDING="5 WIDTH= "375" >
192 <TR>
193 <TD COLSPAN="3"><CENTER><H2>Cyber Greetings</Hz></CENTER>
</TD>
194 </TR>
195 <TR>
196 <TD HEIGHT=" 40 VALIGN="TOP">
I'

197 To:
198 </TD>
199 <TD COLSPAN= 2 '' VALIGN="TOP">
I'

200 <INPUT TYPE=TEXT NAME="Recipient SIZE="25 " >


I'

201 </TD>
202 </TR>
203 <TR>
204 <TD HEIGHT= 80 VALIGN="TOP">Message: < /TD>
'I

205 <TD COLSPAN= 2 VALIGN= TOP >


'I

206 <TEXTAREA ROWS="I" COLS="25 NAME= "Message"


WRAP= PHYSICAL">
I'

207 </TEXTAREA>
208 </TD>
209 </TR>
210 <TR>
211 <TD>Images:</TD>
2 12 <TD HEIGHT="40'' COLSPAN=" 2" >
213 <INPUT TYPE=BUTTON VALUE=" Icons - >
onClick="nextIcons( 1 ; " >
214 &nbsp;&nbsp;&nbsp;
215 <INPUT TYPE=BUTTON VALUE="Backgrounds- > "
216 onClick="nextBackground( ) ;" >
217 </TD>
218 </TR>
219 <TR>
220 <TD>Greeting:</TD>
221 <TD HEIGHT='I 40 '' COLSPAN= 2 >
I'

222 <SCRIPT LANGUAGE="JavaScriptl.2">


223 <!--
224
225 var sel = '<SELECTNAME="Greetings"
226 onChange="showGreeting(this.selectedIndex);">';
227 for (var i = 0; i < parent.greetings.1ength; i++) {
228 sel += '<OPTION>'+ parent.greetings[i];
229 I
230 sel += '</SELECT>';
231 document.writeln(se1);
232
233 //-->
234 </SCRIPT>
235 </TD>
236 </TR>
237 <TR>
Cvber Greetings: arrastrar v soltar sobre el correo electrdnico 395

238 <TD VALIGN=TOP>Sending: </TD>


239 <TD HEIGHT= 4 0 ALIGN= "CENTER">
'I I'

240 <INPUT TYPE=BUTTON VALUE=" Test I'

241 onClick="testGreeting(this.form);">
242 &nbsp;&nbsp;&nbsp;&nbsp;
243 <INPUT TYPE=BUTTON VALUE=" Clear 'I

onClick="resetForm(); " >


244 &nbsp;&nbsp;&nbsp;&nbsp;
245 <INPUT TYPE=SUBMIT VALUE=" Send " >
246 < /FORM>
247 </TD>
248 </TR>
249 </TABLE>
250 </FORM>
251 < /DIV>
252 </BODY>
253 </nmL>

Las variables
Aunque front.htmZ no tiene tantas variables como back.htrnZ, hay unas cuan-
tas en las lineas 28-30:

var curGreet = iconIdx = 0;


var backgroundIdx = 0;
var bRef = parent.Back;

La variable curGreet representa el indice de la lista de seleccidn de tarjetas. Ini-


cialmente su valor es 0. iconkfx es una variable que se utiliza para registrar 10s
iconos a traves de su indice. Inicialmente, tambien ser5 cero. La ultima variable
perteneciente a la lista es bReJ que no es mas que una referencia a1 script y a1
objeto de la ventana del cuadro Back. Estas variables nos facilitaran considera-
blemente la existencia.
396 Cyber Greetings: arrastrar y soltar sobre el correo electronico

ero nos referiremos

referencia a 10s d
",TO C P P in favnr rreanrln 1
Cyber Greetings: arrastrar y soltar sobre el correo electr6nico 397
398 Cyber Greetings: arrastrar y soltar sobre el correo electronico

Mostrar las tarjetas en pantalla


Ahora que tenemos la lista de seleccih de tarjetas, para que el usuario acceda a
la que quiera no tendra mas que seleccionarla de la lista. El controlador de even-
tos onchange de la lista se encarga de llamar a showGreeting ( ) :

function showGreeting(sel1dx) I
if (selIdx > 0 ) {
bRef .hideslide ("greeting" + curGreet) ;
bRef.showSlide("greeting" + selIdx);
curGreet = selIdx;
1
1

Del siguiente bloque merece la pena que nos fijemos en el contenido correspon-
diente a las lineas 225-231. Puesto que el explorador analiza el c6digo HTML, el
c6digo JavaScript que se ejecute creara la lista de selection utilizando para ello
el array de tarjetas que fueron definidas en index.htmZ. Vamos a verlo a conti-
nuacih:

var sel = '<SELECT NAME="Greetings" +


'onChange="showGreeting(this.selectedIndex);">';
for (var i = 0; i < parent.greetings.length; i++) {
sel += ' <OPTION> ' + parent. greetings [ i] ;
1
sel += '</SELECT>' ;
document.writeln(se1);

showGreeting ( 1 espera un argumento: selectedndex de la lista Greetings. Mien-


tras que selldx sea distinto de 0, showGreeting ( ) ocultara la capa de la tarjeta
y mostrara la asociada con la tarjeta que este seleccionada. El valor de selZdx se
convertira en el valor de la capa visible, que se tomar6 como punto de inicio para
la siguiente vez.
Cyber Greetings: arrastrar y soltar sobre e l correo electronic0 399

Moverse a traves de todas las imagenes


Para acceder a las distintas imagenes de fondo, el usuario unicamente tendra
que hacer clic en el boton "Backgrounds - ->I' hasta que encuentre el fondo que
prefiera. Al hacer clic en este b o t h , se llamara a la funci6nnextBackground ( 1 ,
cuyo contenido se encuentra en las lineas 40-50:

function nextBackground0 {
backgroundIdx = (backgroundIdx == bRef.backImgs.length - 1 ?
backgroundIdx = 0 : backgroundIdx + 1);
if(document.al1) {
bRef.document.background.src = bRef.backImgs[backgroundIdxl .src;

else {
bRef . document. layers [ "Back"] .document.images [ 0 I . src =
bRef.backImgs[backgroundIdxl .src;
1

Las imagenes del fondo se cargan de antemano en las lineas 40-44 del archivo
back.html. Como 10s nombres siguen el conveniobackgroundO.jpg,background 1 j p g ,
background2.jpg, etc., podemos usar un entero, backgroundldx, junto con una
suma de cadenas, para repetir la ejecucion a travks de todas las imagenes. Cuando
se carga el documento, el valor de backgroundldx es 0. Cada vez que el usuario
haga clic en "Background- este valor se aumentara una unidad hasta que
->,'I

se agoten las imagenes a las que establecer la referencia. En otras palabras, cuan-
do backgroundIdx sea igual a t o p . Back. backImgs . l e n g t h - 1, se le devol-
Vera el valor 0 para que empiece otra vez desde el principio.
Podemos utilizar este valor para cambiar la propiedad src del objeto Image ade-
cuado. Como la imagen del fondo se encuentra en una capa, tendremos que
volver a ordenarlas para acceder de distintas formas a traves de 10s DOMs de
NN y MSIE.
Para MSIE, la imagen se considera una propiedad del objeto del documento:

top.Back.document.background.src

Para Navigator, tendremos que desplazarnos a travks del objeto del documento
que se encuentra dentro de la capa. Como el nombre de la capa es Back, el acceso
a la imagen deseada tendra el siguiente aspecto:

top.Back.document.layers["Back"l .document.images[Ol.src

Una vez que se ha determinado la sintaxis, tendremos que establecer la ruta de


la propiedad src de backlmgs Image utilizando backgroundldx. Por cierto, no es
400 Cyber Greetings: arrastrar y soltar sobre el correo electronico

la primera vez que utilizamos este sistema de repeticion. Ya lo vimos en 10s ca-
pitulos 3 y 8. Ahora el usuario podra moverse a traves de 10s fondos de la tarje-
ta. Necesitaremos algo parecido para 10s iconos. Aqui es donde nextIcons ( )
entra en accion, en las lineas 52-68:

function nextIcons0 {
for (var i = bRef.iconNum * iconIdx; i < (bRef.iconNum * iconIdx) +
bRef.iconNum; i++) (
if (i < bRef.icons.length & & !onCard(i)) (
bRef.hideSlide(bRef.icons[il);
I
I
iconIdx = (iconIdx > = (bRef.icons.length / bRef.iconNum) - 1 ? 0 :
iconIdx + 1);
for (var i = bRef.iconNum iconIdx; i < (bRef.iconNum * iconIdx) +
bRef.iconNum; i++) (
i f ( i < bRef.icons.length) (
bRef.showSlide(bRef.icons[il);
I
else ( break; }
I
I

El usuario se mueve a traves de 10s iconos, igual que hacia con 10s fondos, per0
ahora hace mas cosas aparte de cambiar la propiedad src de una imagen. Cada
icono es una imagen que tiene su propia capa. Por eso, a1 hacer clic en el b o t h
"Icons - ->I'se desarrollan mas acciones que antes. Ademas de ocultar las capas
que aparecen en la pantalla, tendremos que determinar la capa que se mostrara,
para lo cual tendremos que crear grupos.
No tiene mucho sentido que el usuario haga clic 20 veces para ver 20 iconos.
Resulta bastante aburrido y una auttntica pCrdida de tiempo. Como se puede
ver en las capturas de pantalla que aparecen a1 principio del capitulo, he agru-
pado 10s iconos de cuatro en cuatro. Independientemente del valor seleccionado,
el valor de la variable iconNum se determinara en la linea 24 de back.htmZ. Como
se encuentra en front.htmZ, la referencia del script sera top.Back.iconNurn. La
idea es mostrar iconNum iconos cada vez que el usuario haga clic en el boton
"Icons- ->" Si tenemos 20 iconos, el usuario esperara encontrarse con cinco
grupos. Obviamente, tambitn queremos facilitar la tarea de aiiadir y eliminar
iconos. Si eliminamos u n icono, el usuario se encontrara con cuatro grupos de
cuatro iconos y uno de tres. Es decir, que no tendremos que modificar el con-
tenido de nextlcons ( ) .
Es muy sencillo. Empezaremos por 10s cuatro primeros, luego 10s escondere-
mos y mostraremos 10s cuatro siguientes, y repetiremos el proceso hasta que
nos quedemos sin iconos. Entonces volveremos a empezar. Lo podriamos traducir
Cyber Greetings: arrastrar y soltar sobre el correo electronico 401

como "escondemos 10s cuatro antiguos y mostramos 10s cuatro nuevos". Vamos
a analizar la version en JavaScript. Para identificar cada grupo utilizaremos la
variable iconldx, cuyo valor original sera 0. El primer grupo estara asociado con
0; el segundo con 1, etc.
En el momento en que el usuario selecciona "Icons - ->" tendremos que ocultar
10s iconos asociados con iconldx:

for (var i = bRef.iconNum iconIdx; i < (bRef.iconNum * iconIdx) t


bRef.iconNum; i++) {
if ( i < bRef.icons.length && !onCard(i)) {
bRef.hideSlide(bRef.icons[il);
1
)

El valor de la variable i sera iconNum * iconIdx.i se aumentara 1 siempre que


sea menor que (iconNum * iconIdx) + iconNum. Si le parece algo COnfUSO,
piense un momento en lo que ocurre cuando se completa la carga del documen-
to. iconNurn es igual a 4 e iconldx igual a 0 . Es decir, que la primera vez que se
llama a la funcion, i sera 0, 1 , 2 y 3 . L a siguiente vez, iconldx sera 1 (ocurre mas
tarde en la funcion), por lo que i sera 4, 5, 6 y 7.
La variable i contiene el numero entero que se utilizara para acceder a1 elemen-
to del array de iconos iPor quC? Cada icono tiene su propia capa, icierto? El
codigo del archivo back.htrnZ nombre a las capas de acuerdo con 10s elementos
del dicho array. Por ejemplo, icons [ 01 sera la capa del oso.
Todo lo que tenemos que hacer es esconder las capas asociadas con 0, 1, 2 y 3 ,
a excepcion del caso en el que el usuario haya arrastrado alguno de estos iconos
sobre la zona de la tarjeta. La funcion onCard ( ) se encarga de esto y la veremos
en breve. De momento, vamos a suponer que no se ha movido ningun icono
para facilitar el analisis de la funcion. Llamamos a hideslide desde back.htrnZ y le
pasamos el nombre de la capa, a1 que accedemos a travCs de i :
bRef.hideSlide(bRef.icons[il);

Ya hemos ocultado 10s antiguos iconos, por lo que tendremos que acceder a1
siguiente grupo. Per0 antes, hemos de comprobar que no nos encontremos en el
ultimo grupo. Si es el caso, asignaremos el valor 0 a la variable iconldx. Si no,
aumentaremos el valor de iconldx una unidad. Lo vemos en las lineas 59-60:

iconIdx = (iconIdx >= (bRef.icons.length / bRef.iconNum) - 1 ? 0 :


iconIdx + 1);

Una repeticion mas y el nuevo grupo sera visible. Las lineas 6 1-6 7 contiene un
buclefor que se encarga de ello:
402 Cyber Greetings: arrastrar y soltar sobre el correo electronico

for (var i = bRef.iconNum * iconIdx; i < (bRef.iconNum * iconIdx) +


bRef .iconNum; i++) (
if (i < bRef.icons.length) (
bRef.showSlide(bRef.icons[il);

else { break; }
1

La idea es efectuar iconNurn repeticiones y revelar asi el siguiente grupo de


iconos. Anteriormente hemos incrementado o reiniciado el valor de iconldx en
las lineas 59-60, por eso este grupo repetira el buclefor que vimos con el grupo
anterior. Per0 esta vez utilizaremos showslide( 1 . Per0 hay un pequefio incon-
veniente. LRecuerda que la idea era efectuar iconNurn repeticiones? Per0 iquk
ocurrira si nos encontramos en el ultimo grupo antes de reiniciar las repeticio-
nes y no quedan iconos iconNurn? Si tenemos 20 iconos y queremos mostrar
cuatro a la vez en la pantalla, tendremos cinco grupos de cuatro iconos. Per0 si
tenemos 19 iconos y 10s queremos mostrar de cuatro en cuatro, tendremos cua-
tro grupos de cuatro iconos y uno mas de tres. Esta es la razon por la que nece-
sitamos una declaracion if-else adicional para comprobar si tenemos menos
iconos que el total. En este caso, nextIcons ( ) mostrara el icono. En cualquier
otro, como no habra mas iconos en el grupo, se rompera el bucle.

Consewar en pantalla 10s iconos que se han arrastrado


Como acabamos de ver, la ejecucion de la aplicacion a traves de 10s iconos im-
plica que se han de ocultar algunos y mostrar otros. L a excepcion a esta regla la
encontramos en el area de la tarjeta. Los dejaremos en pantalla, independiente-
mente de lo que ocurra con la aplicacion. L a funcion onCard ( ) se encarga de
determinar si se han de dejar 10s iconos en pantalla o se han de ocultar. El co-
digo se encuentra en las lineas 83-109. oncard ( no se encarga de mostrar o de
ocultar nada. Simplemente devolvera true o false para que el resto de fun-
ciones puedan actuar en consecuencia:

function onCard(iconRef) {
var ref = bRef.refSlide(bRef.icons[iconRefl);
var ref2 = bRef .refSlide ( "Back") ;
if(document.al1) {
if((parseInt(ref.left) >= parseInt(ref2.left)) &&
(parseInt(ref.top) >= parseInt(ref2.top)) &&
(parseInt(ref.left)+ parseInt(ref.width) <= parseInt(ref2.left) +
parseInt(ref2.width)) &&
(parseInt(ref.top) + parseInt(ref.height) <= parseInt(ref2.top) +
parseInt(ref2.height))) (
return true;
1
I
Cyber Greetings: arrastrar y soltar sobre el correo electronico 403

else {
if((ref.left >= ref2.left) &&
(ref.top >= ref2.top) &&
(ref.left + ref.document.images[Ol.width <= ref2.left +
ref2.document.images[O].width) & &
(ref.top + ref.document.images[Ol.height <= ref2.top +
ref2.document.images[O] .height)) {
return true:
J
1
ref.left = ((iconRef P; bRef.iconNum) * 110) + bRef.startWdh;
ref.top = 15;
return false;
1

Antes de entrar a ver mas de cerca la funci6n onCard ( ) , tendremos que saber
quC califica el icono que se encuentra en la zona de la tarjeta. En el caso mas
sencillo, todos 10s bordes del icono (aunque Sean transparentes) se encontraran
dentro de 10s limites de la imagen del fondo. La figura 10.8 nos demuestra lo
que ocurre.

. . . . . .

. . . . . .
. . . . . . ..' . ,. .... . .
, '
.. . . .,
~

. . . . . . . . . . .. . . .

Figura 10.8. Fuera de 10s limites. El unico que esta dentro es el karateca

Suponiendo que el espacio en blanco se encuentre dentro de la zona del fondo,


el muiieco de la izquierda estara fuera de ella. El cactus esta cerca, per0 un par
de bordes se encuentra fuera de la zona de trabajo. De todas formas, se tendra
que eliminar. El unico que permanecera en la pantalla sera el muiieco que hace
karate. Vamos a ver como funciona.
404 Cvber Greetinas: arrastrar v soltar sobre el correo electronico

Todo depende de la posicion de 10s pixeles. Como la imagen del fondo se encentra
dentro de una capa, podremos utilizar DHTML para determinar las posiciones
de su borde izquierdo y superior en relacion con 10s limites del documento. Como
la capa unicamente contiene una imagen, podremos utilizar las propiedades
width y height del objeto Image para determinar las dimensiones de la capa. Esto
mismo se cumple con 10s iconos. Utilizaremos las propiedades left y top de la
capa y width y height de Image. Esta funcion tiene un par de declaraciones if
anidadas, pero las clausulas if y else externas desarrollan basicamente la misma
accion, una para MSIE y otra para Navigator. Esta es la primera mitad de onCard ( 1,
la parte que funciona con IE:

if(document.al1) {
if((parseInt(ref.left) >= parseInt(ref2.left)) & &
(parseInt(ref.top) >= parseInt(ref2.top)) & &
(parseInt(ref.left) + parseInt(ref.width) < = parseInt(ref2.left) t
parseInt(ref2.width)) & &
(parseInt(ref.top) + parseInt(ref.height) <= parseInt(ref2.top) +
parseInt(ref2,height))) {
return true;
1
1

La imagen del fondo tiene cuatro bordes. Igual que el icono. Por lo tanto, ne-
cesitaremos cuatro comprobaciones para asegurarnos que 10s bordes del icono
se encuentran dentro de 10s limites de la imagen del fondo. Esta es la version
escrita de la declaracion if que se encuentra en las lineas 87-94:

IF el borde izquierdo del icono estd en contacto con el borde derecho


del fondo,
AND el borde superior del icono esta en contacto con el borde superior
del fondo,
AND el borde derecho del icono esta en contacto con el borde izquierdo
del fondo,
AND el borde inferior del icono esta en contacto con el borde inferior
del fondo, entonces
RETURN TRUE.

Es muy sencillo determinar las posiciones izquierda y superior de cada capa.


Bastara con utilizar sus propiedades. Determinar las posiciones de 10s bordes
derecho e inferior de cada capa no es mucho mas dificil. La posicion del borde de-
recho sera igual a la del borde izquierdo mas el ancho de la capa. La posicion del
borde inferior sera igual a la del borde superior mas la altura de la capa.
De momento, hemos visto u n par de cosas. En primer lugar, el valor de las va-
riables ref y ref2 se ha ajustado a1 de las capas del icono y del fondo, respecti-
vamente. La unica finalidad de esta accion ha sido facilitar la lectura del codigo.
Cyber Greetings: arrastrar y soltar sobre el correo electronico 405

Por otro lado, la funcion parseInt ( ) se encuentra por todos lados. MSIE de-
vuelve las propiedades Zeft y top en forma de cadenas de 250px en vez de 250.
parseInt ( ) convierte el valor de la cadena a u n ndmero, por lo que volvere-
mos a tirar de las matematicas.
L a clausula else externa desarrolla la misma accion, solo que esta vez para NN.
En realidad no tenemos que llamar a ParseInt ( ) porque Navigator devuelve
las propiedades top y Zeft como numero.

else i
if((ref.left >= ref2.left) &&
(ref.top >= ref2.top) &&
(ref.left + ref.document.images[O].width <= ref2.left +
ref2.document.images[O].width) & &
(ref.top + ref.document.images[O].height <= ref2.top +
ref2.document.irnages[O].height)) {
return true;
)

Asi que, si el icono en cuestion pasa 10s tres examenes, ambos exploradores de-
volveran true. En caso contrario, veremos quC pasa:

ref.left = ((iconRef % bRef.iconNum) 110) + bRef.startWdh;


ref.top = 15;
return false;

onCard ( ) comprueba la posicion del icono. Si no se encuentran dentro de la


pantalla de la tarjeta, 10s devolvera a su posicion original. Todos 10s iconos se
encuentran a 15 pixeles del borde superior del documento. Pero la posicion iz-
quierda de cada uno de ellos dependera del orden en que se encuentre dentro del
grupo. No hay ningdn problema. Un calculo rapido con las variables iconRef e
iconNum puede determinar la posicion original del icono. Como 10s iconos tie-
nen 100 pixeles de ancho y hay una separacion de 10 pixeles entre ellos 10, es-
tablecer la posicion de 10s iconos es una tarea muy sencilla. Y a1 final, la funcion
devuelve el valor false.

Probar el trabajo
Estaria bien que el remitente pudiese ver el aspect0 que tendra el mensaje cuando
lo reciba el destinatario. Para eso esta el boton "Test", que abre una ventana re-
mota donde lo muestra. Las lineas 131-145 tienen el cddigo del que hablamos:

function testGreeting(f0bj) {
var msgStr = '<HTML><TITLE>CyberGreeting Test Page</TITLE>' +
genGreeting(f0bj) + '<TABLE ALIGN="CENTER"><TR><TD><FORM>'+
406 Cvber Greetings: arrastrar v soltar sobre el correo electronico

'<INPUT TYPE=BUTTON VALUE=" OK " onClick="self.close();">'+


'</FORM></TD></TR></TABLE></HTML>';
newwin = open(", " , 'width='+ (bRef.backImgs[backgroundIdx].width
+ 50) + ',height=600,scrollbars=yes');
with(newWin.document) {
open ( 1 ;
writeln(msgStr);
close ( ) ;
1
newWin.focus0;
1

testGreeting ( ) unicamente tendra dos responsabilidades: abrir una venta-


na lo suficientemente ancha como para mostrar todo el mensaje y la otra, escri-
bir su contenido. Este contenido se guardara en la variable msgStr. Se compone
de unos pocos bit de c6digo HTML estatico. El resto es dinamico y procede de la
funcion genGreeting ( ) . msgStr tambikn tendra (a1final) un boton para cerrar
la ventana remota. Es u n toque de elegancia. Una vez que msgStr carga todos
estos elementos, la funcion testGreeting( abre una ventana 50 pixeles mas
ancha que la imagen de fondo y arbitrariamente 600 pixeles de alto. La funcion
escribe el contenido del documento y activa la ventana.

Crear la tarjeta
testGreeting( ) proporciona una ventana para ver el contenido de la tarje-
ta. Sin embargo, genGreeting ( ) se encarga del trabajo interno.
Este es el contenido de las 31 lineas, 147-1 77:

function genGreeting(f0bj) {
var greetingIdx = f0bj.Greetings.selectedIndex;
var msg = f0bj.Message.value;

msg = msg . replace ( / \r+/g, '' ) ;


I'

rnsg = msg.replace(/\n+/g, "<BR><BR>");

var msgStr = '<TABLE BORDER=O><TR><TD COLSPAN=Z><FONT FACE=Arial>' +


'<H2>Your Cyber Greeting</H2>To: ' + fObj.Recipient.value +
'<BR><BR></TD></TR><TR><TD VALIGN=TOP><IMG SRC="' + top.baseURL +
'/images/background' + backgroundIdx + '.jpg">'+
'<DIV STYLE="pos~t~on:relat~ve;left:4O;top:-255;font-fam~ly:Ar~al;
+ 'font-size:48px;font-weight:bold;">' +
parent.greetings[greetingIdx] + '</DIV>';

var iconStr = ' I ;

for (var i = 0; i < bRef.icons.length; i++) {


if (onCard(i)) {
iconStr += '<DIV STYLE="position:absolute;left:'t
bRef.refSlide(bRef.icons[i]).left + ';top:'+
Cyber Greetings: arrastrar y soltar sobre el correo electronico 407

(parseInt(bRef.refSlide(bRef.icons[il).top) -
(document.al1 ? 140 : 1 5 0 ) ) + ' ; " > < I M G S R C = " ' + top.baseURL +
'/images/' + bRef.icons[iI + '.gif"></DIV>';
1
I

mSgStr += iconStr + '</TD></TR><TR><TDWIDTH=' +


bRef.backImgs[backgroundIdxl.width + '><FONT FACE=Arial>' + msg +
'</TD></TR></TABLE>';
return msgStr;
}

Se trata de una funcion algo compleja. Vamos a tratar de simplificar las cosas
examinandola desde el punto de vista de 10s objetos que se han de conseguir.
genGreeting ( ) es la unica que devolvera codigo HTML para representar 10s
siguientes puntos:

El texto que representara la direccion de correo electronico del destinatario.


La imagen del fondo.
La posicion correcta de la tarjeta.
Los iconos correctamente ubicados.
El mensaje del texto.

No es mucho, per0 antes de proceder a crear el contenido, tenemos que hacer


las labores domesticas. Veamos el contenido de las lineas 148-152:

var greetingIdx = fObj.Greetings.selected1ndex;


var msg = f0bj.Message.value;
msg = msg.replace(/\r+/g, " " ) ;
msg = msg.replace(/\n+/g, " < B R > < B R > " )
;

Hemos declarado dos variables locales, greetingldx y msg. greetingldx corres-


ponde a selectedlndex de la lista de seleccion "Greetings".rnsg representa el men-
saje que introduce el usuario en el campo de texto destinado a ello. Ya que la
tarjeta se presentara en formato HTML no se interpretaran 10s caracteres co-
rrespondientes a 10s saltos de linea. Es decir, que se tendran que sustituir por
etiquetas <BR>. Ahora podemos empezar a crear la tarjeta. Lo vamos a hacer de
arriba a abajo. Empezamos con las lineas 154-160:

var msgStr = '<TABLE BORDER=O><TR><TD COLSPAN=2><FONT FACE=Arial>' +


'<H2>Your Cyber Greeting</H2>To: + f0bj.Recipient.value +
' < B R > < B R > < / T D > < / T R > < T R > < T D VALIGN=TOP><IMG SRC="' + top.baseURL +
'/images/background'+ backgroundIdx + '.jpg'>' +
'<DIV STYLE="position:relative;left:4O;top:-255;font-family:Arial; ' +
'font-size:48px;font-weight:bold;">' + parent.greetings[greetingIdxl +
' < / D I V > ';
408 Cyber Greetings: arrastrar y soltar sobre el correo electronico

Todo se encuentra dentro de una tabla, con lo que se facilitara considerablemen-


te el trabajo. De esta forma, a la variable local msgStr se le asignara el valor del
principio de la tabla. La primera fila contiene una cabecera seguida del primer0
de 10s cuatro elementos requeridos, la direction del destinatario. Todo esto se
encuentra dentro del valor del campo Recipient del formulario EntryForm. La ima-
gen del fondo aparecera en la siguiente linea. Por medio de las variables baseURL
y backgroundldx, no resultara dificil crear una cadena que represente la ruta de
la imagen que se utilizara como fondo. No olvidemos que si selectedlndex de la
lista de seleccion Greetings es igual a 4, entonces la imagen background4.jpg sera
la que aparezca en pantalla.
Observese que no se utiliza ningun codigo DHTML para colocar la imagen del
fondo en la pagina. Como la cabecera y la tarjeta unicamente ocuparan una li-
nea, no sera necesario. Sabemos que la imagen aparecera cerca de la parte supe-
rior de la pantalla y que estara alineada a la izquierda. En las siguientes lineas
de codigo, la tarjeta seleccionada se ubicara en el documento tomando como
referencia la imagen del fondo que se encuentra delante de ella. A la izquierda se
dejaran 40 pixeles. En la parte superior -255. Estos valores son completamente
personalizables y se utilizan para compensar la imagen del fondo que se mues-
tra en back.htmZ y colocar el fondo seleccionado en la tarjeta. Cuando agregue-
mos fondos, es muy probable que cambiemos constantemente de la aplicacion a
la vista previa de la tarjeta para ajustar las posiciones de 10s elementos. Des-
pues, si cambiamos a otra imagen de fondo, no tendremos que preocuparnos de
hacer mas cambios.
Para esto necesitaremos tres elementos. A continuacion tenemos que trabajar
con 10s iconos que ha arrastrado el usuario a la zona de la tarjeta. Las lineas
162-1 71 se encargan de ellos:

var iconstr = " ;


for (var i = 0; i < bRef.icons.length; i++) {
if(onCard(i)) {
iconStr + = '<DIV STYLE="position:absolute;left:' +
bRef.refSlide(bRef.icons[il).left +
';top:'+ (parseInt(bRef.refS1ide(bRef.icons[il).top)-
(document.al1 ? 140 : 150)) + ';"><IMGSRC="' +
top.baseURL + '/images/'+ bRef.icons[il + '.gif"></DIV>';
1
>
L a variable local iconStr, que inicialmente contiene una cadena vacia, conten-
dra 10s URL y las posiciones de todos 10s iconos que se encuentren dentro de la
zona de la tarjeta. El procedimiento es muy sencillo: se repetira con todos 10s
iconos. Para cada uno de 10s que se encuentran dentro de la zona de la tarjeta,
creara el codigo HTML que es necesario para duplicar el icono (la imagen) en
Cyber Greetings: arrastrar y soltar sobre el correo electronico 409

otra pagina, en la misma posicion relativa con respecto a la imagen del fondo
(basandose en las posiciones izquierda y superior). No olvidemos que la funcion
onCard ( ) determina la posicion del icono y, por lo tanto, la posibilidad de que
se seleccione.
El codigo generado para cada uno sera una etiqueta IMG que estara rodeada de
etiquetas DIV.Esta etiqueta sera el atributo STYLE que se haya asignado y la po-
sicion izquierda y superior absoluta del icono, menos 140 o 150 pixeles, depen-
diendo de si el explorador es Internet Explorer o Navigator. A continuacion, un
par de preguntas:

1. iPor quC puedo utilizar la posicion izquierda del icono como si ya hubiese
aplicado una cornpensacion de 100 pixeles en la posicion superior?
2. iPor quC sera diferente la cornpensacion de pixeles en cada explorador?

Buenas preguntas.
Vamos a empezar pensando en la ubicacion de la imagen del fondo de la interfaz
de la tarjeta. Se encuentra en la mitad inferior de la pagina, dependiendo de la re-
solucidn del monitor. Ahora pensemos en la posicion que tendra dentro de la
ventana de prueba. Se encuentra cerca de la parte superior de la pagina, justo
debajo de la cabecera y de la direction del destinatario. Los pixeles se encargaran
de compensar la diferencia. Si la imagen de fondo apareciese en la ventana de
pruebas en la misma posicion que en la interfaz de la aplicacion, no seria nece-
sario aplicar esta nueva ubicacion. Como hay una diferencia en la compensa-
ci6n de pixeles en ambos exploradores, cada uno parece que lo coloca en un
punto ligeramente distinto. La diferencia de 10 pixeles se encargara de corregirlo.
DespuCs de crear una capa donde colocaremos el icono, se sumara el valor de
iconStr a msgStr, junto con algo de codigo HTML y msg, con lo que se completa
la tabla de la aplicacion Cyber Greeting:

mSgStr += iconStr + ' < / T D > < / T R > < T R > < TWIDTH='
D +
bRef.backImgs[backgroundIdxl.width + '><FONT FACE=Arial>' + msg +
'</TD></TR></TABLE>';
return msgStr;

rnsg se insertara dentro de una celda de datos que tendra el mismo ancho que
la imagen de fondo (para optimizar el formato de la tarjeta). Se devuelve la ca-
dena completa.

Envio de la tarjeta
El usuario ha creado una tarjeta, ha comprobado su aspect0 final y esta con-
tento con 10s resultados. Todo lo que tiene que hacer ahora es clic en el b o t h
410 Cyber Greetings: arrastrar y soltar sobre el correo electronico

"Send". De esta forma, la aplicacion llamara a la funcion s h i p G r e e t i n g ( ) . Este


es el contenido de las lineas 11 1-129:

function shipGreeting(f0bj) {
if (fObj.Recipient.value == " " ) {
alert('You need an email address in the From: field');
return false;
I
else if (fObj.Message.value == " " ) {
alert ( "You need to type a Message. " ) ;
return false;
1
else if (fObj.Greetings.selected1ndex == 0 ) {
alert('You need to choose a Greeting.');
return false;
1
fObj.EntireMessage.value = genGreeting(f0bj);
fObj.BaseURL.value = top.baseURL;
return true;
1

Esta funcion es muy corta y se accede a ella a traves del controlador de eventos
onsubmit que se encuentra en la linea 1 8 7 . No solamente se limita a preparar
s h i p G r e e t i n g ( ) para que envie el formulario a un servidor Web, sin0 que
tambikn desarrolla una validacion rapida. Como el servidor sigue una serie de
sencillas instrucciones, la ejecucion del programa se desarrollara con suavidad.
Las unicas reglas que he impuesto es que el remitente ha de introducir una di-
recci6n electronica, escribir algo en el campo del mensaje y seleccionar un tipo
de tarjeta de la lista. Es posible que parezca un poco estricto, per0 nos ahorra-
mos problemas.
Si la informacion introducida pasa las pruebas, s h i p G r e e t i n g ( 1 cambia el va-
lor de 10s tres campos ocultos que se encuentran en las lineas 188-190. A 10s
campos EntireMessage, UniquelD y BaseLIRL, que originalmente contenian cade-
nas vacias, se les asigna la informacion que necesitara el script del servidor. Para
evitar que dicho script tenga que crear el codigo HTML de la tarjeta, se asigna a
E n t i r e M e s s a g e - v a l u e el resultados degenGreeting ( 1 . AUniqueID.value
se le asignara una cadena equivalente a u n numero aleatorio comprendido en-
tre 1 y 1.000.000.
Aparte del valor de dicho numero, se incluira en el nombre del archivo que se
utilizara para guardar la informacih de la tarjeta. SiUniqueID. v a l u e es 1 3 2 4 ,
el nombre bajo el que se guardara la tarjeta sera greetZ324.htmZ. No es compli-
cado, y este sistema nos permite crear cientos de miles de nombres unicos para
las tarjetas. La probabilidad de crear dos tarjetas con el mismo nombre es muy
pequeiia. El resto de la information que necesitara el script del servidor serh el
Cyber Greetings: arrastrar y soltar sobre el correo electr6nico 41 1

URL a1 que enviara la tarjeta, las imagenes del fondo y 10s iconos. Asi que a
BaseURL .value se le asigna el valor de t o p . baseURL. DespuCs de esto, se en-
viara el formulario a la direcci6n que se asign6 en el atributo ACTION degreet.pZ.
Lo podemos ver en la linea 186.

Tkcnica de JavaScript:optimizar las funciones

quC pasa con la fun-

rovechar este hecho

Nota: Una nota final antes de entrar a estudiar el script correspondiente a1


servidor. resetForm( ) , que se encuentra en las lineas 70-81, se encarga
de borrar todo el contenido de 10s campos del formulario y presentarlos
vacios cada vez que se carga FORM, ya sea la primera vez o a traves de una
actualizaci6n. Esta llamada procede del controlador de eventos o n h a d que
se encuentra dentro de la etiqueta BODY. Con esta idea en mente, vamos a
ver que ocurre cuando el servidor abre nuestra aplicaci6n.

El servidor
Al igual que ocurria con mopping Cart ael capitulo 8, en esta aplicaci6n nece-
sitaremos trabajar con cierto mecanismo propio del servidor. Las tarjetas que
crea el usuario se generan utilizando el c6digo JavaScript del cliente. A conti-
nuaci6n se enviara esta informaci6n a un script del servidor Web, en un entor-
no de trabajo como Active Server Pages, Server-side JavaScript o Cold Fusion.
Este entorno de trabajo con script leer5 la informaci6n que envia el remitente
y creara un archivo unico donde guardara el c6digo de la tarjeta. Este archivo es
el que se envia a1 destinatario a travCs de un mensaje de correo electr6nico. El
archivo esta listo y esperando a que el destinatario utilice el vinculo que incluye
en el mensaje.
412 Cyber Greetings: arrastrar y soltar sobre el correo electronico

Despues, el script imprimira una respuesta a1 remitente confirmando que todo


ha ido bien y, lo mas importante, para prepara el codigo HTML del formulario
que el remitente enviara a1 destinatario a traves del mensaje de correo.

Posibles ampliaciones
Vamos a ver unas cuantas formas de mejorar esta aplicacion

Agregar un vinculo de regreso a Greetings


iPor quC no aiiadir un vinculo para volver a la pagina de Cyber Greetings? Bas-
taria con agregar la siguiente linea en la funcion genGreet i n g ( ) defront.htm1:

+ ' <A HREF="' + top.baseURL + '/index.html">Ira Cyber Greeting</A>';

Se supone que index.htrnZ,front.html y back.htm1 se encuentran en el directorio


a1 que apunta baseLIRL. Si no es asi, habra que sustituir el valor de top.baseLIRL
de la declaracion anterior por el URL que vayamos a utilizar.

Agregar temas
Esta aplicacion aclama imagenes de temas. Navidad, San Valentin, Verano o
cualquier otra cosa que se nos ocurra. Podemos utilizar fondos que representen
las estaciones del aiio e iconos que se adapten a ellos; o cumpleaiios, felicitacio-
nes variadas, etc.

Anuncios
Si va a permitir que sus usuarios utilicen esta aplicacion gratuitamente, ipor
qu6 no obtener algun beneficio de ello? En la funcion shipGreeting ( ) pode-
mos utilizar algun anuncio (banner) incluyendo el codigo correspondiente den-
tro de una etiqueta IMG. Si utilizamos 10s temas que hemos mencionado antes,
podremos asignar anuncios que estkn relacionados con ellos.

Mejorar la interactividad de las tarjetas


Esta aplicacibn utiliza eventos en JavaScript para crear tarjetas personalizadas.
Personalizadas, si, per0 poco interactivas. Se puede enviar a la gente tarjetas
con secuencias de imagenes, elementos sobre 10s que hacer clic o cualquier otra
idea. Si tiene algun applet en Java, podra ofrecer este tipo de elementos a sus
usuarios. A la gente le encanta juguetear con elementos inservibles. Encontrara
miles de ellos por toda la Web.
Capitulo 11
Ayuda contextual

N o importa la sencillez con la que creemos nuestros programas. Siempre sur-


giran dudas y preguntas. Las respuestas de algunas de ellas son obvias. En cam-
bio, otras pueden ser complicadas. Segun la calidad, la documentation online
puede ayudar a 10s usuarios a solventar sus dudas. Los archivos de ayuda tam-
bien han de ser sencillos de utilizar. Todo esto lo veremos con la aplicacion de
este capitulo.
La ayuda ha de permitir que el usuario se mueva con facilidad por sus conte-
nidos, per0 tambien que su autor pueda configurarla y actualizarla sin proble-
mas. Esta es otra de las propiedades de esta aplicacion. Igual que ocurria con el
programa del capitulo 7, el c6digo de dicha aplicacion se puede volver a utilizar
con otros proyectos.
El nombre que he dado a esta aplicacion es Select List JavaScript (Lista de se-
leccion en JavaScript). Muestra algunas de las aplicaciones que se pueden dar a
las listas de seleccion en JavaScript. La figura 11.1 nos muestra la interfaz de la
aplicacion.
Esta aplicacion muestra c6mo se puede utilizar JavaScript para cambiar el co-
lor de fondo, la carga de documentos y publicar otras listas de seleccion. Mien-
tras que todo esto no es m8s que algo de teoria, lo que realmente nos importa es
el vinculo que se encuentra en la parte inferior de la pantalla: "Help".Abre una
ventana remota donde se muestra informacion relacionada con el tema selec-
cionado. VCase la figura 11.2.
N o parece gran cosa. Sin embargo, a1 hacer clic en el enlace "URL Directory"
(que carga la pagina URL Directory), y pulsar el bot6n "Help" se cargara la do-
cumentacion del procedimiento de carga del URL. Vease la figura 11.3. Ahora
414 Ayuda contextual

10s usuarios no tendran que buscar el archivo de ayuda deseado. Si tienen una
duda sobre el contenido de la pantalla, 10s archivos de ayuda contextual les
mostrarhn la respuesta.

SELECT List JavaScript


Changing Background Colors

Orange
Yellow

Indigo

Ejaciground Colors UHL Directory Mulhple SELECT Lists Help


..
%- --l-l

Figura 11.1. Select List JavaScript

SELECT List JavaScript


1 Changing Background Cotor
Changlg Tha BBekIzOuld Color

ThlSSELECT llSl d l S D l W Me background In


one OfeigMcOlors The bgcolor property of
me documem Object IS set according to the text
dthe OPTION selected Notematme focus IS
removed fromme SELECT iistanerme color
change

Figura 11.2. Explicaci6n del proceso de cambio del color de fondo


Ayuda contextual 41 5

The URL Directory


t JavaScfipt
This SELECTlist displays Sm'eral URLS of
popular search englnes TheTEXlvalueOf
each OPTION IS Used10 Create avalld U R L
This particular application 1s in a frarnesetwnh
N o frames JavaScrlpI concatenates the text of
the OPTION currentlyselecled wih nHp I and
' comrlo create the URL Ofthe corresponding
Search engine The t a m e lo the right IS then
loadedmth the docurnentatmat URL

Background Colors URL Directory Multiple SELECT Lists Help

Figura 11.3. El mismo enlace muestra la misma documentacih

Es como si la aplicaci6n tuviese cierta inteligencia. Siempre sabe d6nde esta el


usuario. Por medio de 10s hipem'nculos se puede acceder a mas informacion. Per0
el usuario no tiene que hacer clic en el vinculo para cargar la siguiente pagina de
documentaci6n. Bastara con que pase el raton sobre cualquiera de 10s enlaces
para que Cste le muestre la informacion en la capa seleccionada (figura 11.4).
Ahora, el usuario no tendra que regresar a1 documento original despuks de re-
visar el contenido del enlace. En cuanto mueva el cursor y lo coloque sobre cual-
quier otro punto de la capa, la aplicaci6n ocultara la informaci6n que acaba de
mostrar. Esta funcionalidad se puede aplicar a todas las aplicaciones en las que
el usuario necesite ayuda.

Requisitos para la ejecucion


Para ejecutar esta aplicaci6n necesitarh MSIE o Navigator 4.x (0posterior) por-
que se trabaja con DHTML y se utiliza el nuevo modelo de eventos. Asegdrese de
trabajar con una resoluci6n de video de 1024x768. No es obligatorio, per0 si no
se hace, la ventana de ayuda ocupara demasiado espacio en la pantalla.

Analisis de la sintaxis
Esta aplicaci6n se compone de una serie de archivos. Son 10s que aparecen a
continuaci6n:
41 6 Ayuda contextual

index.htrn1. Nivel superior. Alberga el marco de trabajo y las principales


variables.
top.htmZ. Muestra la cabecera Select List JavaScript.
nav.htmZ. Muestra 10s vinculos de la phgina.
background.htrnl. Cambia 10s colores del fondo.
rnuZtiselect.htrnZ. Publica una lista de seleccion bashdose en otras dos.
urZdirectory.htrn1. Carga 10s motores de busqueda bashndose en la opcion
de la lista de seleccih.
help/background. htrnl. Documento de ayuda asociado conbackground.htrnl.
heZp/rnuZtiseZect. htrnl. Documento de ayuda asociado conmultiselect.htrnlx.
heZp/urZdirectory.htrnZ.Documento de ayuda asociado conur1directory.htrnZ.
heZp/help.js. Archivo de codigo fuente en JavaScript.

The URL Dilectory


It JavaScript
This SELECT list dlSplWs several URLS 01
populal sealth engines TheTEXTvalue ol
URL Dlrector
T*.- .,-.+,.,-.-..",.....""
each OPTION I S used l o create a valid URL
L ,* *h.me*etmth
A 3 l e l I e r acronym for Undorm ;the text of
Resource Locator hnp IF and
HoIBot
comi 10 treare me UKL oime corresponding
Search engine The frame ID the llQht1sthen
lnfoseek ioadedmthlhe d o c u m e n t a i t h a t u ~ ~

Excite

Eackgruunrl Culot; IJRL hrectoty Mutiple SELECT Lists Help

Figura 11.4. Mas informacion sin necesidad de esperar

No vamos a entrar en 10s detalles propios de las listas de seleccion que hay de-
triis de 10s archivos background.htrnZ, multiseZect.htrn1 y urZdirectory.htm1. Ya lo
hemos visto en otros capitulos, asi que ahora 10s obviaremos. Asegurese de re-
pasar la publicaci6n de las listas que tiene lugar en el archivo rnultiseZect.htmZ.
Siempre serh de ayuda. En lo concerniente a nuestra aplicacih, nos moveremos
en dos fases:

1. Ayuda contextual: carga el documento adecuado dentro de la ventana de


ayuda (en nav. htrnl).
Ayuda contextual 417

2. Muestra y oculta informacion: ejecucion de 10s controladores de eventos


mouseover (en heZp/heZp.js).

Ayuda contextual
Es muy sencillo. Todo ocurre dentro de nav.htmZ. El ejemplo 1 1 . 1 nos muestra
el codigo.

Ejemplo 11.1. nav.htm1


1 <HTML>
2 <HEAD>
3 <TITLE>top.html</TITLE>
4 < /HEAD>
5 <STYLE TYPE= text/ css >
I' 'I

6 <!--
7
8 A
9 {
10 text-decoration: none;
11 1
12
13 BODY
14 {
15 font-family: Arial;
16 text-align: center;
17 1.
18
19 //-->
20 </STYLE>
21 <SCRIPT>
22 < I --

23 var helpwin;
24
25 function inContext(currFi1e) {
26 var start = currFile.lastIndexOf('/')+ 1;
21 var stop = currFile.lastIndexOf('.');
28 var helpName = currFile.substring(start, stop
29 if(he1pWin == null I I helpWin.closed) {
30 helpwin = open('help/' + helpName + '.html' IhelpFile',
31 'width='+ top.wdh + ',height='+ top.hgt +
32 ',left=100,top=100,scrollbars=no');
33 1
34 else {
35 helpWin.location.href = 'help/' + helpName + '.html';
36 1
31 helpWin.focus();
38 1
39
40 //-->
418 Ayuda contextual

41 </SCRIPT>
42 <BODY>
43
44 < A HREF="background.html TARGET="WorkArea">BackgroundColors</A>
45 &nbsp;&nbsp;&nbsp;
46 <A HREF= "urldirectory .html " TARGET= "WorkArea">URL Directory</A>
41 &nbsp;&nbsp;&nbsp;
48 <A HREF= "mu1tiselect.html " TARGET= " WorkArea" >Multiple SELECT
Lists< /A>
49 &nbsp;&nbsp;&nbsp;
50 <A HREF="javascript:
inContext(parent.WorkArea.location.href);"zHelp</A>
51
52
53

La funcion incontext ( ) trabaja con una premisa: todos 10s documentos con
10s que se vaya a mostrar la ayuda, precisaran un archivo con su mismo nom-
bre y la extension .htmZ.Es decir, el archivo background.htm1, el archivo que
muestra la forma en que se modifica el color del fondo, tendra un archivo de ayu-
da background.htrnZ en el directorio help/ (donde se guardan todos estos fiche-
ros). Las lineas 25-38 muestran 10s detalles:

function inContext(currFi1e) {
var start = currFile.lastIndexOf('/') + 1;
var stop = currFile.lastIndexOf('.');
var helpName = currFile.substring(start, stop);
if(he1pWin == null I I helpWin.closed) {
helpwin = open('help/' + helpName + '.html', 'helpFile', 'width=' +
top.wdh + ',height=' + top.hgt +
',left=100,top=100,scrollbars=no');
}
else {
helpWin.1ocation.href = 'help/' + helpName + '.html';
}
helpWin.focus0;
>
Esta funcion espera un URL como argumento. currFiZe puede ser un URL abso-
luto como http://some.pZace.com/some/document.htmZo un URL con una peti-
cion, como docurnent.cgi?search=all. De cada URL, nosotros s610 nos quedaremos
con el nombre. Descartaremos el nombre del host, dominio y directorios. Es de-
cir, nos quedaremos con todo lo que se encuentra desde de la ultima barra in-
clinada (/), si hay alguna, hasta el ultimo punto del URL (suponiendo que todos
10s nombres de archivos tendran una extension).
Por lo tanto, la variable start nos dara el indice de la dltima barra inclinada + 1.
Supongamos que el URL no tiene ninguna barra inclinada. N o ocurre nada. El
Ayuda contextual 41 9

valor que devolverh lastIndexOf ( ) ser6 -1. Al sumarle 1 tendremos 0. De esta


forma se determina el punto donde tenemos que empezar. El valor de la variable
stop sera el indice del ultimo punto del URL. Ahora, el metodo substring ( ) de
la linea 28 obtendra la subcadena deseada del URL y se la asignarh a heZpNarne.
Vamos a verlo:

var helpName = currFile.substring(start, stop);

Las siguientes lineas se encargaran de abrir una ventana utilizando heZpNarne


siguiendo el convenio de nombres establecido. El primer parametro del mCtodo
open ( ) en las lineas 30-32 seiialara sobre la marcha a1 archivo adecuado:

helpwin = open('help/' + helpName + '.html', 'helpFile', 'width=' +


top.wdh + ',height=' + top.hgt + ',left=100,top=100,scrollbars=no');

Observese que el ancho y el alto de la ventana remota tambien se fija sobre la


marcha a travCs de las variables topwdh y top.hgt. El valor de ambas sera 3 0 0 .
Dichas variables se encuentran dentro de index.htrnZ. Como la aplicaci6n esta-
blece referencias a estas variables desde varios puntos, las he escrito con mayfiscu-
la para facilitar su localizaci6n. En breve veremos las variables encargadas de
determinar las dimensiones de la ventana remota. Todo lo que tiene que hacer el
vinculo sera llamar a la funci6n. Lo tenemos en la linea 50:

<A HREF="javascript: inContext(parent.WorkArea.location.href);">Help</A>

El vinculo llama a incontext ( y le entrega el URL del documento que est5


cargado en el Area de trabajo llamado WorkArea. Si existe algun documento con
el mismo nombre en el directorio help/, el sistema de ayuda adaptarh sus di-
mensiones para mostrarlo en pantalla.

Ticnica de Ilavascript: controlar las ventanas remotas

ero no sr:

hlru
0
U C L l U l l d 31 LlCllC

una nu te alla:
42 0 Ayuda contextual

no se na iniciaao
ntonces incon text
Y
ado un obieto venta
true siempie que se cierre la'ven-
Por lo tanto, si if heZpWin.cZosed es

ii vuelve a pulsar dicho b

c ~ A lnndrpmnr Pvitar pcta c h i

Nota: Este metodo de localizar el nombre del archivo a partir de / y . no re-


sulta generalmente demasiado certero. Por ejemplo, si aparece una URL
que seiiala a un archivo predeterminado, como http://web.net.corn/ o ../,
la regla dejara de tener validez. LA que tipo de archivo no referiremos ahora?
Hemos de asegurarnos bien de modificar el codigo para evitar este tipo de
situaciones.

Mostrar y ocultar informacion adicional


La tknica emergente que hemos visto antes se encargar5 de cargar 10s docu-
mentos que se vayan necesitando. Cuando se utilizan 10s hipervinculos y even-
tos mouseover para mostrar informaci6n extra, nos tendremos que basar en el
c6digo DHTML, parte del cual hemos visto en capitulos anteriores y el resto lo
veremos ahora. Afortunadamente, la mayor parte del c6digo se encontrar5 en el
archivo de c6digo fuente en JavaScript heZp/heZp.js. El ejemplo 11.2 nos mues-
tra 6ste c6digo.
Ayuda contextual 42 1

Ejemplo 11.2. help/help.js

1 var NN = (document.layers ? true : false);


2 var hideName = (NN ? 'hide' : 'hidden');
3 var showName = (NN ? 'show' : 'visible');
4 var zIdx = -1;
5 var helpWdh = 2 0 0 ;
6 var helpHgt = 2 0 0 ;
7 var x , y , totalwidth, totalHeight;
8
9 function genLayer(sName, sLeft, sTop, swdh, sHgt, sVis, copy) {
10 if ( N N ) (
11 document.writeln('<LAYER NAME="' + sName + LEFT=' + sLeft
' I '

12 + ' TOP=' + sTop + WIDTH=' + sWdh + ' HEIGHT=' + sHgt +


13 ' VISIBILITY='" + sVis + "' z-Index=' + (++zIdx) + ' > ' + copy
14 + '</LAYER>');
15 }
16 else {
17 document.writeln('<DIVID="' + sName +
18 ' " STYLE="position:absolute; overf1ow:none; left:' -I
19 sLeft + 'px; top:' + sTop + 'px; width:' + sWdh + 'px;
height : +
20 sHgt + 'px; visibility:' + svis + ' ; z-Index=' + (++zIdx) +
,#,>! +

21 copy + '</DIV>'
22 );
23 >
24 >
25
26 function hideSlide(name) {
27 refSlide(name).visibility = hideName;
28 }
29
30 function showSlide(name) {
31 refSlide(name).visibility = showName;
32 1
33
34 function refSlide(name) {
35 if ( N N ) ( return dpcument.layers[namel; 1
36 else ( return eval('document.al1.' + name + '.style'); )
37 I
38
39 function motionListener0 (
40 if (NN) (
41 window.captureEvents(Event.MOUSEM0VE);
42 window.onmousemove = grabXY;
43 1
44 else {
45 document.onmousemove = grabXY;
46 I
41 >
422 Avuda contextual

48
49 function grabXY(ev) {
50 if(") {
51 x = ev.pageX;
52 y = ev.pageY;
53 1
54 else {
55 x = event.^;
56 y = event.y;
57 1
58 1
59
60 function helpDisplay(name, action) {
61 if(action) {
62 totalwidth = x + helpWdh;
63 totalHeight = y + helpHgt;
64 x = (totalwidth > opener.top.wdh ? x -
65 (totalwidth - opener.top.wdh + 7 5 ) : X I ;
66 y = (totalHeight > opener.top.hgt ? y -
67 (totalHeight - opener.top.hgt) : y ) ;
68 refSlide(name).left = x - 10;
69 refSlide(name).top = y + 8 ;
70 showSlide(name) ;
71 1
72 else { hideSlide(name); I
73 1
74
75 motionlistener();

Vamos a examinar la funcionalidad de estos dos pasos. En primer lugar vere-


mos la creacion de las capas que contienen la informacion adicional. Luego
veremos como se muestran y ocultan dichas capas.

Crear capas
Si ha revisado 10s capitulos 3, 4, 6, 7 6 9, estara familiarizado con las dos pri-
meras docenas de lineas de codigo. Si no lo ha hecho, es conveniente que repase
el contenido del capitulo 3 y revise 10s detalles de las funciones genLayer ( ) ,
hideslide ( 1, showslide ( ) y refslide ( ) . El proceso de creacion de capas
sera el mismo que ha visto en otros capitulos. Tendremos que aiiadir un paso
adicional. Las variables heZpWdh y heZpHgt tendran un valor de 200 pixeles cada
una. Son las encargadas de definir las dimensiones (ancho y alto) predetermina-
das de cada capa. Es muy importante porque necesitaremos esas variables (y
top.wdh y top.hg) para establecer posiciones en todo momento.
Estas funciones seran las herramientas que necesitaremos para crear las capas.
Todo lo que tendremos que hacer sera llamar a genLayer ( ) y entregarle el con-
tenido y el resto de variables. Todos 10s archivos de ayuda contienen una llamada
Avuda contextual 423

a esta funcion. C6mo se repite en todos 10s documentos, nos limitaremos a ob-
servar lo que ocurre en uno de ellos, por ejemplo, en heZp/background.htmZ:

var helpone = 'ISPAN CLASS="helpSet">Thisproperty is a string that ' +


'reflects the current background color of the document.</SPAN>';
var helpTwo = '<SPAN CLASS="helpSet">Thisproperty of the +
'<TT>window</TT>object contains the object hierachy of the current '
+ 'Web page.</SPAN>';

genLayer("bgColor",0, 0, helpwdh, helpHgt, hideName, helpone);


genLayer("document",0, 0, helpWdh, helpHgt, hideName, helpTwo);

La variable denominada helpone contiene la cadena que mostrara el vinculo


adicional ( bgColor " ) y heZpTwo hara lo propio con el enlace del documento.
'I

No se trata simplemente de texto. Ambas cadenas contienen etiquetas SPANque


se han asignado a la definici6n de las clases de las hojas de estilo .helpset. La cla-
se .heZpSet se define dentro de las etiquetas STYLE. Aqui tenemos una pequefia
parte. No sera precisamente la definicidn de la clase de hojas de estilo mas elabo-
rada que encuentre en su vida, per0 bastara para definir las capas de nuestra
aplicacion.

.helpset
{
background-color: #CCFFCC;
padding: 5px;
border: 2px;
width: 200px;
font: normal lOpt Arial;
text-align: left;
1

El script contiene dos llamadas a genLayer ( 1 . ObsCrvese que en vez de pasar


numeros para establecer el ancho y el alto de cada capa, se entregan las varia-
bles heZpWdh y heZpHgt. Esto nos preparara para lo que se avecina mas tarde. La
variable hideName se encargarti de ocultar inicialmente todas las capas.
Ya hemos creado las capas. Tendremos que mostrarlas cuando el usuario lo
solicite. Esta funcionalidad proviene de las funciones motionListener ( ) ,
grabXY ( ) y helpDisplay ( 1. La primera, motionListener ( 1 , la tenemos en
las lineas 39-47:

function motionlistener() {
if ( N N ) {
window.captureEvents(Event.MOUSEM0VE);
window.onmousemove = grabXY;
1
424 Ayuda contextual

else {
document.onmousemove = grabXY;
1

Mostraremos la capa sin importar el vinculo que se haya utilizado. Para ello,
tendremos que determinar la posicion del cursor del raton en la pantalla y de-
tectar cuando pasa por encima de un vinculo. La funcion motionListener ( )
hace que el controlador de eventos onMouseMove llame a la funcion grabXY ( )
siempre que el usuario mueva el cursor. Tanto Navigator como MSIE pueden tra-
bajar con onMouseMove, per0 Navigator lo hace a travks del objeto window y
MSIE a travks del objeto document. Navigator t a m b i h necesitara una llamada
a1 mCtodo captureEvents ( ) para determinar el evento mousemove.
La funcion grabXY ( asigna las variables x e y a las coordenadas horizontal y
vertical (en pixeles) que tiene el cursor y las actualiza cada vez que este se mue-
ve por la pantalla. Aqui tenemos el contenido de las lineas 49-58:

function grabXY(ev) {
if(") {
x = ev.pageX;
y = ev.pageY;
1
else {
x = event.screenX;
y = event.screenY;
>
>
Estas coordenadas se implementan de distinto mod0 en MSIE y NN. Navigator 4
crea un objeto sobre la marcha para cada llamada que se haga a1 controlador de
eventos. El parametro ev determinara el objeto. Por otro lado, Internet Explorer,
tiene un evento propio. Llama a grabXY ( ) cada vez que el usuario mueve el ra-
ton, con lo que se actualizara constantemente el valor de las variables x e y.
Cuando el cursor se coloca sobre un vinculo, 10s valores de x e y establecerhn un
buen punto de referencia para mostrar capas con ayuda adicional.

Mostrar la informacion
Si el usuario pasa el cursor sobre un vfnculo, llama a la funcionhelpDisplay ( ) .
Este es el primer codigo HTML (de background.htmZ) que llama a la funcibn. A
continuacion aparece la propia funcion:

<A HREF="javascript: void(0); "


onMouseOver="helpDisplay('bgColor', true);"
onMouseOut="helpDisplay('bgColor',false);">
Ayuda contextual 425

bgColor
</A>

El controlador de eventos onMouseOver es el responsable de mostrar la capa;


onMouseOut serh el responsable de volver a ocultarla. Los dos colaboran con
helpDisplay ( 1. Veamos las lfneas 60-73:

function helpDisplay(name, action) {


if(action1
totalwidth = x + helpWdh;
totalHeight = y + helpHgt;
x = (totalwidth > opener.top.wdh ? x -
(totalwidth - opener.top.wdh + 75) : x);
y = (totalHeight > opener.top.hgt ? y -
(totalHeight - opener.top.hgt) : y ) ;
refSlide(name).left = x - 10;
refSlide(name).top = y + 8;
showSlide(name);
1
else ( hideSlide(name1; )
)

helpDisplay ( ) espera dos argumentos. Uno sera el nombre de la capa que tie-
ne que mostrar/ocultar. El otro sera un valor booleano que determinara quC ha
de hacer con la capa. Lo primer0 que se determinarh sera si la capa se tiene que
mostrar u ocultar. Si action es f a l s e , el procedimiento sera muy sencillo. Bas-
tar5 con llamar a hideSli.de ( ) . si action es t r u e , habra que mostrar la capa.
LBastaria con llamar a showSli.de ( ) ? No. Es algo mas complicado.

evoluci6n de c
426 Avuda contextual

I aocumenro

Controlar la ubicacion del vinculo


Si llamamos a la funci6n showslide ( ) tendremos el trabajo deseado, per0 po-
demos aprovechar alguna ventaja. iRecuerda las lineas 30-32 correspondientes
a1 archivo nav.html?

' helpwin = open('help/' + helpName + '.html', 'helpFile', 'width=' +


top.wdh + ',height=' + top.hgt + ',left=lOO,top=10O,scrollbars=no');

El ancho de la ventana es de 300 pixeles. Y tambitn el alto. volvamos a las li-


neas 5 y 6 del archivo heZp.js:

var helpWdh = 200;


var helpHgt = 200;

El ancho y alto de cada capa sera de 200 pixeles. En realidad, la altura de la ven-
tana se ajusta sobre la marcha a la cantidad de contenido del documento, igual
que ocurre con las celdas de una tabla. Per0 aun necesitaremos una referencia.
No es necesario ser un experto en matematicas para darse cuenta de que si el
vinculo tiene un ancho superior a 10s 100 pixeles (algo menos, porque 10s 300
pixeles representan el ancho de la ventana, no el ancho del documento interior)
por lo menos habra una parte de la capa que no estara a la vista. Para evitarlo,
haremos unos cuantos cAlculos previos a la colocacion de la capa.
El funcionamiento es el siguiente: si la coordenada horizontal del rat6n mas el
ancho de la capa que se ha de mostrar es mayor que el ancho de la ventana re-
mota, la capa se mover6 a la izquierda un numero de pixeles para compensar.
Consideremos el contenido de la linea 62:
totalwidth = x + helpWdh;

La variable totaZWidth es igual a la coordenada horizontal mas el ancho de la


capa. Ahora vamos a ver por quC utilizamos variables para determinar las dimen-
siones de las capas. Consideremos 64-65.
x = (totalwidth > opener.top.wdh ? x -
(totalwidth - opener.top.wdh + 75) : x);
Ayuda contextual 427

Si totalwidth es mayor que el ancho de la ventana remota (menos el borde), se


tendra que ajustar la coordenada horizontal. Para ajustarla a la izquierda nos
limitamos a restar la diferencia entre totalwidth y el ancho de la ventana remo-
ta. De esta forma nos aseguramos que las capas se muestran horizontalmente.
Y lo mismo se aplica a la altura. Lo podemos ver en las lineas 63 y 66-67. Es po-
sible que este planteamiento no funcione si el valor de helpHgt es demasiado pe-
quefio y la capa que se carga contiene demasiado texto.
428 Ayuda contextual

. . .. . .. ., . .
. I .- .. .. .. .., . . ' . , . .. . ? ~-~ .* . .

Posibles ampliaciones
La aplicacih de ayuda que hemos visto en este capitulo posiblemente sirva con
la mayoria de aplicaciones de tamaiio medio-pequeiio. Per0 segun se vaya au-
mentando el tamaiio, necesitaremos mas propiedades. Vamos a considerar las
siguientes sugerencias a la hora de crear nuestra ayuda online.

Tabla de contenidos
Algunos usuarios buscan informacih que no esta relacionada con el context0
en el que se encuentran. Una de las formas m8s sencillas de facilitarles la labor
es crear una tabla de contenidos donde se muestren vinculos a todos 10s docu-
mentos de ayuda. Para ello, se puede utilizar una pi5gina HTML esthtica o c6di-
go JavaScript que se encargue de generarla sobre la marcha a partir de un array
de elementos:

function showcontents0 {
var helpDocs = ['background', 'multiselect', 'urldirectory'l;
var helpLinks = '<UL>';
for (var i = 0; i < helpDocs.length; i++) {
helpLinks += '<LI><A H R E F = " ' + helpDocs[il + '.html">' +
helpDocs[il + '</A>';
I
helpLinks = '</UL>';
Ayuda contextual 429

document.writeln(he1pLink.s);
1

Archivos de ayuda en 10s que se pueda buscar informacion


Si se necesitan varios archivos de ayuda, ipor quk no utilizar una aplicacion
como la que se vio en el capitulo l ? Es una forma elegante de mejorar la inter-
actividad de 10s usuarios.

Consulte con un profesional


Hay ocasiones en las que 10s usuarios no encuentran lo que buscan. Si dispone
del personal necesario, podria plantearse la idea de afiadir formularios basados
en direcciones de correo electronico a travCs de las cuales 10s usuarios envien las
preguntas a1 personal cualificado.

Atencion telefonica
Si realmente quiere ofrecer un buen servicio a sus clientes, proporcione una lis-
ta con numeros de telkfono y direcciones de correo electronico para que puedan
ponerse en contact0 con seres humanos que se encargarhn de atender sus du-
das. Al igual que ocurre con 10s formularios basados en el correo electronico, este
tip0 de servicio requiere contar con 10s recursos adecuados. Asegurese que tiene
personal suficiente atendiendo las llamadas telefonicas Habrh gente que llamarh.
Epilog0

Esta es otra de las razones por las que queria mi propio libro. DespuCs de avan-
zar, pagina a pagina, hasta el ultimo capitulo de la una de esas publicaciones
Web, con una extension unicamente comparable a la Santa Biblia o a Guerra y
Paz, iqut suele haber? Unos cuantos apendices y el indice. Esta bien. Per0 es como
escalar hasta lo alto de una montaiia y no tener otra cosa que hacer que mirar
hacia abajo. LDonde esta el mkrito?
Si llegado a este punto echa la vista atras, Vera que ha andado un largo cami-
no, que no se ha limitado a pasar una pagina tras otra. Piense en lo que sabia de
codigo JavaScript cuando cornpro el libro. Piense ahora en todo lo que ha apren-
dido. Uno se siente bien cuando es testigo de sus logros y es consciente del ca-
mino que le ha guiado hasta la cima.
Obviamente, podemos disfrutar de nuestro Cxito, per0 no estaremos demasia-
do comodos. L a tecnologia cambia a una velocidad vertiginosa. En el momento
en que este libro llegue a su estanteria, 10s desarrolladores habran mejorado las
tCcnicas y extensiones de JavaScript. Voy a revisar su codigo ASAP: Si desarrolla
una aplicacion que merezca la pena, no dude en decirmelo. iNos vemos en la
Red!

-Jerry Brandenbaugh
hotstyle@mail.serve. com
Apendice A
Referencia de JavaScript

En general, trabaja con una estructura de tres capas: nucleo, cliente y servidor.
El ndcleo de JavaScript hace referencia a esas propiedades que se pueden utilizar
tanto en el servidor como en el cliente. Los lados cliente y servidor incluyen una
serie de extensiones propias de un entorno determinado. Por ejemplo, en el lado
del cliente en JavaScript contiene la ventana y 10s objetos document, elementos
que no puede utilizar el servidor. Del mismo modo, el c6digo JavaScript del lado
del servidor contiene el objeto File.
El material que veremos aqui pertenece a la version 1 . 3 de JavaScript para el
cliente y a1 nucleo de JavaScript 1.4.El contenido de las siguientes paginas se ha
extraido directamente del sitio Web de Netscape:
ht tp://developerl .netscape.com:8O/docs/manuals/js/core/jsref/index.
htm
h ttp://developerl.netscape.com:80/docs/manuals/js/client/jsreJ/index.h t m
La informacion sobre Microsoft Jscript la encontrara en la siguiente direccion:
http://msdn.microsoft.com/scripting/default.htm?/scripting/jscript/default.h t m
Tenga esta informacion a mano, ya que las puede necesitar para efectuar una
consulta rapida. En cualquier caso, en las direcciones de Microsoft y Netscape
encontrara las ultimas novedades.

Compatibilidad con el explorador


L a tabla A.l muestra las versiones de JavaScript que soportan las distintas ver-
siones de Navigator e Internet Explorer.
434 Referencia de IavaScriDt

Tabla A.1. Compatibilidad de JavaScript

Version de JavaScript Version de Navigator Version de MSIE


JavaScript 1.O Navigator 2.0 MSIE 3.x
JavaScript 1.1 Navigator 3,O
JavaScript 1.2 Navigator 4.0-4.05 MSIE 4.x- 5.0
JavaScript 1.3 Navigator 4,06-4,5
JavaScript 1.4

Objetos, metodos y propiedades


Esta seccion rnuestra 10s objetos y su descripcion, compatibilidad, propiedades
y rnttodos.

Anchor
Un punto del docurnento a1 que seiiala un vinculo de hipertexto. Con la etique-
ta A en HTML, o mediante la llarnada a1 metodo String.anchor,el motor de ru-
tinas de JavaScript crea un objeto Anchor que se corresponde con cada etiqueta A
del documento que tiene el atributo NAME. Coloca estos objetos en la propiedad
document.anchors dentro de u n array. Para acceder a un objeto Anchor, indexa-
remos dicho array. Para acceder a un objeto Anchor, indexaremos dicho array.
Compatibilidad
JavaScript 1.O / cliente.
Resumen del mktodo
Este objeto hereda 10s mktodos watch y unwatch de Object.

Applet
La etiqueta HTML <APPLET>. El motor de rutinas de JavaScript crea u n objeto
Applet que se corresponde con cada uno de 10s applet que aparecen en el docu-
mento. Coloca estos objetos en un array en la propiedad docurnent.appZets. Para
acceder a un objeto Applet, indexaremos dicho array.
Compatibilidad
JavaScript 1.1 / cliente.
Resumen de propiedades
El objetoApplet hereda todas las propiedades publicas del applet de Java applet.
Las propiedades se detallan en la tabla A.2.
Referencia de IavaScriDt 435

Resumen del mktodo


El objeto Applet hereda todos 10s mktodos publicos del applet de Java. Los me-
todos se detallan en la tabla A.3.

Tabla A.2.Propiedades de Applet


~ ~~ ~

Propiedad Description Version


constructor Especifica la funcion que crea u n prototipo 1.1
del objeto.
Index Para un array que se crea a partir de una 1.2
expresion regular, esta propiedad se refiere
a1 indice (empezando a contar desde cero)
de la cadena.
input Para un array que se crea a partir de una 1.2
expresi6n regular, muestra el valor original
de la cadena frente a1 de la expresion buscada.
length Muestra el numero de elementos de un array. 1.1
prototype Permite agregar propiedades a todos 10s 1.1
objetos.

Tabla A.3. MCtodos de Applet


~~

Metodo Description Version


concat Especifica la funci6n que crea u n prototipo 1.2
del objeto.
join Junta todos 10s elementos de UII array dentro 1.1
de una cadena.
POP Elimina y devuelve el ultimo elemento de una 1.2
cadena.
push Aiiade uno o mas elementos a1 final de un array 1.2
y muestra la nueva longitud del array.
reverse Transpone todos 10s elementos de un array: el 1.2
primer elemento del array se convertira en el
ultimo y el ultimo en el primero. El primer
elemento del array se convertira en el ultimo
y el ultimo en el primero.
shift Elimina y devuelve el primer elemento de una 1.2
cadena.
436 Referencia de JavaScript

Metodo Descripcion Version


slice Extrae una seccion de u n array y devuelve 1.2
el array resultante.
sort Ordena 10s elementos de un array. 1.1
splice M a d e y/o elimina 10s elementos de un array. 1.2
tosource Muestra u n literal del array que representa 1.3
el valor de array determinado; este valor se
puede utilizar para crear un array nuevo.
h u l a el metodo Object . tosource.
t o S t r ing Devuelve una cadena que representa el array 1.1
y sus elementos. h u l a el mCtodo
Object.tostring.
unshi f t Afiade uno o mas elementos a1 principio de 1.2
un array y muestra la nueva longitud
correspondiente a1 array.
valueof Devuelve el valor original del array. h u l a el 1.1
mCtodoObject .valueof.

Area
Define u n Area de una imagen o u n mapa de imageries. Cuando el usuario hace
clic en ella, la referencia del hipervinculo se carga en la ventana de destino. Los
objetos Area son del tip0 Link.
Compatibilidad
JavaScript 1.1 / cliente.

Array
Permite trabajar con arrays.
Compatibilidad
JavaScript 1.1 / nucleo.

Boolean
El objeto Boolean se utiliza con 10s valores booleanos.
Compatibil idad
JavaScript 1.1 / nucleo.
Referencia de JavaScript 437

Resumen de propiedades
Las propiedades de Boolean se detallan en la tabla A.4.
Resumen del mtftodo
Los mttodos de Boolean se detallan en la tabla A.5.
Tabla A.4. Propiedades de Boolean

Propiedad Descripcion Version


constructor Especifica la funcion que crea un prototipo 1.1
del objeto.
prototype Define una propiedad que se comparte con 1.1
todos 10s objetos Boolean.

Tabla A.5. Metodos de Boolean

Metodo Descripcion Version


~ ~

tosource Muestra un literal que representa el valor 1.3


del objeto Boolean determinado; este valor se
puede utilizar para crear un objeto nuevo.
Anula el mttodo Object. tosource.
toSt ring Devuelve una cadena que representa el objeto es- 1.1
pecificado. Anula el metodo Object. tostring.
va 1ueO f Devuelve el valor original de un objeto Boolean. 1.1
Anula el metodo Object .valueof.

Button
Boton de un formulario HTML.
Compatibilidad
JavaScript 1.O / cliente.
Controladores de eventos
onBlur, onclick, onFocus, onMouseDown, onMouseUp.
Resumen de propiedades
Las propiedades se detallan en la tabla A.6.
Resumen del mktodo
Los mttodos se detallan en la tabla A.7. Ademas, este objeto hereda 10s mtto-
dos watch y unwatch de Object.
438 Referencia de IavaScriDt

Tabla A.6. Propiedades de Button

Propiedad Descripcion Version


form Especifica el formulario que contiene el 1.o
objeto Button.
name Muestra el atributo NAME. I .o
type Muestra el atributo TYPE. 1.1
value Muestra el atributo VALUE. 1 .o

Tabla A.7. Mktodos de Button

MCtodo Descripcion Version


blur Desactiva el boton. 1.o
click Simula la accion de hacer clic sobre el boton. 1.o
focus Activa el boton. 1.o
handleEvent Llama a1 controlador de un evento determinado. 1.2

Checkbox
Una casilla de verificacion de un formulario HTML. Una casilla de verificacion
es un interruptor que puede activar y desactivar el usuario.
Compatibilidad
JavaScript I .O / cliente.
Controladores de eventos
onBlur,onclick,onFocus.
Resumen de propiedades
Las propiedades se detallan en la tabla A.8.
Resumen del me'todo
Los mttodos se detallan en la tabla A.9. Ademas, este objeto hereda 10s mtto-
dos watch y unwatch de Object.
Tabla A.8. Propiedades de Checkbox

Propiedad Descripcion Version


checked Propiedad booleana que muestra el estado
actual de la casilla de verif icacibn.
Referencia de JavaScript 439

Propiedad Descripcion Version


defaultchecked Propiedad booleana que muestra el
atributo CHECKED.
form Especifica el formulario que contiene 1 .o
el objeto Checkbox.
name Muestra el atributo NAME. 1.o
type Muestra el atributo TYPE. 1.1
value Muestra el atributo VALUE. 1.o
~

Tabla A.9. MCtodos de Checkbox

Metodo Descripcion Version


blur Desactiva la casilla de verificacion. 1 .o
click Simula la acci6n de hacer clic sobre la 1 .o
casilla de verificacion.
focus Activa la casilla de verificacibn. 1 .o
handleEvent Llama a1 controlador de u n evento 1.2
determinado.

Permite trabajar con fechas y horas.


Compatibilidad
JavaScript 1 .O / nucleo.
Resumen de propiedades
Las propiedades se detallan en la tabla A.lO.
Resumen del me'todo
Los m6todos se detallan en la tabla A. 11.
Tabla A.lO. Propiedades de Date

Propiedad Description Version


constructor Especifica la funci6n que crea un prototipo 1.1
del objeto.
pro totype Permite agregar propiedades a un objeto Date. 1.1
440 Referencia de IavaScriDt

Tabla A.11. Metodos de Date

MCtodo Descripcion Version


getDate Muestra el dia del mes para una fecha 1 .o
determinada, de acuerdo con la hora
local.
getDay Muestra el dia de la semana para una 1 .o
fecha determinada, de acuerdo con la
hora local.
getFullYear Muestra el aiio para una fecha deter- 1.3
minada, de acuerdo con la hora local.
getHours Muestra la hora para una fecha deter- 1 .o
minada, de acuerdo con la hora local.
getMilliseconds Muestra 10s milisegundos para una 1.3
fecha determinada, de acuerdo con la
hora local.
getMinutes Muestra 10s minutos para una fecha 1 .o
determinada, de acuerdo con la hora
local.
getMonth Muestra el mes para una fecha deter- 1 .o
minada, de acuerdo con la hora local.
getseconds Muestra 10s segundos para una fecha 1 .o
determinada, de acuerdo con la hora
local.
getT<me Muestra el valor numtrico correspon- 1.o
diente a la hora, de acuerdo con la
hora local.
getTimezoneOffset Muestra la compensacion en minutos 1 .o
de la hora local, para un lugar
determinado.
getUTCDate Muestra el dia (fecha) del mes para 1.3
una fecha determinada, de acuerdo
con la hora universal.
getUTCDay Muestra el dia de la semana para una 1.3
fecha determinada, de acuerdo con la
hora universal.
getUTCFullYear Muestra el aiio para una fecha determi- 1.3
nada, de acuerdo con la hora universal.
Referencia de IavaScriDt 44 1

Metodo Descripcion Version


getUTCHours Muestra las horas de la fecha 1.3
especificada, de acuerdo con la
hora universal.
getUTCMilliseconds Muestra 10s milisegundos para una 1.3
fecha determinada, de acuerdo con
la hora universal.
getUTCMinutes Muestra 10s minutos para una fecha 1.3
determinada, de acuerdo con la hora
universal.
getUTCMonth Muestra el mes para una fecha deter- 1.3
minada, de acuerdo con la hora
universal.
getUTCSeconds Muestra 10s segundos de una fecha 1.3
determinada, de acuerdo con la hora
universal.
getyear Muestra el aiio de una fecha determi- 1 .o
nada, de acuerdo con la hora local.
parse Devuelve el numero de milisegundos 1 .o
que han transcurrido desde el 1 de
Enero de 1970 hasta la fecha especifi-
cada, de acuerdo con la hora local.
setDate Determina el dia del mes para una 1 .o
fecha determinada, de acuerdo con la
hora local.
setFullYear Determina el afio de una fecha deter- 1.3
minada, de acuerdo con la hora local.
setHours Determina las horas de una fecha 1 .o
determinada, de acuerdo con la
hora local.
setMilliseconds Determina 10s milisegundos de una 1.3
fecha determinada, de acuerdo con
la hora local.
setMinutes Determina 10s minutos de una fecha 1 .o
determinada, de acuerdo con la hora
local.
setMonth Determina el mes de una fecha deter- 1 .o
minada, de acuerdo con la hora local.
442 Referencia de IavaScriDt

Metodo Descripcion Version


setseconds Determina 10s segundos de una 1.o
fecha determinada, de acuerdo
con la hora local.
setTime Determina el valor de u n objeto 1 .o
Date de acuerdo con la hora local.
setUTCDate Determina el dia del mes para una 1.3
fecha determinada, de acuerdo con
la hora universal.
setUTCFullYear Determina el aiio de una fecha 1.3
determinada, de acuerdo con la
hora universal.
setUTCHours Determina la hora de una fecha 1.3
determinada, de acuerdo con la
hora universal.
setUTCMilliseconds Determina 10s milisegundos de 1.3
una fecha determinada, de acuerdo
con la hora universal.
setUTCMinutes Determina 10s minutos de una fecha 1.3
determinada, de acuerdo con la hora
universal.
setUTCMonth Determina el mes de una fecha 1.3
determinada, de acuerdo con la
hora universal.
setUTCSeconds Determina 10s segundos de una 1.3
fecha determinada, de acuerdo con
la hora universal.
setyear Determina el aiio de una fecha deter- 1 .O
minada, de acuerdo con la hora local.
toGMTString Convierte la fecha en una cadena, 1 .o
usando 10s convenios GMT de Internet.
toLocaleString Convierte la fecha en una cadena, 1.o
utilizando 10s convenios locales.
tosource Muestra u n literal que representa el 1.3
valor del objeto Date determinado;
este valor se puede utilizar para crear
u n objeto nuevo. h u l a el mttodo
Object. tosource.
Referencia de IavaScriDt 443

Metodo Descripcion Version


toStr ing Devuelve una cadena que representa 1.1
el objeto Date especificado. h u l a el
metodo Ob j ec t .tostring.
toUTCString Convierte la fecha en una cadena, 1.3
utilizando el convenio de la hora
universal.
UTC Devuelve el numero de milisegundos 1 .o
de un objeto Date que han transcurrido
desde el 1 de Enero de 1970, OO:OO:OO,
hora universal.
valueof Devuelve el valor original de un objeto 1.1
Date. h u l a el mttodo Object .valueof.

Document
Contiene informaci6n sobre el documento y proporciona mktodos para mos-
trar la salida HTML a1 usuario.
Compatibilidad
JavaScript 1.O / cliente.
Controladores de eventos
onclick, onDblClick, onKeyDown, onKeyPress, onKeyUp, onMouseDown,
onMouseUp.
Resumen de propiedades
Las propiedades se detallan en la tabla A. 12.
Resumen del mktodo
Los mttodos se detallan en la tabla A. 13. Ademas, este objeto hereda 10s mkto-
dos watch y unwatch de Object.
Tabla A.12. Propiedades de Document

Propiedad Descripcion Version


alinkcolor Una cadena que especifica el atributo 1.o
ALINK.
anchors Un array que contiene una entrada 1.o
para cada una de las referencias del
documento.
444 Referencia de IavaScriDt

Propiedad Descripcion Version


app1ets Un array que contiene una entrada 1.1
para cada applet del documento.
bgColor Una cadena que especifica el atributo 1.o
BGCOLOR.
cookie Especifica una cookie. 1 .o
domain Especifica el nombre del dominio del 1.1
servidor que entrega el documento.
embeds Un array que tiene una entrada para 1.1
cada complemento del documento.
fgColor Una cadena que especifica el atributo 1.o
TEXT.
formame Una propiedad independiente para 1.1
cada uno de 10s formularios que se
nombran en el documento.
forms Un array que contiene una entrada 1.1
para cada formulario del documento.
images Un array que contiene una entrada 1.1
para cada imagen del documento.
1astModified Una cadena que especifica la fecha de 1.o
la ultima modificacidn del documento.
layers Un array que tiene una entrada para 1.2
cada capa dentro del documento.
1inkCo1or Cadena que especifica el atributo LINK. 1 .o
1inks Un array que contiene una entrada 1.o
para cada vinculo en el documento.
plugins Un array que tiene una entrada para 1.o
cada complemento del documento.
referrer Una cadena que especifica el URL del 1.1
documento que establece la llamada.
title Una cadena que especifica el contenido 1.o
de la etiqueta TITLE.
URL Una cadena que especifica el URL 1 .o
cornpleto de un documento.
vlinkColor Una cadena que especifica el atributo 1 .o
VLINK.
Referencia de IavaScrkt 445

Tabla A.13. Mktodos de Document

Metodo Descripcion Version


captureEvents Prepara el documento para que capture 1.2
todos 10s eventos de un tip0 determinado.
close Cierra una salida de datos e imprime 10s 1.o
datos en la pantalla.
getselection Una cadena que contiene el texto de una 1.2
seleccion.
handleEvent Llama a1 controlador de u n evento 1.2
determinado.
open Abre una entrada de datos para recoger la 1.o
salida de 10s mktodos writeln.
releaseEvents Prepara la ventana o el documento para que 1.2
suelte 10s eventos de un tip0 determinado
que ha capturado, enviandolos a 10s objetos
que se encuentran en otro nivel dentro de la
jerarquia de eventos.
routeEvent Entrega uno de 10s eventos que ha capturado 1.2
a la jerarquia usual de eventos.
write Escribe una o mas expresiones HTML en el 1 .o
documento que se encuentra en la ventana
especificada.
writeln Escribe una o mas expresiones HTML en el 1 .o
documento que se encuentra en la ventana
especificada y coloca a continuaci6n de cada
uno el caracter de salto de linea.

Event
Es el objeto que contiene una serie de propiedades las cuales describen u n even-
to de JavaScript. Estas propiedades se pasan como argument0 de un controla-
dor de eventos en el momento en que Cste tenga lugar.
Compatibilidad
JavaScript 1.2 / cliente.
Resumen de propiedades
Las propiedades se detallan en la tabla A.14. No todas las propiedades se co-
rresponden con todos 10s tipos de eventos.
446 Referencia de JavaScript

Tabla A. 14.Propiedades

Propiedad Descripcion Version


data Devuelve un array de cadenas que contienen 1.2
10s URL de 10s objetos que se han soltado.
Se pasa junto con el evento DragDrop.
height Representa la altura de la ventana o del marco. 1.2
layerx Numero que especifica el ancho del objeto 1.2
cuando se pasa con el evento resize o la posicion
horizontal del cursor (en pixeles) relativa a la
capa en la que tiene lugar el evento. ObsCrvese
que layerx es sinonimo de x.
layerY Numero que especifica el alto del objeto cuando 1.2
se pasa con el evento resize o la posicion vertical
del cursor (en pixeles) relativa a la capa en la
que tiene lugar el evento. Obskrvese que layery
es sinonimo de y.
modifiers Cadena que se utiliza para determinar 10s modi- 1.2
ficadores asociados con el raton o con el evento
principal. Los valores del modificador principal
son: ALT-MASK, CONTROL-MASK, SHIFT-MASK
y META-MASK.
PageX Numero que especifica la posicion horizontal 1.2
del cursor (en pixeles), relativa a la pagina.
pagey Numero que especifica la posicion vertical del 1.2
cursor (en pixeles), relativa a la pagina.
screenX Numero que especifica la posicion horizontal 1.2
del cursor (en pixeles), relativa a la pantalla.
sc r eenY Numero que especifica la posicion vertical del 1.2
cursor (en pixeles), relativa a la pantalla.
target Cadena que representa el objeto a1 que se envi6 1.2
originalmente el evento (todos 10s eventos).
type Cadena que representa el tip0 de evento. 1.2
(Todos 10s eventos).
which Numero que determina el b o t h del ratdn en 1.2
el que se ha hecho clic o el valor ASCII de la tecla
pulsada. Para un raton, 1 representa a1 b o t h
izquierdo, 2 a1 del medio y 3 a1 b o t h derecho.
Referencia de JavaScript 447

Propiedad Descripcion Version


width Representa el ancho de la ventana o del marco. 1.2
X Sinonimo para layerx. 1.2
Y Sinonimo para layery. 1.2

Fileupload
Compatibilidad
JavaScript 1.O / cliente.
Se refiere a1 elemento de actualizacion de u n archivo del formulario HTML. El
elemento de actualizacion del archivo permite que el usuario suministre un archi-
vo como cadena de entrada.
Controladores de eventos
onBlur,onchange,onFocus.
Resumen de propiedades
Las propiedades se detallan en la tabla A.15.
Resumen deZ me'todo
Los mCtodos se detallan en la tabla A. 16. Ademas, este objeto hereda 10s mCto-
dos watch y unwatch de Object.
Tabla A.15. Propiedades de Fileupload

Propiedad Descripcion Version


form Especifica el formulario que contiene el 1.o
objeto FiZellpZoad.
name Muestra el atributo NAME. 1 .o
type Muestra el atributo TYPE. 1.1
value Muestra el valor actual del campo del elemento 1 .O
de actualizacion del archivo; se corresponde
con el nombre del fichero se va a actualizar.

Tabla A.16. Mktodos de Fileupload

Metodo Descripci6n Version


blur Desactiva el objeto. 1 .o
focus Activa el objeto. 1 .o
448 Referencia de IavaScriDt

MOtodo Descripcion Version


handleEvent Llama a1 controlador de un evento 1.2
determinado.
select Selecciona el area de entrada correspondiente 1.o
a1 campo de actualizacion perteneciente a1
archivo.

Form
Permite que 10s usuarios introduzcan texto y seleccionen elementos del formu-
lario, tales como casillas de verificacion, botones de opcion y listas de seleccibn.
Tambikn se puede utilizar u n formulario con la finalidad de publicar datos en
un servidor.
Compatibilidad
JavaScript 1.O / cliente.
Controladores de eventos
onReset, onsubmit.
Resumen de propiedades
Las propiedades se detallan en la tabla A. 17.
Resumen del mktodo
Los mktodos se detallan en la tabla A.18.

Tabla A. 17. Propiedades de Form

Propiedad Descripcion Version


action Muestra el atributo ACTION. 1.o
el emen t s Array que muestra todos 10s elementos 1 .o
de un formulario.
encoding Muestra el atributo ENCTYPE. 1.o
length Muestra el numero de elementos de un 1.o
formulario.
method Muestra el atributo METHOD. 1.o
name Muestra el atributo NAME. 1.o
target Muestra el atributo TARGET. 1.o
Referencia de lavaScriDt 449

Tabla A.18. Metodos de Form

Metodo Descripcion Version


handleEvent Llama a1 controlador de un evento 1.2
determinado.
reset Simula la acci6n correspondiente a hacer 1.1
clic sobre el b o t h de inicio para llamar
a1 formulario.
submit Envia un formulario. 1 .o

Frame
Una ventana puede mostrar, en una misma pantalla, varios marcos indepen-
dientes, cada uno con su propio URL. Para crear estos marcos se utiliza la eti-
queta FRAME dentro de <FRAMESET>.Los marcos pueden apuntar a distintos
URL y estar relacionados con URL independientes, todo dentro de la misma pan-
talla. Un conjunto de marcos componen una pagina. El objeto Frame se utiliza
para constituir estos marcos. En cualquier caso, JavaScript representa un mar-
co a traves de un objeto window. Cada objetoFrame es a su vez u n objeto window
por lo que contendra todas sus propiedades y metodos. Existen unas pocas dife-
rencias entre una ventana que constituye un marco y una ventana que sea de
orden superior. Para completar informacion sobre 10s marcos, consulte la sec-
cionwindow.
Compatibilidad
JavaScript 1.O / cliente.

Function
Determina una cadena de c6digo en JavaScript la cual se compilara como una
funcion.
Cumpatibilidad
JavaScript 1.1 / nucleo.
Resumen de propiedades
Las propiedades se detallan en la tabla A. 19.
Resumen del mtftodo
Los metodos se detallan en la tabla A.20.
450 ~
Referencia de IavaScriDt

Tabla A. 19. Propiedades de Function

Propiedad Descripcion Version


arguments Un array que se corresponde con 10s 1.1
argumentos que se le pasan a la funcion.
arguments.callee Especifica el cuerpo de la funcion que se 1.2
esta ejecutando.
arguments.caller Especifica el nombre de la funcion que 1.1
invoc6 a la que se esta ejecutando.
arguments.length Especifica el numero de argumentos que 1.1
se le han entregado a la funcion.
arity Especifica el numero de argumentos que 1.2
espera la funcion.
constructor Especifica la funcion que crea un 1.1
prototipo del objeto.
length Especifica el numero de argumentos 1.1
que espera la funci6n.
prototype Permite agregar propiedades a un 1.1
objeto Function.

Tabla A.20. Mttodos de Function

Metodo Descripcion Version


apply Permite aplicar un mttodo a otro objeto 1.3
en el contexto de u n objeto diferente.
call Permite llamar (ejecutar) un mttodo en 1.3
otro objeto en el contexto de un objeto
diferente (el que establece la llamada).
tosource Una cadena que representa el codigo 1.3
fuente de la funcion. h u l a el metodo
Object. tosource.
toS tring Una cadena que representa el c6digo 1.1
fuente de la funcion. h u l a el metodo
Object .tostring.
valueof Una cadena que representa el codigo 1.1
fuente de la funcion. h u l a el mttodo
Object .valueof.
Referencia de JavaScript 45 1

Hidden
Se trata de un objeto Text que se suprime de la representacion del formulario
HTML en la pantalla. El objeto Hidden se utiliza fundamentalmente con la fina-
lidad de proporcionar 10s pares nombre/valor en el momento en que se envia el
formulario.
Compatibil idad
JavaScript 1.O / cliente.
Resumen de propiedades
Las propiedades se detallan en la tabla A.2 1.
Resumen del mktodo
Este objeto hereda 10s mktodos watch y unwatch de Object.

Tabla A.21. Propiedades de Hidden

Propiedad Descripcion Version


form Especifica el formulario que contiene el
objeto Hidden. 1.o
name Muestra el atributo NAME. 1.o
type Muestra el atributo TYPE. 1.1
value Muestra el valor del objeto Hidden. 1.o

History
Contiene un array de informacion acerca de todos 10s URL que han sido visita-
dos por el cliente desde una ventana. Esta informacion se guarda generalmente
en una lista a la que se puede acceder a traves del menu Ir a correspondiente a1
explorador.
Compatibilidad
JavaScript 1.O / cliente.
Resumen de propiedades
Las propiedades correspondientes aparecen detalladas a continuaci6n en la ta-
bla A.22.
Resumen del mktodo
Los metodos se detallan en la tabla A.23. Este objeto hereda 10s metodos watch
y unwatch de Object.
452 Referencia de JavaScript

Tabla A.22. Propiedades de History

Propiedad Descripcion Version


current Especifica el URL de la entrada del historial. 1.1
length Muestra el numero de entradas del historial. 1 .o
next Especifica el URL do la siguiente entrada del 1.1
historial.
previous Especifica el URL de la entrada anterior del 1.1
historial.

Tabla A.23. Metodos de History

Metodo Descripcion Version


back Muestra el anterior URL del historial. 1.o
forward Muestra el siguiente URL del historial. 1 .o
go Carga un URL del historial. 1 .o

Image
Referencia a una imagen del formulario HTML.
Compatibilidad
JavaScript 1.1/ cliente.
Controladores de eventos
onAbort, onError,onKeyDown,onKeyPres s,onKeyUp,onload.
Resumen de propiedades
Las propiedades se detallan en la tabla A.24.
Resumen del mitodo
Los mktodos se detallan en la tabla A.25. Este objeto hereda 10s metodos watch
y unwatch de Object.
Tabla A.24. Propiedades de Image

Propiedad Descripcion Version


border Muestra el atributo BORDER. 1.1
co m p 1ete Valor booleano que indica que el explorador 1.1
ha terminado de cargar la imagen.
Referencia de JavaScript 453

Propiedad Descripcion Version


height Muestra el atributo HEIGHT. 1.1
hspace Muestra el atributo HSPACE. 1.1
lowsrc Muestra el atributo LOWSRC. 1.1
name Muestra el atributo NAME. 1.1
src Muestra el atributo SRC. 1.1
vspace Muestra el atributo VSPACE. 1.1
width Muestra el atributo WIDTH. 1.1

Tabla A.25. Metodos de Image

Metodo Descripcion Version


handleEvent Llama a1 controlador de un evento 1.2
determinado.

Java
Objeto de alto nivel utilizado para acceder a cualquier clase de Java del paque-
te j a m . *. El objeto Java se utiliza como sinonimo de la propiedad Packagexjava.
Compatibilidad
JavaScript 1.1.

J avaArray
Array de Java a1 que se accede desde el c6digo JavaScript, sera del tip0 JavaArray.
Compatibilidad
JavaScript 1.1 / nucleo.
Resumen de propiedades
Las propiedades se detallan en la tabla A.26.
Resumen del mdtodo
Los mktodos se detallan en la tabla A.27.
Tabla A.26. Propiedad JavaArray

Propiedad Descripcion Version


length El numero de elementos del array de Java 1.1
que estan representados por JavaArray.
454 Referencia de lavascript

Tabla A-27.Metodo JavaArray

Metodo Descripcion Version


toStr i n g En JavaScript 1.4, este metodo se anula a 1.1
traves de J a v a . l a n g . 0 b j e c t . t o s t r i n g .
En JavaScript 1 . 3 (y versiones anteriores),
este metodo muestra una cadena que
identifica el objeto como un JavaArray.

JavaClass
Una referencia JavaScript a la clase Java. Un objeto JavaClass es una referen-
cia a una de las clases del paquete Java, comonetscape. j a v a s c r i p t .JSObj e c t .
Un objeto JavaPackage constituye una referencia a un paquete de Java, como
n e t s c a p e . j a v a s c r i p t . En JavaScript, lajerarquia JavaPackage y JavaClass
refleja el paquete Java y la jerarquia de clases.
Compatibilidad
JavaScript 1.1/ nucleo.
Resumen de propiedades
Las propiedades del objeto JavaClass constituyen campos estaticos de la clase
Java.
Resumen del mktodo
Las propiedades del objeto JavaClass son mCtodos estaticos de la clase Java.

Java0bject
Pertenece a1 tip0 de objeto Java a1 que se accede desde el c6digo de JavaScript.
El objeto JavaObject es un ejemplo de la clase Java que se ha creado o pasado a
JavaScript. JavaObj e c t es un contenedor de la clase; todas las referencias que se
hagan a ella se efectuan a travCs deJavaObject. Cualquier dato Java que se impor-
ta a JavaScript se convertira a 10s tipos propios de JavaScript. Cuando se devuel-
ve el objetoJavaObject a Java, se extrae del contenedor para que lo pueda utilizar
el c6digo Java.
Compatibilidad
JavaScript 1.1 / nucleo.
Resumen de propiedades
Las propiedades de JavaPackage son 10s objetos JavaClass y cualquier otro
objeto JavaPackage que contenga.
Referencia de JavaScript 455

J avaPackage
En Java, un paquete es una coleccion de clases de Java o de otros paquetes de
Java. Por ejemplo, el netscape contiene el paquete netscape. javascript;
este paquete contiene las clases JSObject y JSException.
En JavaScript, JavaPackage es una referencia a1 paquete de Java. Por ejem-
plo, una referencia a netscape es un objeto JavaPackage.netscape. javascript
es un objeto JavaPackage y una propiedad de netscape JavaPackage.Un
objeto JavaClass es una referencia a una de las clases del paquete, tales como
netscape. javascript .JSObject. Lajerarquia de JavaPackage JavaClass
muestran el paquete de Java y la jerarquia de la clase. Aunque 10s paquetes y
clases contenidos en JavaPackage son sus propiedades, no podremos utilizar
una declaration for. . . in para numerarlos como hacemos con las propiedades
de otros objetos.
Compatibilidad
JavaScript 1.1 / nucleo.
Resumen de propiedades
Las propiedades de JavaPackage son 10s objetos JavaClass y cualquier otro
objeto JavaPackage que contenga.

Layer
Corresponde a una capa de la pagina HTML y ofrece u n mod0 de manipularla.
Compatibilidad
JavaScript 1.2 / cliente.
Resumen de propiedades
Las propiedades se detallan en la tabla A.28.
Resumen del mktodo
Los mCtodos se detallan en la tabla A.29. Este objeto hereda 10s mCtodos watch
y unwatch de Object.
Tabla A.28.Propiedades de Layer

Propiedad Descripcion Version


above La capa que se encuentra encima (en 1.2
vertical), entre todas las capas del
documento o en la ventana en la que
se encuentra dicha capa.
456 Referencia de JavaScript

Propiedad Descripcion Version


background Imagen usada como fondo en una capa. 1.2
bgCo 1or El color que se utiliza como fondo plano 1.2
en una capa.
below La capa que se encuentra debajo (en 1.2
vertical), entre todas las capas del
documento o en la ventana en la que
se encuentra dicha capa.
clip.bottom El limite inferior del rectangulo (la 1.2
parte visible de la capa).
clip.height Altura del rectangulo (la parte visible 1.2
de la capa).
clip.left El limite izquierdo del rectangulo 1.2
(la parte visible de la capa).
clip.right El limite derecho del rectangulo 1.2
(la parte visible de la capa).
clip.top El limite superior del rectangulo 1.2
(la parte visible de la capa).
clip.width El ancho del rectangulo (la parte 1.2
visible de la capa).
document Capa asociada a1 documento. 1.2
left Posicion horizontal del borde izquierdo 1.2
de la capa (en pixeles) relativo a1 origen
de la capa principal.
name Una cadena que especifica el nombre 1.2
que se le ha asignado a la capa a travCs
del atributo ID de la etiqueta LAYER.
Posici6n horizontal de la capa (en pixeles), 1.2
relativa a la pagina.
Posicion vertical de la capa (en pixeles), 1.2
relativa a la pagina.
La capa que contiene a la capa actual o, 1.2
si la capa no esta anidada a otra, la
ventana donde aparece Csta.
siblingAbove L a capa de encima (en vertical), entre las 1.2
que comparten la misma capa principal o
cero, si la capa no tiene nada por encima.
Referencia de JavaScript 457

Propiedad Descripcion Version


siblingBelow La capa que se encuentra debajo (en 1.2
vertical), entre todas las capas del
documento o cero si la capa no tiene
nada por debajo.
src Una cadena que especifica el URL 1.2
del contenido de la capa.
Posicion vertical del borde izquierdo 1.2
de la capa (en pixeles) relativo a1 origen
de la capa principal.
visibility Determina si la capa sera visible o no. 1.2
zIndex Posicion relativa de la capa (en vertical) 1.2
respecto del resto de capas similares.

Tabla A.29. Metodos de Layer

Metodo Descripcion Version


captureEvents Prepara la ventana o el documento para 1.2
que capture todos 10s eventos de un tipo
determinado.
handleEvent Llama a1 controlador de un evento 1.2
determinado.
load Cambia la fuente de una capa por el 1.2
contenido de un archivo determinado
y a la vez modifica el ancho de la capa
HTML para ajustarla a 10s nuevos
contenidos.
moveAbove Guarda esta capa encima de la especificada 1.2
en el argumento, sin cambiar su posicion
vertical u horizontal.
moveBelow Guarda esta capa debajo de la especificada 1.2
en el argumento, sin cambiar su posicion
vertical u horizontal.
moveBy Cambia la posicion de la capa aplicando 1.2
las cantidades especificadas, medidas en
pixeles.
moveTo Coloca la esquina superior izquierda de la 1.2
ventana en las coordenadas especificadas.
458 Referencia de JavaScript

Metodo Descripcion Version


moveToAbsolute Cambia la posicion de la capa a las 1.2
coordenadas del pixel especificado que
se encuentre dentro de la pagina (en
vez de encontrarse dentro de la capa).
releaseEvents Prepara la capa para que capture 10s 1.2
eventos de u n tipo determinado que
ha capturado, envihndolos a 10s objetos
que se encuentran en otro nivel dentro
de la jerarquia de eventos.
resizeBy Modifica el tamaiio de la capa especifi- 1.2
cando 10s valores del alto y ancho
(en pixeles).
resizeTo Modifica el tamaiio de la capa para que 1.2
tenga las dimensiones especificadas
(en pixeles).
routeEvent Entrega uno de 10s eventos que ha 1.2
capturado a la jerarquia usual de
eventos.

link
Por medio de las etiquetas HTMLA o AREA,o llamando a1 metodo String.link,
El motor de rutinas de JavaScript crea un objeto Link que se corresponde con
cada etiqueta A y AREA del documento que sustituye a1 atributo HREF. Coloca
estos objetos en un array en la propiedad document.Zinks. Para acceder c1 un ob-
jeto Link, indexaremos dicho array.
Compatibilidad
JavaScript 1.O / cliente.
Controladores de eventos
Los objetos Area tienen 10s siguientes controladores de eventos: onDblClick,
onMouseOut, onMouseOver.
Los objetos Link cuentan con 10s siguientes controladores de eventos: onClick,
onDblClick,onKeyDown,onKeyPress,onKeyUp,OnMouseDown,onMouseOut,
onMouseUp,onMouseOver.
Resumen de propiedades
Las propiedades se detallan en la tabla A.30.
Referencia de JavaScript 459

Resumen del mktodo


Los metodos se detallan en la tabla A.31. Este objeto hereda 10s metodoswatch
y unwatch de Object.
Tabla A.30. Propiedades de Link

Propiedad Descripcion Version


~~

Hash Especifica el nombre del anchor (referencia) 1.o


en el URL.
Host Especifica el nombre del host y del dominio, 1.o
o bien la direction Ie correspondiente a un
servidor de red.
Host name Muestra la parte host:puerto del URL. 1.o
Ref. Muestra todo el URL. 1.o
P a thname Muestra la parte ruta-direccion del URL. 1.o
Port Muestra el puerto de comunicaciones que 1.o
utiliza el servidor.
Protocol Especifica el principio del URL, incluyendo 1.o
10s dos puntos.
Search Especifica la cadena de la consulta. 1.o
Target Muestra el atributo TARGET. 1.o
Text Una cadena que especifica el contenido de la 1.o
correspondiente etiqueta A.

Tabla A.31. Metodos de Link

Metodo Descripcion Version


HandleEvent Llama a1 controlador de un evento 1.2
determinado.

Location
Contiene informacion del URL.
Compa t ibi 1idad
JavaScript 1.O / cliente.
Resumen de propiedades
Las propiedades se detallan en la tabla A.32.
460 Referencia de JavaScript

Resumen del mktodo


L o s mktodos se detallan en la tablaA.33. Este objeto hereda 10s mCtodos watch
y unwatch de Object.
Tabla A.32. Propiedades de Location

Propiedad Descripcion Version


Hash Especifica el nombre del anchor (referencia) 1.o
en el URL.
Host Especifica el nombre del host y del dominio, 1.o
o la direccion IP, de un servidor de red.
Hostname Muestra la parte host:puerto del URL. 1.o
Ref. Muestra todo el URL. 1 .o
Pathname Muestra la parte ruta-direccion del URL. 1.o
Port Muestra el puerto de comunicaciones que 1.o
utiliza el servidor.
Protocol Especifica el principio del URL, incluyendo 1.o
10s dos puntos.
Search Especifica la cadena de la consulta. 1.o

'labla A.33. Mktodos de Location

Metodo Descripcion Version


Reload Carga el URL de la entrada seleccionada 1.1
del historial.
Replace Obliga a actualizar el documento que se 1.1
encuentra en la ventana.

Se trata de u n objeto que cuenta con propiedades y mktodos especializados en


' unciones yconstantes matematicas. Por ejemplo, el valor del objeto PI de Math
2s pi.

Compatibilidad
JavaScript 1.O / nucleo.
Resumen de propiedades
Las propiedades se detallan en la tabla A.34.
Referencia de JavaScript 461

Resumen del mktodo


Los mttodos se detallan en la tabla A.35.

Tabla A.34. Propiedades de Math

Propiedad Descripcion Version


E Constante de Euler y base natural de 10s 1.o
logaritmos, cuyo valor aproximado es 2,718.
LN10 Logaritmo natural de 10, cuyo valor 1.o
aproximado es 2,302.
LN2 Logaritmo natural de 2, cuyo valor 1.o
aproximado es 0,693.
LOGlOE Logaritmo en base 10 de E, cuyo valor 1.o
aproximado es 0,434.
LOG2E Logaritmo en base 2 de E, cuyo valor 1.o
aproximado es 1.442.
PI Relacidn existente entre el radio de una 1.o
circunferencia y su diametro, cuyo valor
aproximado es 3,14159.
SQRT1-2 Raiz cuadrada de 1/2; o lo que es lo mismo, 1.o
1 sobre la raiz cuadrada de 2, cuyo valor
aproximado es 0,707.
SQRT2 Raiz cuadrada de 2, cuyo valor aproximado 1.o
es 1.414.
~~

Tabla A.35. Mktodos de Math

Metodo Descripcion Version


Abs Devuelve el valor absoluto de un numero. 1 .O
Acos Devuelve el arc coseno (en radianes) de un 1.o
numero.
Ash Devuelve el arc sen0 (en radianes) de un 1.o
numero.
Atan Devuelve la arc tangente (en radianes) de u n 1.o
numero .
atan2 Devuelve la arc tangente del cociente de sus 1.o
argumentos.
462 Referencia de JavaScript

Metodo Descripcion Version


Ceil Devuelve el entero mas pequeiio que sea 1 .o
mayor o igual que un numero.
cos Devuelve el coseno de un numero. 1.o
EXP Devuelve la potencia E elevado a un numero, 1 .o
donde el numero es el argument0 y E es la
constante de Euler, la base de 10s logaritmos
naturales.
Floor Devuelve el entero mas grande que sea menor 1 .o
o igual que un numero.
Log Devuelve el logaritmo natural (base E) de un 1 .o
numero.
Max Devuelve el mas grande de dos numeros. 1 .o
Min Devuelve el mas grande de dos numeros. 1 .o
Pow Devuelve la base de una potencia. 1 .o
Random Devuelve un numero pseudo-aleatorio 1 .o
entre 0 y 1 .
Round Devuelve el valor de un numero, redondeado 1 .o
a1 entero mas cercano.
Sin Devuelve el sen0 de u n numero. 1 .o
Sqrt Devuelve la raiz cuadrada correspondiente a 1 .o
u n numero.
Tan Devuelve la tangente de un numero. 1 .o

MimeType
Se trata de u n tipo MIME (Multipart Internet Mail Extension) que soporta el
cliente.
Compatibilidad
JavaScript 1.1 / cliente.
Resumen de propiedades
Las propiedades se detallan en la tabla A.36.
Resumen del mktodo
Este objeto hereda 10s metodos watch y unwatch de Object.
Referencia de JavaScript 463

Tabla A.36. Propiedades de MimeType

Propiedad Descripcion Version


Description Una descripcion del tipo MIME. 1.o
EnabledPlugin Referencia a un objeto Plugin configurado 1.O
para el tipo MIME.
Suffixes Una cadena que muestra las posibles 1.o
extensiones de archivo para el tipo MIME,
como por ejemplo, "mpeg, mpg, mpe, mpv,
vbs, mpegv".
Tme El nombre del tipo MIME, por ejemplo, 1 .o
"video/mpeg" o "audio/x-wav".

Navigator
Permite trabajar con valores numkricos. El objeto Number es un objeto que se
utiliza como contendedor de 10s valores numkricos primitivos.
Compatibilidad
JavaScript 1.O / cliente.
Resumen de propiedades
Las propiedades se detallan en la tabla A.37.
Resumen del mAodo
Los mktodos se detallan en la tablaA.38. Este objeto hereda 10s mktodos watch
y unwatch de Object.
Tabla A.37. Propiedades de Navigator

Propiedad Descripcion Version


AppCodeName Determina el nombre del c6digo del 1 .o
explorador.
AppName Determina el nombre del explorador. 1.o
AppVersion Determina la informacion de la version 1 .o
de Navigator.
Language Indica la traduccion que se utilizara de 1.2
Navigator.
MimeTypes Un array con todos 10s tipos MIME que 1.1
soporta el cliente.
464 Referencia de IavaScriDt

Propiedad Descripcion Version


Platform Indica el tip0 de maquina para la que 1.2
se compil6 Navigator.
Plugins Un array con todos 10s complementos 1.1
que tiene instalados el cliente.
UserAgent Especifica la cabecera del agente-usuario. 1.1

Tabla A.38. Metodos de Navigator

Metodo Descripcion Version


JavaEnabled Comprueba si Java esta activo. 1.1
plugins.refresh Permite que 10s nuevos complementos 1.1
instalados esten disponibles para su us0
y (opcionalmente) actualiza 10s docu-
mentos que contienen complementos.
Preference Permite que un script obtenga y establezca 1.2
las preferencias de Navigator.
taintEnabled Determina si 10s datos grises se encuentran 1.1
activos.

Netscape
Objeto de alto nivel utilizado para acceder a cualquier clase de Java del paque-
te netscape.*. netscape es un objeto de alto nivel predefinido en JavaScript. Se
puede acceder automaticamente a tl sin utilizar un constructor o llamar a un
metodo.
Compatibilidad
JavaScript 1.1 / nucleo.

Number
Permite trabajar con valores numtricos. El objeto Number es un objeto que se
utiliza como contendedor de 10s valores numericos primitivos.
Compatibilidad
JavaScript 1.1/ nucleo.
Resumen de propiedades
Las propiedades se detallan en la tabla A.39.
Referencia de JavaScript 465

Resumen del me'todo


Los metodos se detallan en la tabla A.40.
Tabla A.39. Propiedades de Number

Propiedad Descripcion Version


constructor Especifica la funcion que crea un 1.1
prototipo del objeto.
MAX-VALUE El numero mas grande que se puede 1.1
representar.
MIN-VALUE El numero mas pequeiio que se puede 1.1
representar.
NaN Valor especial "not a number" (no es 1.1
un numero).
NEGATIVE-INFINITY Valor especial que representa a infinito 1.1
negativo (se obtiene en caso de
desbordamiento.
POSITIVE-INFINITY Valor especial que representa a infinito; 1.1
se obtiene en cam de desbordamiento.
prototype Permite agregar propiedades a1 objeto 1.1
Number.

Tabla A.40. MCtodos de Numero

Metodo Descripcion Version


tosource Muestra un literal que representa el valor 1.3
del objeto Number determinado; este
valor se puede utilizar para crear u n objeto
nuevo. h u l a el metodo Object . tosource.
tostring Devuelve una cadena que representa el objeto es- 1.1
pecificado. h u l a el metodo Object.tostring.
valueof Devuelve el valor original del objeto especifi-
cado. h u l a el metodo Object .valueof. 1.1

Object
Obj ect es un tipo primitivo de JavaScript. Todos 10s objetos de JavaScript des-
cienden de Object. Es decir, 10s objetos de JavaScript contendran 10s mktodos
definidos para Objec t .
466 Referencia de JavaScript

Compatibilidad
JavaScript 1.0 / nucleo
Resumen de propiedades
Las propiedades se detallan en la tabla A.41
Resumen del me'todo
Los mCtodos se detallan en la tabla A.42.
Tabla A.41. Propiedades de Object

Propiedad Descripcion Version


constructor Especifica la funcion que crea un prototipo 1.1
del objeto.
pro totype Permite agregar propiedades a todos 10s objetos. 1.1

Tabla A.42. MCtodos de Object

Metodo Descripcion Versi6n


eva1 Obsoleto. Evalua una cadena de c6digo 1.1
JavaScript en el context0 del c6digo que
especifique el objeto.
tosource Muestra un literal que representa el valor 1.3
del objeto determinado; este valor se puede
utilizar para crear un objeto nuevo.
toString Devuelve una cadena que representa el objeto 1 .O
especificado.
unwatch Elimina un punto de vista de una propiedad 1.2
del objeto.
valueof Devuelve el valor original del objeto 1.1
especificado.
watch M a d e u n punto de vista de una propiedad 1.2
del objeto.

Option
Se corresponde con una de las opciones de la lista SELECT,
Compatibilidad
JavaScript 1.1 / cliente.
Referencia de JavaScript 467

Resumen de propiedades
Las propiedades se detallan en la tabla A.43.
Resumen del mktodo
Los mktodos se detallan en la tabla A.44. Este objeto hereda 10s metodos w a t c h
y unwatch de Object.

Tabla A.43. Propiedades de Option

ProDiedad Descrimion Version


defaultselected Determina el estado inicial de la 1.1
seleccion de la opcion.
selected Determina el estado actual de la 1.1
seleccion de la opci6n.
text Especifica el texto correspondiente a 1.1
la opcion.
value Determina el valor que se devuelve 1.1
a1 servidor en el momento en que
se selecciona la opci6n y se envia
1 formulario.

Tabla A.44. MCtodos de Option

Metodo Descripcion Version


reload Carga el URL de la entrada seleccionada 1.1
del historial.
replace Obliga a actualizar el documento que se 1.1
encuentra en la ventana.

Packages
Objeto de alto nivel utilizado para acceder a cualquier clase de Java desde el
codigo de JavaScript.
Compatibilidad
JavaScript 1.1 / nucleo.
Resumen de propiedades
Las propiedades correspondientes aparecen detalladas a continuacion en la ta-
bla A.45.
468 Referencia de JavaScript

Tabla A.45. Propiedades de Packages

Propiedad Descripcion Version


className El nombre valido de una clase de Java de u n 1.1
paquete distinto a netscape, java o sun que
estk disponible para JavaScript.
java Cualquier clase del paquete j ava . * . 1.1
netscape Cualquier clase del paquete netscape.*. 1.1
sun Cualquier clase del paquete sun.*. 1.1

Password
Un campo de texto de u n formulario HTML que oculta su valor detras de una
serie de asteriscos (*). Cuando un usuario introduce un texto en un campo, 10s
asteriscos (*) ocultan la entrada.
Compatibilidad
JavaScript 1.O / cliente.
Controladores de eventos
onBlur,onFocus.
Resumen d e propiedades
Las propiedades se detallan en la tabla A.46.
Resumen del mktodo
Los mttodos se detallan en la tablaA.47. Este objeto hereda 10s mttodos watch
y unwatch de Object.

Tabla A.46. Propiedades de Password

Propiedad Descripcion Version


defaultvalue Muestra el atributo VALUE. 1.o
form Especifica el formulario que contiene 1.o
el objeto Password.
name Muestra el atributo NAME. 1.o
tme Muestra el atributo TYPE. 1.1
value Muestra el valor del campo del objeto 1 .o
Password.
Referencia de JavaScript 469

Tabla A.47. Mktodos de Password


~~ ~

Metodo Description Version


blur Desactiva el objeto. 1 .o
focus Activa el objeto. 1.o
handleEvent Llama a1 controlador de u n evento 1.2
determinado.
select Selecciona el area de entrada del objeto. 1.o

Plugin
Modulo complementario instalado en el cliente. Los objetos plugin son objetos
predefinidos de JavaScript a 10s que se accede a traves del array navigatorplugins.
El modulo Plugin es un complemento instalado en el cliente. Un complemento
constituye un modulo de software que puede llamar el explorador para mostrar
datos incluidos dentro del propio explorador o tipos especiales.
Compatibilidad
JavaScript 1.1 / cliente.
Resumen de propiedades
Las propiedades se detallan en la tabla A.48.
Tabla A.48. Propiedades de Plugin
______~ ~

Propiedad Description Version


description Una descripcion del complemento. 1.1
filename Nombre del archivo donde se encuentra el 1.1
complemento.
length Numero de elementos que tiene el array del 1.1
complemento de 10s objetos MimeType.
name Nombre del comulemento. 1.1

Este objeto hereda 10s metodos watch y unwatch de Object.

Radio
Boton independiente que pertenece a u n conjunto de botones de opci6n de un
formulario HTML. El usuario puede seleccionar uno de 10s botones de opcion
que se muestren en una lista de estos elementos.
Referencia de JavaScript 469

Tabla A.47. MCtodos de Password

Metodo Descripcion Version


blur Desactiva el objeto. 1 .o
focus Activa el objeto. 1.o
handleEvent Llama a1 controlador de u n evento 1.2
determinado.
select Selecciona el area de entrada del objeto. 1.o

Plugin
Modulo complementario instalado en el cliente. Los objetos plugin son objetos
predefinidos de JavaScript a 10s que se accede a travks del array navigator.plugins.
El modulo Plugin es un complemento instalado en el cliente. Un complemento
constituye un modulo de software que puede llamar el explorador para mostrar
datos incluidos dentro del propio explorador o tipos especiales.
Compatibilidad
JavaScript 1.1 / cliente.
Resumen de propiedades
Las propiedades se detallan en la tabla A.48.
Tabla A.48. Propiedades de Plugin

Propiedad Descripcion Version


description Una description del complemento. 1.1
fi1ename Nombre del archivo donde se encuentra el 1.1
complemento.
length Numero de elementos que tiene el array del 1.1
complemento de 10s objetos MimeType.
name Nombre del complemento. 1.1

Este objeto hereda 10s mktodos watch y unwatch de Object.

B o t h independiente que pertenece a u n conjunto de botones de opcion de un


formulario HTML. El usuario puede seleccionar uno de 10s botones de opcion
que se muestren en una lista de estos elementos.
470 Referencia de IavaScriDt

Compatibilidad
JavaScript 1.O / cliente.
Resumen de propiedades
Las propiedades correspondientes aparecen detalladas a continuacion en la ta-
bla A.49.
Resumen del me'todo
Los metodos se detallan en la tabla A.50 Este objeto hereda 10s mktodos watch
y unwatch de Object.
Tabla A.49. Propiedades de Radio

Propiedad Descripcion Version


checked Permite programar la selection de u n 1.o
b o t h de opci6n
defaultchecked Muestra el atributo CHECKED. 1.o
form Especifica el formulario que contiene 1.o
el objeto Radio.
name Muestra el atributo NAME. 1 .o
type Muestra el atributo TYPE. 1.1
value Muestra el atributo VALUE. 1.o

Tabla A.50.Metodos de Radio

Metodo Descripcion Version


blur Desactiva el boton de opcion. 1.1
click Simula la accion de hacer clic sobre el 1 .o
b o t h de opcion.
focus Activa el b o t h de opcion. 1.1
handleEvent Llama a1 controlador de un evento 1.2
determinado.

RegExp
Objeto de una expresion regular que contiene el modelo de dicha expresion.
Tiene propiedades y mktodos para utilizar la expresion regular parq localizar y
sustituir elementos de una cadena. Ademas de las propiedades de una expresion
regular individual que se puede crear usando el constructor RegExp, 10s objetos
Referencia de JavaScript 471

de este tip0 definidos de antemano poseen propiedades estaticas que se estable-


cen siempre que se utiliza una expresidn regular.
Compatibilidad
JavaScript 1.2 / nucleo.
Resumen de propiedades
Las propiedades se detallan en la tabla A.5 1.
Resumen del mktodo
Los mktodos se detallan en la tabla A.52.
Tabla A. 5 1. Propiedades de RegExp

Propiedad Descripcion Version


$1, . . . I $9 Si hay alguna, las subcadenas resultantes 1.2
apareceran entre partntesis.
$- Igual que input. 1.2
$* Igual que multiline. 1.2
$& Igual que 1astMatch. 1.2
$+ Igual que lastparen. 1.2
$' Igual que leftcontext. 1.2
$' Igual que rightcontext. 1.2
constructor Especifica la funcion que crea un prototipo 1.2
del objeto.
global Determina si se probara la expresion regular 1.2
frente a todos 10s resultados posibles de la
cadena. En caso contrario, unicamente se
probara con el primero.
ignorecase Determina si se ignoraran las mayusculas 1.2
cuando se trata de localizar un resultado
en una cadena.
input Cadena frente a la que se marca la expresion 1.2
regular.
lastIndex El indice donde empieza el siguiente resultado. 1.2
lastMatch El ultimo resultado. 1.2
lastparen Si hay alguna, la ultima subcadena que aparece 1.2
entre parkntesis.
leftcontext subcadena que precede a1 resultado mas reciente. 1.2
472 Referencia de JavaScript

Propiedad Descripcion Version


mu1 t iline Determina si la btisqueda tendra lugar con 1.2
cadenas de varias lineas.
proto type Permite agregar propiedades a todos 10s objetos. 1.1
rightcontext La subcadena que sigue a1 resultado mas 1.2
reciente.
source El texto del modelo. 1.2

Tabla A.52. MCtodos de RegExp

Metodo Descripcion Version


compi1e Compila u n objeto de una expresion regular. 1.2
exec Busca un resultado en la cadena que tiene 1.2
como parametro.
test Prueba un resultado en la cadena que tiene 1.2
como parametro.
tosource Muestra un literal yue representa el valor del 1.3
objeto determinado; este valor se puede utilizar
para crear un objeto nuevo. Anula el mktodo
Object.tosource.
toString Devuelve una cadena que representa el objeto 1.1
especificado. Anula el metodo
Object.tostring.
valueof Devuelve el valor original del objeto especifi- 1.1
cado. Anula el mktodo Obj ect .valueof.

Boton de reinicio de un formulario HTML. Un b o t h de reinicio devuelve el va-


lor predeterminado a todos 10s elementos.
Compatibilidad
JavaScript 1.O / cliente.
Controladores de eventos
onBlur,onclick,onFocus.
Resumen de propiedades
Las propiedades se detallan en la tabla A 5 3
Referencia de JavaScript 473

Resumen del me'todo


Los metodos se detallan en la tabla A.54. Este objeto hereda 10s mktodos watch
y unwatch de Object.
Tabla A.53. Propiedades de Reset

Propiedad Descripcion Version


form Especifica el formulario que contiene el 1.o
objeto Reset.
name Muestra el atributo NAME. 1.o
type Muestra el atributo TYPE. 1.1
value Muestra el atributo VALUE. 1.o

Tabla A.54.Mktodos de Reset

Metodo Descripcih Version


blur Desactiva el b o t h de reinicio. 1.o
click Simula la acci6n de hacer clic sobre el b o t h 1.o
de reinicio.
focus Activa el boton de reinicio. 1.o
handleEvent Llama a1 controlador de un evento 1.2
determinado.

Screen
Compatibilidad
JavaScript 1.O / cliente.
Resumen de propiedades
Las propiedades se detallan en la tabla A.55.
Tabla A.5 5. Propiedades de Screen

Propiedad Descripcihn Version


availHeight Determina el alto de la pantalla (en pixeles) 1.2
menos las propiedades permanentes o semi-
permanentes de la interfaz del usuario que
muestra el sistema operativo, como la barra
de tareas en Microsoft Windows.
474 Referencia de JavaScript

Propiedad Descripcion Version


availwidth Determina el ancho de la pantalla (en pixeles) 1.2
menos las propiedades permanentes o semi-
permanentes de la interfaz del usuario que
muestra el sistema operativo, como la barra
de tareas en Microsoft Windows.
colorDepth En caso de utilizar alguna paleta de colores, 1.2
determina la profundidad de bits de dicha
paleta; en cualquier otro caso, el valor se
obtendrii descreen .pixelDepth.
height Altura de la pantalla. 1.2
pixelDepth Resoluci6n de color de la pantalla (bits por 1.2
pixel).
width Ancho de la pantalla. 1.2

Select
Una lista de seleccion de u n formulario HTML. El usuario puede seleccionar
uno o varios de 10s elementos que aparecen en la lista, dependiendo de las con-
diciones bajo las que se crease la lista.
Compatibilidad
JavaScript 1.O / cliente.
Controladores de eventos
onBlur, onchange, onFocus.
Resumen de propiedades
Las propiedades se detallan en la tabla A.56.
Resumen del mktodo
Los metodos se detallan en la tabla A.5 7. Este objeto hereda 10s metodos watch
y unwatch de Object.
Tabla A.56. Propiedades de Select

Propiedad Descripcion Version


form Especifica el formulario que contiene la 1.O
lista de seleccion.
length Muestra el numero de opciones de la lista 1 .O
de seleccion.
Referencia de JavaScriDt 475

Propiedad Descripcion Version


name Muestra el atributo NAME. 1.o
options Muestra el atributo OPTION. 1.o
selectedIndex Muestra el indice de la opci6n seleccionada 1.O
(0,si se hubiesen seleccionado varias, el de
la primera de ellas).
type Determina que el objeto representa una 1.1
lista de selecci6n y si admite selecciones
multiples.

Tabla A.57. Metodos de Select

Metodo Descripcidn Version


blur Desactiva la lista de selecci6n. 1 .o
focus Activa la lista de selecci6n. 1.o
handleEvent Llama a1 controlador de un evento 1.2
determinado.

String
Un objeto que representa una serie de caracteres de una cadena.
Compat ibil idad
JavaScript 1.O / nucleo.
Resumen de propiedades
Las propiedades se detallan en la tabla A.58.
Resumen del rnktodo
Los mktodos se detallan en la tabla A.59. Este objeto hereda 10s mktodos watch
y unwatch de Object.
Tabla A.58. Propiedades de String

Propiedad Descripcion Version


constructor Especifica la funci6n que crea un prototipo 1.1
del objeto.
length Muestra la longitud de la cadena. 1.o
prototype Permite agregar propiedades a un objeto String. 1.1
476 Referencia de JavaScript

Tabla A.59. Metodos de String

Metodo Descripcion Version


anchor Crea una referencia HTML que se utiliza 1.o
como destino del hipertexto.
big Muestra la cadena con fuentes grandes, 1.o
como si estuviese escrito dentro de la
etiqueta <BIG>.
blink Muestra la cadena parpadeando, como si 1 .o
estuviese escrito dentro de la etiqueta
<BLINK>.
bold Muestra como si estuviese escrito dentro 1.o
de la etiqueta <B>.
charAt Devuelve el valor original del indice 1.o
especificado.
charCodeAt Devuelve un numero que indica el valor 1.2
Unicode del carhcter correspondiente con
un indice dado.
concat Combina el texto de dos cadenas y 1.2
devuelve la cadena resultante.
fixed Muestra la cadena con una fuente fija, 1.o
como si estuviese escrito dentro de la
etiqueta <TT>.
fontcolor Muestra la cadena con un color determi- 1 .o
nado, como si estuviese escrito dentro de
la etiqueta <FONT COLOR=color>.
fontsize Muestra la cadena con un tamaiio de fuen- 1 .o
te determinado, como si estuviese escrito
dentro de la etiqueta <FONT SIZE=size>.
fromCharCode Muestra la cadena creada utilizando para 1.2
ello la secuencia de valores Unicode que
haya sido especificada.
indexof Devuelve el indice del primer resultado que 1 .o
coincida con el valor especificado en la
llamada que se establece a1 objeto String;
si no lo encuentra, devolvera -1.
italics Muestra una cadena en cursiva, como si 1 .o
estuviese escrito dentro de la etiqueta <I>.
Referencia de JavaScript 477

Metodo Descripcion Version


lastindexof Devuelve el indice del ultimo resultado 1 .o
que coincida con el valor especificado
en la llamada que se establece a1 objeto
String; si no lo encuentra, devolvera - 1 .
1ink Crea un hipervinculo HTML de texto que 1.o
solicita otro URL.
match Se utiliza comparar una expresion regular 1.2
con una cadena.
replace Se utiliza para localizar una comparacion 1.2
entre una expresion regular y una cadena,
y para sustituir la subcadena localizada
por otra nueva.
search Compara una expresion regular y la cadena 1.2
que se haya especificada.
slice Extrae una seccion de una cadena y 1.O
devuelve una nueva cadena.
small Muestra la cadena con fuentes pequeiias, 1 .o
como si estuviese escrito dentro de la
etiqueta <SMALL>.
split Divide un objeto String en un array de 1 .o
cadenas, separando las cadenas en
subcadenas.
strike Muestra la cadena ajustando el texto, 1 .o
como si estuviese escrito dentro de la
etiqueta <STRIKE>.
sub Muestra la cadena como una subcadena, 1 .o
como si estuviese escrito dentro de la
etiqueta <SUB>.
substr Devuelve 10s caracteres de una cadena, 1 .o
empezando en un punto determinado
y ejecutandose la peticion a travCs de
un numero determinado de caracteres.
substring Devuelve 10s caracteres de una cadena 1.o
que se encuentren entre dos indices.
SUP Muestra la cadena como una supercadena, 1.o
como si estuviese escrito dentro de la
etiqueta <SUP>.
478 Referencia de JavaScript

Metodo Descripcion Version


toLowerCase Muestra el contenido de la cadena en 1.o
minusculas.
tosource Muestra un literal que representa el valor 1.3
del objeto determinado; este valor se puede
utilizar para crear un objeto nuevo. Anula
el mttodo Object . tosource.
tostring Devuelve una cadena que representa el 1.1
objeto especificado. Anula el mttodo
Object.tostring.
touppercase Muestra el contenido de la cadena en 1.o
mayusculas.
valueof Devuelve el valor original del objeto 1.1
especificado. h u l a el mttodo
0bject.valueOf.

Submit
Se corresponde con el boton "Enviar"de formulario HTML. Este b o t h hace que
se envie el formulario a1 servidor.
Compatibilidad
JavaScript 1.O / cliente.
Controladores de eventos
onBlur, onclick, onFocus.
Resumen de propiedades
Las propiedades se detallan en la tabla A.60.
Resumen del mAodo
Los mktodos se detallan en la tabla A.61. Este objeto hereda 10s methMds watch
y unwatch de Object.

Thbla A.60. Resumen de Submit

Propiedad Description Version


form Especifica el formulario que contiene el 1.o
objeto Submit.
name Muestra el atributo NAME. 1.o
Referencia de JavaScript 479

Propiedad Descripcion Version


type Muestra el atributo TYPE. 1.1
value Muestra el atributo VALUE. 1 .o

Tabla A.6 1. Resumen del Submit

Mktodo Descripcion Version


blur Desactiva el boton de envio. 1.o
click Simula la accion de hacer clic sobre el b o t h 1 .o
de envio.
focus Activa el b o t h de envio. 1.o
handleEvent Llama a1 controlador de un evento determinado. 1.2

sun
Objeto de alto nivel utilizado para acceder a cualquier clase de Java del paque-
te sun. *. Sun es un objeto de alto nivel predefinido en JavaScript. Se puede ac-
ceder automaticamente a 61 sin utilizar un constructor o llamar a un mitodo. El
objeto sun se utiliza como sinonimo de la propiedad Packages.sun.
Compatibilidad
JavaScript 1.1 / nucleo.

Text
Constituye un campo de insertion de texto de un formulario HTML. En un
campo de texto, el usuario puede introducir una palabra, una frase o una serie
de numeros.
Compatibilidad
JavaScript 1.O / cliente.
Controladores de eventos
onBlur, onchange, onFocus, onselect.
Resumen de propiedades
Las propiedades se detallan en la tabla A.62.
Resumen del mAodo
Los m6todos se detallan en la tabla A.63. Este objeto hereda 10s m6todos watch
y unwatch de Object.
480 Referencia de IavaScrilst

Tabla A.62. Propiedades de Text

Propiedad Descripcion Version


Defaultvalue Muestra el atributo VALUE. 1.o
Form Especifica el formulario que tiene el objeto Text. 1.0
Name Muestra el atributo NAME. 1.o
type Muestra el atributo TYPE. 1.1
value Muestra el valor del campo del objeto Text. 1.o

Tabla A.63. Metodos de Text

Metodo Descripcion Version


blur Desactiva el objeto. 1 .o
focus Activa el objeto. 1.o
handleEvent Llama a1 controlador de un evento determinado. 1.2
select Selecciona el area de entrada del objeto. 1.o

Textarea
Campo de insercion de varias lineas de un formulario HTML. En un area de
texto, el usuario puede introducir palabras, frases o numeros.
Compatibilidad
JavaScript 1.1 / cliente.
Controladores de eventos
onBlur, onchange,onFocus,onKeyDown,onKeyPress,onKeyUp,onSe1ect .
Resumen de propiedades
Las propiedades se detallan en la tabla A.64.
Resumen del mktodo
Los mktodos se detallan en la tabla A.65. Este objeto hereda 10s mktodos watch
y unwatch de Object.
Tabla A-64. Propiedades de Textarea

Propiedad Descripcion Version


defaultvalue Muestra el atributo VALUE. 1 .o
form Especifica el formulario que tiene el objeto 1 .o
Textarea.
Referencia de IavaScriot 48 1

Propiedad Description Version


name Muestra el atributo NAME. 1 .o
tme Especifica que el objeto es Textarea. 1.1
value Muestra el valor del objeto Textarea. 1 .o

Tabla A.65. Mktodos de Textarea

Metodo Descripcion Version


blur Desactiva el objeto. 1.o
focus Activa el objeto. 1.o
handleEvent Llama a1 controlador de u n evento determinado. 1.2
select Selecciona el Brea de entrada del objeto. 1.o

Window
Representa una ventana o un marco del explorador. Es el objeto de nivel supe-
rior de 10s grupos Location, History y de 10s documentos.
Compatibilidad
JavaScript 1.O / cliente.
Controladores de eventos
onBlur,onDragDrop,onError,onFocus,onload,onMove,onResize,y tam-
biCn onunload.
Resumen de propiedades
Las propiedades se detallan en la tabla A.66.
Resumen del mktodo
Los mCtodos se detallan en la tabla A.67. Este objeto hereda 10s mCtodos watch
y unwatch de Object.

Tabla A.66. Propiedades de Window

Propiedad Descripcion Version


closed Determina si se ha cerrado la ventana. 1.1
defaultstatus Muestra el mensaje predeterminado que se 1.0
se mostrarh en la barra de estado de la
ventana.
482 Referencia de JavaScript

Propiedad Descripcion Version


document Contiene informacion sobre el documento 1.o
y proporciona mktodos para mostrar la
salida HTML a1 usuario.
frames Array que muestra todos 10s marcos de 1 .o
una ventana.
history Contiene informacion sobre 10s URL que 1.1
ha visitado el cliente desde una ventana.
innerHeight Determina la dimension vertical (en pixeles) 1.2
del area contenida en la ventana.
innerwidth Determina la dimension horizontal (en 1.2
pixeles) del area contenida en la ventana.
length Numero de marcos de la ventana. 1 .o
location Contiene informacion del URL. 1.o
locationbar Representa la barra de direction del 1.2
explorador.
menubar Representa la barra de menus del explorador. 1.2
name Nombre unico que se utiliza para referirse 1.o
a esta ventana.
opener Determina el nombre de la ventana del docu- 1.1
mento que establece la llamada cuando se
abre la ventana utilizando el mktodo open.
outerHeight Determina la dimension vertical (en pixeles) 1.2
de 10s limites externos de la ventana.
outerwidth Determina la dimension horizontal (en 1.2
pixeles) de 10s limites externos de la ventana.
pageXOffset Proporciona la posicion x (en pixeles) de la 1.2
pagina que se muestra en la ventana.
pageYOffset Proporciona la posicion y (en pixeles) de la 1.2
pagina que se muestra en la ventana.
parent Un sinonimo para ventana o marco que 1.o
contiene el marco de trabajo.
personalbar Representa la barra de personalizada del 1.2
explorador (tambikn conocida como barra
de directorios).
scrollbars Representa las barras de desplazamiento 1.2
de la ventana del explorador.
Referencia de JavaScriDt 483

~ ~~

Propiedad Descripcion Version


self Un sinonimo para la ventana actual. 1.o
status Especifica una prioridad o u n mensaje 1.o
que aparecerh en la barra de estado de la
ventana.
statusbar Representa la barra de estado de la ventana 1.2
del explorador.
toolbar Representa la barra de herramientas de la 1.2
ventana del explorador.
Un sin6nimo para la ventana superior del 1.o
explorador.
window Un sinonimo para la ventana actual. 1.o

Tabla A.67. Metodos de Window

Metodo Descripcion Version


alert Muestra un cuadro de dialog0 1.o
Alert con un mensaje y el
boton "Aceptar".
back Deshace la ultima accion de 1.2
cualquier cuadro que se encuentre
situado dentro de la ventana de
alto nivel.
blur Desactiva el objeto especificado. 1.o
captureEvents Prepara la ventana o el documento 1.2
para que capture todos 10s eventos
de un tip0 determinado.
clearInterva1 Elimina la caducidad que se ha 1.2
establecido con el metodo
setInterval.
clearTimeout Elimina la caducidad que se ha 1 .o
establecido con el metodo
setTimeout.
close Cierra la ventana especificada. 1 .o
confirm Muestra u n cuadro de dihlogo 1.o
Confirm con u n mensaje y el
boton "Aceptar".
484 Referencia de JavaScript

Metodo Descripcion Version


disableExternalCapture Desactiva el evento externo 1.2
capturado por el mCtodo
enableExternalCapture.
enableExternalCapture Permite que una ventana con 1.2
varios marcos capture eventos
de paginas procedentes de
distintas ubicaciones (servidores).
find Localiza el texto especificado en 1.2
la cadena de busqueda en el
contenido de la ventana
especificada.
focus Activa el objeto especificado. 1.1
forward Muestra el siguiente URL del 1.2
his torial.
handleEvent Llama a1 controlador de un 1.2
evento determinado.
home Dirige el servidor hacia el URL es- 1.2
pecificado en las preferencias del
usuario como pagina principal.
moveBy Mueve la ventana la cantidad que 1.2
se haya especificado.
moveTo Coloca la esquina superior 1.2
izquierda de la ventana en as
coordenadas especificadas.
open Abre una ventana nueva el el 1.o
explorador.
print Imprime el contenido de la 1.2
ventana o del marco.
prompt Muestra un cuadro de dialog0 1 .o
Prompt con un mensaje y un
campo de entrada.
releaseEvents Prepara la ventana para que 1.2
capture 10s eventos de un tip0
determinado que ha capturado,
enviandolos a 10s objetos que
estan en otro nivel dentro de la
jerarquia de eventos.
Referencia de JavaScript 485

Metodo Descripcion Version


resizeBy Modifica el tamaiio de la ventana 1.2
moviendo su esquina superior
derecha la cantidad especificada.
resizeTo Modifica el tamaiio de la ventana 1.2
y le asigna el alto y el ancho que
se haya especificado.
routeEvent Entrega un evento de 10s captura- 1.2
dos a la jerarquia usual de eventos.
scroll Desplaza la ventana hasta la 1.1
coordenada especificada.
scro11By Mueve la vista de la ventana la 1.2
cantidad que se haya especificado.
scro11To Desplaza la vista de la ventana 1.2
colocando su esquina superior
izquierda en las coordenadas que
se hayan especificado.
setInterval Evalua una expresion o una 1.2
llamada a una funcion cada cierto
numero de milisegundos.
setTimeout Evalua una expresion o una 1 .o
llamada a una funcion una vez
despuks de que transcurra cierto
numero de milisegundos.
stop Detiene la carga en proceso. 1.2

Propiedades y funciones de alto nivel


Las propiedades y funciones de alto nivel no estan asociadas con ningun obje-
to. La tabla A.68 muestra sus propiedades. La tabla A.68 detalla las funciones.
Tabla A.68. Propiedades de alto nivel

Propiedad Descripcion Version


infinity Valor numirico que representa el infinito. 1.3
NaN Valor especial que representa "not a number" 1.3
(no es un numero).
undefined Valor no definido. 1.3
486 Referencia de JavaScript

Tabla A.69. Funciones de alto nivel

Funcion Descripcion Version


escape Devuelve la codification hexadecimal de 1.o
un argumento que se encuentra en IS0
Latin-1; se utiliza para crear cadenas
que se aiiadiran a un URL.
eva1 Evalua una cadena de codigo en 1.o
JavaScript sin una referencia a un objeto
en particular.
i sFinite Evalua un argumento para determinar 1.3
que no es un numero.
isNaN Devuelve una cadena que representa 1.o
el objeto especificado. h u l a el mktodo
0bject.toString.
Number Convierte un objeto en un numero. 1.2
parseFloat Analiza un argumento en forma de cadena 1.o
y a continuacion devuelve un numero con
coma flotante.
parseInt Analiza un argumento en forma de cadena 1.o
y devuelve un entero.
string Convierte un objeto en una cadena. 1.2
unescape Devuelve la cadena ASCII correspondiente 1.o
a1 valor hexadecimal que haya sido
especificado.

Controladores de eventos
Esta seccion contiene la sintaxis correspondiente a 10s 2 3 controladores de even-
tos de JavaScript.

onAbort
Controlador de eventos para Image.Ejecuta el codigo en JavaScript cuando se
anula un evento; es decir, cuando el usuario anula la carga de una imagen (por
ejemplo, haciendo clic en u n enlace o en el boton "Detener").
Compatibilidad
JavaScript 1 . 1 .
Referencia de JavaScript 487

Propiedades
onAbort tiene las siguientes propiedades:
Type. Indica el tipo de evento.
Target. Indica el objeto a1 que se envi6 originalmente el evento.

onBlur
Controlador de eventos para Button,Checkbox,Fileupload,Layer,Password,
Radio,Reset,Select,Submit,Text,Textarea y Window.Ejecuta el codigo
en JavaScript si hay u n evento blur; es decir, si 10s elementos de u n formulario
dejan de estar activos porque se desactiva la ventana o marco que 10s contiene.
Compa tibi 1idad
JavaScript 1 .O.
Propiedades
onBlur tiene las siguientes propiedades:
Type. Indica el tipo de evento.
Target. Indica el objeto a1 que se envi6 originalmente el evento.

onchange
Ejecuta el c6digo en JavaScript cuando tiene lugar u n evento change; es decir,
cuando un campo Select,Text o Textarea de u n formulario deja de estar ac-
tivo porque se ha modificado su valor. Controlador de eventos para FileUpload,
Select,Text y Textarea.
Compatibilidad
JavaScript 1.O.
Propiedades
onchange tiene las siguientes propiedades:
Type. Indica el tipo de evento.
Target. Indica el objeto a1 que se envio originalmente el evento.

Controlador de eventos Button,Document,Checkbox,Link,Radio,Reset


y Submit.Ejecuta el cddigo en JavaScript cuando tiene lugar u n evento click; es
decir, cuando se hace clic sobre alguno de 10s objetos del formulario. (Un evento
click es una cornbinacion de 10s eventos MouseDown y Mouseup.)
488 Referencia de JavaScript

Compat ibil idad


JavaScript 1.O.
Propiedades
onclick tiene las siguientes propiedades:
Type. Indica el tipo de evento.
Target. Indica el objeto a1 que se envio originalmente el evento.
LayerX, layerY, page)<, pageY, screen)(, screenY. Representa la ubicacion
que tiene el cursor en el momento en que ocurre el evento.
Which. Representa con un 1 el clic con el boton izquierdo del raton y con
u n 3 el clic con el boton derecho.
Modifiers. Contiene una lista con 10s modificadores principales que se man-
tendran activos mientras el evento tenga lugar.

onDblClick
Controlador de eventos para Document y Link. Ejecuta el codigo en JavaScript
cuando tiene lugar u n evento DblClick; es decir, cuando se hace doble clic sobre
alguno de 10s objetos del formulario o sobre un vinculo.
Compatibilidad
JavaScript 1.2.
Propiedades
onDblClick tiene las siguientes propiedades:
Type. Indica el tipo de evento.
Target. Indica el objeto a1 que se envi6 originalmente el evento.
LayerX, layerY, pageX, pageY, screen)(, screenY. Representa la ubicacion
que tiene el cursor en el momento en que ocurre el evento.
Which. Representa con un 1 el clic con el b o t h izquierdo del raton y con
un 3 el clic con el boton derecho.
Modifiers. Contiene una lista con 10s modificadores principales que se man-
tendran activos mientras el evento tenga lugar.

onDragDrop
Controlador de eventos para Document y Link. Ejecuta el c6digo en JavaScript
cuando tiene lugar u n evento DragDrop; es decir, cuando el usuario arrastra un
objeto en la ventana del explorador, como ocurre con 10s archivos.
Compatibilidad
JavaScript 1.2.
Referencia de JavaScript 489

Propiedades
onDragDrop tiene las siguientes propiedades:

Type. Indica el tipo de evento.


Target. Indica el objeto a1 que se envio originalmente el evento.
lvt. Devuelve un array de cadenas que contienen 10s URL de 10s objetos que
se han soltado.
Modifiers. Contiene una lista con 10s modificadores principales que se man-
tendran activos mientras el evento tenga lugar.
ScreenX, screenY. Representa la ubicacibn que tiene el cursor en el mo-
mento en que ocurre el evento.

onError
Ejecuta el codigo en JavaScript en el caso de que tenga lugar un evento error;
es decir, en 10s casos en que la carga de u n documento o de una imagen provoca
un error.
Compatibilidad
JavaScript 1.1.
Propiedades
onDragDrop tiene las siguientes propiedades:

Type. Indica el tip0 de evento.


Target. Indica el objeto a1 que se envio originalmente el evento.

onFocus
Controlador de eventos para Button,Checkbox,Fi leUpload,Frame,Layer,
Password,Radio,Reset,Select,Submit,Text,Textarea y Window.Ejecu-
ta el codigo en JavaScript cuando tiene lugar un eventofocus; es decir, cuando
se activa una ventana, cuadro, marco de trabajo o uno de 10s elementos de un
formulario.
Compatibilidad
JavaScript 1.O.
Propiedades
onFocus tiene las siguientes propiedades:

Type. Indica el tipo de evento.


Target. Indica el objeto a1 que se envio originalmente el evento.
490 Referencia de JavaScript

onKeyDown
Controlador de eventos para Document,Image,Link y Textarea.Ejecuta el
codigo en JavaScript si tiene lugar un evento KeyDown; es decir, cuando el usua-
rio pulsa una tecla.
Cornpatibizidad
JavaScript 1.2.
Propiedades
onKeyDown tiene las siguientes propiedades:

Type. Indica el tipo de evento.


Target. Indica el objeto a1 que se envio originalmente el evento.
LayerX, layerY, pageX, pageY, screenX, screenY. Para un evento que tiene
lugar en una ventana, representa la ubicacion que tiene el cursor en el mo-
mento en que ocurre el evento. Para un evento que tiene lugar en un formu-
lario, representa la posicion del elemento de dicho formulario.
Which. Muestra el valor ASCII correspondiente a la tecla pulsada. El me-
todostring.f romCharCodese usa para obtener la tecla, numero o simbo-
lo que se corresponde con la tecla pulsada. El mCtodoString . charCodeAt
se utiliza para obtener el codigo ASCII correspondiente con la tecla pulsada.
Modifiers. Contiene una lista con 10s modificadores principales que se man-
tendran activos cuando el evento tenga lugar.

onKeyPress
Controlador de eventos para Document,Image,Link y Textarea.Ejecuta el
codigo en JavaScript si tiene lugar u n evento Keypress; es decir, cuando el ~isiia-
rio mantiene pulsada una tecla.
Compatibilidad
JavaScript 1.2.
Propiedades
onKeyPress tiene las siguientes propiedades:

Type. Indica el tipo de evento.


Target. Indica el objeto a1 que se envi6 originalmente el evento.
LayerX, layerY, pageX, pageY, screen)<,screen\(. Para un evento que tiene
lugar en una ventana, representa la ubicacion que tiene el cursor en el mo-
mento en que ocurre el evento. Para un evento que tiene lugar en un formu-
lario, representa la posicion del elemento de dicho formulario.
Referencia de JavaScript 49 1

Which. Muestra el valor ASCII correspondiente a la tecla pulsada. El me-


todo String.f romCharCodese usa para obtener la tecla, numero o simbo-
lo que se corresponde con la tecla pulsada. El mktodos tring .charCodeAt
se utiliza con objeto de obtener el codigo ASCII correspondiente con la tecla
pulsada.
Modifiers. Contiene una lista con 10s modificadores principales que se man-
tendran activos cuando el evento tenga lugar.

onKeyUp
Controlador de eventos para Document,Image,Link y Textarea.Ejecuta el
codigo en JavaScript si tiene lugar u n evento KeyUp;es decir, cuando el usuario
suelta una tecla.
Compatibilidad
JavaScript 1.2.
Propiedades
onKeyUp tiene las siguientes propiedades:
Type. Indica el tipo de evento.
Target. Indica el objeto a1 que se envio originalmente el evento.
LayerX, layerY, pageX, pageY, screen)(, screenY. Para u n evento que tiene
lugar en una ventana, representa la ubicacion que tiene el cursor en el mo-
mento en que ocurre el evento. Para un evento que tiene lugar en un formu-
lario, representa la posicion del elemento de dicho formulario.
Which. Muestra el valor ASCII correspondiente a la tecla pulsada. El me-
todo String.f romCharCodese usa para obtener la tecla, numero o simbo-
lo que se corresponde conla tecla pulsada. El metodostring . charCodeAt
se utiliza para obtener el codigo ASCII correspondiente con la tecla que ha
sido pulsada.
Modifiers. Contiene una lista con 10s modificadores principales que se man-
tendran activos cuando el evento tenga lugar.

onload
Controlador de eventos para Image, Layer y Window. Ejecuta el codigo en
JavaScript cuando tiene lugar un evento load; es decir, cuando el explorador
completa la carga de una ventana o de todos 10s marcos que se encuentran den-
tro de una etiqueta <FRAMESET>.
Compatibilidad
JavaScript 1.O.
492 Referencia de JavaScript

Propiedades
onLoad tiene las siguientes propiedades:
Type. Indica el tip0 de evento.
Target. Indica el objeto a1 que se envio originalmente el evento.
Width, height. Represetan el ancho y alto de la ventana en la que tiene lugar
el evento (siempre y cuando no ocurra dentro de un marco).

onMouseDown
Controlador de eventos para Button, Document y Link. Ejecuta el c6digo en
JavaScript cuando tiene lugar u n evento MouseDown; es decir, cuando el usua-
rio pulsa uno de 10s botones del raton.
Cornpatibilidad
JavaScript 1.2.
Propiedades
onMouseDown tiene las siguientes propiedades:
Type. Indica el tip0 de evento.
Target. Indica el objeto a1 que se envi6 originalmente el evento.
LayerX, layerY, pageX, pageY, screen)<,screenY. Representa la ubicaci6n
que tiene el cursor en el momento en que ocurre el evento MouseDown.
Which. Representa con un 1 el clic con el b o t h izquierdo del rat6n y con
u n 3 el clic con el b o t h derecho.
Modifiers. Contiene una lista con 10s modificadores principales que se man-
tendran activos mientras el evento tenga lugar.

onMouseMove
Como el rat6n se mueve con tanta frecuencia, por defect0 onMouseMove no
sera evento de ningun objeto. Se ha de asociar con un objeto determinado. Eje-
cuta el codigo en JavaScript cuando tiene lugar u n evento MouseMove; es decir,
cuando el usuario mueve el cursor.
Cornpatibilidad
JavaScript 1.2.
Propiedades
onMouseMove tiene las siguientes propiedades:
Type. Indica el tipo de evento.
Target. Indica el objeto a1 que se envi6 originalmente el evento.
Referencia de JavaScript 493

LayerX, layerY, pageX, pageY, screen)<, screenY. Representa la ubicacion


que tiene el cursor en el momento en que ocurre el evento MouseMove.

onMouseOut
Controlador de eventos para Layer y Link. Ejecuta el codigo JavaScript cuan-
do tiene lugar un evento Mouseout; es decir, cada vez que el puntero abandona
u n area (dentro de la imagen del cliente) o u n enlace que se encuentra dentro de
otro enlace o zona activa.
Compatibilidad
JavaScript 1.2.
Propiedades
onMouseOut tiene las siguientes propiedades:
Type. Indica el tip0 de evento.
Target. Indica el objeto a1 que se envio originalmente el evento.
LayerX, layerY, pageX, pageY, screen)(, screenY. Representa la ubicacion
que tiene el cursor en el momento en que ocurre el evento Mouseout.

onMouseover
Controlador de eventos para Layer y Link. Ejecuta el codigo JavaScript cuan-
do tiene lugar un evento Muuseover; es decir, cada vez que el puntero pasa por
encima de un objeto o de un Area, procedente de cualquier punto que sea exte-
rior a ellos.
Compatibil idad
JavaScript 1.2.
Propiedades
onMouseOver tiene las siguientes propiedades:
Type. Indica el tipo de evento.
Target. Indica el objeto a1 que se envi6 originalmente el evento.
LayerX, layerY, pageX, pageY, screenX,screenY. Representa la ubicacion
que tiene el cursor en el momento en que ocurre el evento Mouseover.

onMouseUp
Controlador de eventos para Button, Document y Link. Ejecuta el c6digo en
JavaScript cuando tiene lugar u n evento Mouseup; es decir, cuando el usuario
suelta uno de 10s botones del ratbn.
494 Referencia de IavaScriDt

Compatibilidad
JavaScript 1.2.
Propiedades
onMouseUp tiene las siguientes propiedades:
9 Type. Indica el tipo de evento.
Target. Indica el objeto a1 que se envio originalmente el evento.
LayerX, layerY, page)<, page\(, screen)<,screenY. Representa la ubicacion
que tiene el cursor en el momento en que ocurre el evento Mouseup.
Which. Representa con u n 1 el clic con el boton izquierdo del raton y con
u n 3 el clic con el boton derecho.
W. Contiene una lista con 10s modificadores principales que se mantendran
activos cuando el evento tenga lugar

onMove
Controlador de eventos para Window. Ejecuta el c6digo en JavaScript cuando
tiene lugar un evento move; es decir, cuando el usuario o el script pasa a una ven-
tana o marco.
Compatibilidad
JavaScript 1.2.
Propiedades
onMove tiene las siguientes propiedades:
Type. Indica el tipo de evento.
Target. Indica el objeto a1 que se envio originalmente el evento.
ScreenX, screenY. Representa la posicion de la esquina superior izquierda
de la ventana o del marco.

onReset
Controlador de eventos para Form. Ejecuta el c6digo en JavaScript cuando tie-
ne lugar un evento reset; es decir, si un usuario borra el contenido del fomulario
(hace clic sobre el boton "Borrar").
Compatibilidad
JavaScript 1.1.
Propiedades
onReset cuenta con las dos propiedades que podemos ver detalladas a conti-
nuacion:
Referencia de JavaScript 495

Type. Indica el tipo de evento.


Target. Indica el objeto a1 que se envi6 originalmente el evento.

onResize
Controlador de eventos para W i n d o w . Ejecuta el codigo en JavaScript cuando
tiene lugar un evento resize; es decir, cuando el usuario o el script modifican el
tamaiio de una ventana o marco.
Compatibilidad
JavaScript 1.2.
Propiedades
onResize tiene las siguientes propiedades:
Type. Indica el tipo de evento.
Target. Indica el objeto a1 que se envio originalmente el evento.
Width, height. Representa la altura de la ventana o del marco.

onselect
Controlador de eventos para Text y Textarea.Ejecuta el codigo en JavaScript
cuando tiene lugar un evento select; es decir, cuando el usuario selecciona parte
del contenido de un campo o zona de texto.
Compatibilidad
JavaScript 1 .O.
Propiedades
onselect tiene las siguientes propiedades:
Type. Indica el tipo de evento.
Target. Indica el objeto a1 que se envio originalmente el evento.

onsubmit
Controlador de eventos para Form. Ejecuta el codigo en JavaScript cuando tie-
ne lugar un evento submit; es decir, cuando el usuario envia un fomulario.
Compatibilidad
JavaScript 1 .O.
Propiedades
onsubmit cuenta con las dos propiedadesque podemos ver detalladas a conti-
nuacion:
496 Referencia de JavaScript

Type. Indica el tip0 de evento.


Target. Indica el objeto a1 que se envio originalmente el evento.

onunload
Controlador de eventos para Window. Ejecuta el codigo en JavaScript cuando
tiene lugar u n evento submit; es decir, cuando el usuario envia un fomulario.
Compa tibilidad
JavaScript 1.O
Propiedades
onunload tiene las siguientes propiedades:
Type. Indica el tipo de evento.
Target. Indica el objeto a1 que se envio originalmente el evento.
Apendice B
Web Resources

Este apkndice contiene vinculos dirigidos a paginas Web relacionadas con


JavaScript. Encontraremos referencias relacionadas con JavaScript, Per1 y CGI,
asi como con fuentes graficas. En la ultima seccion se muestran una serie de si-
tios Web que contienen aplicaciones similares a las vistas en este libro. Siempre
esta bien observar como solucionan 10s problemas 10s profesionales de la codifi-
caci6n.

Sitios Web interesantes relacionados con JavaScript


Hay demasiados sitios Web relacionados con JavaScript como para listarlos aqui
todos. La mayor parte del cddigo de las paginas a las que hacemos referencia
aqui contienen JavaScript y DHTML. Para visitar uno de ellos habra que insta-
lar Macromedia Flash, aunque las versiones 4.x 6 5.x de MSIE y NN ya cuentan
con 61.
GaboCorp:
h ttp://www.gabocorp. com/
Doc Ozone:
h ttp://www.ozones.com/blueprint. html
Scrutinizer by Vivatrix:
h t tp://viva trix.com/demos/en/scrutinizer
Honda Automobiles:
h ttp://www. honda 1 999.com/
498 Web Resources

Haznet's Fallout Shelter:


http://www. hudziak. com/haznet/javascript. html

Referencia a JavaScript

Estos URL me han sido de gran ayuda durante 10s ultimos meses. Igual que me
han ayudado a mi, pueden ayudar a cualquier otro. En ellos encontrara mate-
rial de referencia, tutoriales, scripts, articulos, etc. sobre JavaScript.

Core JavaScript Reference:


ht tp://deveZoper 1 .netscape.com/docs/manua Is/js/core/jsref/conten ts.htm
Doc JavaScript:
h ttp://www. webreference.com/js/
HotSyte-The JavaScript Resource:
http://www.serve.com/hotsyte/
Microsoft Scripting Technologies (JScript):
http://msdn.microsoft. com/scripting/default. htm?/scripting/jscript/default.htm
Microsoft Developer Network (JScript):
http://msdn. microsoft.com/developer/default. htm
The JavaScript Source:
http://javascript. internet. com/
Cut-n-Paste JavaScript:
http://www.infohiway.com/javascript/indexf:htm
JavaScriptWorld:
http://www.jsworld.com/
webmonkey/ JavaScript :
http://www.hotwired. com/webmon key/javascript/?tw =javascript
CNET Builder.com:
http://builder. cnet.com/Programming/
JavaScripts .corn:
ht tp://www.javascripts. com/
eScriptZone.com:
http://www.escriptzone. com/
Web Resources 499

Preguntas y respuestas sobre JavaScript


Seguro que tiene preguntas. Y seguro que Cstas tienen respuestas. A continua-
cion tiene algunas de las direcciones donde encontrara preguntas y respuestas
relacionadas con JavaScript, desde las mas sencillas a las mas complejas.
1RT.org:
http://www.irt.org/script/faq.htrn
JS Beginners:
http://www.geol.uni-erlangen.de/geojs/JS-tutorial/JS-beginners. htrnl
DevEdge NewsGroup FAQ:
http://developer.netscape.corn/support/faqs/charnpions/javascript.htrnl
JavaScript Beginner's FAQ
http://www.it9 7.de/JavaScript/JS-tutorial/3rdless.htrnl
The JavaScript Mini-FAQ
http://www.dannyg.corn/javascript/jsrninifaq.html

DHTML Reference
Siempre es conveniente conocer la ultima inforrnacion existente sobre DHTML.
La mayor parte de 10s articulos de estas paginas tratan temas relacionados con
IE y NN.
DevHead dHTML:
http://www.zdnet.corn/devhead/filters/dhtml/
Dynamic HTML Zone:
h ttp://www.d h trnlzone.corn/index.h trn1
Dynamic Drive:
h ttp://www.dynamicdrive.com/
Inside DHTML:
http://www.insidedhtrnl.com/
The Dynamic Duo:
http://www.dansteinrnan.corn/dynduo/
Dynamic HTML Guru Resource:
http://www.h trnlguru.corn/
500 Web Resources

w ebmonkey/dynamic-html:
h ttp://www. hotwired. com/webmonkey/dynamic-h tml/
Frequently Asked Questions About Dynamic HTML:
http://www. microsoft.com/workshop/author/dhtml/dh tmlqa.asp

Document Object Model Reference


Estas fuentes ofrecen informacion sobre el esthndar Document Object Models
que ha establecido World Wide Web Consortium.
Document Object Model:
h t tp://www. w3.org/DOM/
Document Object Model FAQ:
ht tp://www. w3.org/DOM/faq. h tml

Perl/CGI Reference
Si quiere saber mas sobre Common Gateway Interface y Perl, empiece por aqui.
Los recursos de Perl son tan grandes e impresionantes como el propio lenguaje.
Asegdrese de bajarse e instalar todos 10s modulos para que funcionen bien.
Perl.com :
h ttp://www.perl.com/pace/pub
Perl Reference Page:
h t tp://reference.perl.com/query. @?section =tutorials
An Introduction to The Common Gateway Interface:
h t tp://www. u toronto.ca/webdocs/CGl/cgi I . h tml

Graphics Resources
Parece que dltimamente 10s caminos de JavaScript y de 10s graficos se cruzan
con demasiada frecuencia. Por eso he incluido la siguiente lista de URL. En ella
encontrara articulos y graficos gratuitos.
Cooltype. com :
ht tp://cool type.webpedia. com/
AndyArt:
http://www.andyart.com/
Web Resources 501

Site Builder Workshop-Image Gallery:


ht tp://www.microsoft .com/workshop/c-frame. htm #/ga 11ery/images/defau1 t .asp

Aplicaciones sirnilares
En esta seccion encontrara enlaces con sitios Web que contienen aplicaciones
similares a las vistas en este libro.

Motores de busqueda para el cliente


La mayoria de 10s motores de busqueda escritos en JavaScript que he visto en
la Web se han diseiiado para entregar las peticiones a varios buscadores, como
Yahoo!, Infoseek y AltaVista. Per0 habia unas cuantas con aplicaciones de bus-
queda en JavaScript propias del cliente. Todas parecen funcionar bien, per0 nin-
guna de ellas tiene la funcionalidad de la aplicacion vista en el primer capitulo.
En cualquier caso, merece la pena echarles un vistazo. Si estudia su codigo, se-
guro que encuentra algo que le servirii de utilidad.
The Computer Crap Search Engine:
h t tp://www.geocities.com/Sil iconValley/Horizon/2 1 88/search.h tml
He visto titulos mas creativos, per0 el autor Nathan Wiegand utiliza un codigo
parecido a1 que hemos visto aqui.
JavaScript Search Functions:
ht tp://www.serve.com/ho tsyte/wildman/web-search/site-search. h tml
Esta aplicacion pertenece a Tim Hobbs de hotsyte.com.

Examenes online
Aunque hay que buscar mucho, la verdad es que en la Web tambien hay prue-
bas escritas con JavaScript. He encontrado las siguientes.
D2 Test:
http://inetpubl.com/psy/psy.htm
Esta prueba de concentracih, creada por Inet Publishing, utiliza JavaScript para
medir la concentration del usuario y contabilizar sus aciertos contrarreloj .
Hardware Fundamentals Practice Test:
http://www.cit.ac.nz/smac/hflOO/testIs. htm
Esta prueba es muy interesante. Examina 10s conocimientos que se tienen so-
bre el formato y particiones de disco, la capacidad de almacenamiento de estas
unidades, etc. Lo mejor de todo es que esta aplicacion nos permite saber, en el
502 Web Resources

mismo momento en que se dC la respuesta, si Csta ha sido correcta o no. En rea-


lidad se trata de una serie de pruebas. Los distintos examenes se encuentran en
10s archivos comprendidos entre test2s.htm y test9.htm (no hay test6.htm). Tam-
biCn hay examenes sobre programacion en C en la direccion http://www.cit.ac.nz/
smac/cprogram/c-054~.htm. Sus creadores pertenecen a Central Institute of Techno-
logy de Nueva Zelanda.
Norm's Multiple Choice Test:
http://lisa. unomaha.edu/Z.O/test. html
Es posible que no termine de gustarle el fondo amarillo, pero el caso es que tam-
biCn se ha creado con JavaScript. En vez de tener que escribir la pagina Web una
y otra vez, esta prueba inserta las preguntas y respuestas directamente desde 10s
campos del formulario. Tambikn registra la puntuacion.
Test 2000:
http://www.jchelp.com/test2000/drvframe. htm
Esta prueba con varias opciones se ha creado para ayudar a 10s estudiantes a
preparar el examen de conducir de California (Estados Unidos). En vez de mos-
trar 10s resultados con cada pregunta, esta version del examen 10s muestra a1 fi-
nal. TambiCn muestra las respuestas correctas y su respectiva explication.

Diapositivas
A continuacion mostramos algunas de las aplicaciones interesantes relaciona-
das con las secuencias de imagenes.
Project Management: A Slide Show:
h t tp://www.geocities.corn/-mohan-iyer/slideshow. h t m
Esta aplicacion DHTML tiene diapositivas animadas. Los graficos tardan un poco
en cargar, pero la espera merece la pena. El codigo JavaScript responsable se en-
cuentra en el archivo slideshow.js.
web blazonry:
http://blazonry.com/javascript/slideshow/
Esta diapositiva muestra la evolucion del sitio Web de mkaz, presumiblemente
desde sus comienzos. Puras y simples secuencias de imagenes.
Apartment Home Animated Virtual Tour:
http://www. mark-taylor. com/virtual tour/index.h t m
Estas diapositivas automatizadas guian a1 usuario en una visita por un piso. La
diapositiva tarda bastante en cargar. Pero el desarrollador tambien lo ha tenido
en cuenta e intenta entretenernos para que no hagamos clic en ningun lado.
Web Resources 503

Interfaces para motores de busqueda multiples


No hay limites en la creacion de motores de busqueda en JavaScript para la Red.
Aqui tenemos algunos de 10s que he encontrado en mi deambular por la Web.
Ultimate Universal Interface for Search Engines (UUISE):
http://www.cris.corn/-anathema/UUlSE/index. html
Este programa utiliza una serie de cookies para permitir que el usuario perso-
nalice su motor de busqueda. Los usuarios pueden seleccionar hasta 10 motores
de busqueda, cada uno de ellos con una serie de opciones. El resto del programa
se mostrara en una ventana remota con un campo de texto e imagenes que en-
lazaran con 10s 10 motores de busqueda seleccionados.
WebSight:
http://rampages.onramp.net/-jnardo/websight/websitel.htm
Esta aplicacion se parece bastante a la vista en estas paginas. Per0 permite filtrar
10s resultados mediante operadores booleanos. Ademas cuenta con u n archivo
de ayuda.
Virtual Meta Search 2:
h t tp://Webcas tlinks.com/vmsearch/vmsearch. h tml
Esta aplicacion permite seleccionar 6 motores de busqueda entre un total de 3 1
y devuelve 10s resultados en marcos independientes.
Computer ESP Bargain Agent:
h ttp://www.s hopper.com/shop/
En vez de motores de busqueda basados en bases de datos locales, esta aplicacion
busca la inforrnacion en ordenadores online, es decir en bases de datos remotas.

Generador de secuencias
L a Red esta llena de aplicaciones especializadas en secuencias de imagenes. iPor
quk no darse una vuelta por alli y ver quC contienen?
The Mighty Mouseover Machine:
http://builder.cnet.com/Programming/Kahn/Ol28 98/index. html
Posiblemente, el sitio Web de Charity Khan tiene mas visitas que Builder.com.
Su aplicacion utiliza una ventana remota para crear una plantilla de imagenes
y crear automaticamente el c6digo correspondiente.
OnMouseOver Whipper:
h ttp://wsabstract. com/mousewhipper/index. h tm
504 Web Resources

La aplicacion de este sitio Web unicamente permite cargar un par de imagenes


a la vez. En cualquier caso, es muy interesante porque la parte encargada de la
generacion del codigo puede detectar el alto y ancho de cada imagen. La aplica-
cion se encarga de todo. Basta con crear un objeto Image() y utilizarlo para ob-
tener las propiedades de las imagenes. Es un buen trabajo.
JavaScript Rollover Generator:
h tml
h ttp://webreview.com/wr/pub/98/03/13/coder/rollover.
Al igual que Whipper, unicamente puede cargar un par de imagenes a la vez.
Antes de generar el codigo se puede previsualizar el resultado.

Bibliotecas
La verdad es que no he encontrado mucho sobre las bibliotecas en JavaScript.
Las que aparecen en esta seccion cuentan con bastante codigo DHTML. Si dispo-
ne de algun tiempo libre, bajese estas librerias y trate de imaginarse el c6digo. Es
posible que termine por perder alguna neurona, per0 merecera la pena.
DHTMLLib Version 2:
h ttp://www. insidedhtml.com/dhtmllib/pagel .asp
Esta libreria procedente de InsideDHTML.com se ha construido para varios ex-
ploradores. Se basa en el modelo de objetos de MSIE, por lo que podrA escribir co-
digo para N N y MSIE (es decir, para ambos modelos de objetos).
FreeDOM:
h t t p : / / w w w . builder.com/Programm ing/FreeDOM/
Esta biblioteca de C I Net permite crear y manipular objetos JavaScript fuera
del contexto de 10s DOM.
The JavaScript Menu Component:
http://developer. netscape.com/viewsource/smith-menu/smith-menu.html
Bibliotecas para Netscape (menu.js)que permiten agregar menus flotantes con
varios submenus. iHe dicho que se pueden usar con varios exploradores? Echele
u n vistazo.
JavaScript DHTML Collapsible Lists:
h ttp://developer. netscape. com/docs/technote/dynhtml/collapse/index.html#
xbrowser
Este codigo permite organizar estructuras similares a la del arbol de directo-
rios del Explorador de archivos de Windows, con las que organizar y enlazar el
contenido de la Web. Incluso encontrara un generador de codigo que le permiti-
ra crear DHTML.
Web Resources 505

ScriptBuilder.com:
http://www.scriptbuilder.com/netobjects/library. nsf/By +language
Este almacen de script de Netobjects esta lleno de codigo listo para pegar en
cualquier aplicacion. No todo se encuentra en archivos .js, per0 esto se puede
cambiar con un editor de texto.

Cookies
Los dos vinculos siguientes conducen a un conjunto de aplicaciones basadas en
cookies. Las hay desde muy sencillas hasta bastante complicadas, material su-
ficiente como para estar entretenido durante un buen rato.
Cookie Demos:
h t t p : / / w w w . cookiecentral.com/demomain. h t m
Esta pagina contiene una lista de enlace con demostraciones de cookies per-
tenecientes a Cookie Central. Aplicaciones especializadas en configurar las pre-
ferencias de 10s usuarios, contabilizar el numero de visitantes o bien detectar el
explorador del usuario.
Shopping Cart Using Cookies:
h ttp://www. ozemail.com.au/-dcrombie/cartdemo/index. html
Aqui aprenderemos a aplicar las cookies a 10s carros de la compra. Se puede
combinar con la aplicacion vista en el capitulo 8.

Carros de la compra
Los carros de la compra escritos en JavaScript no son ninguna novedad. A con-
tinuacion tiene una lista con varios de ellos. Todos, a excepcion de 10s dos pri-
meros, son gratuitos.
Shop@ssitant:
ht tp://www.jZoyd.co.uk/
Esta aplicacion comercial dispone de una serie de propiedades y ventajas para
10s compradores virtuales. La ha desarrollado un grupo de "gurus"de JavaScript,
per0 no se asuste.
Shopmaster :
h ttp://www. shopmaster.net/shopmaster/shop. h t m
Las versiones de demostracion de 10s carros de la compra que encontrara en es-
tas paginas Web son muy robustas. Cuentan con un conjunto de imagenes en
miniatura con las que se puede visualizar su aspecto, informacion, etc.
506 Web Resources

A Simple Shopping Cart Program:


http://lymdenlodge.hypermart.net/ShoppingCart.htm
Muy sencillo, per0 puede llegar a ser todo lo que se necesite. En una misma pa-
gina encontrara informacion sobre seleccion de producto, calculo de costes y
pagos.
JShop JavaScript Shopping Cart:
http://javaboutique.internet.com/JShop/
Esta aplicacion procedente de JavaBoutique representa una buena base para
un carro de la compra. Tiene muchas posibilidades, per0 hay que trabajar bas-
tante el codigo para sacarle algo de provecho.
Wildman's Shopping Cart:
http://www.serve.com/hotsyte/wildman/shopping_cart/shop_cart._intro.html
El creador Timothy Hobbs ha publicado su aplicacion en HotSyte. Utiliza cookies
para guardar las selecciones del usuario.
Shopping Cart Using Cookies:
http://www.ozemail.com.au/-dcrombie/cartdemo/index. html
Un buen punto para ver cdmo se pueden aplicar las cookies a 10s carros de la
compra. Luego puede modificar el c6digo y aiiadirlo a la aplicacion del capitulo 8.

Encriptadores
L a Web esta llena de sitios repletos de informacion sobre encriptacih. Per0 muy
pocos trabajan con JavaScript. Si encuentra alguno, aparte de 10s que mostra-
mos en esta lista, por favor, digamelo.
Ciphers by Gordon McComb:
h ttp://gmccomb.com/commerce/frame.html
Este sitio Web viene de la mano de uno de 10s primeros autores que ha escrito
algo sobre JavaScript. Merece la pena fijarse en 10s ejemplos de cifrado y protec-
cion con contrasefia que ha desarrollado utilizando JavaScript.
RSA Algorithm JavaScript Page:
h t tp://www.orst.edu/dept/honors/makmu r/
Esta introduccion, la cual cuenta con varias paginas, a1 mundo de la encriptacion
a RSA muestra la manera en que se puede implementar un formulario (muy ba-
sico) de RSA mediante la utilizacion de JavaScript. Merece la pena que desempolve
sus conocimientos de Eactorizacion de numeros primos si tiene pensado exami-
nar este codigo.
Web Resources 507

Ciphers en JavaScript:
ht t p / / w w w .serve.com/hotsyte/ciphers/
Obviamente, se trata de mi sitio Web. Esta aplicacion cubre la encriptacion por
sustitucion y transposicion.

Concept0 "arrastrar y soltar"


Estos enlaces conducen a unos cuantos modelos de eventos para 10s explora-
dores Web y JavaScript.
DHTMLLib Demos/ Drag and Drop:
h t tp://www.insidedhtml.com/dhtmllib/demos/dragdrop.asp
Esta demo es una parte de la biblioteca SiteExperts DHTMLLib de la que ya he-
mos hablado. Incorpora codigo DHTML independiente del explorador y permite
arrastrar y soltar elementos.
Dynamic Duo-Drag and Drop Concepts:
http://www.dansteinman.com/dynduo/dragconcepts/dragconcepts. html
Dan Steinman nos muestra un tutorial en JavaScripVDHTML sobre 10s even-
tos del raton. Esta lleno de ejemplos y de codigo.
Netscape's Visual DHTML:
http://developer.netscape.com/docs/examples/dynhtml/visual/index.h t m
La verdad es que no estoy muy conforme con la tecnologia dependiente del ex-
plorador Web, pero en este caso hark una excepcion. Estamos ante un gran edi-
tor DHTML para Netscape. Esta aplicacion utiliza JavaScript 1.2 para crear una
gran interfaz grafica para el usuario. Aun cuando no tenga pensado utilizar esta
aplicacion, ejecute la demostracion para ver c6mo funciona.
Drag and Drop:
http://www.dpunkt.de/javascript/bsp/script2/dragdrop/index.html
Otra pagina (esta vez escrita en aleman) que muestra como aprovechar la pro-
piedad arrastrar y soltar.
Coolnerds Dynamic HTML Examples:
h t tp://www.mil 1iscrip.com/webau th/dhtml/dragdrop.h tm
Esta aplicacion se parece aljuguete Mr Potato. Con ella se puede construir una
interfaz completamente personalizada arrastrando y soltando 10s elementos que
se desean (tales como el pelo, ojos, boca, nariz, etc., del juguete). En realidad se
trata de un ejemplo en VBScript, pero que nos permite ver la forma en que fun-
ciona DOM.
508 Web Resources

Ayuda contextual
Aun no he encontrado ninguna aplicacion de ayuda online en la Web como la
descrita en el capitulo 1 1 . En cualquier caso, en 10s siguientes vinculos encon-
trara buen material. Cada uno de ellos utiliza su propio codigo para generar efec-
tos diferentes pero a la vez muy parecidos.
The Microsoft Home Page:
h t t p : / / w w w .microsoft.com/ms. htm
Aparte de la pagina principal, 10s desarrolladores de Microsoft han incluido lis-
tas expandibles dentro de nav. Cada una de estas listas contiene una serie de
vinculos dirigidos a otras paginas.
The JavaScript Menu Component:
http://developer.netscape.com/viewsource/smith-menu/smith-menu.html
Este articulo muestra el proceso de creacion de menus DHTML independientes
del explorador similares a 10s que hay en la pagina Web de Microsoft. Su autor,
Gary Smith, trabaja con u n sistema orientado a objetos muy interesante.
The Menu Toolkit:
h t t p : / / w w w .insidedh tml.com/constsets/menus/menubar.asp
Esta version de demostracion procede de uno de 10s multiples conjuntos de he-
rramientas DHTML que se han publicado en la Web. Revise todo el codigo.
Apendice C
Scripts en Per1

La primera seccion de este apCndice contiene generalidades sobre Perl y men-


ciones a algunas de sus ventajas. La segunda seccion indica donde se puede con-
seguir codigo en Perl, presentando varias opciones segun el sistema operativo
con que trabaje. Las dos ultimas explican el funcionamiento de 10s script en Per1
que se han visto en 10s capitulos 8 y 10.

PerI/CG I
Per1 es u n acronimo de Practical Extraction and Report Language. Original-
mente se diseiio para manipular archivos y cadenas de texto, per0 en breve tam-
b i h se empleo para administrar las tareas del sistema y para crear contenidos
para la Web de forma dinarnica. Las raices de este lenguaje de programacion las
encontramos en C, sed, awk y sh.

iQu6 tiene Per1 que es tan bueno?


Per1 es tan popular por muchas razones. En lo que se refiere a sus propiedades
de lenguaje de programacion, es muy sencillo de aprender. Es muy potente. Se
puede utilizar en cualquier escenario de programacion, como por ejemplo:
En la creacion de contenidos dinamicos para paginas Web.
En un script en CGI para u n sin fin de aplicaciones Web.
Para acceder a bases de datos.
Para construir motores de busqueda y robots para la Web.
Para casos de protection por contraseiia o encriptacion.
Apendice C
Scripts en Per1

L a primera seccion de este apCndice contiene generalidades sobre Perl y men-


ciones a algunas de sus ventajas. La segunda seccion indica ddnde se puede con-
seguir c6digo en Perl, presentando varias opciones segun el sistema operativo
con que trabaje. Las dos ultimas explican el funcionamiento de 10s script en Perl
que se han visto en 10s capitulos 8 y 10.

Perl/CG I
Perl es un a c r h i m o de Practical Extraction and Report Language. Original-
mente se disefio para manipuIar archivos y cadenas de texto, per0 en breve tam-
b i h se empleo para administrar las tareas del sistema y para crear contenidos
para la Web de forma dinamica. Las raices de este lenguaje de programacion las
encontramos en C, sed, awk y sh.

iQue tiene Per1 que es tan bueno?


Perl es tan popular por muchas razones. En lo que se refiere a sus propiedades
de lenguaje de programacion, es muy sencillo de aprender. Es muy potente. Se
puede utilizar en cualquier escenario de programacion, como por ejemplo:
En la creaci6n de contenidos dinamicos para paginas Web.
En un script en CGI para un sin fin de aplicaciones Web.
Para acceder a bases de datos.
Para construir motores de busqueda y robots para la Web.
Para casos de protecci6n por contraseiia o encriptaci6n.
510 Scripts en Per1

Para la administracion de sistemas, registros de sitios Web y programacion


de tareas.
Para tareas en red y otras secuencias de comandos.
Para servidores de charla y boletines de mensajes.

Per0 Perl tambien se esta utilizando en otras areas, como por ejemplo:
Para ampliar Java, C, VisualBasic, Delphi y otros codigos.
En las aplicaciones XML (Extensible Markup Language).
Como PerlScript, u n motor de secuenciacion de comandos en ActiveX.

Perl es gratuito. Lo puede conseguir en la Web de CPAN (Comprehensive Perl


Archive Network), en http://www.perZ.com/CPAN/. Los usuarios de Windows tam-
bien pueden obtenerlo del sitio Web de Activestate, http://www.artivestate.com/.
Perl tiene muchos seguidores. Los programadores no paran de crear nuevos mo-
dulos y aplicaciones listos para implementar en cualquier pagina Web. La gran
mayoria de este material es gratuito. Otra de las ventajas de la popularidad de
Perl es que se pueden encontrar toneladas de documentacion, programadores con
gran experiencia y soporte en cualquier punto del planeta.
Perl se puede ejecutar en un gran numero de plataformas, incluyendo Unix,
VMS, MS-DOS, Windows NT/98/95, OS/2 y muchas mas. La mayoria del co-
dig0 es independiente de la plataforma, es decir, que se puede ejecutar en cual-
quier sistema operativo.

Desventajas de Per1
L a gran desventaja de Perl es su efectividad. La ejecucion del codigo en Perl es
mucho mas lenta que la escrita con otros lenguajes de programacion como C.
E n el entorno de la Web, 10s script CGI que se escriben en Perl (y en otros lengua-
jes) se han de leer directamente del disco duro y cargar como un proceso nuevo
cada vez que se le llama. Las tecnologias como Active Server Pages y 10s servlet
en Java se pueden ejecutar en el mismo espacio de memoria que el servidor Web,
con lo que se acelerara notablemente la velocidad de ejecucion. En cualquier
caso, las ultimas versiones de Perl que se han desarrollado, como Perl para ISAPI
o Perl-Ex, han aumentado notablemente la efectividad.
Otro de 10s inconvenientes es que a Perl no se le considera u n lenguaje de pro-
gramacion elegante. Perl sacrifica la belleza de la programacion por la practica.
Funciona, aunque puede resultar bastante feo.
Per1 y CCI
Si solicita un archivo con la extension .htmla traves de un explorador Web, ob-
tendra u n documento estatico. Es decir, el fichero existe dentro de un directorio
Scripts e n Per1 51 1

de un ordenador. Se puede sentar junto a1 ordenador donde se encuentra el archi-


vo, abrirlo con un editor de textos y ver 10s mismos datos que se estan enviando
a1 explorador.
Si solicita un archivo .cgi ( o.pl, .plx o cualquier otra extension), no se encontra-
ra con el codigo escrito dentro de dicho archivo. El servidor estara ejecutando el
codigo a travCs de la maquina virtual que se le haya indicado (en nuestro caso,
Perl) y enviando a continuacion a1 servidor el resultado de dicha ejecucion. L o
que aparece en el explorador no es exactamente lo que se encuentra guardado
dentro del explorador Web. El contenido se ha creado en el momento en que se
ha solicitado.
Todo el proceso de pedir y recibir la salida de archivos tiene lugar a travCs de la
interfaz CGI. CGI es el estandar que se utiliza para interactuar con 10s servido-
res HTTP Por cierto, el lenguaje que se encuentra en el lado del servidor no tiene
por quC ser Perl. Hay muchos script CGI escritos en C y C + + , Python, Fortran,
Applescript y otros. Si desea conocer mas detalles, consulte la pagina de pregun-
tas y respuestas (FA0 sobre la programacion en Perl que se encuentra en http://
www.cpan.org/doc/FAQs/cgi/perl-cgi-faq.h tml.

iPor que utilizar CCI?


Aunque generalmente 10s script CGI se pueden imitar con tecnologias como
ColdFusion, Active Server Pages y otras, la verdad es que el estandar CGI aun se
usa mucho en la Web. Hay miles de servidores que trabajan en Perl. Y ademas,
es gratuito. Como este material se encuentra en casi cualquier sitio, parece logi-
co escribir la parte de las aplicaciones en JavaScript que se encuentran en el lado
del servidor en algo que todo el mundo pueda comprender con facilidad. Creo
que Vera que estos script son muy sencillos.

Obtener Per1
Habra de instalar Perl en su ordenador. La mayoria de 10s servidores Web lo
tienen. Si tiene que hacerlo, podra obtener (gratuitamente) la ultima version de
Perl en ftp://ftp. rge.com/pub/languages/perl/ports/index. html.
Todo lo que tendra que hacer es clic sobre el vinculo correspondiente a su sis-
tema operativo. Si trabaja con WinNT/98/95, podra obtener la ultima version
de 10s ficheros ejecutables de Perl, y la inforrnacion correspondiente, de la direc-
cion http://www.activestate. com/pw32/. Si desea mas potencia y propiedades,
merece la pena que pruebe Active-Perl, una version Win32 de Perl, y que podra
encontrar en la direccion http://www.activestate.com/ActivePerV.
Ambos sitios Web proporcionan documentacion donde se explica como insta-
lar y configurar Perl. Per0 si trabaja con Windows, la cosa se simplifica aun mas.
512 ScriDts en Per1

Tanto si instala la version Win32 o el paquete ActivePerl, el proceso de instala-


cion unicamente comprendera unos pocos pasos a travCs de 10s cuales se confi-
gurara el servidor Web (por ejemplo, Internet Information Server de Microsoft,
Peer Web Services o Personal Web Server) para ejecutar 10s script en Perl. La ins-
talacion y configuracion de Perl para Unix y otros sistemas operativos es algo
mas complicada. Asegurese de revisar la documentacion correspondiente.
Una vez que ha instalado Perl, tendra que configurar el servidor Web para que
ejecute 10s script en Perl. Para saber si el servidor Web ya esta ejecutando Perl,
pongase en contact0 con el administrador de la Web. En caso de que estC insta-
lando usted solo el servidor Web, habra llegado el momento de configurarlo.
Utilizando 10s servidores Web mas populares, como Internet Information Server
de Microsoft o Enterprise Server de Netscape, Apache de Apache Group, y otros
muchos, el proceso de configuracion es muy sencillo. Bastara con consultar la
documentacion online del software de su servidor Web.

Script de Shopping Bag: bag.pl


En el ejemplo C-1 tenemos el c6digo fuente del script visto en el capitulo 8. Es
muy sencillo y se puede ver como funciona la aplicacibn Shopping Bag desde
que inicia su ejecucion hasta que completa el proceso. El script cumple su fina-
lidad, per0 es algo primitivo. Si quiere un programa para comprar online que
cuente con mas opciones propias del servidor, mi consejo es que busque una so-
lucion mas robusta.

Nota: No olvide que 10s usuarios suministran informaci6n muy importante


en el formulario de compra. Si no se protege correctamente esa informacion
antes de su envio, puede caer en malas manos que hagari un us0 ilegal de
ella. Para evitarlo, conviene que el servidor Web pueda trabajar con algun
tipo de proteccion, como SSL, un protocolo que se utiliza en encriptacion.
La mayor parte de 10s servidores Web pueden trabajar con 61. Es conveniente
que contacte con su administrador Web. Podrh encontrar mas informacion
acerca de SSL en la siguiente direccion:http://webopedia. internet.com/TERM/
S/SSL.html.

bag# desarrolla tres trabajos:

1 . Obtiene toda la informacion sobre 10s productos y el usuario.


2. Guarda luego esta informacion en un archivo de texto unico dentro del ser-
vidor Web.
3 . Imprime una pagina de confirmacion que se enviara de vuelta a1 explora-
dor Web del usuario.
Scripts en Per1 513

Nota: Algunos servidores Web requieren que se ejecuten 10s script CGI con
la extension .cgi en lugar de hacerlo con . p l . No hay ningdn problema en
cambiar el nombre del archivo que nos ocupa por bag.cgi.

Vamos a ver cdmo desarrolla bag# las tres acciones. Cuando mire el codigo,
recuerde que es posible que no comprenda la sintaxis. Este libro no es de Perl.
Basta con que intente comprender qui ocurre de un paso a otro.

Ejemplo C . l . bag.pl

1 #!/usr/bin/perl
2
3 require (' cgi - 1ib .p 1 " ;
4
5 print "Content-type: text /html\n\n";
6
7 &Readparse (*in);
8
9 srand($$ ,.
time) ;
10 $filename = $in{'lname') . int(rand(999));
11
12 if (-e "$filename.txt"){
13 print "The order for $filename has already been placed.";
14 exit 0 ;
15 1
16
17 open (FILE,">$filename.txt " ) ;
18 select (FILE);
19 printInfo ( ) ;
20 close (FILE);
21 select (STDOUT
22 printInfo ( 1 ;
23
24 exit 0 :
25
26 sub printInfo
27 $clock = localtime ( ) ;
28
29 print <<CUSTOMER-INFO;
30 <PRE><FONT FACE=Tahoma SIZE=3>
31 <H2>Shopping Bag Order Confirmation Receipt</H2>
32
33 <B>$clock</B>
34 cB>Reference Code: $filename</B>
35
36
37 ___--____--__---__--
38 Customer Information
39
514 Scripts en Per1

40 Customer First Name: $in{'fname')


41 Customer Last Name $in{'lname')
42 Company Name:$in{'cname')
43 Street Address l:$in{'saddressl')
44 Street Address 2:$in{'saddress2')
45 City:$in{'city')
46 State/Province:$in{'stpro')
47 Country: $in{'country'}
48 Zip/Mail Code: $in{'zip')
49
50 CUSTOMER-INFO
51
52 print <<PAYMENT-INFO;
53
54 Payment Information
55
56 Credit Card Type:$in{'ctype')
51 Credit Card Number:$in{'cnumb'}
58 Expiration Date: $in{'edate')
59
60 PAYMENT-INFO
61
62 print "Product Information\n\n";
63
64 $idx = 0;
65
66 while ($in{'prod' . $idx)) {
67 @getProdInfo = split("-",$in{'prod' . Sidx));
68 print "Product PLU:\t\t$getProdInfo[Ol\n";
69 print "\tQuantity:\t$getProdInfo[ll\n\n"~
70 $idx++;
71 1
72
73 print <<TOTAL-INFO;
74
75 Total Information (\$US)
76
77 Subtotal: $in('subtotal'l
78 Tax Total:$in{'taxtotal')
79 Ship Total: $in{'shiptotal')
80 Bag Total:$in{'bagtotal')
81
82
83
84
85

Obtener la inforrnacion del product0


El primer paso es algo complejo. Pero para 10s programadores, este c6digo su-
pone un aliento de aire fresco. Empecemos por el contenido de las lineas 1-7:
Scripts e n Per1 515

#!/usr/bin/perl

require "cgi-1ib.pl";

print "Content-type: text/html\n\n";

&ReadParse(*in);

La primera linea es comun a todos 10s script CGI. Se encarga de indicar a1 ex-
plorador Web d6nde puede encontrar Perl. La ruta varia de una maquina a otra,
por lo que tendra que consultar con su administrador Web. Si trabaja con un
sistema Windows, podrh ignorarlo. La siguiente linea indica a Perl que ha de
incluir el c6digo que se encuentra en la biblioteca llamada cgi-Zib.pZ (nombre
estandar que se incluye con la mayoria de las instalaciones en Perl). Contiene el
codigo que se encargara de leer la informacion que se envia a travCs del formu-
lario HTML. Todo lo que hay que hacer es llamar a la subrutina adecuada de
Perl y el script leer&la informacion del formulario que se encuentra en las varia-
bles de la aplicacion.
La siguiente linea imprime una cabecera HTTP Dicha cabecera se encarga de
identificar el tipo MIME (Multipart Internet Mail Extension) y de comunicarselo
a continuaci6n a1 explorador, el cualdeterminara el tip0 de informacidn con la
que se va a encontrar. Sera text/htmZ. Entre otros tipos MIME tenemos image/gif
y text/plain.
JavaScript utiliza funciones y mktodos; Perl utiliza funciones, mCtodos y sub-
rutinas. La subrutina ReadParse ( ) de cgi-Zib.pZ lee 10s datos que ha introduci-
do el usuario en el formulario HTML y 10s coloca en un array asociativo llamado
%in.Los datos del formulario 10s veremos en breve. De momento basta con que
sepamos que 10s tenemos. Vamos a seguir avanzando.

Guardar la inforrnacion en un archivo del servidor Web


Ya tenemos 10s datos del formulario. Vamos a crear un archivo donde guarda-
remos dicha informacih. El c6digo de las lineas 9-1 5 crea un nombre de archi-
vo unico:

srand($$ time);
A

$filename = $in{'lname') .
int(rand(999));
if (-e "$filename.txt") {
print "The order for $filename has already been placed.";
exit 0;
1

El c6digo srand ( $ $ , time) ; inicia el generador de numeros aleatorios de


Perl. A continuacih, Perl crea una variable llamada $flZename donde guarda el
516 Scripts e n Perl

nombre del archivo unico. Para crear dicho nombre une el apellido del compra-
dor con u n numero aleatorio comprendido entre 0-999:

$filename = $in{'lname') . int(rand(999));

El apellido del usuario se ha obtenido de 10s datos del formulario. Como 10s da-
tos se han guardado en el array asociativo %in, todo lo que tendremos que hacer
sera acceder a1 elemento que contenga el apellido. A continuation podemos ver
la sintaxis:

$in{'lname'l

Si hace algo de memoria, recordara que Zname era uno de 10s campos que se
enviaban del formulario HTML. En Perl, lo mismo que sucede en JavaScript, las
referencias a 10s arrays asociativos se establecen a partir de su nombre. Por eso
$in{ ' lname ' 1 apunta a la informacion que ha introducido el usuario como
apellido (por ejemplo, "Garcia").
L a funcion de Perl rand ( ) genera aleatoriamente un numero con coma flotan-
te comprendido entre 0 y el numero que se le entrega, que es 999. Es decir, la fun-
cion de Perl i n t ( ) devuelve a1 numero entero el valor que rand ( ) le entregue.
Asi, $filename puede tener valores como Garcia23, Garcia997, Garcia102, etc.
Una vez que se ha determinado el nombre del archivo, Perl comprueba si dicho
archivo ya existe. Con este sistema se evita anular las ordenes que un compra-
dor haya hecho en el transcurso del dia. Por ejemplo, Garcia es un apellido muy
corriente. Si Javier Garcia y Ramon Garcia efectuan compras el mismo dia y por
alguna extraiia coincidencia, el generador de numeros aleatorios les asigna el
mismo numero entero, sus archivos serian exactamente iguales. El que compra-
se en segundo lugar borraria la informacion del primero:

i f (-e "$filename.txt") {
print "The order for $filename has already been placed.";
exit 0;
1

Si el archivo ya existe, Perl mostrara u n mensaje a1 usuario donde se le comu-


nicara que ya existe u n pedido con ese numero y saldra del script. El comprador
tendra que volver a cargar el script para generar otro archivo aleatorio. Si no
existe, Perl procedera con la ejecucion de las lineas 17-20:

open(FILE, ">$filename.txt");
select (FILE);
printInfo0;
close(F1LE);
Scripts en Per1 51 7

Este codigo abre (crea) un archivo utilizando la variable $filename y aiiade la


extension. t x t . El codigo select ( F I L E ) le indica a Per1 que ha de guardar la in-
formation en el archivo que se acaba de crear hasta que se le diga lo contrario.
L o unico que queda por hacer es guardar el contenido. De eso mismo se encarga
la subrutina printInfo ( ) . Lo podemos ver en las lineas 26-85:

sub printInfo0 {
$clock = localtime ( ) ;

print <<CUSTOMER-INFO;
<PRE><FONT FACE=Tahoma SIZE=3>
<H2>Shopping Bag Order Confirmation Receipt</H2>

<B>$clock</B>
<B>Reference Code: $filename</B>

Customer First Name:$input{'fname')


Customer Last Name: $input{'lname')
Company Name: $input{'cname')
Street Address 1: $input{'saddressl'}
Street Address 2: $input{'saddress2')
City: $input{'city'l
State/Province: $input{'stpro')
country:$input{'country')
Zip/Mail Code:$input{'zip')

CUSTOMER-INFO

print <<PAYMENT-INFO;

Payment Information

Credit Card Type: $input{'ctype'}


Credit Card Number: $input{'cnumb')
Expiration Date:$input{'edate')

PAYMENT-INFO

print "Product Information\n\n";

$idx = 0 ;

while ($input{'prod' . $idx)) {


@getProdInfo = split("-",$input{'prod' . Sidx));
print "Product PLU:\t\f$getProdInfo[Ol\n";
print "\tQuantity:\t$getProdInfo [ 11 \n\n";
518 Scrit>tsen Per1

$ idx++:
1

print <<TOTAL-INFO;

Total Information (\$US)

Subtotal: Sinput('subtota1')
Tax Total: SinputI'taxtotal')
Ship Total: Sinput('shiptota1')
Bag Total: SinputI'bagtotal')

tomo su propio nombre implica, printInfo ( ) imprime 10s datos del formu-
lario. Lo primer0 que hace es crear un sello basado en la fecha y en la hora, para
lo que usa la variable $clock, asignandola a la salida de la funcibn local t i m e ( ) .
Lo utilizaremos en un momento. En la lfnea 29 empieza la impresi6n. El c6digo
<<CUSTOMER-INFO identifica una cadena, que serA en realidad una serie de ca-
denas que se encuentran entre dos identificadores. CUSTOMER-INFO es el iden-
tificador. La utilizacion de las cadenas resulta muy util puesto que no hay que
preocuparse de 10s retornos de carro o de las comillas dobles. Estos caracteres se
utilizan de la misma forma que cualquier otro. De esta forma, Per1 siempre apun-
tar&a la informacion que se encuentre entre CUSTOMER-INFO y CUSTOMER-INFO,
que ser&:

<PRE><FONT FACE=Tahoma SIZE=3>


<HZ>Shopping Bag Order Confirmation Receipt</H2>

<B>$clock</B>
<B>Reference Code: $filename</B>

------_-_--_-_--_---
Customer Information

Customer First Name:$input('fname')


Customer Last Name: $input('lname')
Company Name: $input('cname')
Street Address 1: $input{'saddressl')
Street Address 2: $input('saddress2'}
City: $input('city']
State/Province: $input('stpro')
Country:$input{'country')
Zip/Mail Code:$input{'zip')
Scripts e n Per1 519

ObsCrvese que no tenemos que unir varias cadenas que se extienden por varias
lineas. Basta con escribir. TambiCn conviene destacar las variables que se inter-
pretan. En otras palabras, $input { ' lname ' } no imprime $input { ' lname ' } ;
imprimira algo como "Garcia".
Si estudia el c6digo con cuidado, comprobara que lo primero que se imprime
son 10s datos postales correspondientes a1 comprador. Se encuentran en la in-
formaci6n de pago. Procede de ciertos campos del formulario, comofnarne, Znarne,
andcity. El usuario se habrii encargado de rellenarlos, por eso sabemos que esta-
ran alli.
iQuC sucede con la informacion del producto? El numero de productos puede
ser cualquiera. iC6mo sabra Perl la cantidad de productos que ha seleccionado
el usuario? Realmente no lo sabra. A continuacibn, las lineas 62-71 explican la
raz6n:

print "Product Information\n\n";

$idx = 0;

while ($in{'prod' . $idxl) {


@getProdInfo = split("-", $in{'prod' . Sidx]);
print "Product PLU:\t\t$getProdInfo[Ol\n";
print "\tQuantity:\t$getProdInfo[l]\n\n' ;
$ idx++ ;
I

No olvidemos que Shopping Bag generaba un campo oculto para el formulario


por cada producto que encargase el comprador. El nombre de cada uno de estos
campos se obtenia a partir del convenio "prod" + un entero (por ejemplo, prod0,
prodl, prod2, etc.). Perl se limita a declarar una variable $idx y le asigna el valor
0. Por medio del bucle de la linea 66, Perl utiliza $in{ 'prod' . $idxl con ob-
jet0 de ver si prod0 existe. En caso afirmativo, la variable tendra la informaci6n
del producto.
A continuacibn, Perl utilizara su funcibn split ( ) con objeto de crear un array
llamado @getProdlnfo con dos elementos. El primero contendra el numero PLU
del producto; el segundo elemento contendra la cantidad pedida. Esta informa-
ci6n se imprimira y luego se aumentara el valor correspondiente a $idx. Perl pro-
bar6 conprodl, prod2, etc. hasta encohtrar unvalor para $in{ 'prod' . Sidxl
que no exista.
Cuando se completa el proceso, Perl imprime el subtotal, las tasas y el total en
las lineas 73-84. Observese que el signo $ va precedido de una barra invertida
(\$). Como queremos imprimir la cadena $US, tendremos que indicarle a Perl
que no hay ninguna variable llamada $US:
520 Scripts en Per1

print <<TOTAL-INFO:

Total Information (\$US)

Subtotal : $input{'subtotal')
Tax Total: $input{ 'taxtotal')
Ship Total: Sinput('shiptota1')
Bag Total: $input{'bagtotal')

Mostrar informacion al comprador


Esto nos lleva a1 final de la subrutina. En las lineas 20-24 Perl continua con la
ejecucion del c6digo llamando a printInfo ( ) :

close (FILE);
select (STDOUT);
printInfo ( ) ;

exit 0:

En el momento en que el archivo tenga toda la informacihn procederemos a


guardarlo. close ( F I L E ) se encarga de ello. iQue hay del comprador? Estaria
bien comunicarle que se ha recibido el pedido. iPor quk no se remite la misma in-
formation guardada en el archivo a1 usuario? La llamada a select ( S T D O U T )
indica a Perl que vuelva a imprimir la informacion y la remita a1 explorador
(recuerde que dijimos en la linea 1 7 que el destino donde se guardaria la infor-
maci6n era $'Zename. t x t ) . Ahora volvemos a llamar a la funcion print Inf o ( ) ,
con lo que se completara la transaccion. El comprador obtendra la informacion.

Configurar el Script
Este script crea todos 10s archivos de texto de 10s pedidos de 10s productos y 10s
guarda en su mismo directorio. En otras palabras, 10s archivos de 10s pedidos se
guardaran en el mismo directorio donde se encuentre bag.pZ. No se puede utili-
zar ninguna estructura de directorios adicional. Cualquier carpeta con permi-
sos de ejecucion (necesarios para que el servidor pueda ejecutar el script) y de
escritura (para que el script pueda crear y escribir 10s archivos) sera valida.
Si desconoce el concept0 de permisos, sepa que controlan el acceso que tendran
10s usuarios a 10s distintos directorios y archivos. Por ejemplo, para recuperar 10s
archivos HTML, el directorio que 10s contenga necesitara contar con permisos
de lectura para acceder a su contenido. Para ejecutar 10s script (CGI o cualquier
ScriDts en Per1 521

otro), la carpeta necesitara contar con permisos de ejecucion. Para crear y mo-
dificar 10s archivos de dicho directorio, tambien necesitara permisos de escritu-
ra. El directorio que contiene bag.pZ necesitara permisos de escritura y ejecucion.
L a verdad es que parece sencillo, per0 tiene trampa.
Garantizar permisos de escritura y ejecucion a un directorio abre las puertas a
un par de riesgos relacionados con la seguridad. Si tiene u n servidor Web, sus
usuarios le pediran que guarde el script en un directorio que solamente tenga
permisos de ejecucion, como &-bin/ o Scripts/.y escriba 10s archivos en otro di-
rectorio que unicamente tenga permisos de lectura. En este caso, tendra que mo-
dificar el script para reflejar el nuevo directorio donde se guardaran 10s pedidos.
Bastara con incluir el nombre de la carpeta en la configuracion de la variable
$filename. Supongamos escribira 10s archivos en la carpeta orders/, que se en-
cuentra un nivel por encima del directorio donde se encuentra el script. Todo lo
que se ha de hacer es cambiar la linea 10:

$filename = $in{'lname ) . int rand(999));

por :

$filename = I'. . /orders '' . $in 'lname') . int(rand(999));

Si orders/ se encuentra fuera de la raiz de directorios, entonces habria que escri-


bir algo asi:

$filename = "/orders/" . $in{'lname'} . int(rand(999));

En este momento el script se encuentra situado en un directorio y 10s archivos


en otro.
Ya estamos listos para continuar. El atributo ACTION que se encuentra situado
en la linea 276 del archivo managerhtml tiene que contener el URL correct0 de
bag.pZ.

El script de CyberCreeting: greet.pl


En el Ejemplo C.2 tenemos el contenido del script que ya se ha visto en el capi-
tulo 10. Este script leera la information que envia el usuario, creara u n archivo
unico y escribira el codigo de la tarjeta. A continuacion, el script enviara a1 re-
mitente una pagina de confirmaci6n con u n formulario HTML. A partir de di-
cho formulario, el remitente enviara u n mensaje que contendra u n enlace con el
archivo greet.pl que acaba de crear. Cuando el destinatario abra el mensaje y
haga clic en el vinculo, ejecutara este archivo.
522 Scrim en Per1

Configuracion del script


A diferencia del script del capitulo 8 , para utilizar greet.pZ hay que conformar
una estructura de directorios. Independientemente de la carpeta donde se guar-
de greet.pZ, el directorio habra de contar con permisos de lectura y ejecucion.
TambiCn necesitara u n subdirectorio llamado greetings/ que tenga permisos de
escritura.
Como he mencionado anteriormente, si nunca antes se habia encontrado con
permisos, recuerde que son 10s encargados de determinar el tip0 de acceso que
tendran 10s usuarios a 10s directorios y archivos. Por ejemplo, para recuperar
10s archivos HTML, el directorio que 10s contiene necesitara contar con permi-
sos de lectura. Para ejecutar 10s script CGI (y cualquier otro), el directorio nece-
sitara permisos de ejecucion. Para crear y modificar 10s archivos que se encuentren
dentro de este directorio, sera necesario contar con permisos de escritura.
De hecho, lo primer0 que vamos a hacer es determinar la estructura de direc-
torios con la que trabajaremos. Supongarnos que vamos a guardar todos 10s
archivos de esta aplicacion dentro del directorio @-bin/ del servidor Web. El
aspect0 de la estructura de directorios sera la siguiente:

cgi -bin/
greet . p l
index.html
back. html
front.htm1
greetings/
images1

@-bin/ contiene el archivo greet.pZ y otros tres ficheros HTML de la aplicacion


del cliente. La carpeta images/ contiene todos 10s iconos e imagenes de fondo
que podra usar el usuario para crear sus tarjetas. Pero no hemos de olvidar que
el codigo que se generara en el lado del cliente se dirigira a este directorio cuan-
do quiera cargar 10s graficos de las tarjetas.
Inicialrnente, el directorio greetings/ estara vacio. En el se guardaran 10s archi-
vos unicos que contienen la inforrnacion de cada tarjeta. Este directorio ha de
contar con permisos de escritura. Una vez que tenemos Clara la estructura de di-
rectories que se utilizara y 10s archivos en sus ubicaciones correspondientes,
podremos continuar con el script. El atributo ACTION de la linea 186 del archivo
front.htmZ ha de contener el URL del archivo greet.pl.
Puede ser que no le agrade la estructura de directorios que acabamos de ver.
Posiblemente desee guardar 10s archivos HTML en otro directorio que no sea
cgi-bin. Sin problema. Bastara con que se asegure que el script apunta a la di-
reccion correcta.
Scrbts en Per1 523

Nota: Algunos servidores Web requieren que se ejecuten 10s script CGI con
la extensibn .cgien vez de hacerlo con .pZ. No hay ningfin problema en cam-
biar el nombre del archivo.

Vamos a revisar las tres acciones que desarrolla el script:


1. Obtener primer0 la tarjeta personalizada del formulario que ha enviado el
usuario.
2. Guardar la informacion en un archivo de texto unico que se encontrara en
el servidor Web.
3. Imprimir una pagina de confirmation y remitirla a1 explorador Web, junto
con un formulario de correo.
No es necesario que comprenda la sintaxis del c6digo que aparece a continua-
ci6n. Recuerde que este libro no est6 dedicado a Perl. BastarA con que compren-
da lo que hace el c6digo de un paso a otro.
Ejemplo C.2. greet.pl
1 #!/usr/bin/perl

3 require 'cgi-1ib.pl';
4
5 &ReadParse(*in);
6 $msg = $in('EntireMessage');
7 $fileID= $in('UniqueID'l;
8 $recip = $in(*Recipient');
9 $baseURL = $in{'BaseURL');
10
11 open(FILE, ">greetings/greet$fileID.html") 1 1 die "No can do:
$!";
12 select(F1LE);
13 print <<GREETING;
14 <HTML>
15 <HEAD>
16 <TITLE>Your Personal Cyber Greeting</TITLE>
11 < /HEAD>
18 <BODY>
19 Smsg
20 </BODY>
21 </HTML>
22 GREETING
23 close (FILE);
24 select(STD0UT);
25
26 print "Content-type: text/htrnl\n\n";
27
28 print <<RESPONSE;
524 Scripts en Per1

29
30 <HTML>
31 <HEAD>
32 <TITLE>Cyber Greeting Response</TITLEz
33 < /HEAD>
34 <BODY>
35 <TABLE WIDTH= 500" >
36 <TR>
31 <TD>
38 <H2>Congratulations!</H2>
39 You have successsfully created a Cyber Greeting for
40 <B>$recip</B>.All you have to do is send him or her an
41 e-mail to announce the greeting. Just push the button below,
42 and the e-mail will be on the way.
43 <CENTER>
44 <FORM NAME= SendEmai1 ENCTYPE=" text /p1ain
I' 'I I'

45 ACTION="mailto:$recip?Subject=You Have A Cyber Greeting!">


46 <INPUT TYPE=HIDDEN NAME="Message"
41 VALUE="You have a Cyber Greeting. You can pick it up at
48 $baseURLgreet$fileID.html">
49 <INPUT TYPE=SUBMIT VALUE="Send CyberGreeting">
50 </FORM>
51 </CENTER>
52 <BR>
53 You might experience a delay while your e-mail software
54 contacts your mail server.
55 <BR><BR>
56 <A HREF="index.html">ReturnTo Cyber Greetingc/A>
51 </TD>
58 </TR>
59 < /TABLE>
60 </BODY>
61 < /HTML>
62
63 RESPONSE
64
65 exit 0:

Obtener la inforrnacion de la tarjeta


Lo primer0 que tenemos que hacer es recoger la informacidn que nos ha envia-
do el usuario a travks del formulario. Empezamos con las lineas 1-5, que son casi
identicas a las del script en Per1 que vimos anteriormente:

#!/usr/bin/perl

require 'cgi-1ib.pl';

&ReadParse(*in);
Scrbts en Per1 525

Como ya vimos anteriormente, la primera linea se limita a decirle a1 servidor


donde podra encontrar Perl. La linea 3 instruye a Perl para que incluya el c6digo
de una biblioteca llamada cgi-1ib.pl.Contiene la subrutina en Perl ReadParse ( )
que leer6 la informaci6n que se remite en el formulario HTML.
ReadParse ( ) lee 10s datos del formulario y 10s guarda en un array asociado
llamado %in. A continuaci6n crea un elemento array con el nombre de cada uno
de 10s elementos que se han enviado. A cada elemento se le asigna un valor que
se corresponde con el que tiene el elemento enviado. Si miramos el contenido del
formulario HTML del archivo front.htrnZ, veremos que %in tiene 10s siguientes
elementos:
$in( ' EntireMessage' I-. Mensaje con formato escrito por el usuario.
$in{ ' UniqueID' I -. Numero aleatorio que se ha utilizado para crear el
archivo unico.
$in{ BaseURL ' I -. Ruta del directorio base.
I

$in { ' Recipient ' 1 -. Direccibn de correo electr6nico correspondiente a1


destinatario.
$in( ' Message ' I -. Mensaje original (sin formato) que introdujo el usua-
rio. No lo necesitamos puesto que ya tenemos la version formateada en
$in{'EntireMessage'I.
$in{ 'Greetings' I -. Contiene el nombre del destinatario de la tarjeta.
Este nombre se ha obtenido de la informaci6n contenida enfront.htrn2.
Para facilitar las referencias a estos elementos, el script asigna sus valores a va-
riables con nombres muy cortos. Unicamente necesitaremos cuatro, tal y como
se puede ver en las lineas 6-9:

$msg = $in{'EntireMessage'l;
$fileID= $in{'UniqueID'l;
$recip = $in{'Recipient');
$baseURL = $in{'BaseURL');

Guardar la tarjeta en un archivo unico


Ya tenemos toda la informacibn necesaria para imprimir la tarjeta en un archi-
vo. Todo lo que tenemos que hacer es crear un archivo de texto donde guardar
el codigo HTML apropiado. Veamos el contenido de las lineas 11-22:

open(FILE, ">greet$fileID.html") 1 1 die "No can do: $ ! " ;


select (FILE);
print <<GREETING;
<HTML>
<HEAD>
<TITLE>Your Personal Cyber Greeting</TITLE>
526 Scripts en Perl

Estas pocas lineas de codigo se encargan de crear un nuevo archivo utilizando


el numero aleatorio en JavaScript que se ha creado en el explorador y que se
utiliza como parte del nombre del archivo. Si el numero fuese 25000, el nombre
del archivo seria greet25000.htrnZ y se encontraria en el directorio greetings/.
Despues de crear u n archivo unico, Perl escribe algo de c6digo HTML dentro de
el, incluyendo el codigo DHTML procedente del campo EntireMessage que se en-
cuentra en el archivofront.htrnl, y que ahora esta en la variable $rnsg.
El codigo <<GREETING identifica una cadena, que no es otra cosa que una serie
de cadenas que se encuentran situadas entre dos identificadores. El identificador
es GREETING. El us0 de las cadenas resulta muy util porque no hay que preocu-
parse de 10s retornos de carro o de las comillas dobles. Estos caracteres se usan
igual que cualquier otro. De esta forma, Perl siempre apuntara a la information
que se encuentre entre GREETING y GREETING. Bastar5 con escribir y punto.
Tambien se interpretan las variables. Es decir, $msg no imprime "$msg", sino la
tarjeta con formato.
Y esto es todo. El script acaba su ejecucion en la linea 23, y estara listo para que
el destinatario acceda a el a traves del vinculo del mensaje de correo electrhico.

lmprimir una pagina de confirmacion


Lo unico que queda por hacer es enviar la pagina de confirmacion a1 remiten-
te. Esta pagina contendra un formulario HTML que enviara a1 destinatario un
aviso indicandole que tiene una tarjeta y el URL donde la podra encontrar. El
c6digo de las lineas 26-63 se encarga de ello.
La linea 2 6 , print "Content-type: text/html\n\n",imprime una cabe-
cera HTTP Se encarga de indicar el tipo MIME a1 explorador, para que este sepa
el tip0 de information que ha de esperar.
Como se puede ver en las lineas 28 y 6 3 , RESPONSE identifica otra cadena. Las
variables aparecen en negrita:

print "Content-type: text/html\n\n";

print <<RESPONSE;

<HTML>
<HEAD>
Scripts en Per1 527

<TITLE>Cyber Greeting Response</TITLE>


</HEAD>
<BODY>
<TABLE WIDTH= 500 >
<TR>
<TD>
<H2>Congratulations!</H2>
You have successsfully created a Cyber Greeting for <B>$recip</B>
All you have to do is send him or her an e-mail to announce the
greeting. Just push the button below, and the e-mail will be on
the way.
<CENTER>
<FORM NAME= SendEmai1 ENCTYPE= text / p 1ain
'I I'

ACTION="mailto:$recip?Subject=You Have A Cyber Greeting!">


<INPUT TYPE=HIDDEN NAME="Message"
VALUE="You have a Cyber Greeting. You can pick it up at
$baseURL/greet$fileID.html">
<INPUT TYPE=SUBMIT VALUE="Send CyberGreeting">
< /FORM>
</CENTER>
<BR>
You might experience a delay while your e-mail software
contacts your mail server.
<BR><BR>
<A HREF="index.html">Return To Cyber Greeting</A>
</TD>
</TR>
</TABLE>
< / BODY>
< /HTML>

RESPONSE

La mayor parte del codigo es HTML y se encargara de confirmar a1 remitente el


envio de la tarjeta y de comentarle las instrucciones que tiene que seguir con
objeto de enviar el mensaje de correo. Vamos a ver ahora el contenido correspon-
diente a las lineas 44-50. Es el responsable de enviar un mensaje de correo elec-
trdnico a1 destinatario:

<FORM NAME=" SendEmai1 " ENCTY PE=" text / p 1ain "


ACTION="mailto:$recip?Subject=You Have A Cyber Greeting!">
<INPUT TYPE=HIDDEN NAME= "Message"
VALUE="You have a Cyber Greeting. You can pick it up at
$baseURL/greet$fileID.html">
<INPUT TYPE=SUBMIT VALUE= Send CyberGreeting">
< /FORM>

El script generara un formulario con el protocolo m a i l t o : dentro del atributo


ACTION y el valor text/plain en el atributo ENCTYPE. Este formulario tiene
528 Scripts en Per1

un campo oculto denominado Message el cual contiene el aviso de la tarjeta y el


URL donde se encuentra. Este URL se crea a partir de las variables $baseURL y
$filelo.
El formulario tambiCn cuenta con un b o t h de envio que se utilizara para en-
viar el mensaje de correo electronico a1 destinatario. Si las propiedades de correo
electronico del explorador del usuario se encuentran configuradas correctamen-
te, en el momento en que se haga clic sobre dicho boton se procedera con el envio
del mensaje.
hdice alfabetico

miembros, 282
propiedades, 485
A, 41 Anchor, 434
ABSMIDDLE, 42 AND, 35
ACTION, 152 answer(), 76
action, 359 Anuncio directo, 269
addOpt0, 269 Anuncios, 412
adImages, 67 Aplicaci6n
administer.htm1, 79 de cliente, 3 7
ADSL, 28 rendimiento, 3 7
aFrame, 85 Applet, 434
age, 264 Appliances, 293
agregate, 185 Aptiva, 23
Agrupaciones, 68 Archivo
alert(), 39, 300 de cbdigo, 31
Algoritmo, 36 fuente, 38
Caesar, 359 Area, 436
Vinegere, 360 Arial, 41
ALIGN, 41 Arquitectura orientada a objetos, 26
ALINK, 93 Arrastrar y soltar, 371
Allaire, 27 Array, 38, 436
allconfirmation, 40 de elementos, 85
allImages, 141, 243 desordenar, 8 7
allowAny(),40, 49 dimensiones, 5 1
allowArray(), 48 manipular, 8 7
allstring, 40 multidimensional, 243
Alto nivel, 25 objeto de, 87
funciones, 485 ordenacih, 66
530 indice alfabktico

arrayHandles, 141 Both


arrays.js, 194 avance, 58
Atributo retroceso, 58
falsificar, 7 7 boxStr, 237
SRC, 77 BR, 41
Atributos, 187 break, 40
Audiencia, 2 8 bRef, 395
automate(), 112, 130 browseContro1, 284
Automatic, 101 Bucle
autoPilot0, 128 anidado, 51
availHeight, 110 usual, 48
availwidth, 110 buildQuestion0, 86
avgo, 194 Busqueda
Ayuda motor de, 67
contextual, 4 13 opciones, 67
online, 31 refinar, 67
BUTTON, 42,437

B, 43
C
background, 236 C + + , 26
backgroundIdx, 395 Cabecera, 27
Bag(),295 Cadena
bagTotal: 295 de b~squeda,35
Barra de desplazamiento, 108 delimitada, 44
Barra invertida, 44 sustitucih, 354
Base de datos, 35, 65 Caesar, 344
rendimiento, 66 caesarAlgorithm(),359
basechar, 356 Callsearch(), 151
baseUrl, 3 77 Cambio de efectividad, 30
Begin, 85 camelcap(), 116, 223
BGCOLOR, 41 Campos, 188
bkgImage, 246 Cancelacion de carga, 30
BLOCKQUOTE, 298 Capas, 112
BOA5501,291 captureDefaultProfile(), 189
BODY, 41, 121 Carga
Bolsa de la compra, 2 77 de trabajo, 35
agregar productos, 314 dinhmica, 142
crear, 295 Carro de la compra, 274
Boolean, 436 Cartel publicitario, 6 7
Booleano, 35 category, 291, 295
BORDER, 41 categoryset, 294, 296
hdice alfabetico 531

ceil, 91 Conexidn, 29
ceiling, 42 Configuracibn predeterminada, 46
CELLPADDING, 41,299 confirm(), 95, 256
CENTER, 41 Constructor, 32
CGI, 26, 509 Control, 153
ChangeAction(),385 Conversidn de cadenas, 329
changeBag(),303 convertstring(),40, 47
changeslide(), 121, 126 Cookies, 26, 231
charAt, 39 cookiesjs, 198
cheapcheck(), 305 copy, 113
CHECKBOX, 67,237,438 CopyArray, 39, 41
chickenput(),95 correct, 84
Cifrado, 339 Correo electrbnico, 3 71
correo, 369 Correo encriptado, 369
funcionamiento, 341 count, 175
Cipher(), 352 crear, 422
cipherkray, 353 administracibn, 145
cipherData, 362 funciones, 113
ciphertext, 342 cReset, 300
Cisco, 28 Cuadros anidados, 75
cleanText, 353 Cuerpo del texto, 55
clearInterval(), 129 curCLoc, 299
closeUpShop(),282 curGreet, 395
Cbdigo curPLoc, 299
archivo de, 3 1 currentMatch, 39
comtin, 30 currentRecord, 41
fuente, 44 currPfres, 249
generacibn, 44 curslide, 108, 127
ocultar, 31 Cyber Greetings, 3 71
piratear, 344
referencia a, 32
reutilizar, 3 1 data, 359
cOffset, 299 Date, 439
ColgFusion, 2 7 Dato formateado, 44
COLSPAN, 299 DD, 42
Combinaciones, 86 dDidLyr, 108
Compaq, 23 Declarar
compareElement, 40 funcibn, 32
Compatibilidad, 65 variable, 32
Compilacibn, 26 decPlace0, 303
Concatenar cadena, 44 Degradacibn, 30
532 hdice alfabetico

Deletecookie(),199 EMCA-262,27
Dell, 23 enableEffects0,204
delOpt(), 269 Encuesta, 99
description, 290 engHgt, 141
dHgtLyr, 108 enginelinkso, 145
DHTML, 25, 101,266 engWdh, 141
capas, 108 Enlace, 36
dhtml.js, 202 Entero, 86
diaply(), 213 EntireMessage, 41 0
Diapositivas, 101 entry, 39
aleatorias, 131 Error, 26
animar, 131 de sintaxis, 127
elementos, 122 Esc, 44
Dimensiones, 5 1 escape(),152
Direccibn IF: 111 Esquema
display(), 308 de aplicacibn, 73
DIV, 114 deflujo, 74
dive.html,234,258 Estado del cliente, 296
divide, 41 Estrategia
docObj, 39 de inversibn, 243
Document, 443 de programacibn, 2 7
document Estratificacibn, 26
bgcolor, 76 eval(), 125
close(), 77 EVEN, 68
fgcolor, 76 Event, 445
open0, 77 event .js
write(), 60 Examen online, 69
writelno, 60 explain(), 94
Documento Exploradores cruzados, 30
administracibn, 284
cargar, 389 F
descripcibn, 43
Imagen, 27 FACE, 41
localizador de, 36 face, 236
titulo, 43 Factura, 328
Dow Jones, 236 false, 40
DT, 42 Familia de fuentes, 243
Fase de compilacibn, 26
E Fileupload, 44 7
findings, 40
Editor de textos, 31 Flash, 25
else, 39 Flexibilidad, 85
indice alfabktico ~~
533

focus(),39 Getcookie(), 199, 261


FONT, 41 getCookieVal(),199
fontName, 25 1 getPrefs(),247, 262
footer, 299 getSelect0, 174
for, 48 Gif, 67
Form, 448 gimmeContro1, 284
formatResults(), 41, 55 gimmeOne0, 300
formObj, 87, 253 Global
forms, 39 elemento, 45
Formulario, 28 variable, 32
borrar, 256 grabXY(), 382
cambiar, 256 gradeTest0, 91, 95, 97
Formulario de entrada, 372 Guide, 101
Frame, 449
frame, 39 H
frames.js, 2 10
frames[], 140 handle, 141
FRAMESET, 284 HEAD, 32
freshstarto, 298 help(), 305
front.htm1, 389 helpName, 4 19
Fuentes helpone, 423
famiha, 243 helpwin, 419
tamafio, 243 HIDDEN, 305,451
Funcih hideslide(), 124, 202
constructora, 77 hidestatus, 117
declarar, 32 high(), 194
Function, 449 Hipervinculo, 95
function, 39 History, 451
Hoja de estilos, 108, 266
G Host, 111
HotBot, 144
genBoxes(),141, 267 howMany, 42, 84
generateEntryForm0, 178, 189 HR, 41
genGreeting(),406 HREF, 90, 160
genJavaScript0, 181, 190 htmlArray, 261
genlayero, 113 htmlStr, 238
argumentos, 118 HTTe 26
genlayouto, 383
genlinks, 26 7
genScreen0, 116, 121
genSelect0, 267 I, 41
getAttributes0, 260 i + + , 40
534 hdice alfabCtico

IBM, 23 INPUT, 237


icon, 290 Insercidn de datos, 26
if, 39 integrate(), 194
Image, 452 Interfaz, 35
imagehdex, 123 Intranet, 28
imagelinks, 175 inventory.js, 285
ImageMachine investor, 264
imageNames, 243 isOver, 123
Imageries is%, 108, 126
pares de, 175 itemInfo(), 304
preparacibn, 249 itemReset(),85
ruta, 176
secuencia, 131
imageprefix, 123
J
imagePreload0, 112, 144, 213 Java, 453
images.js, 2 13 JavaArray, 453
imageSwap0, 120, 151,213 JavaClass, 454
IMG, 115 JavaObject, 454
img, 108 JavaPackage, 455
imgBdr, 177 javascript, 75
imgDefaults, 177 Jerarquias, 26
imgDwn, 177 js, 66
imgHgt, 177 Jscript, 26
imglink, 177 jsGrep(), 194
imgOut, 108,123
imgOver, 108, 123 K
imgPath, 108
imgPrim, 176 keeper, 84
imgRoll, 176 keepIn(),2 10
imgStr, 115 keepKeys0, 204
imgTemplate, 180 keepout(), 2 10
keyword, 361
imgtext, 177
imgWdh, 177
incontext(), 418 1
indexNames, 243 LANGUAGE, 31
indexof, 40 LAYER, 113,455
IndexOfO, 261 length, 39
indezArray, 141 Lenguaje de programacibn, 2 7
InfoSeek, 140 LIKE, 68
innerHeight, 110 LINK, 93, 458
innerwidth, 110 linkParts, 267
hdice alfabktico 535

Lista N
de seleccibn, 325
desplegable, 277 NAME, 42
Liveconnect, 69 NASDAQ, 236
Llave nav.htm1, 39, 64
publica, 342 navbaro, 214
simitrica, 342 navbar.js
Localizador, 36 Navegacih, 112
low(), 194 Navigator, 463
lyrCount, 146 Netscape, 464
Netscape Navigator, 23
new, 39
newsName, 243
Macintosh, 23 nextBackground(), 399
Macromedia, 25 nextIcons(),400
mailto, 369 nextQ, 84
makeObj0, 218 nextRound0, 8 7
makepatho, 141, 249 NN, 108
makeproducts, 2 93 noMatch(),41, 54
manager.htm1, 283, 298 Nombre
Maquina local, 29 asociado, 115
Maquina virtual, 2 7 convenio para, 115, 264
Marco NOSHADE, 41
comunicaci6n, 396 NOT, 68
definicih, 396 NOT LIKE, 154
Marketing, 269 null, 260
Math, 87, 460 Number, 464
Memoria, 35 numberFormat, 300, 302
Mensaje de correo, 27 numbers.js, 2 16
menuConstraint, 108, 117
menuManager-0, 124 0
menuStr, 117
Mktodo, 35 Object, 465
Microsoft Internet Explorer, 23 objects.js, 218
MIDDLE, 42 Objeto
mimeType, 76, 182,462 agregar propiedades, 3 16
Modularidad, 46 asignar mktodo, 352
Mostrar informacibn, 424 definido por el usuario, 32
motionListener(),389 herencia de, 363
Motor de busqueda, 35, 133 imagen, 26
msgStr, 407 implementacih, 78
multi.htm1, 136 objProfile0, 2 18
536 indice alfabktico

occupation, 264 optStr(), 141


offset, 41 OR, 35
onAbort, 486 Orden
onBlur, 487 cronol6gico, 66
onCard0, 401 de un array, 66
onchange, 246,302,487
onChangeStr, 23 7 P
onclick, 42, 487
onclick, 95 41
onDblClick, 488 Packages, 46 7
onDragDrop, 488 PBgina
oneDimension, 242 de ayuda, 36
onError, 489 de resultados, 36
onFocus, 489 Pantalla de inicio, 69
onFocus0, 301 parent, 39
onKeyDown, 490 parseFloat(), 302
onKeyPress, 490 parseInt(), 87
onKeyUp, 491 parseObj0, 218
onload, 244,491 Password, 468
ONLY, 68 pathprep(), 183
onMouseDown, 492 payInfo(),304
onMouseEvent, 119 Pedido
onMouseMove, 492 modificar, 2 76
onMouseOut, 95,493 revisar, 276
onMouseOver, 94,493 Perfil, 43
onMouseUp, 493 Perl, 244
perlyr, 141
onMove, 494
Peticih, 35
onReset, 494
plaintext, 342
onResize, 495
platform, 183
onselect, 495
plu, 290
onsubmit, 42,495
Plugin, 468
onsubmit(),410
pOffset, 299
onunload, 496 Politica de cuadros, 2 11
00, 146 populateForm0, 246
open, 41 portal(), 299
opener, 283 Porthtil, 38
Operador ternario, 63 prefArray, 238
Operando, 63 Preferencias, 23 1
Optimizar funciones, 41 1 almacenadas, 246
OPTION, 155 formas, 244
Option, 466 Prefijo, 36
hdice alfabetico 537

prefs.htm1, 235 recall(), 300


prefsArray, 248 records.js
prefStr, 247 Recursos del sistema, 35
preLoadImages(), 123 redondear, 92
prepStr(), 223 reentry, 40
Presarion, 23 ref.left, 405
Presentacion, 36 ref.top, 405
pReset, 300 reference, 4 1
prevNextResults(),42 refineAllString, 40
price, 290 refineElement, 40
PrimJavascript, 185 refslide(), 124, 202, 389
printResults0, 93, 98 RegExp, 470
Procesador, 35 Registro, 35
prodline, 291 partes, 43
product, 290 separador para, 43
Product0 sustitucion, 65
bdsqueda, 2 73,316 Registro, 328
categoria, 293 Rendimieto, 66
convertir, 3 17 reorder(), 223
crear, 293 reorganize(), 194
mostrar, 296 replace(),267
navegacion por, 3 19 requireAll0, 40, 5 1
profiles, 40 Reset, 472
Propiedad de evaluacion, 96 resetImage(),252
Pseudo-c6digo, 3 10 resetopener(), 283
purify(), 353 Resoluci6n, 23
result .sort (), 66
0 results, 84
resultset, 41
qFrame, 85, 90 return(), 39, 237
qIdx, 84 round(), 216, 302
query, 39 routecipher( ), 366
question(), 76 RSA Data Security, 342
runningTab(), 302

Radio, 468
rank, 84, 91 safecharso, 238
rankIdx, 93 scientific, 115
rawNumStr0, 302 Screen, 473
rawText, 353 screen, 110
RDSI, 28 SCRIPT, 27, 260
538 hdice alfabetico

scriptClose, 186 show(), 94


SEARCHALL, 39,45 showGreeting0, 398
SEARCHANY, 39,45 showKeys(),204
searchArray, 40 showMatches, 39
searchType, 39, 45 showName, 108
SEARCHURL, 3 9 , 4 1 , 4 5 showSlide0, 124, 202
secHTML, 185 showspeed, 108
secJavaScript, 175 showstore, 305
sel, 398 showXY(),204
seleccibn, 2 73 shrink(), 194
SELECT, 155 shuffle(), 85
Select, 474 Simbolos, 49
SELECTED, 141 Sintaxis, 31
selectedIndex, 25 1 alternativa, 364
self.focus(), 2 84 error, 127
sendText0, 369 Sistema de cifrado
Servidor de aplicaciones, 35 de llave ptiblica, 342
Servidor de llave simetrica, 342
carga, 26 definicibn, 352
de Shopping Cart, 41 1 por sustitucibn, 356
Web, 111 seleccibn, 366
setArrays(), 188 SIZE, 42
SetCoolue(),199 slide(),114
setInterval0, 129 slideshow, 118
setPrefs0, 247 sName, 113
setslide(), 119, 128 Sobrecarga del sistema, 38
setTimeout() , 30 1 Solaris, 23
shakeUp0, 87 sort(),41, 243
sHgt, 113 Span(), 384
sHgtPos, 108 SPARC, 23
shiftDiff, 358 split(), 40, 47
shiftIdx, 362 SRC, 31, 75
shipGreeting(),410 start, 175
shipRate, 295 stepup, 87
shipTota1, 295 STOP,113
Shopping Bag, 271 stopOk, 84
cbdigo, 28 1 strategy, 243
revisibn, 280 String, 475
shopset.htm1, 283 strings.js, 223
shopwin, 282 structure, 115
shoStore0, 298 Submit, 478
hdice alfabktico 539

substitUte(),356 touron, 108, 117


substitutioncipher, 363 TR, 41,301
substring(), 39, 49 true, 40
subTotal, 295 trueTop, 42
Sun, 23,479 truncate(), 194
support(), 76 twoDimension, 242
Sustitucibn bdsica, 356 twoPlaces0, 216
sVis, 113
swapcode, 185 U
swapImage(), 1 10
sWdh, 113 unescape() , 152
sWidPos, 108 UniqueID, 4 10
unit, 290
T units, 76
Unix, 23
TABLE, 41 URL, 3 5 , 4 3
tableBotton, 301 userPrefs, 247
tagInfo, 261 Usuario, 28
Tahoma, 236
TARGET, 43
taxRate, 295
V
taxTota1, 295 Validacibn, 26
TD, 41 validate(),46
Temas, 268 VALIGN, 41
agregar, 4 12 Valoracih, 70
tempArray, 87 value, 39, 252
tempunit, 86 var, 39
Ternario, 63 Variable
testGreeting, 405 de estilo, 266
TEXT, 41 declaracibn, 32
Text, 479 determinada por el explorador, 108
text/css, 236 global, 32, 44
TEXTAREA, 223,480 relacionada con ejecucibn
TH, 301 automhtica, 108
ThinkPad, 23 relacionada con una imagen, 108
this, 78 Ventana
TITLE, 39, 43 admininstracibn, 284
tolowercase, 29 1 alto, 110
TO8 41 ancho, 110
totalInfo(),304 verifyManage(), 40, 53
totals(), 2 16 Versiones, 30
touppercase, 40 Vinculo. 149
540 hdice alfab6tico

cbdigo, 321 wordcount (), 22 3


vinegereAlgorithm(),360 writeln, 41
Vingere, 345
visibility, 110, 113 X
VLINK, 93
void(),425 XML, 2 7

Y
Yahoo!, 154
WDDX, 27
while, 39
WIDTH, 41
Z
Window, 481 zIdx, 108
Soporte Tecnico de Anaya Multimedia
Si tiene algun problema relacionado con el contenido de nues-
tras publicaciones, o con el material suministrado en las mis-
mas, por favor comuniquelo a nuestro Departamento de
Soporte Tecnico a traves de alguno de estos medios:

0 TELEFONO, en el numero 91 320 90 36. El horario es de


Lunes a Viernes desde las 9 a las 15 horas. En el resto
del dia existe un contestador automatic0 en donde podra
dejar sus mensajes, que seran respondidos a la mayor
brevedad posible.
0 FAX,en el numero 91 320 44 19.
0 CORREO A NUESTRA DIRECCION, c/ Juan IgnaCiO LUCa de
Tena 15.28027 Madrid.

0 CORREO
ELECTRONICOa la direccion, a-multimedia8anaya.es.

Anaya Multimedia dispone de un servicio especial mediante el


cual, cualquier persona que disponga de una DlRECClON DE
CORREO ELECTRONIC0 puede recibir informacion detallada de
las nuevas publicaciones de nuestra editorial.
Si esta interesado en suscribirse, puede hacerlo directamente
a traves de nuestro Web:
http://www.anayamultimedia.es
Una vez aqui, dirijase a la seccion Servicios y despues, al
apartado Novedades a usuarios.
Tambien puede contactar con nosotros a traves de cualquiera
de 10s medios antes mencionados.

También podría gustarte