Herramientas de usuario

Herramientas del sitio


notas:catalis-con-objetos

Rediseño de Catalis (2007)

[Archivo original modificado 13-mayo-2007 - El formato no ha sido adaptado para el wiki]

If I had to start thinking of Catalis as a new application…

REFACTORIZACION

* Mejorar el código JS: diseño, estructura, objetos Esto se llama REFACTORIZACION (refactoring). En del.icio.us puse algunos links sobre este tema. En cuanto a libros, los que tratan en más detalle cuestiones de refactorización vinculadas a JavaScript son:

  1. Ajax in action
  2. Ajax design patterns

Este artículo introduce el tema (lo imprimí):

Javascript Refactoring For Safer Faster Better AJAX

OBJETOS

    
  Los objetos básicos que necesitamos manejar en el cliente son:
  
	--------------------------------------------------------------------------
	  - REGISTRO MARC, como estructura de datos (bibliográfico, autoridades)
	--------------------------------------------------------------------------
	      propiedades:
			- leader
			- directorio?
			- campos de control
			- campos de datos (indicadores, subcampos)
			    getDatafields()
		  métodos:
		    - editar el registro (i.e. solicitarlo al servidor y presentarlo
			  en el form de edición)
			     editRecord()
			- render (en formulario)
			    showRecordInForm()
			    renderDatafields()
			    renderLeader()
			    renderField008()
				toggleSubfieldLabels() ???
			- display del registro completo (diversos formatos)
			    viewRecordDetails()
			    showRecordDetails()
			    viewRecord()
			    marc2marcTagged()
			    marc2aacr()
				más las funciones en recordVisualization.htm
			- display de porciones del registro (e.g. leader) ?
			- historia del registro (quién y cuándo lo modificó)
			- duplicar el registro
			    duplicateRecord()
			- exportar
			    buildISO2709()
			    exportRecord()
			    serialize008()
			    serializeRecord()
			- importar/load (desde diversos formatos) ¿Método de qué objeto?
			  En marc_record.js (http://www.pusc.it/bib/mel/marc_record.js),
			  el objeto MARC_record.prototype tiene un método load().
			  ¿Y si queremos importar un lote?
			    aacr2marc()
			    getIsoRecord()
			    importRecord()
			    DGM_translate()
			    modifyImportedField()
			    parseISO2709()
			- actualizar propiedades dinámicas, e.g. directorio, longitud, 005 (ver marc_record.js)
			- responder preguntas varias: ¿está presente un campo? ¿está presente un campo+subcampo?
			  ¿fue modificado el registro? ¿tipo de registro? ¿cuál es la posición de este campo?
			    isTagPresent()
			    isSubfieldPresent()
			    modifiedRecord() + las funciones en saveChanges.htm
			    checkModified()
			    promptSaveChanges()  ?
			    getMaterialType()
			    firstPositionAfter()
			    findNewFieldPosition()
			- devolver un array de los campos con cierto tag
			- agregar un campo (o el objeto campo debe tener un método "agregar al registro"?)
			  	 promptNewField()
				 createField()
				 createFieldList()
				 newFieldShortcut()
				 más las funciones en selectField.htm
			- editar datos que requieren una ventana especial
				 rawEdit()
			    editCodedData()    <= no es necesaria una ventana modal
			    editField041()     <= no es necesaria una ventana modal
			    editField044()     <= no es necesaria una ventana modal
			    editField046()     <= no es necesaria una ventana modal
			    editField047()     <= no es necesaria una ventana modal
				 editEjemplares()   <= no es necesaria una ventana modal
				 editPostItNote()   <= no es necesaria una ventana modal
				showCodeTable()
				más todas las funciones en editCodes.htm
			- validar los datos
			    marcValidate()
			- mostrar las claves que irán al diccionario
			    showKeys()
			- modificaciones automáticas (¿métodos del registro o del campo?)
				 canConvertTo440()
				 canConvertTo700()
				 canConvertTo246()
			    map490to440()
				 map830to440()
				 map100to700()
				 map740to246()
				 enhance505()
			- grabar el registro en la base de datos
			    saveRecord()
			- borrar el registro
			    deleteRecord()
			- crear un registro? Método de qué objeto?
			    getNewRecordParams()
			    createRecord()
			
			---------------------------------------
			Objeto CAMPO DE DATOS: un elemento TR
			---------------------------------------
			  propiedades
			    - tag
			    - indicadores
				    getIndicators()
					updateIndicators()  [setIndicators?]
				- subcampos
				    getSubfields()
				- bloque al que pertenece
				    getFieldBlockName()
				- tipo de campo, i.e. controlado, sólo lectura. etc. (??)
			  métodos
			    - agregar un subcampo (o el subcampo debe tener un método "agregar al campo"?)
				    promptNewSubfield()
					createSubfield()
					createSubfieldList()
					newSubfieldShortcut()
				- eliminar el campo
				    canRemoveF()
				    removeField()
				    deleteObj()  ?
				- editar indicadores
				    editIndicators()    <= no es necesaria una ventana modal
					más todas las funciones en editIndicators.htm
				- duplicar el campo (si es repetible)
				    canDuplicateF()
				- subir/bajar
				    canMoveUpF()
					canMoveDownF()
				    moveField()
				- actualizar la puntuación
				    updatePunctuation()
					punctuation()
				- posición del campo en el registro (index)
				    indexOfField()
				- siguiente campo en el registro
				    nextField()
				- ¿campo anterior?
				- ¿está presente un subcampo?
				    isSubfieldPresent()  ?
				- firstSubfieldBox(), lastSubfieldBox()
				- displayField()  <= render
					
			-----------------------------------
			Objeto SUBCAMPO: un elemento TR
			-----------------------------------
			  propiedades
			    - código de subcampo
				- valor del subcampo
			    - campo en el cual se encuentra (referencia al objeto DOM, tag)
				- ¿es editable, readonly, controlado?
			  métodos
			    - posición del subcampo dentro del campo (index)
					indexOfSubfield()
				- resaltar el subcampo cuando recibe foco
				    highlightSubfieldBox()
				- eliminar el subcampo
					canRemoveSf()
				    deleteObj() ?
					removeSubfield()
				- duplicar el subcampo (si es repetible)
					canDuplicateSf()
				- subir/bajar
				    canMoveUpSf()
					canMoveDownSf()
				    moveSubfield()
				- displaySubfield() <= render
				- ¿subcampo siguiente/anterior?
				- otros
				    getSubfieldLabel()
				
		¿Necesitamos un objeto INDICADORES para cada campo de datos?
		posibles miembros:
		  1 => primer indicador
		  2 => segundo indicador
		  editar
			
		¿Y los CAMPOS DE CONTROL? Cada uno tiene características especiales.
		Podemos tener un objeto Field008, con una propiedad por cada elemento
		de datos, y métodos para serializar y renderizar.
		Análogamente, objetos Field006, Field007.
		
		Más aun, podemos tener un objeto CODE, para modelar los códigos que se
		usan en cualquier parte del registro.
		
	--------------------------------------------------------------------------
		¿Y los HOLDIGNS?
	--------------------------------------------------------------------------
		El campo 859 es un campo de datos, pero cumple una función muy especial.
		Cada ocurrencia del 859 debe ser un objeto (item, ejemplar).
		
		sortEjemplares()
		más todas las funciones en editEjemplares.htm
		
	--------------------------------------------------------------------------
		¿Cómo se modelan las RELACIONES ENTRE REGISTROS?
	--------------------------------------------------------------------------
		E.g. para analíticas, o seriadas.
		
	--------------------------------------------------------------------------
	  - REGISTRO MARC, como objeto DOM (tal como se ve en el form)
	--------------------------------------------------------------------------
	    ¿pueden ambos objetos ser el mismo, o 'casi' el mismo?
		El objeto DOM 'sabe' algo importante: el *orden* de sus elementos.
		En el objeto registro, cada campo puede tener un puntero al objeto DOM
		respectivo.
		
	--------------------------------------------------------------------------
	  - Objeto COLECCIÓN DE REGISTROS MARC: un custom object, o array
	--------------------------------------------------------------------------
	    Resultado de una consulta a la base de datos, o importación en lote.
	  
	--------------------------------------------------------------------------
	  - NOTIFICACIONES y demás MENSAJES de la interfaz
	--------------------------------------------------------------------------
	    Con texto generado desde el cliente o desde el servidor, ver Ajax in action, Ch. 6.
		  catalisMessage()
		  head.htm
		  
	--------------------------------------------------------------------------
	  - ELEMENTOS DE LA INTERFAZ: dialog windows, tabs, menúes, toolbars
	--------------------------------------------------------------------------
	      showPopup() ?
		  hidePopup()
		  setDimensions()
		  catalis_confirm()
		  updateDialogHeight()
		  updateDialogWidth()
		  showSearchDiv()
		  showEditDiv()
		  handleNextTask()  (callback?)
		  refreshTitleBar()
		  showNewRecordMenu()
		  showFieldMenu()
		  showSubfieldMenu()
		  killmenu()
		  showHiddenData()
		  disableKeys()
		  showSpecialChars()
		  
	--------------------------------------------------------------------------
	  - BASE DE DATOS (bibliográfica, de autoridades, ...)
	--------------------------------------------------------------------------
	      base de datos activa
		     nombre (ver head.htm)
			  cantidad de registros
			  últimas modificaciones
			  usuarios que trabajan sobre esta base
		     handleMaxMfn()
			  showNewRecords()
		  búsquedas?
		    showSearchForm() ?
		    handleKwSearch()
			 searchFromDictionary()
		  diccionario ?
		    showDictionaryKeys()
			 updateDictionaryList()
		  cambio de base de datos
		    databaseChange()
			
		¿Podemos estar trabajando con más de una base a la vez?
		
		
	--------------------------------------------------------------------------
	  - USUARIO (sólo puede haber uno por vez)
	--------------------------------------------------------------------------
	      usuario activo
		   bases a las que tiene acceso, permisos
		   historial, últimos registros procesados ?
		   preferencias ?
		
	--------------------------------------------------------------------------
	  - SESIÓN?
	--------------------------------------------------------------------------
	      handleLoginSubmit()
	      endSession()
		  Historial (¿persistente? Puede estar asociado al usuario)
	  
	--------------------------------------------------------------------------
	  - COMUNICACIÓN CON EL SERVIDOR (AJAX)
	--------------------------------------------------------------------------
		tareas actuales: carga de tablas, búsquedas, acceso al diccionario,
		                 cambio de base, editar/grabar/borrar registro
		tareas futuras: guardar preferencias, plantillas, notas personales
		  
	     importXML()
		  getXMLFile()
		  loadXML()
		  saveRecord()
		  handleLoginSubmit()
		  endSession()
	  
	--------------------------------------------------------------------------
	  - ERRORES? (errores al ejecutar código JS)
	--------------------------------------------------------------------------
	    Cada error que se produzca durante la ejecución de JS debe ser
		interceptado para procesarlo: mostrar mensaje al usuario, guardarlo en
		un log, reportarlo.
		Si JS tiene un objeto Error, ¿necesitamos crear algo?
		
		handleErrors()
	  
	--------------------------------------------------------------------------
	  Objetos con información estática (no se modifican desde JS)
	--------------------------------------------------------------------------
	  
		  - TABLA DE CAMPOS Y SUBCAMPOS MARC (marc21.xml) <= JSON?
		  
		  - TABLAS DE CÓDIGOS (country.xml, language.xml, etc.) <= JSON?
		  
		  - REGLAS DE PUNTUACIÓN: por ahora, es una única función con un switch/case
		  
	  	  - REGLAS DE VALIDACIÓN ?
		  
	--------------------------------------------------------------------------
	  Objetos con información cuasi-estática (se pueden llegar a modificar desde
	  JS, pero con poca frecuencia)
	--------------------------------------------------------------------------
	  
		  - PLANTILLAS PARA REGISTROS (¿son casos particulares de registros MARC?)
			  loadTemplates()
			  loadLocalTemplateData()
			  
		  - PARÁMETROS DE CONFIGURACIÓN
		      Actualmente, en head.htm
		  
		  - DOCUMENTACIÓN, AYUDA CONTEXTUAL
			Las modificaciones podrían ser notas personales del catalogador, que
			deberían guardarse en una base de datos.
			  showDoc()
			  docURL()
			  docIframeHide()
			  docIframeShow()
			  marcHelpPopup()
			  generalHelpPopup()
			  canShowDocF()
			  canShowDocSf()
		  
		  
	En el cliente necesitamos tener un modelo (objetos JS) de registro MARC, que
	será poblado con datos recibidos del servidor, para luego mostrarlos en el form,
	y recíprocamente será poblado con datos del form para luego enviarlos al
	servidor. En el servidor casi no necesitaremos manipular registros, pero
	igualmente convendría tener un modelo que nos separe de la base de datos.
  • Separación más estricta entre HTML, CSS y JS.
  • MVC? Ajax in Action se dedica al tema, especialmente Ch. 4
  • chequeo del código (JSLint, JavaScript Lint)
  • compresión del código (JsMin, packer, jsjuicer)
  • Mejor documentación del código (JSDoc, Natural Docs)

Ejemplo de uso de ND: http://docs.mootools.net/files/Moo-js.html

  Otro ejemplo: http://adlib.ozonecreations.com/documentation.php
  Mirando el código fuente (completo) de mootools podemos aprender a usar Natural
  Docs aplicado a JS.
  Para ejecutar ND en Windows:
  Directorio: G:\programas\NaturalDocs
    Comando: NaturalDocs -i G:\httpd\htdocs\catalis-obj\catalis\js -o HTML G:\catalis\doc -p G:\catalis\ndinfo
  
* Testeo, assert (ver libro pdf sobre DOM que tengo en el INMABB, y http://aymanh.com/9-javascript-tips-you-may-not-know)
  Para mostrar el nombre de la función que llamó a assert:
  if (arguments.callee.caller != null) {
	  msg += 'function ' + arguments.callee.caller.toString().match(/function\s+(\w+)/)[1]
  }
  (tomado de http://www.visibone.com/javascript/assertiveness.jpg)
  
* Ajax propiamente implementado (XMLHttpRequest) OBVIO!
* Abstraer acceso a la base de datos
* Server-side language: PHP
* PHP+WXIS, PHP+Malete?
* Ampliar número de browsers soportados
* IE memory leaks
  http://outofhanwell.com/ieleak/
* Firefox memory leaks? http://dbaron.org/mozilla/leak-monitor/

Interfaz

  • Análisis y rediseño
  • Interfaz multilingüe

Funcionalidad

  • Control de autoridades (en sentido amplio, i.e. incluyendo tesauros)
  • Validación (ver p.ej. http://search.cpan.org/dist/MARC-Lint/ )
  • Analíticas, y otras relaciones entre registros
  • Interfaz web para administración: bases, usuarios, backups, etc.
  • Exportación de registros en lotes.
  • Poll the server, para saber si otro usuario modificó la base.
  • Integración con OPACMARC?

Otros aspectos

  • MARCXML
  • Formatos visuales con XSLT (ver Ajax in action, ch. 12)
  • Transferencia de datos: ¿XML o JSON?
  • Seguridad: Proteger passwords, sesiones, restringir acceso
    1. Ajax in action, ch. 7
  • Performance: Ajax in action, ch. 8

ASPECTOS DE LA INTERFAZ DE USUARIO

  • dimensiones de la página? ver el autoajuste de Google Calendar
  • menúes
  • tooltips
  • generación de mensajes
  • ventanas modales

posibles: Cross Browser DHTML Modal Dialogs For Web Apps <http://sublog.subimage.com/articles/2006/01/01/subModal>

              (parece satisfactoria)
              jquery-modalContent plugin (No callbacks?) <http://jquery.glyphix.com/>
              Google Docs, ex Writely (JS code legible y comentado! http://docs.google.com/javascript/dlgutils.js)
              Yahoo! Mail (código no legible)
* autocompletamiento (para campos controlados, con Ajax)
* mensajes de ayuda y de validación

Estudiar interfaces avanzadas, como las de varios productos de Google o Yahoo. La más interesante parece ser la de Google Calendar: http://www.google.com/calendar/. Aspectos que me interesan de Google Calendar:

  1. Las dimensiones del área de trabajo se adaptan automáticamente a las dimensiones de la ventana.
  2. Diálogos popup (son varios, mirarlos bien)
  3. Pestañas (tabs)
  4. Navegación (mes a mes, semana a semana, etc.)
  5. scroll de la grilla: usa dommousescroll

Si miro el código JS con WebDevToolbar, no veo *todo*. P.ej., hay una llamada a la función _ResizeCalFrame(), pero no se ve esa función en el código. Si hacemos javascript:alert(_ResizeCalFrame) se muestra un código comprimido, donde la función se llama Jf(), pero que *tampoco* lo encuentro en el código. Algo similar sucede con _initScrollFF(), donde ahora la función es mostrada con el nombre bC(). Podemos estudiarlo con Firebug.

Google Book Search tiene un scroll por paneles que funciona bien en Firefox, y un bonito “Accordion” en el panel derecho.

Google Docs & Spreadsheets también puede tener algo aprovechable:

  1. Diálogo modal para insertar caracteres especiales, links, tablas, etc.
  2. Menú contextual

Fading notifications: http://javascript.nwbox.com/asyncAlert/

En el capítulo 7 de Ajax Patterns and Best Practices encontré una referencia a TiddlyWiki, un wiki hecho enteramente en JavaScript (unas 7000 líneas de código!), del que se pueden aprender unas cuantas cosas, en aspectos de GUI y de código. Me gusta la transición del link “opciones”. La forma en que se almacena la configuración puede ser útil.

En http://www.bambooinvoice.org/clients hay un efecto “acordeón” que me gusta.

En http://demo.roundcube.net/?_task=mail hay autoajuste del tamaño de la interfaz.

En http://a9.com/refactoring?grp=reference tenemos

  1. un widget que permite modificar el tamaño de los paneles
  2. el tamaño de la interfaz se autoajusta a la ventana
  3. tooltips en la “i”

En http://www.answers.com/user tenemos un interesante sistema de “ayuda contextual” con AJAX, activado al hacer doble click sobre un término de la página.

El nuevo Yahoo! Mail tiene cosas para investigar: diálogos modales, p.ej.

Diálogo para pedido de confirmación: http://developer.yahoo.com/yui/examples/container/simpledialog/2.html

Jack Slocum (yui.ext) http://www.jackslocum.com/blog/index.php

Our Story http://www.ourstory.com/ Algunos detalles interesantes.

Google Reader usa un mecanismo no usual para el preview de posts: en la “List view”, expande cada ítem de la lista, dentro de la misma lista (en vez de usar otro panel). También ofrece una “Expanded view”, donde todos los posts están expandidos.


¿USAREMOS ALGUNA BIBLIOTECA (LIBRARY) DE JAVASCRIPT? Hay que leer un poco sobre las ventajas/desventajas de cada una de las más populares: jQuery, moo.fx, Dojo, Prototype, Yahoo, Mochikit, JSAN, Dean Edwards, Fork JavaScript

http://www.sitepoint.com/print/javascript-library
http://www.paulhammond.org/2006/06/atmedia2/javascript

jQuery parece suficiente para lo que necesitamos. Es liviana, sencilla, y con numerosos plugins. De todos modos, no me gusta cargar el browser con funciones que no serán utilizadas.

Leer sobre jQuery en: Beginning JavaScript with DOM Scripting / C. Heilmann.

Algunos plugins de jQuery que pueden ser útiles en Catalis:

Patterns ⇒ el libro Ajax Design Patterns, de Michael Mahemoff, parece útil. Tiene un sitio web también.

YUI ofrece tabs: http://developer.yahoo.com/yui/tabview/

Efectos bonitos: bytefx http://www.devpro.it/bytefx/


	
	prototype property: según Flanagan,
	// The constructor function initializes those properties that
	// will be different for each instance. 
	function Rectangle(w, h) { 
		this.width = w; 
		this.height = h; 
	} 
	// The prototype object holds methods and other properties that 
	// should be shared by each instance. 
	Rectangle.prototype.area = function( ) { return this.width * this.height; } 
	A constructor provides a name for a "class" of objects and initializes properties, such
	as width and height, that may be different for each instance of the class. The prototype
	object is associated with the constructor, and each object initialized by the constructor
	inherits exactly the same set of properties from the prototype. This means that the
	prototype object is an ideal place for methods and other constant properties. 
	
	Flanagan explica claramente la diferencia entre propiedades y métodos de una 'instancia',
	versus propiedades y métodos de la 'clase' (9.3. Simulating Classes in JavaScript).

Un buen paso inicial puede ser éste: recopilar las funciones JS usadas en Catalis, que son todas globales, y agruparlas en objetos.

Las que comienzan con * ya han sido asociadas a un objeto (ver más arriba).

aacr2marc.js

  • aacr2marc (aacrText) ⇐ para importar asientos AACR2

aux-windows.js

  • rawEdit(oldDatafields, aacr)
  • editCodedData(dataElement)
  • editField041()
  • editField044()
  • editField046()
  • editField047()
  • editIndicators(field)
  • editEjemplares()
  • editPostItNote()
  • promptNewField()
  • promptNewSubfield(field)
  • catalis_confirm(question,w,h,pos)
  • showSpecialChars()
  • promptSaveChanges()

catalis.js

  • importXML(sourceURL)
  • getXMLFile(sourceURL)
  • loadXML()
  • setDimensions()
  • toggleSubfieldLabels()
  • highlightSubfieldBox(subfieldBox)
  • showDoc(tag)
  • docURL(docSource,tag)
  • docIframeHide()
  • docIframeShow()
  • marcHelpPopup(tag,code)
  • generalHelpPopup(messageType)
  • getDatafields(fieldBlock)
  • getFieldBlockName(tag)
  • indexOfField(field,fieldBlock)
  • indexOfSubfield(subfield)
  • firstPositionAfter(tag,fieldBlock)
  • findNewFieldPosition(tag,fieldBlock)
  • isSubfieldPresent(field,code)
  • deleteObj(subfield)
  • refreshTitleBar()
  • showCodeTable(name)
  • showKeys()
  • showSearchDiv()
  • showEditDiv()
  • handleMaxMfn(maxmfn)
  • handleKwSearch()
  • deleteRecord()
  • databaseChange(newdb)
  • viewRecordDetails(evt,recordID,recordDisplayStyle)
  • editRecord(recordID,evt)
  • showSearchForm(formType)
  • searchFromDictionary(dictionaryTerm,rowNumber)
  • updateDictionaryList(key1,reverse)
  • modifiedRecord()
  • handleNextTask(elementID)
  • updateDialogHeight(objWindow)
  • updateDialogWidth(objWindow)
  • showHiddenData()
  • showPopup(x,y,width,height,refObject)
  • hidePopup()
  • showNewRecords(evt)
  • map490to440(field490)
  • map830to440(field830)
  • map100to700(field100)
  • map740to246(field740)
  • enhance505(old505,enhance)
  • catalisMessage(msg,button)
  • endSession()
  • newFieldShortcut()
  • newSubfieldShortcut(field)
  • checkModified(elementID)
  • sortEjemplares(ej1,ej2)

init()

  defineSomeVars()
  checkKey(evt)
  indexFromRecordID (recordID) <= no es necesaria, podemos usar Array.prototype.indexOf()
  displayPermanentTitle(uiElement,text,w,h) [EXPERIMENTO]

contextmenu-field.js

  • canMoveUpF(field)
  • canMoveDownF(field)
  • canRemoveF(field)
  • canDuplicateF(field)
  • canShowDocF(field)
  • canAddField(field)
  • canConvertTo440(field)
  • canConvertTo700(field)
  • canConvertTo246(field)
  • showFieldMenu(field)
  • killmenu()

contextmenu-subfield.js

  • canMoveUpSf(subfield)
  • canMoveDownSf(subfield)
  • canRemoveSf(subfield)
  • canDuplicateSf(subfield)
  • canShowDocSf(subfield)

canAddSubfield(subfield)

  • showSubfieldMenu(subfield)
  • killmenu()

create-field-subfield.js

  • createFieldList(tags)
  • createSubfieldList(field,codes)
  • createField(tag, ind, subfields)
  • createSubfield( code, sfText, label, fieldTag )

data-in.js

  • getNewRecordParams()
  • createRecord(newRecParams)
  • showRecordInForm(receivedRecord)
  • showRecordDetails(receivedRecord)
  • showDictionaryKeys(dictionaryKeys,reverse)
  • duplicateRecord()
  • renderDatafields(datafields)
  • renderLeader(leaderData)
  • renderField008(f008,materialType)

data-out.js

  padWithZeros(num, totalLength)
  invalidChars(subfield)
* serialize008(materialType)
* serializeRecord(leader, controlFields, dataFields, localData)
* buildISO2709()
* exportRecord()
* viewRecord()
* saveRecord()

disable-keys.js

  • disableKeys()

dom-functions.js

  crossBrowserNodeSelector(xmlObj,path)
* removeField(field)
  removeAllChildNodes(theObject)
* removeSubfield(subfield)
* getSubfields(field, array, empty)
* getIndicators(field)
* updateIndicators(field, newValues)
* getSubfieldLabel(subfield)
* firstSubfieldBox(field)
* lastSubfieldBox(field)
* displayField(newField,refNode)
* displaySubfield(newSubfield, field)
  parentField(object, objType)
  parentSubfield(object, objType)
  childSubfieldBox(subfield)
* nextField(field)
  nextSubfieldBox(subfield)
* isTagPresent(tag)
* moveField(field, dir)
* moveSubfield(subfield, dir)

eventHandlers.js

setSearchFormEvents()
setEditionFormEvents()
setControlFormEvents()
setWindowEvents()
setToolbarEvents()
setHeaderEvents()
setEventHandlers()

import.js

  • getIsoRecord()
  • importRecord(isoRecord)
  • DGM_translate(dgm)
  • modifyImportedField(tag,ind,sf)
  • parseISO2709(isoRecord)

isbncheck.js

displayMessage(box,type,number_OK)
checkStandardNumber(box,type)
isNumberValid(myNumber,myType) {
gettype(num)
getckdig(num)
fullnum(num)
hyphenate(str)
prefix(str)
shp(pref)
valid(num,type)
getISBN(istring)

marc2marcTagged.js

  • marc2marcTagged(leader, f001, f003, f005, f006, f007, f008, marcDatafields, ejemplares, postItNote)

menu-newrecord.js

  • canDuplicate()
  • showNewRecordMenu()
  • killmenu()

punctuation.js

  • updatePunctuation(field)
  • punctuation(tag,sf)

simModal.js

deadend() {
disableForms() {
enableForms() {
blockEvents() {
unblockEvents() {
openSimDialog(url, width, height, returnFunc, args) {
checkModal() {
finishChecking() {

templates.js

  • loadTemplates() {
  • loadLocalTemplateData() {

validate.js

  • marcValidate()

COMMON


getMaterialType.js

  • getMaterialType(leader06,leader07)

isbn-hyphen.js

ISN_clean (isbn_proto)
prefix_length_from_map (s, map)
country_group_code (isbn_proto)
canonical_ISBN (isbn_proto)

marc2aacr.js

arabic2Roman(arabic)
extractSubfield(field,sfCode)
fieldHighlight(tag,className)
newNote(tag)
* sortEjemplares(ej1,ej2)
printCallNumber(ejemplar)
* marc2aacr(materialType,f001,f005,f008,marcDatafields,ejemplares)

Hay también algunas funciones definidas en archivos HTML (en cgi-bin): $ find . | xargs grep 'function '

./catalis/html/login.htm:function handleKey(evt)
./catalis/html/login.htm:           * handleLoginSubmit()
./catalis/html/head.htm:            * handleErrors(errorMessage, url, line)
./catalis/html/head.htm:            showSystemInformation()
./catalis/html/head.htm:            handleLoad()
./catalis/html/updMaxMfn.htm:       init()
./catalis/html/head-recordList.htm: selectFirstRecord()
./catalis/html/head-recordList.htm: init()
./catalis/html/saved-record.htm:    init()
./catalis/html/send-keys.htm:       init()
./catalis/html/display-record.htm:  init()
./catalis/html/deleted-record.htm:  init()

Aprendiendo del libro 'Pro JavaScript Techniques', by John Resig (Published December 11th, 2006, by Apress)

Cap. 13: An Ajax Wiki

Es el ejemplo más complejo del libro.

List of all the files needed to power this wiki application:

  • index.html: The main application page, pulling all the client code together.
  • install.html: The main installation file, to be run before first using the application.
  • css/style.css: All the CSS styling for the client side of the application.
  • js/wiki.js (Listing 13-9): The main JavaScript code, responsible for binding events and running SQL queries.
  • js/sql.js (Listing 13-10): The code responsible for communicating with the server, retrieving the JSON data from the SQL queries.
  • js/textile.js: A copy of the JavaScript Textile library (for converting text to HTML): http://jrm.cc/extras/live-textile-preview.php.
  • js/jquery.js: A copy of the current release of jQuery: http://jquery.com/.
  • api/: The main server code responsible for translating SQL query results to JSON and returning them to the client. This directory contains each code version in Perl, PHP, Python, and Ruby. I've included a copy of the Ruby version of the code in Listing 13-11.
  • data/wiki.db: The SQLite database that stores the wiki.

wiki.js


Quiero aprender a usar OO JavaScript, pero me cuesta tomarle la mano.

Requester = function() {}

es lo mismo que

Requester = new Object() ?

No!


El problema que no termino de resolver es cómo manejar estas dos representaciones
del registro y sus campos: por un lado, un objeto JS 'puro', y al mismo tiempo
un objeto DOM, la versión 'viva' que el usuario maneja directamente.

Al objeto 'puro' lo necesito para
    * manipular registros recibidos desde afuera (es decir, desde el servidor,
    o importados), antes de presentarlos en el formulario;
    * posiblemente para generar salidas (es decir, visualizaciones, exportaciones,
    grabaciones); en este caso creo que sólo necesitaría leer, pero no modificar
	datos.

Mientras se está editando un registro, el registro *es* los datos del formulario, y
toda operación sobre el registro es una operación sobre el formulario. Pero entonces,
¿eso implica que toda la lógica de manipulación del registro --esto es, crear un
campo, crear un subcampo, verificar la presencia de un campo, etc.-- estará
'contaminada' por la particular estructura DOM elegida para este formulario, sin
poder hacer abstracción de ésta?

Las funciones del archivo dom-functions.js de Catalis ...
  1. ——————–

¿Vale la pena dividir los métodos en “getter” vs. “setters”? Uso comillas porque no

estoy seguro del sentido preciso con que se usan esos términos en otros lenguajes,
pero la idea es distinguir entre aquellos métodos que solamente devuelven un valor,
de aquellos que además modifican algún objeto.

También podemos organizar el código de esta manera:

MarcField.prototype = {

addSubfield = function() {
	...
}
remove = function(arg) {
	...
}

}

Enero 10 y 11 Vamos a recorrer uno por uno los archivos *.js, para extraer las funciones existentes y organizarlas en base a los objetos definidos. Sólo las copiamos, sin modificar el código.

1. dom-functions.js [OK] 2. catalis.js [OK] 3. create-field-subfield.js [OK] 4. contextmenu-field.js [OK] 5. contextmenu-subfield.js [OK] 6. data-out.js [OK] 7. data-in ⇐ getNewRecordParams() debería estar en aux-windows.js 8. eventHandlers.js [OK] 9. marc2MarcTagged.js [OK] 10. disable-keys.js [OK] 11. punctuation.js [OK] 12. menu-newrecord.js [OK] 13. templates [OK]

Enero 13 ¿El siguiente paso? Queremos empezar a modificar el código, de manera progresiva. Pero ¿cómo? Creo que deberíamos hacerlo desde afuera hacia adentro (o desde arriba hacia abajo). Es decir, continuar con el rediseño general de la aplicación, definiendo los objetos y métodos a utilizar.

Enero 14. Si el objeto MarcSubfield termina siendo una fila de tabla (TR), ¿cómo se supone que le asociamos métodos? Con Mozilla podríamos usar HTMLTableRowElement.prototype, pero IE no nos deja.

Estos problemas con IE se tratan en:

  1. Crossbrowser HTMLElement Prototyping <http://www.browserland.org/scripts/htmlelement/>
  2. Adding Methods to Native HTML Elements [Jan 24, 2006] <http://huwebdev.blogspot.com/2006/08/adding-methods-to-native-html-elements.html>
  3. Emulating Prototyping of DOM Objects in Internet Explorer <http://delete.me.uk/2004/09/ieproto.html>

Pero, aun suponiendo que pudiésemos añadir métodos al prototype de los elementos TR, no los estaríamos restringiendo a los TR de un tipo específico (campos, subcampos), que es justamente el objetivo de usar el enfoque orientado a objetos.

Por lo tanto, parece que sólo nos quedaría esta posibilidad: que nuestro objeto sea un 'custom object' con una propiedad que lo vincule al objeto DOM (TR) asociado; a su vez el objeto DOM tendría una propiedad que lo vincule con el custom object. Así, a partir de un evento que sucede en el objeto DOM, tenemos que invocar un método del custom object asociado, el cual tendrá como resultado visible una modificación en el objeto DOM. Suena a mucha complicación innecesaria, esto de tener básicamente *dos* objetos hermanados.

Leer:

EJ - The only JavaScript library you'll ever need http://www.robertnyman.com/2006/11/07/ej-the-only-javascript-library-youll-ever-need/

Event Delegation versus Event Handling (sobre las ventajas de usar event bubbling, especialmente para interfaces con muchos elementos; ver también ppk 7D.) http://icant.co.uk/sandbox/eventdelegation/ http://icant.co.uk/sandbox/bonsaimenu/index.html

==Working examples: Custom events: http://developer.yahoo.com/yui/examples/event/custom-event.html Event delegation: http://developer.yahoo.com/yui/examples/event/event-delegation.html

Event-Driven Web Application Design (Christian Heilmann, YUI Blog) (acerca de event delegation y custom events) http://yuiblog.com/blog/2007/01/17/event-plan/

Publishing Custom Events in JavaScript Dustin Diaz (Yahoo) http://www.dustindiaz.com/custom-events/

El concepto de “event delegation” puede sernos útil para manejar los eventos asociados a los numerosos elementos “repetibles” en la interfaz: campos, subcampos, datos codificados.

Please read this! OpenCataloger, Koha http://wiki.liblime.com/doku.php?id=catalogingproject

Command pattern (Wikipedia) http://en.wikipedia.org/wiki/Command_pattern

Sobre este tema (command pattern) hay material en varios capítulos de Ajax in action.


Sobre bloqueo de registros (record locking): AJAX & Record Locking http://www.ddj.com/dept/lightlang/192700218


On-Demand Javascript http://ajaxpatterns.org/wiki/index.php?title=On-Demand_Javascript


Para evitar pérdida de datos al abandonar la página accidentalmente: onbeforeunload - http://www.4guysfromrolla.com/webtech/100604-1.shtml Verificar si hay cambios sin guardar.

Feb 17.

User interface components - A survey of everything (?) we need.

1st level:

  login
  logout
  select database

2nd level:

  create/import record

Documentation: should be accessible not only from the editing panel but from everywhere

Tooltips or other contextual help popups: used all over the application.

Main division in two panels:

(1) DATABASE SEARCH & BROWSE

  Search: forms for different types of search (keyword, control number, boolean condition)
          Also: recently added/modified records
          Probably similar to OPAC search form.
  Browse index: input initial term, display list of terms.
  Lists of retrieved records: each "search" produces a list, consisting of 0 or more records
      (possibly paginated), and a "header" with data about the search.
      We can switch between lists using e.g. tabs.
  Search history?
  Record details: different views of a record (AACR2, MARC, OPAC, etc.). This functionality is
      also present in the editing panel.
  

(2) RECORD EDITING

  List of data fields (TABLE ROWS)
  Leader and control fields
  Holdings => DIALOG
  Annotations => DIALOG (can we attach annotations to specific fields?)
  Toolbar: add field, add subfield, view, save, export, advanced editing (all open DIALOGs)

Switch between (1) and (2): TABs. We could add optionally extra tabs to access related websites: LC catalog, local OPAC, etc. Firefox or IE7 users may prefer to use native browser tabs.

We also need to take into account working with an authority database, besides the usual bibliographic db.

For next version of Catalis, consider using the BSD License http://www.opensource.org/licenses/bsd-license.php


07 marzo 2007 ⇐= esto lo imprimí el mismo 7 de marzo

Análisis de las solicitudes al servidor desde formularios y links. ¿Qué se puede sacar en limpio de todo esto? ¿Cuál es una mejor manera de organizar estas peticiones? Mejor = más fácil de administrar/mantener


FORM LOGIN


  <form name="loginForm" id="loginForm" action="[pft]v6001^u[/pft]" method="POST">
      <input type="hidden" name="IsisScript" value="[pft]v2000[/pft]">
      <input type="hidden" name="tarea" value="INICIAR_SESION">
      <input type="text" name="userid" value="[pft]v2002[/pft]">
      <input type="password" name="pw">
      <input type="submit" value="Iniciar sesión">
  </form>
  El envío de este formulario causa la carga de una nueva página. La base no se especifica, y se accede a la
  primera de la lista de bases para el usuario.

BOTON 'ULTIMOS REGISTROS'


  <button id="btnNewRecords">Últimos 10 registros</button>
  document.getElementById("btnNewRecords").onclick = showNewRecords;
  function showNewRecords() {
      if (ie) {
          document.frames.searchResultsIframe.location.href = document.getElementById("searchResultsIframe").src;
      } else if (moz) {
          document.getElementById("searchResultsIframe").src = document.getElementById("searchResultsIframe").src;
      }
  }
  Carga una nueva página en el iframe.

CAMBIO DE BASE / BOTÓN 'FIN SESIÓN' (un mismo form para ambas tareas)


  <form id="logoutForm" action="[pft]v6001^u[/pft]" method="POST">
      <input type="hidden" name="IsisScript" value="[pft]v2000[/pft]">
      <input type="hidden" name="userid" value="[pft]v2002[/pft]">
      <input type="hidden" name="db" value="[pft]v2104[/pft]">
      <input type="hidden" name="tarea" value="FIN_SESION">
      <select id="selDatabase"> options...</select>  <== no tiene sentido dentro de este form, pues usa hiddenFORM
      <button id="btnFinSesion">Fin sesión</button>
  </form>
  document.getElementById("selDatabase").onchange = function() {
      checkModified(this.id); ==> databaseChange()
  }
  document.getElementById("btnFinSesion").onclick = function() {
      checkModified(this.id); ==> endSession()
  }
  function databaseChange(newdb) {
      var form = document.getElementById("hiddenFORM");
      form.db.value = newdb;
      form.tarea.value = "MAIN_PAGE";
      form.method = "GET";
      form.target = "_top";
      form.submit();
  }
  
  function endSession() {
      if ( confirm("¿Confirma que desea finalizar la sesión?") ) {
          document.getElementById("logoutForm").submit();
      }
  }
  
  El envío de este formulario causa la carga de una nueva página.
  

Búsquedas:

  1. ——————————–

FORM BUSQUEDA POR PALABRAS

  1. ——————————–

<form id=“kwSearchForm” action=“[pft]v6001^u[/pft]” method=“GET” target=“searchResultsIframe”>

          <input type="hidden" name="IsisScript" value="[pft]v2000[/pft]">
          <input type="hidden" name="userid" value="[pft]v2002[/pft]">
          <input type="hidden" name="db" value="[pft]v2104[/pft]">
          <input type="hidden" name="tarea" value="BUSCAR">
          <input type="hidden" name="dictionaryTerm" value="">
          <input type="text" id="kwSearchBox" name="busq_kw">
          <input type="submit" value="Buscar">
      </form>
      document.getElementById("kwSearchForm").onsubmit = function() {
          handleKwSearch();
          return false;
      }
      function handleKwSearch() {
          var form = document.getElementById("kwSearchForm");
          form.dictionaryTerm.value = "";
          var query = document.getElementById("kwSearchBox").value;
          query = query.replace(/\s+/g," ");
          query = query.replace(/^\s+|\s+$/g,"");
          document.getElementById("kwSearchBox").value = query;
          form.submit();
      }
      
      El envío de este formulario causa la carga de una nueva página en el iframe.
      
      
  ---------------------------------
  FORM BUSQUEDA POR MFN
  ---------------------------------
      <form id="mfnSearchForm" name="mfnSearchForm" action="[pft]v6001^u[/pft]" method="GET" target="searchResultsIframe">
          <input type="hidden" name="IsisScript" value="[pft]v2000[/pft]">
          <input type="hidden" name="userid" value="[pft]v2002[/pft]">
          <input type="hidden" name="db" value="[pft]v2104[/pft]">
          <input type="hidden" name="tarea" value="MFN_RANGE">
          <input type="text" id="mfnSearchBox" name="mfn">
          <input type="submit" value="Mostrar">
      </form>
      
      El envío de este formulario causa la carga de una nueva página en el iframe.
  1. ——————————–

FORM BUSQUEDA POR COND. BOOLEANA

  1. ——————————–

<form id=“testConditionSearchForm” name=“testConditionSearchForm” action=“[pft]v6001^u[/pft]” method=“GET” target=“searchResultsIframe”>

          <input type="hidden" name="IsisScript" value="[pft]v2000[/pft]">
          <input type="hidden" name="userid" value="[pft]v2002[/pft]">
          <input type="hidden" name="db" value="[pft]v2104[/pft]">
          <input type="hidden" name="tarea" value="TEST_CONDITION">
          <input type="hidden" name="maxMFN" value="">
          <input type="text" id="testConditionSearchBox" name="condition">
          <input id="grCheckbox" type="checkbox" name="gr" value="On"><label for="grCheckbox">grupo repetible</label>
          <input type="submit" value="Buscar">
      </form>
      
      El envío de este formulario causa la carga de una nueva página en el iframe.

Diccionario:

  
  ---------------------------------
  FORM VER TERMINOS DEL DICCIONARIO
  ---------------------------------
      <form id="indexForm" action="[pft]v6001^u[/pft]" method="GET" target="hiddenIFRAME">
          <input type="hidden" name="IsisScript" value="[pft]v2000[/pft]">
          <input type="hidden" name="userid" value="[pft]v2002[/pft]">
          <input type="hidden" name="db" value="[pft]v2104[/pft]">
          <input type="hidden" name="tarea" value="SHOW_KEYS">
          <input type="hidden" name="reverse" value="">
          <input type="text" id="dictBrowseBox" name="dictkey">
          <input type="submit" value="Ver">
      </form>
      document.getElementById("indexForm").onsubmit = function() {
          updateDictionaryList(this.dictkey.value);
          return false;
      }
      function updateDictionaryList(key1,reverse) {
          var form = document.getElementById("indexForm");
          form.dictkey.value = key1;
          form.reverse.value = ( reverse ) ? "On" : "";
          form.submit();
      }
  1. ——————————–

LINK EN TERMINO DEL DICCIONARIO

  1. ——————————–

newLink.onclick = function() {

          searchFromDictionary(this.title,this._index);
          return false;
      }
      function searchFromDictionary(dictionaryTerm,rowNumber) {
          var form = document.getElementById("kwSearchForm");
          form.dictionaryTerm.value = dictionaryTerm;
          form.submit();
      }
      
  ---------------------------------
  BOTONES 'ANTERIORES' y 'SIGUIENTES' EN EL DICCIONARIO
  ---------------------------------
      newButton.onclick = function() {
          updateDictionaryList(this._term,"reverse");
  	}
      newButton.onclick = function() {
          updateDictionaryList(this._term);
      }
      
      

Listado de registros:

  1. ——————————–

BOTÓN 'EDITAR' (también se puede editar desde botones en la vista detallada y en el form de edición)

  1. ——————————–

'<button onclick=parent.editRecord(“',v001,'”,event)','>',v001,'</button>'

      function editRecord(recordID,evt) {
          var form = document.getElementById("hiddenFORM");
          form.tarea.value = "EDITAR_REG";
          form.recordID.value = recordID;
          form.method = "GET";
          form.target = "hiddenIFRAME";
          form.submit();
      }
      
  ---------------------------------
  LINK 'VER DETALLES' (también desde botones en la vista detallada)
  ---------------------------------
      <a ... onclick=top.viewRecordDetails(null,"',v001,'",top.g_RecordDisplayStyle);return(false)>
      function viewRecordDetails(evt,recordID,recordDisplayStyle) {
          var form = document.getElementById("hiddenFORM");
          if ( recordID == null ) {
              recordID = form.recordID.value;
          }
          form.tarea.value = ( "etiq" == recordDisplayStyle ) ? "SEND_ETIQUETADO" : "SEND_RECORD";
          form.recordID.value = recordID;
          form.target = "hiddenIFRAME";
          form.method = "GET";
          form.submit();
      }
  
  ---------------------------------
  BOTONES 'ANTERIORES' y 'SIGUIENTES'
  ---------------------------------
      PARA MFNRANGE
      <form action="[pft]v6001^u[/pft]" method="get" style="display: inline;">
          <input type="hidden" name="IsisScript" value="[pft]v2000[/pft]">
          <input type="hidden" name="userid" value="[pft]v2002[/pft]">
          <input type="hidden" name="tarea" value="MFN_RANGE">
          <input type="hidden" name="db" value="[pft]v2104[/pft]">
          <input type="hidden" name="from" value="[pft]f(rmax(f(val(v2006)-20,1,0),x1,'1'),1,0)[/pft]">
          <button onclick="this.form.submit()">Anteriores</button>
      </form>
      <form action="[pft]v6001^u[/pft]" method="get" style="display: inline;">
          <input type="hidden" name="IsisScript" value="[pft]v2000[/pft]">
          <input type="hidden" name="userid" value="[pft]v2002[/pft]">
          <input type="hidden" name="tarea" value="MFN_RANGE">
          <input type="hidden" name="db" value="[pft]v2104[/pft]">
          <input type="hidden" name="from" value="[pft]f(val(v2006)+20,1,0)[/pft]">
          <button onclick="this.form.submit()">Siguientes</button>
      </form>
      PARA BUSQUEDAS
      <form action="[pft]v6001^u[/pft]" method="get" style="display: inline;">
          <input type="hidden" name="IsisScript" value="[pft]v2000[/pft]">
          <input type="hidden" name="userid" value="[pft]v2002[/pft]">
          <input type="hidden" name="tarea" value="BUSCAR">
          <input type="hidden" name="db" value="[pft]v2104[/pft]">
          <input type="hidden" name="busq_kw" value="[pft]v3005[/pft]">
          <input type="hidden" name="dictionaryTerm" value="[pft]v3009[/pft]">
          <input type="hidden" name="from" value="[pft]f(val(v2006)-20,1,0)[/pft]">
          <button onclick="this.form.submit()">Anteriores</button>
      </form>
      <form action="[pft]v6001^u[/pft]" method="get" style="display: inline;">
          <input type="hidden" name="IsisScript" value="[pft]v2000[/pft]">
          <input type="hidden" name="userid" value="[pft]v2002[/pft]">
          <input type="hidden" name="tarea" value="BUSCAR">
          <input type="hidden" name="db" value="[pft]v2104[/pft]">
          <input type="hidden" name="busq_kw" value="[pft]v3005[/pft]">
          <input type="hidden" name="dictionaryTerm" value="[pft]v3009[/pft]">
          <input type="hidden" name="from" value="[pft]f(val(v2006)+20,1,0)[/pft]">
          <button onclick="this.form.submit()">Siguientes</button>
      </form>

Edición de registros:

  1. ——————————–

BOTÓN 'GRABAR' (toolbar)

  1. ——————————–

<button id=“btnGrabar”>&nbsp;</button>

      document.getElementById("btnGrabar").onclick = function() {
          saveRecord();
      }
      function saveRecord() {
          var form = document.getElementById("hiddenFORM");
          form.marcFields.value = marcFields;
          form.recordID.value = document.getElementById("marcEditForm").f001.value;
          form.tarea.value = "GRABAR_REG";
          form.method = "POST";  // method="GET" genera errores, como es de esperar
          form.target = "hiddenIFRAME";
          form.submit();
      }
  
  ---------------------------------
  BOTÓN 'BORRAR' (toolbar)
  ---------------------------------
      <button id="btnBorrar">X</button>
      document.getElementById("btnBorrar").onclick = function() {
          deleteRecord();
      }
      function deleteRecord() {
          if ( confirm("BORRADO DEL REGISTRO\n\nSi borra este registro, no podrá recuperarlo. ¿Confirma el borrado?") ) {
              var form = document.getElementById("hiddenFORM");
              form.tarea.value = "BORRAR_REG";
              form.recordID.value = document.getElementById("marcEditForm").f001.value;
              form.target = "hiddenIFRAME";
              form.method = "GET";
              form.submit();
          }
      }
  1. ——————————–

EDITAR SIGUIENTE/ANTERIOR EN LA LISTA DE RESULTADOS

  1. ——————————–

document.getElementById(“btnPrevResult”).onclick = function() {

          checkModified(this.id); ==> editRecord()
      }
      document.getElementById("btnNextResult").onclick = function() {
          checkModified(this.id); ==> editRecord()
      }
  

FORM OCULTO (compartidos por varias tareas):


  <form id="hiddenFORM" action="[pft]v6001^u[/pft]" method="" target="">
      <input type="hidden" name="IsisScript" value="[pft]v2000[/pft]">
      <input type="hidden" name="userid" value="[pft]v2002[/pft]">
      <input type="hidden" name="db" value="[pft]v2104[/pft]">
      <input type="hidden" name="tarea" value="">
      <input type="hidden" name="mfn" value="">
      <input type="hidden" name="recordID" value="">
      <input type="hidden" name="debug" value="">
      <!-- TEXTAREA usado para enviar el registro MARC al servidor -->
      <textarea name="marcFields" style="display: none;" -id="recordTextarea"></textarea>
  </form>
  <button id="editRecordBtn">Editar</button>
  document.getElementById("editRecordBtn").onclick = function() {
      editRecord(null,event);
  }
  
  <button id="aacrDisplayBtn">AACR2</button>
  document.getElementById("aacrDisplayBtn").onclick = function() {
      viewRecordDetails(event,null,"aacr");
  }

hasta acá imprimí el 7 de marzo

14 de marzo

MVC - Model-View-Controller

Una charla de ayer con Rubén Mansilla me llevó a poner otra vez algo de atención en el MVC.

Hay un ejemplo breve y bastante claro en

JavaScript Model-View-Controller with Dojo toolkit http://www.alexatnet.com/blog/2/2006/08/04/javascript-model-view-controller-with-dojo-toolkit

El modelo es una lista (i.e. un array), con métodos para añadir, quitar y mover ítems; la vista es un elemento select más algunos botones, con métodos para poblar el select con los datos provenientes del modelo, y para asociar cambios en el modelo a cambios en la vista; y el controlador consiste de los “event handlers” asociados a los elementos de la interfaz. Encontré un par de anomalías, las reporté al autor y dijo que hará las modificaciones. Ver anotaciones en la copia impresa del código.

Cita de http://isegserv.itd.rl.ac.uk/blogs/alistair/archives/21 (My First AJAX Application, Alistair Miles, 10 June 2006):

In designing the application, I've tried to follow a strict Model-View-Controller architecture. A set of controller functions are invoked when the appropriate view event is fired. Controller functions execute some functionality (e.g. make an asynchronous HTTP request) and then make appropriate changes to a model object. If properties of the model object are changed, the view is notified and updated as appropriate (i.e. the appropriate view functions are invoked). Everything flows in the direction view (event) > controller > model > view. I've tried make the best of both the functional and the object-oriented features of the Javascript language, for example I use the Controller and View objects simply to collect a set of related functions that can be called statically, however a single instance of the Model object is created and used. E.g. when the “Search” button is pressed, the static method Controller.searchAll() is invoked. This changes the “model” variable by calling model.setProperty(Model.PROP_RESULTS, results), which then causes the view to be updated via the static method View.updateResults(). The static methods of the View object really only provide convenience layer above manipulations on the actual DOM document being viewed.

La aplicación a la que se refiere es: http://isegserv.itd.rl.ac.uk/claddier/search/single/ (guardada como “CLADDIER Discovery Service.html” en Ubuntu Desktop; ver archivo mvc.js) Documentación en http://isegserv.itd.rl.ac.uk/claddier/docs/api/

Estos otros ejemplos podrían ser útiles: http://polyglotinc.com/AJAXscratch/MVC/examples/index.html “Bruce Wallace has developed a library of JavaScript classes that implement a Model-View-Controller framework for Web Applications.”

Aquí hay una descripción más o menos clara del MVC: http://www.phpwact.org/pattern/model_view_controller

En el foro de Ext, este thread puede ser relevante: http://www.yui-ext.com/forum/viewtopic.php?t=3299&highlight=controller Allí Animal escribe:

/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/- The way I handle it, and I imagine most people will is with a singleton app controller using the pattern

      var appName = {};
      
      appName.appController = function() {
          var private = "data";
          return {
      
              //Create page layout, extract UpdateManagers for later use etc
              init: function() {
              },
      
              getPublicData() {
                 return "stuff";
              }
          }
      }();
      
      Ext.onReady(appName.appController.init, appName.appController, true); 

The object returned there knows about your page layout and performs services on it.

Commonly needed items can either be made public by placing them in the “appName” namespace, or kept private within the appController. /-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-

13 de mayo, post similar de Animal acerca de cómo organizar el código de una app: http://extjs.com/forum/showthread.php?p=30527#post30527 En ese thread, jsakalos mentions his policies for application design.

…………………………………. 15 de marzo.

Encontré Freja: “Freja is an Open-Source MVC Javascript Framework”

  http://www.csscripting.com/wiki/index.php?title=Freja
  It stands (tentatively) for 'Framework for REstful Javascript Applications'.
  Freja takes the traditional web development model upside-down, so before going any further,
  consider its founding principles:
  
      * Build and run client-side.
      * See the server as a Web Service provider.
      * Aim for zero-latency. 
  
  Se ve interesante, al menos como fuente de ideas. Hay sólo un par de ejemplos de uso real.

Un paper básico sobre MVC.

  Applications Programming in Smalltalk-80(TM): How to use Model-View-Controller (MVC)
  by Steve Burbeck, Ph.D. (1992)
  http://st-www.cs.uiuc.edu/users/smarch/st-docs/mvc.html
  Lo imprimí. Como habla principalmente sobre Smalltalk, sólo rescaté unas pocas ideas básicas.

Model-View-Controller según Microsoft:

  http://msdn2.microsoft.com/en-us/library/ms978748.aspx
  Printer friendly: http://msdn2.microsoft.com/en-us/library/ms978748(d=printer).aspx

Ajax in Action, cap. 3 habla de Web server MVC, y cap. 4, de browser MVC.

  En 13.7 se usa MVC en el refactoring del RSS reader.

Para debugging en IE, probar:

IE WebDeveloper

  http://www.ieinspector.com/dominspector/index.html

MS Script Editor?

Companion.JS


21 de marzo de 2007 Uso de objetos para mejor manejo de los campos y subcampos.

Ejemplo:

* Clase DataField

  • subclase PersonalName
  • subclase CorporateName
  • subclase MeetingName
  • subclass UniformTitle
  • subclass Note
  • subclase AddedEntry
    • Field700 (hereda de PersonalName y de AddedEntry)

Los métodos de visualización, edición, validación pueden organizarse de acuerdo a esta estructura.

Pero no hay que tomárselo demasiado en serio, porque pueden aparecer inconsistencias en MARC que causen problemas y al final no resulte tan cómodo de aplicar/usar.

catalis desarrollo

notas/catalis-con-objetos.txt · Última modificación: por 127.0.0.1