Index: openacs-4/packages/dotlrn-messages/tcl/apm-callback-procs.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/dotlrn-messages/tcl/apm-callback-procs.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/dotlrn-messages/tcl/apm-callback-procs.tcl	31 Aug 2010 21:32:23 -0000	1.1
@@ -0,0 +1,57 @@
+ad_library {
+    Procedures for registering implementations for the
+    dotLRN Messages package. 
+    
+    @creation-date 2010-01-20
+    @author Pedro Castellanos (pedro@viaro.net)
+}
+
+namespace eval dotlrn_messages {}
+
+ad_proc -public dotlrn_messages::install {} {
+    dotLRN messages package install proc
+} {
+    register_portal_datasource_impl
+}
+
+ad_proc -public dotlrn_messages::uninstall {} {
+    dotLRN messages package uninstall proc
+} {
+    unregister_portal_datasource_impl
+}
+
+ad_proc -public dotlrn_messages::register_portal_datasource_impl {} {
+    Register the service contract implementation for the dotlrn_applet service contract
+} {
+    set spec {
+        name "dotlrn_messages"
+	contract_name "dotlrn_applet"
+	owner "dotlrn-messages"
+        aliases {
+	    GetPrettyName dotlrn_messages::get_pretty_name
+	    AddApplet dotlrn_messages::add_applet
+	    RemoveApplet dotlrn_messages::remove_applet
+	    AddAppletToCommunity dotlrn_messages::add_applet_to_community
+	    RemoveAppletFromCommunity dotlrn_messages::remove_applet_from_community
+	    AddUser dotlrn_messages::add_user
+	    RemoveUser dotlrn_messages::remove_user
+	    AddUserToCommunity dotlrn_messages::add_user_to_community
+	    RemoveUserFromCommunity dotlrn_messages::remove_user_from_community
+	    AddPortlet dotlrn_messages::add_portlet
+	    RemovePortlet dotlrn_messages::remove_portlet
+	    Clone dotlrn_messages::clone
+	    ChangeEventHandler dotlrn_messages::change_event_handler
+        }
+    }
+    
+    acs_sc::impl::new_from_spec -spec $spec
+}
+
+ad_proc -public dotlrn_messages::unregister_portal_datasource_impl {} {
+    Unregister service contract implementations
+} {
+    acs_sc::impl::delete \
+        -contract_name "dotlrn_applet" \
+        -impl_name "dotlrn_messages"
+}
+
Index: openacs-4/packages/dotlrn-messages/tcl/dotlrn-messages-procs-oracle.xql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/dotlrn-messages/tcl/dotlrn-messages-procs-oracle.xql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/dotlrn-messages/tcl/dotlrn-messages-procs-oracle.xql	31 Aug 2010 21:32:23 -0000	1.1
@@ -0,0 +1,24 @@
+<?Xml version="1.0"?>
+
+<queryset>
+<rdbms><type>oracle</type><version>8.1.6</version></rdbms>
+
+
+<fullquery name="dotlrn_messages::remove_applet.delete_applet_from_communities">
+  <querytext>
+
+	delete from dotlrn_community_applets where applet_id = :applet_id
+    
+  </querytext>
+</fullquery>
+
+<fullquery name="dotlrn_messages::remove_applet.delete_applet">
+  <querytext>
+
+	delete from dotlrn_applets where applet_id = :applet_id
+    
+  </querytext>
+</fullquery>
+
+</queryset>
+
Index: openacs-4/packages/dotlrn-messages/tcl/dotlrn-messages-procs-postgresql.xql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/dotlrn-messages/tcl/dotlrn-messages-procs-postgresql.xql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/dotlrn-messages/tcl/dotlrn-messages-procs-postgresql.xql	31 Aug 2010 21:32:23 -0000	1.1
@@ -0,0 +1,24 @@
+<?Xml version="1.0"?>
+
+<queryset>
+<rdbms><type>postgresql</type><version>7.1</version></rdbms>
+
+
+<fullquery name="dotlrn_messages::remove_applet.delete_applet_from_communities">
+  <querytext>
+
+	delete from dotlrn_community_applets where applet_id = :applet_id
+    
+  </querytext>
+</fullquery>
+
+<fullquery name="dotlrn_messages::remove_applet.delete_applet">
+  <querytext>
+
+	delete from dotlrn_applets where applet_id = :applet_id
+    
+  </querytext>
+</fullquery>
+
+</queryset>
+
Index: openacs-4/packages/dotlrn-messages/tcl/dotlrn-messages-procs.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/dotlrn-messages/tcl/dotlrn-messages-procs.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/dotlrn-messages/tcl/dotlrn-messages-procs.tcl	31 Aug 2010 21:32:23 -0000	1.1
@@ -0,0 +1,201 @@
+ad_library {
+    
+    Procs to set up the dotLRN messages applet
+    
+    @author Pedro Castellanos (pedro@viaro.net)
+    @creation-date 2010-01-20
+}
+
+namespace eval dotlrn_messages {}
+
+ad_proc -public dotlrn_messages::applet_key {} {
+    What's my applet key?
+} {
+    return dotlrn_messages
+}
+
+ad_proc -public dotlrn_messages::package_key {} {
+    What package do I deal with?
+} {
+    return "messages"
+}
+
+ad_proc -public dotlrn_messages::my_package_key {} {
+    What package do I deal with?
+} {
+    return "dotlrn-messages"
+}
+
+ad_proc -public dotlrn_messages::get_pretty_name {} {
+    returns the pretty name
+} {
+    return "#messages.messages#"
+}
+
+ad_proc -public dotlrn_messages::add_applet {} {
+    One time init - must be repeatable!
+} {
+    dotlrn_applet::add_applet_to_dotlrn -applet_key [applet_key] -package_key [my_package_key]
+}
+
+ad_proc -public dotlrn_messages::remove_applet {} {
+    One time destroy. 
+} {
+    set applet_id [dotlrn_applet::get_applet_id_from_key [my_package_key]]
+    db_exec_plsql delete_applet_from_communities { *SQL* } 
+    db_exec_plsql delete_applet { *SQL* } 
+}
+
+ad_proc -public dotlrn_messages::add_applet_to_community {
+    community_id
+} {
+    Add the messages applet to a specifc dotlrn community
+} {
+    set portal_id [dotlrn_community::get_portal_id -community_id $community_id]
+
+    # create the messages package instance 
+    set package_id [dotlrn::instantiate_and_mount $community_id [package_key]]
+
+    # set up the admin portal
+    set admin_portal_id [dotlrn_community::get_admin_portal_id \
+                             -community_id $community_id
+                        ]
+
+    #messages_admin_portlet::add_self_to_page \
+        #-portal_id $admin_portal_id \
+        #-package_id $package_id
+    
+# AR: There will be no portlet, just the admin one
+    set args [ns_set create]
+    ns_set put $args package_id $package_id
+    add_portlet_helper $portal_id $args
+
+    return $package_id
+}
+
+ad_proc -public dotlrn_messages::remove_applet_from_community {
+    community_id
+} {
+    remove the applet from the community
+} {
+    ad_return_complaint 1 "[applet_key] remove_applet_from_community not implimented!"
+}
+
+ad_proc -public dotlrn_messages::add_user {
+    user_id
+} {
+    one time user-specifuc init
+} {
+    # noop
+}
+
+ad_proc -public dotlrn_messages::remove_user {
+    user_id
+} {
+} {
+    # noop
+}
+
+ad_proc -public dotlrn_messages::add_user_to_community {
+    community_id
+    user_id
+} {
+    Add a user to a specifc dotlrn community
+} {
+    set package_id [dotlrn_community::get_applet_package_id -community_id $community_id -applet_key [applet_key]]
+    set portal_id [dotlrn::get_portal_id -user_id $user_id]
+    
+    # use "append" here since we want to aggregate
+# AR: There will be no portlet, just the admin one
+    set args [ns_set create]
+    ns_set put $args package_id $package_id
+    ns_set put $args param_action append
+    add_portlet_helper $portal_id $args
+}
+
+ad_proc -public dotlrn_messages::remove_user_from_community {
+    community_id
+    user_id
+} {
+    Remove a user from a community
+} {
+    set package_id [dotlrn_community::get_applet_package_id -community_id $community_id -applet_key [applet_key]]
+    set portal_id [dotlrn::get_portal_id -user_id $user_id]
+
+# AR: There will be no portlet, just the admin one
+    set args [ns_set create]
+    ns_set put $args package_id $package_id
+
+    remove_portlet $portal_id $args
+}
+
+ad_proc -public dotlrn_messages::add_portlet {
+    portal_id
+} {
+    A helper proc to add the underlying portlet to the given portal. 
+    
+    @param portal_id
+} {
+    # simple, no type specific stuff, just set some dummy values
+# AR: There will be no portlet, just the admin one
+    set args [ns_set create]
+    ns_set put $args package_id 0
+    ns_set put $args param_action overwrite
+    add_portlet_helper $portal_id $args
+}
+
+ad_proc -public dotlrn_messages::add_portlet_helper {
+    portal_id
+    args
+} {
+    A helper proc to add the underlying portlet to the given portal.
+
+    @param portal_id
+    @param args an ns_set
+} {
+# AR: There will be no portlet, just the admin one
+    messages_portlet::add_self_to_page \
+        -portal_id $portal_id \
+        -package_id [ns_set get $args package_id] \
+        -param_action [ns_set get $args param_action]
+}
+
+ad_proc -public dotlrn_messages::remove_portlet {
+    portal_id
+    args
+} {
+    A helper proc to remove the underlying portlet from the given portal. 
+    
+    @param portal_id
+    @param args A list of key-value pairs (possibly user_id, community_id, and more)
+} { 
+    messages_portlet::remove_self_from_page \
+        -portal_id $portal_id \
+        -package_id [ns_set get $args package_id]
+}
+
+ad_proc -public dotlrn_messages::clone {
+    old_community_id
+    new_community_id
+} {
+    Clone this applet's content from the old community to the new one
+} {
+    ns_log notice "Cloning: [applet_key]"
+    set new_package_id [add_applet_to_community $new_community_id]
+    set old_package_id [dotlrn_community::get_applet_package_id \
+                            -community_id $old_community_id \
+                            -applet_key [applet_key]
+                       ]
+
+    return $new_package_id
+}
+
+ad_proc -public dotlrn_messages::change_event_handler {
+    community_id
+    event
+    old_value
+    new_value
+} { 
+    listens for the following events: 
+} { 
+}   
Index: openacs-4/packages/messages/catalog/messages.en_US.ISO-8859-1.xml
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/catalog/messages.en_US.ISO-8859-1.xml,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/catalog/messages.en_US.ISO-8859-1.xml	31 Aug 2010 21:30:54 -0000	1.1
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<message_catalog package_key="messages" locale="en_US" charset="ISO-8859-1">
+
+  <msg key="add">Add</msg>
+  <msg key="add_attachment">Add attachment</msg>
+  <msg key="as_read">Mark as read</msg>
+  <msg key="as_unread">Mark as unread</msg>
+  <msg key="at">At</msg>
+  <msg key="attachments">Attachments</msg>
+  <msg key="back">&lt;&lt; Back</msg>
+  <msg key="back_folders">Back Folders</msg>
+  <msg key="body_empty">Send this message without text in the body?</msg>
+  <msg key="body_mail">%user_name% has sent you a message:
+
+-------------------------------------
+
+%subject%
+
+%msg_content%
+
+-------------------------------------
+
+
+You can see from the following link:
+
+http://yourpage.com/%message_url%</msg>
+  <msg key="cancel">Cancel</msg>
+  <msg key="close_window">Close Window</msg>
+  <msg key="community">Community</msg>
+  <msg key="compose_mail">Compose Mail</msg>
+  <msg key="configuration">Configuration</msg>
+  <msg key="confirm_send_msg">Your message has been sent</msg>
+  <msg key="confirm_trash_empty">All messages in the Trash have been deleted forever</msg>
+  <msg key="create">Create</msg>
+  <msg key="date">Date</msg>
+  <msg key="delete">Delete</msg>
+  <msg key="delete_confirm">Do you want to continue?</msg>
+  <msg key="discard">Discard</msg>
+  <msg key="drafts">Drafts</msg>
+  <msg key="edit_folder">To edit the name click on the name of the folder</msg>
+  <msg key="edit_name_folder">Edit</msg>
+  <msg key="empty_trash">Empty Trash now</msg>
+  <msg key="folder_create_successfully">Folder created successfully</msg>
+  <msg key="folder_name">Folder name</msg>
+  <msg key="folder_trash_msg">(messages that have been in Trash more than 15 days will be automatically deleted)</msg>
+  <msg key="folders">Folders</msg>
+  <msg key="folders_created">Folders created</msg>
+  <msg key="forward">Forward</msg>
+  <msg key="from">From</msg>
+  <msg key="has_been_moved_to">The conversation has been moved to</msg>
+  <msg key="hide_details">Hide details</msg>
+  <msg key="important">Important</msg>
+  <msg key="inbox">Inbox</msg>
+  <msg key="insert_signature">Insert signature</msg>
+  <msg key="lt_folder_name_error">The name of the folder may not be longer than 20 characters</msg>
+  <msg key="max_show_messages">Conversations per page</msg>
+  <msg key="me">me</msg>
+  <msg key="message">Message</msg>
+  <msg key="message_sent_by">Message sent by</msg>
+  <msg key="messages">Messages</msg>
+  <msg key="more_actions">More actions</msg>
+  <msg key="more_msg_moved_to">conversations have been moved to</msg>
+  <msg key="more_msg_read">conversations have been marked as read.</msg>
+  <msg key="more_msg_unread">conversations have been marked as unread.</msg>
+  <msg key="move_to">Move to</msg>
+  <msg key="msg_cancel">The information be lost. Do you want to continue?</msg>
+  <msg key="msg_continue_empty">This action will affect %total_messages% conversation(s) in Trash. Are you sure you want to continue?</msg>
+  <msg key="msg_delete_folder">The folder is deleted, the messages are moved out to receive. Are sure to continue?</msg>
+  <msg key="msg_has_been_sent">Your message has been sent.</msg>
+  <msg key="msg_invalid_number">You must enter valid number in size of folder</msg>
+  <msg key="msg_invalid_user">is an invalid user. </msg>
+  <msg key="msg_loading">Loading</msg>
+  <msg key="msg_read">The conversation has been marked as read.</msg>
+  <msg key="msg_select_msg">You must select at least one message</msg>
+  <msg key="msg_subject_forward">fwd</msg>
+  <msg key="msg_subject_reply">Re</msg>
+  <msg key="msg_system">Messaging system: to send messages to people who belong to this group if you want to send messages to a person belonging to another group, you ought to go to that course/community and send from there.</msg>
+  <msg key="msg_unread">The conversation has been marked as unread.</msg>
+  <msg key="name_new_folder">Write the name of the new Folder</msg>
+  <msg key="new_folder">New Folder</msg>
+  <msg key="new_folder_name_error">Please, write a name of the new folder</msg>
+  <msg key="newer">&lt; Newer</msg>
+  <msg key="newer_msg">Newer</msg>
+  <msg key="newest">&lt;&lt; Newest</msg>
+  <msg key="no">No</msg>
+  <msg key="no_members_found">No members found</msg>
+  <msg key="no_messages_found">You don't have any messages here.</msg>
+  <msg key="no_subject">no subject</msg>
+  <msg key="normal">Normal</msg>
+  <msg key="of">Of</msg>
+  <msg key="older">Older &gt;</msg>
+  <msg key="older_msg">Older</msg>
+  <msg key="oldest">Oldest &gt;&gt;</msg>
+  <msg key="other_msg_reply">You are answering another message.</msg>
+  <msg key="print">Print</msg>
+  <msg key="range_messages_show">%first_row% -- %last_number% Of %total_messages%</msg>
+  <msg key="recipients">Recipients</msg>
+  <msg key="recipients_empty">Please specify at least one recipient.</msg>
+  <msg key="reply">Reply</msg>
+  <msg key="reply_all">Reply all</msg>
+  <msg key="save">Save</msg>
+  <msg key="save_info">Save</msg>
+  <msg key="save_sent_message">Save sent message</msg>
+  <msg key="search">Search</msg>
+  <msg key="send">Send</msg>
+  <msg key="send_to_all">Send to everyone</msg>
+  <msg key="sending">Sending</msg>
+  <msg key="sent">Sent</msg>
+  <msg key="show_details">Show details</msg>
+  <msg key="signature">Signature</msg>
+  <msg key="subject">Subject</msg>
+  <msg key="subject_empty">Send message without a subject?</msg>
+  <msg key="subject_mail">%user_name% has sent you a message in the community &quot;%community_name%&quot;</msg>
+  <msg key="sucsessful_create_folder">Sucsessful create folder</msg>
+  <msg key="to">To</msg>
+  <msg key="to_help">[i] If you know the name and/or last name of the user that sends the message, type it into the text field and will show to users who match the writing in this field</msg>
+  <msg key="today">Today</msg>
+  <msg key="trash">Trash</msg>
+  <msg key="view_messages">View messages</msg>
+  <msg key="write_message">Write message</msg>
+  <msg key="write_message_to">Writing message to</msg>
+  <msg key="yes">Yes</msg>
+</message_catalog>
Index: openacs-4/packages/messages/catalog/messages.es_ES.ISO-8859-1.xml
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/catalog/messages.es_ES.ISO-8859-1.xml,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/catalog/messages.es_ES.ISO-8859-1.xml	31 Aug 2010 21:30:54 -0000	1.1
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<message_catalog package_key="messages" locale="es_ES" charset="ISO-8859-1">
+
+  <msg key="add">Agregar</msg>
+  <msg key="add_attachment">Adjuntar archivo</msg>
+  <msg key="as_read">Marcar como leido</msg>
+  <msg key="as_unread">Marcar como no le�do</msg>
+  <msg key="at">de</msg>
+  <msg key="attachments">Archivos adjuntos</msg>
+  <msg key="back">&amp;lt;&amp;lt; Regresar</msg>
+  <msg key="back_folders">Regresar a Carpetas</msg>
+  <msg key="body_empty">�Deseas enviar este mensaje sin texto?</msg>
+  <msg key="body_mail">%user_name% te ha enviado un mensaje:
+
+-------------------------------------
+
+%subject% 
+
+%msg_content%
+
+-------------------------------------
+
+lo puedes ver en el siguiente enlace:
+
+http://yourpage.com/%message_url%
+
+</msg>
+  <msg key="cancel">Cancelar</msg>
+  <msg key="close_window">Cerrar Ventana</msg>
+  <msg key="community">Comunidad</msg>
+  <msg key="compose_mail">Redactar Correo</msg>
+  <msg key="configuration">Configuraci�n</msg>
+  <msg key="confirm_send_msg">Tu mensaje ha sido enviado.</msg>
+  <msg key="confirm_trash_empty">Todos los mensajes de la papelera se han suprimido definitivamente</msg>
+  <msg key="create">Crear</msg>
+  <msg key="date">Fecha</msg>
+  <msg key="delete">Borrar</msg>
+  <msg key="delete_confirm">�Desea continuar?</msg>
+  <msg key="discard">Descartar</msg>
+  <msg key="drafts">Borradores</msg>
+  <msg key="edit_folder">Para editar el nombre, haga click sobre el nombre del folder.</msg>
+  <msg key="edit_name_folder">Editar</msg>
+  <msg key="empty_trash">Vaciar la Papelera ahora</msg>
+  <msg key="folder_create_successfully">El folder se ha creado satisfactoriamente</msg>
+  <msg key="folder_name">Nombre del Folder</msg>
+  <msg key="folder_trash_msg">(los mensajes que se encuentren en la Papelera durante m�s de 15 d�as se suprimir�n autom�ticamente)</msg>
+  <msg key="folders">Carpetas</msg>
+  <msg key="folders_created">Carpetas creadas</msg>
+  <msg key="forward">Reenviar</msg>
+  <msg key="from">De</msg>
+  <msg key="has_been_moved_to">La conversaci�n se ha movido a</msg>
+  <msg key="hide_details">Ocultar Detalles</msg>
+  <msg key="important">Importante</msg>
+  <msg key="inbox">Recibidos</msg>
+  <msg key="insert_signature">Insertar firma</msg>
+  <msg key="lt_folder_name_error">El nombre del folder no puede ser mayor a 20 caracteres.</msg>
+  <msg key="max_show_messages">Mensajes por p�gina</msg>
+  <msg key="me">Usuario</msg>
+  <msg key="message">Mensaje</msg>
+  <msg key="message_sent_by">Mensaje enviado por</msg>
+  <msg key="messages">Mensajes</msg>
+  <msg key="more_actions">M�s acciones</msg>
+  <msg key="more_msg_moved_to">conversaciones se han movido a</msg>
+  <msg key="more_msg_read">conversaciones se han marcado como le�das.</msg>
+  <msg key="more_msg_unread">conversaciones se han marcado como no le�das.</msg>
+  <msg key="move_to">Mover a</msg>
+  <msg key="msg_cancel">La informaci�n se perder�. �Desea continuar?</msg>
+  <msg key="msg_continue_empty">Esta acci�n afectara a %total_messages% conversaci�n(es) en Papelera. �Esta seguro de que deseas continuar?</msg>
+  <msg key="msg_delete_folder">La carpeta se elimina, los mensajes se mover�n a Recibidos. �Est� seguro de continuar?</msg>
+  <msg key="msg_has_been_sent">Tu mensaje ha sido enviado.</msg>
+  <msg key="msg_invalid_number">Debe de ingresar n�mero v�lido en mensajes por folder</msg>
+  <msg key="msg_invalid_user">es un usuario no v�lido.</msg>
+  <msg key="msg_loading">Cargando</msg>
+  <msg key="msg_read">La conversaci�n se ha marcado como le�da.</msg>
+  <msg key="msg_select_msg">Debe de seleccionar al menos un mensaje</msg>
+  <msg key="msg_subject_forward">Rv</msg>
+  <msg key="msg_subject_reply">Re</msg>
+  <msg key="msg_system">Sistema de env�o de mensajes: permite enviar mensajes a las personas que pertenecen a este grupo, si deseas enviar mensajes a una persona que pertenece a otro grupo, debeis ir a dicho curso/comunidad y enviar el mensaje desde alli.</msg>
+  <msg key="msg_unread">La conversaci�n se ha marcado como no le�da.</msg>
+  <msg key="name_new_folder">Escribir el nombre del nuevo folder</msg>
+  <msg key="new_folder">Nuevo Folder</msg>
+  <msg key="new_folder_name_error">Por favor, escriba el nombre del nuevo folder</msg>
+  <msg key="newer">&amp;lt; Anterior</msg>
+  <msg key="newer_msg">Anterior</msg>
+  <msg key="newest">&amp;lt;&amp;lt; Primera p�gina </msg>
+  <msg key="no">No</msg>
+  <msg key="no_members_found">No se ha encontrado ning�n miembro.</msg>
+  <msg key="no_messages_found">Usted no tiene ning�n mensaje.</msg>
+  <msg key="no_subject">sin asunto</msg>
+  <msg key="normal">Normal</msg>
+  <msg key="of">De</msg>
+  <msg key="older">Siguiente &amp;gt;</msg>
+  <msg key="older_msg">Posterior</msg>
+  <msg key="oldest">�ltima p�gina &amp;gt;&amp;gt;</msg>
+  <msg key="other_msg_reply">Usted est� respondiendo un mensaje.</msg>
+  <msg key="print">Imprimir</msg>
+  <msg key="range_messages_show">%first_row% -- %last_number% De %total_messages%</msg>
+  <msg key="recipients">Destinatarios</msg>
+  <msg key="recipients_empty">Especifica al menos un destinatario.</msg>
+  <msg key="reply">Responder</msg>
+  <msg key="reply_all">Responder a todos</msg>
+  <msg key="save">Guardados</msg>
+  <msg key="save_info">Guardar</msg>
+  <msg key="save_sent_message">Guardar mensaje enviado</msg>
+  <msg key="search">Buscar</msg>
+  <msg key="send">Enviar</msg>
+  <msg key="send_to_all">Enviar a todos</msg>
+  <msg key="sending">Enviando</msg>
+  <msg key="sent">Enviados</msg>
+  <msg key="show_details">Mostrar detalles</msg>
+  <msg key="signature">Firma</msg>
+  <msg key="subject">Asunto</msg>
+  <msg key="subject_empty">�Deseas enviar este mensaje sin asunto?</msg>
+  <msg key="subject_mail">%user_name% te ha enviado un mensaje en la comunidad &quot;%community_name%&quot;</msg>
+  <msg key="sucsessful_create_folder">Folder creado satisfactoriamente</msg>
+  <msg key="to">Para</msg>
+  <msg key="to_help">[i] Si conoce el nombre y/o apellidos del usuario al que le envia el mensaje, escr�balo en el campo de texto Para y se le mostrar�n aquellos usuarios que coincidan con lo escrito en dicho campo</msg>
+  <msg key="today">Hoy</msg>
+  <msg key="trash">Papelera</msg>
+  <msg key="view_messages">Ver mensajes</msg>
+  <msg key="write_message">Redactar correo</msg>
+  <msg key="yes">Si</msg>
+</message_catalog>
Index: openacs-4/packages/messages/sql/oracle/messages-create.sql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/sql/oracle/messages-create.sql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/sql/oracle/messages-create.sql	31 Aug 2010 21:29:50 -0000	1.1
@@ -0,0 +1,114 @@
+--
+-- Assessment Package
+--
+-- @author pedro@viaro.net
+-- @creation-date 2009-10-30
+--
+
+
+create sequence messages_msg_sequence start with 1;
+--messages
+create table messages_messages  (
+        msg_id               integer not null
+                             constraint messages_messages_msg_id_pk
+                             primary key,
+        owner_id             integer not null,
+        community_id         integer not null,
+        sent_date            date default sysdate not null,
+        parent_id            integer,
+        mime_type           varchar2(200) default 'text/plain',
+        subject             varchar2(200),
+        msg_content         clob
+);
+
+create index messages_community_id_index on messages_messages(community_id);
+create index messages_parent_id_index on messages_messages(parent_id);
+
+--recipients mail
+
+create table  messages_recipients (
+    msg_id          integer not null
+                    constraint recipients_party_id_fk
+                    references messages_messages(msg_id)
+                    on delete cascade,
+    party_id        integer not null
+                    constraint recipients_msg_id_fk
+                    references parties(party_id)
+                    on delete cascade,
+    constraint messages_recipients_pk primary key (msg_id, party_id)
+);
+
+create table  messages_recipients_roles (
+    msg_id          integer not null
+                    constraint recipients_roles_msg_id_fk
+                    references messages_messages(msg_id)
+                    on delete cascade,
+    rel_id          varchar(100) not null
+                    constraint recipients_rel_id_fk
+                    references acs_rel_types (rel_type)
+                    on delete cascade,
+    constraint messages_recipients_rel_pk primary key (msg_id, rel_id)
+);
+
+--Attachments
+create table messages_attachments (
+    msg_id          integer not null
+                    constraint messages_attachment_msg_id_fk
+                    references messages_messages
+                    on delete cascade,
+    attachment_id   integer not null,
+    attachment_item_id integer not null,
+    constraint msg_attachment_pk primary key (msg_id,attachment_id)
+);
+
+--user messages to specific folder
+create table messages_user_messages (
+    msg_id          integer not null
+                    constraint user_messages_msg_id_fk
+                    references messages_messages
+                    on delete cascade,
+    user_id         integer not null
+                    constraint user_messages_user_id_fk
+                    references users
+                    on delete cascade,
+    parent_id       integer
+                    constraint user_msg_parent_id_fk
+                    references messages_messages(msg_id)
+                    on delete cascade,
+    folder_id       integer default 0,
+    new_p           char default 't'
+                    check (new_p in ('t','f')),
+    constraint messages_user_messages_pk primary key (msg_id,user_id,folder_id)
+);
+
+create index user_messages_user_id_index on messages_user_messages(user_id);
+
+--user folders
+create sequence messages_folders_sequence start with 5;
+create table messages_folders (
+    user_id         integer
+                    constraint messages_folders_user_id_fk
+                    references users
+                    on delete cascade,
+    folder_id       integer not null,
+    folder_name     varchar(50) not null
+);
+
+insert into messages_folders values(0,0,'#messages.inbox#');
+insert into messages_folders values(0,1,'#messages.sent#');
+insert into messages_folders values(0,2,'#messages.save#');
+insert into messages_folders values(0,3,'#messages.trash#');
+
+
+--message configuration
+create table messages_options (
+        user_id             integer 
+                            constraint messages_options_pk
+                            primary key
+                            constraint messages_options_user_fk
+                            references users,
+        max_messages        integer default 50,
+        save_sent_p         integer default 1,
+        signature_p         integer default 0,
+        signature           varchar2(1000) default ''
+);
Index: openacs-4/packages/messages/sql/oracle/messages-drop.sql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/sql/oracle/messages-drop.sql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/sql/oracle/messages-drop.sql	31 Aug 2010 21:30:42 -0000	1.1
@@ -0,0 +1,9 @@
+drop sequence messages_msg_sequence;
+drop table  messages_recipients
+drop table messages_attachments;
+drop table messages_user_messages;
+drop index messages_user_messages_index;
+drop sequence messages_folders_sequence;
+drop table messages_folders;
+drop table messages_options;
+drop table messages_messages;
\ No newline at end of file
Index: openacs-4/packages/messages/sql/postgresql/messages-create.sql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/sql/postgresql/messages-create.sql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/sql/postgresql/messages-create.sql	31 Aug 2010 21:30:42 -0000	1.1
@@ -0,0 +1,137 @@
+--
+-- Assessment Package
+--
+-- @author pedro@viaro.net
+-- @creation-date 2009-10-30
+--
+
+
+create sequence messages_msg_sequence start with 1;
+--messages
+create table messages_messages  (
+        msg_id               integer not null
+                             constraint messages_messages_msg_id_pk
+                             primary key,
+        owner_id             integer not null,
+        community_id         integer not null,
+        sent_date            timestamp default now() not null,
+        parent_id            integer,
+        mime_type           varchar(200) default 'text/plain',
+        subject             varchar(200),
+        msg_content         text
+);
+
+create index messages_community_id_index on messages_messages(community_id);
+create index messages_parent_id_index on messages_messages(parent_id);
+
+--recipients mail
+
+create table  messages_recipients (
+    msg_id          integer not null
+                    constraint recipients_party_id_fk
+                    references messages_messages(msg_id)
+                    on delete cascade,
+    party_id        integer not null
+                    constraint recipients_msg_id_fk
+                    references parties(party_id)
+                    on delete cascade,
+    constraint messages_recipients_pk primary key (msg_id, party_id)
+);
+
+create table  messages_recipients_roles (
+    msg_id          integer not null
+                    constraint recipients_roles_msg_id_fk
+                    references messages_messages(msg_id)
+                    on delete cascade,
+    rel_id          varchar(100) not null
+                    constraint recipients_rel_id_fk
+                    references acs_rel_types (rel_type)
+                    on delete cascade,
+    constraint messages_recipients_rel_pk primary key (msg_id, rel_id)
+);
+
+--Attachments
+create table messages_attachments (
+    msg_id          integer not null
+                    constraint messages_attachment_msg_id_fk
+                    references messages_messages
+                    on delete cascade,
+    attachment_id   integer not null,
+    attachment_item_id integer not null,
+    constraint msg_attachment_pk primary key (msg_id,attachment_id)
+);
+
+--user messages to specific folder
+create table messages_user_messages (
+    msg_id          integer not null
+                    constraint user_messages_msg_id_fk
+                    references messages_messages
+                    on delete cascade,
+    user_id         integer not null
+                    constraint user_messages_user_id_fk
+                    references users
+                    on delete cascade,
+    parent_id       integer
+                    constraint user_msg_parent_id_fk
+                    references messages_messages(msg_id)
+                    on delete cascade,
+    folder_id       integer default 0,
+    new_p           char default 't'
+                    check (new_p in ('t','f')),
+    constraint messages_user_messages_pk primary key (msg_id,user_id,folder_id)
+);
+
+create index user_messages_user_id_index on messages_user_messages(user_id);
+
+--user folders
+create sequence messages_folders_sequence start with 5;
+create table messages_folders (
+    user_id         integer
+                    constraint messages_folders_user_id_fk
+                    references users
+                    on delete cascade,
+    folder_id       integer not null,
+    folder_name     varchar(50) not null
+);
+
+insert into messages_folders values(0,0,'#messages.inbox#');
+insert into messages_folders values(0,1,'#messages.sent#');
+insert into messages_folders values(0,2,'#messages.save#');
+insert into messages_folders values(0,3,'#messages.trash#');
+
+
+--message configuration
+create table messages_options (
+        user_id             integer 
+                            constraint messages_options_pk
+                            primary key
+                            constraint messages_options_user_fk
+                            references users,
+        max_messages        integer default 50,
+        save_sent_p         integer default 1,
+        signature_p         integer default 0,
+        signature           varchar(1000) default ''
+);
+
+
+CREATE FUNCTION concat (text, text) RETURNS text AS $$
+  DECLARE
+    t text;
+  BEGIN
+    IF  character_length($1) > 0 THEN
+      t = $1 ||', '|| $2;
+    ELSE
+      t = $2;
+    END IF;
+    RETURN t;
+  END;
+$$ LANGUAGE plpgsql;
+
+
+CREATE AGGREGATE concat (
+    BASETYPE = text,
+    SFUNC = textcat,
+    STYPE = text,
+    INITCOND = ''
+);
+
Index: openacs-4/packages/messages/sql/postgresql/messages-drop.sql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/sql/postgresql/messages-drop.sql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/sql/postgresql/messages-drop.sql	31 Aug 2010 21:30:42 -0000	1.1
@@ -0,0 +1,12 @@
+drop sequence messages_msg_sequence;
+drop index messages_community_id_index;
+drop index messages_parent_id_index;
+drop table  messages_recipients;
+drop table messages_recipients_roles;
+drop table messages_attachments;
+drop table messages_user_messages;
+drop index user_messages_user_id_index;
+drop sequence messages_folders_sequence;
+drop table messages_folders;
+drop table messages_options;
+drop table messages_messages cascade;
\ No newline at end of file
Index: openacs-4/packages/messages/tcl/apm-callback-procs.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/tcl/apm-callback-procs.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/tcl/apm-callback-procs.tcl	31 Aug 2010 21:30:42 -0000	1.1
@@ -0,0 +1,43 @@
+# /packages/messages/tcl/apm-callback-procs.tcl
+
+ad_library {
+
+    Messages Package APM callbacks library
+
+    Procedures that deal with installing, instantiating, mounting.
+
+    @author pedro@viaro.net
+    @creation-date 2009-11-26
+
+}
+
+namespace eval messages {}
+namespace eval messages::apm {}
+
+ad_proc -public messages::apm::package_install {
+
+} {
+    Does the integration whith the notifications package.
+} {
+    db_transaction {
+        #Create the conten types
+        content::type::new -content_type {messages_attachments} -supertype {content_revision} -pretty_name {Message Attachment} -pretty_plural {Message Attachments} -table_name {messages_attachments} -id_column {attachment_id}
+
+        #Create content type attributes for content type messages
+        content::type::attribute::new -content_type {messages_attachments} -attribute_name {attachment_item_id} -datatype {number} -pretty_name {Attachment Item ID} -column_spec {integer}
+        content::type::attribute::new -content_type {messages_attachments} -attribute_name {msg_id} -datatype {number} -pretty_name {Message ID} -column_spec {integer}
+     }
+}
+
+ad_proc -public messages::apm::package_instantiate {
+    -package_id:required
+} {
+
+    Define Message folders
+
+} {
+    # create a content folder
+    set folder_id [content::folder::new -name "messages_attachments_$package_id" -label "messages_attachments_$package_id" -package_id $package_id ]
+    # register the allowed content types for a folder
+    content::folder::register_content_type -folder_id $folder_id -content_type messages_attachments -include_subtypes t
+}
\ No newline at end of file
Index: openacs-4/packages/messages/tcl/messages-procs.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/tcl/messages-procs.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/tcl/messages-procs.tcl	31 Aug 2010 21:30:42 -0000	1.1
@@ -0,0 +1,285 @@
+ad_library {
+    Procedures in the messages namespace.
+
+    @creation-date 12-28-2009
+    @author pedro@viaro.net
+}
+
+namespace eval messages {} 
+
+ad_proc -public messages::send_mail { 
+    -contacts_ids
+    -community_id
+    -owner_id 
+    -subject
+    {-message_url ""}
+    {-parent_id ""}
+    {-count_attachment 0}
+    {-message ""}
+    {-rel_types ""}
+} { 
+    Send mail to diferent users
+} {
+    set community_name [dotlrn_community::get_community_name $community_id]
+    set user_id [ad_conn user_id]
+    set user_name [acs_user::get_element -user_id $user_id -element name]
+    if { ![exists_and_not_null parent_id] }  {
+        set parent_id ""
+    }
+    db_transaction {
+        set msg_id [db_nextval messages_msg_sequence]
+        db_dml send_mail {}
+    }
+    
+    if { ![empty_string_p $rel_types] } {
+        set rel_types "'[join $rel_types "','"]'"
+        set rel_types_user_ids_list [db_list get_rel_types_user_ids {}]
+        if { [empty_string_p $contacts_ids] } {
+            append contacts_ids_roles [join $rel_types_user_ids_list ","]
+        } else {
+            append contacts_ids_roles "," [join $rel_types_user_ids_list ","]
+        }
+    } else {
+        set contacts_ids_roles ""
+    }
+
+    set party_id_list [lsort -unique [split $contacts_ids ","]]
+    if { ![empty_string_p $contacts_ids_roles] } {
+        set party_id_list_roles [lsort -unique [split $contacts_ids_roles ","]]
+    } else {
+        set party_id_list_roles [list]
+    }
+   
+    foreach party_id $party_id_list {
+        set email_user [acs_user::get_element -user_id $party_id -element email]
+        set package_id [dotlrn_community::get_package_id_from_package_key -package_key kernel -community_id 0]
+        set from_addr [parameter::get -parameter "OutgoingSender" -package_id $package_id]
+	## E-mail notification is disabled by default, to enable just uncomment the following line in every place in this file
+        #acs_mail_lite::send -send_immediately -to_addr $email_user -from_addr $from_addr -subject [encoding convertto iso8859-15 [_ messages.subject_mail]] -body [_ messages.body_mail]
+        db_dml recipients {}
+        db_dml user_messages {}
+    }
+    if { ![empty_string_p $rel_types] } {
+        foreach party_id $party_id_list_roles {
+            if { [lsearch $party_id_list $party_id] == -1 && ![empty_string_p $party_id]} {
+                set email_user [acs_user::get_element -user_id $party_id -element email]
+                set package_id [dotlrn_community::get_package_id_from_package_key -package_key kernel -community_id 0]
+                set from_addr [parameter::get -parameter "OutgoingSender" -package_id $package_id]
+		## E-mail notification is disabled by default, to enable just uncomment the following line in every place in this file
+                #acs_mail_lite::send -send_immediately -to_addr $email_user -from_addr $from_addr -subject [encoding convertto iso8859-15 [_ messages.subject_mail]] -body [_ messages.body_mail]
+                db_dml user_messages {}
+            }
+        }
+        set rel_types [split $rel_types ","]
+        foreach rel_type $rel_types {
+            db_dml recipients_roles {}
+        }
+
+    }
+    set save_sent_p [db_string get_save_sent {} -default 1]
+
+    if { $save_sent_p } {
+        db_dml sent_message {}
+    }
+    return $msg_id
+    ad_script_abort
+}
+
+ad_proc -public messages::reply_mail { 
+    -general_parent_id
+    -contacts_ids
+    -community_id
+    -owner_id 
+    -subject
+    {-message_url ""}
+    {-parent_id ""}
+    {-count_attachment 0}
+    {-message ""}
+    {-msg_id_old ""}
+    {-attachment_ids ""}
+    {-rel_types ""}
+} { 
+    Send reply, reply-all and forward mail to diferent users
+} {
+    set message [list "$message" "text/html"]
+    set community_name [dotlrn_community::get_community_name $community_id]
+    set user_id [ad_conn user_id]
+    set user_name [acs_user::get_element -user_id $user_id -element name]
+    #set subject [db_string get_subject {}]
+    db_transaction {
+        set msg_id [db_nextval messages_msg_sequence]
+        if { !$general_parent_id } {
+            set lang [string range [lang::conn::locale -package_id [ad_conn package_id]] 0 2]
+            set subject "[_ messages.msg_subject_forward]: $subject"
+            db_dml send_mail_forward {}
+            if { ![empty_string_p $attachment_ids] } {
+                set attachment_ids_clause "and attachment_id in ($attachment_ids)"
+            } else {
+                set attachment_ids_clause ""
+            }
+            set attachment_list [db_list_of_lists get_attachment_id { *SQL*}]
+            foreach attachment $attachment_list {
+                set attachment_id [lindex $attachment 0]
+                set attachment_item_id [lindex $attachment 1]
+                db_dml attachment_files {}
+            }
+           } else {
+            set subject "[_ messages.msg_subject_reply]: $subject"
+            if { [string equal $contacts_ids ""] } {
+                set contacts_ids "$user_id"
+            } else {
+                append contacts_ids ",$user_id"
+            }
+            db_dml send_mail {}
+        }
+    }
+    if { ![empty_string_p $rel_types] } {
+        set rel_types "'[join $rel_types "','"]'"
+        set rel_types_user_ids_list [db_list get_rel_types_user_ids {}]
+        if { [empty_string_p $contacts_ids] } {
+            append contacts_ids_roles [join $rel_types_user_ids_list ","]
+        } else {
+            append contacts_ids_roles "," [join $rel_types_user_ids_list ","]
+        }
+    } else {
+        set contacts_ids_roles ""
+    }
+    set party_id_list [lsort -unique [split $contacts_ids ","]]
+    set party_id_list_roles [lsort -unique [split $contacts_ids_roles ","]]
+    foreach party_id $party_id_list {
+        set email_user [acs_user::get_element -user_id $party_id -element email]
+        set package_id [dotlrn_community::get_package_id_from_package_key -package_key kernel -community_id 0]
+        set from_addr [parameter::get -parameter "OutgoingSender" -package_id $package_id]
+	## E-mail notification is disabled by default, to enable just uncomment the following line in every place in this file
+        #acs_mail_lite::send -send_immediately -to_addr $email_user -from_addr $from_addr -subject [encoding convertto iso8859-15 [_ messages.subject_mail]] -body [_ messages.body_mail]
+        db_dml recipients {}
+        if { !$general_parent_id } {
+            db_dml user_messages_forward {}
+        } else {
+            db_dml user_messages {}
+        }
+    }
+    if { ![empty_string_p $rel_types] } {
+        foreach party_id $party_id_list_roles {
+            if { [lsearch $party_id_list $party_id] == -1 && ![empty_string_p $party_id]} {
+                set email_user [acs_user::get_element -user_id $party_id -element email]
+                set package_id [dotlrn_community::get_package_id_from_package_key -package_key kernel -community_id 0]
+                set from_addr [parameter::get -parameter "OutgoingSender" -package_id $package_id]
+		## E-mail notification is disabled by default, to enable just uncomment the following line in every place in this file
+                #acs_mail_lite::send -send_immediately -to_addr $email_user -from_addr $from_addr -subject [encoding convertto iso8859-15 [_ messages.subject_mail]] -body [_ messages.body_mail]
+                if { !$general_parent_id } {
+                    db_dml user_messages_forward {}
+                } else {
+                    db_dml user_messages {}
+                }
+            }
+        }
+        set rel_types [split $rel_types ","]
+        foreach rel_type $rel_types {
+            db_dml recipients_roles {}
+        }
+    }
+    set party_id [ad_conn user_id]
+    set save_sent_p [db_string get_save_sent {} -default 1]
+    if { $save_sent_p } {
+        if { !$general_parent_id } {
+            db_dml sent_message_forward {}
+        } else {
+            db_dml sent_message {}
+        }
+    }
+    return $msg_id
+    ad_script_abort
+}
+
+ad_proc -public messages::delete_trash_messages { 
+
+} { 
+    Delete all messages in the thash folder
+} {
+    set folder_id [messages::get_folder_id -folder_name "trash"]
+    set limit_days_in_trash 15
+    db_transaction {
+        db_dml delete_messages {}
+    }
+}
+
+ad_proc -public messages::get_folder_id { 
+    -folder_name
+} { 
+    Get the folder id to standar folders to users.
+} {
+    switch $folder_name {
+	inbox {
+	    return 0
+	}
+	sent {
+	    return 1
+	}
+	save {
+	    return 2
+	}
+	trash {
+	    return 3
+	}
+    }
+}
+
+
+ad_proc -public messages::get_history_conversation {
+    -parent_id
+    -msg_id
+    -user_id
+} {
+    Get the history conversation until the message forward 
+} {
+    set blockquote_conversation_begin "<blockquote style='border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;'><br/><br/>"
+    set blockquote_conversation_end "</blockquote>"
+    set conversation "<br><div id=\"history_conversation\">"
+    set count_messages 0
+    append not_folder_ids [messages::get_folder_id -folder_name "sent"] "," \
+                      [messages::get_folder_id -folder_name "trash"]
+    db_multirow messages get_history_conversation { *SQL* } {
+        set day_recive [lc_time_fmt $sent_date "%d %b, %Y" ]
+        set ansi_time "$day_recive - $time"
+        set msg_content [template::util::richtext::get_property contents $msg_content]
+        append conversation "$blockquote_conversation_begin
+                    El $day_recive, a las $time, [acs_user::get_element -user_id $owner_id -element name] escribio: <br><br>
+                    $msg_content<br><br>"
+        incr count_messages
+    }
+    for {set i 0} {$i <= $count_messages} {incr i} {
+        append blockqoute_end $blockquote_conversation_end
+    }
+    append conversation "$blockqoute_end </div>"
+    return $conversation
+}
+
+ad_proc -public messages::get_community_users_fb {
+    -community_id
+} {
+    Memoize de members of the community
+} {
+    #Recipients are obtained and placed in the form autocomplete style
+    set contacts_list [join [db_list members { *SQL* }] ","]
+    return $contacts_list
+}
+
+ad_proc -public messages::string_truncate_middle {
+    {-ellipsis "..."}
+    {-len "100"}
+    -string
+} {
+    cut middle part of a string in case it is to long copied from xotcl-request-monitor
+} {
+    set string [string trim $string]
+    if {[string length $string]>$len} {
+        set half [expr {($len-2)/2}]
+        set left  [string trimright [string range $string 0 $half]]
+        set right [string trimleft [string range $string end-$half end]]
+        return $left$ellipsis$right
+    }
+    return $string
+
+}
+
Index: openacs-4/packages/messages/tcl/messages-procs.xql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/tcl/messages-procs.xql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/tcl/messages-procs.xql	31 Aug 2010 21:30:42 -0000	1.1
@@ -0,0 +1,203 @@
+<?xml version="1.0"?>
+
+<queryset>
+
+<fullquery name="messages::send_mail.send_mail">
+      <querytext>
+	insert into messages_messages (msg_id, owner_id, community_id, subject, msg_content) 
+                   values (:msg_id,:owner_id,:community_id,:subject,:message)
+        </querytext>
+</partialquery>
+
+    <fullquery name="messages::send_mail.recipients">
+        <querytext>
+            insert into messages_recipients (msg_id, party_id) 
+                   values (:msg_id,:party_id)
+        </querytext>
+    </fullquery>
+
+    <fullquery name="messages::send_mail.recipients_roles">
+        <querytext>
+            insert into messages_recipients_roles (msg_id, rel_id) 
+                   values (:msg_id,$rel_type)
+        </querytext>
+    </fullquery>
+
+    <fullquery name="messages::send_mail.user_messages">
+        <querytext>
+            insert into messages_user_messages (msg_id, user_id, folder_id, new_p,parent_id) 
+                   values (:msg_id,:party_id,0,'t',:msg_id)
+        </querytext>
+    </fullquery>
+
+    <fullquery name="messages::send_mail.get_save_sent">
+        <querytext>
+            select save_sent_p
+            from messages_options
+            where user_id = :user_id
+        </querytext>
+    </fullquery>
+
+    <fullquery name="messages::send_mail.sent_message">
+        <querytext>
+            insert into messages_user_messages (msg_id, user_id, folder_id, new_p,parent_id) 
+                   values (:msg_id,:owner_id,1,'t',:msg_id)
+        </querytext>
+    </fullquery>
+
+    <fullquery name="messages::reply_mail.update_new">
+        <querytext>
+            update messages_user_messages
+            set new_p = 't'
+            where msg_id = :msg_id
+        </querytext>
+    </fullquery>
+ 
+    <fullquery name="messages::reply_mail.get_attachment_id">
+        <querytext>
+            select attachment_id, attachment_item_id
+            from messages_attachments
+            where msg_id = :msg_id_old
+                  $attachment_ids_clause
+        </querytext>
+    </fullquery>
+
+    <fullquery name="messages::reply_mail.send_mail_forward">
+        <querytext>
+            insert into messages_messages (msg_id, owner_id, community_id, subject, msg_content) 
+                   values (:msg_id,:owner_id,:community_id,:subject,:message)
+        </querytext>
+    </fullquery> 
+
+    <fullquery name="messages::reply_mail.send_mail">
+        <querytext>
+            insert into messages_messages (msg_id, owner_id, community_id, subject, msg_content,parent_id) 
+                   values (:msg_id,:owner_id,:community_id,:subject,:message,:general_parent_id)
+        </querytext>
+    </fullquery> 
+
+    <fullquery name="messages::reply_mail.recipients">
+        <querytext>
+            insert into messages_recipients (msg_id, party_id) 
+                   values ($msg_id,$party_id)
+        </querytext>
+    </fullquery>
+
+    <fullquery name="messages::reply_mail.recipients_roles">
+        <querytext>
+            insert into messages_recipients_roles (msg_id, rel_id) 
+                   values (:msg_id,$rel_type)
+        </querytext>
+    </fullquery>
+
+    <fullquery name="messages::reply_mail.user_messages_forward">
+        <querytext>
+            insert into messages_user_messages (msg_id, user_id, folder_id, new_p,parent_id)
+                   values (:msg_id,:party_id,0,'t',:msg_id)
+        </querytext>
+    </fullquery>
+
+    <fullquery name="messages::reply_mail.sent_message_forward">
+        <querytext>
+            insert into messages_user_messages (msg_id, user_id, folder_id, new_p,parent_id) 
+                   values (:msg_id,:owner_id,1,'t',:msg_id)
+        </querytext>
+    </fullquery>
+
+    <fullquery name="messages::reply_mail.get_save_sent">
+        <querytext>
+            select save_sent_p
+            from messages_options
+            where user_id = :user_id
+        </querytext>
+    </fullquery>
+
+    <fullquery name="messages::reply_mail.attachment_files">
+        <querytext>
+            insert into messages_attachments values (:msg_id,:attachment_id,:attachment_item_id)
+        </querytext>
+    </fullquery>
+
+    <fullquery name="messages::reply_mail.sent_message">
+        <querytext>
+            insert into messages_user_messages (msg_id, user_id, folder_id, new_p,parent_id) 
+                   values (:msg_id,:owner_id,1,'t',:parent_id)
+        </querytext>
+    </fullquery>
+
+    <fullquery name="messages::reply_mail.user_messages">
+        <querytext>
+            insert into messages_user_messages (msg_id, user_id, folder_id, new_p,parent_id)
+                   values (:msg_id,:party_id,0,'t',:parent_id)
+        </querytext>
+    </fullquery>
+
+    <fullquery name="messages::delete_trash_mails.delete_messages">
+        <querytext>
+            delete from messages_user_messages 
+            where msg_id in ( select um.msg_id 
+                              from (select parent_id,msg_id from messages_user_messages where folder_id = :folder_id and msg_id = parent_id) um, 
+                                   (select msg_id,to_char(sent_date, 'DD-MM-YYYY') as sent_date from messages_messages where sent_date < (SELECT sysdate - :limit_days_in_trash FROM dual)) m
+                              where um.msg_id = m.msg_id
+                            )
+        </querytext>
+    </fullquery>
+
+    <fullquery name="messages::delete_trash_mails.get_msg_ids_users">
+        <querytext>
+            select msg_id, user_id from messages_user_messages where folder_id = :folder_id
+        </querytext>
+    </fullquery>
+
+    <fullquery name="messages::reply_mail.get_subject">
+        <querytext>
+            select subject from messages_messages where msg_id = :msg_id_old
+        </querytext>
+    </fullquery>
+
+    <fullquery name="messages::send_mail.get_rel_types_user_ids">
+        <querytext>
+            select user_id 
+            from dotlrn_member_rels_approved 
+            where community_id = :community_id 
+                  and rel_type in ($rel_types)
+        </querytext>
+    </fullquery>
+
+    <fullquery name="messages::reply_mail.get_rel_types_user_ids">
+        <querytext>
+            select user_id 
+            from dotlrn_member_rels_approved 
+            where community_id = :community_id 
+                  and rel_type in ($rel_types)
+        </querytext>
+    </fullquery>
+
+    <fullquery name="messages::get_history_conversation.get_history_conversation">
+        <querytext>
+            select msg_content, owner_id, subject,
+                   sent_date, to_char(sent_date, 'HH24:MI:SS') as time
+            from messages_messages
+            where msg_id in (select msg_id
+                             from messages_user_messages
+                             where user_id = :user_id
+                                 and folder_id not in ($not_folder_ids)
+                                 and parent_id = :parent_id)
+            and msg_id <= :msg_id
+            order by msg_id desc
+
+        </querytext>
+    </fullquery>
+
+    <fullquery name="messages::get_community_users_fb.members">
+        <querytext>
+            select '{name:"'||name.party_name||'", p:"'||name.person_id||'"}'
+            from (select persons.first_names||' '||persons.last_name as party_name, persons.person_id
+                  from persons,dotlrn_member_rels_approved
+                  where dotlrn_member_rels_approved.community_id = :community_id
+                        and dotlrn_member_rels_approved.user_id = persons.person_id
+                   order by party_name) as name
+        </querytext>
+    </fullquery> 
+
+</queryset> 
\ No newline at end of file
Index: openacs-4/packages/messages/www/contacts.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/contacts.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/contacts.adp	31 Aug 2010 21:30:45 -0000	1.1
@@ -0,0 +1,19 @@
+<if @option@ eq "reply">
+    <span id=span_@contact_id@ class="token">
+        <span><span><span><span>
+            <input type="hidden" value=@contact_id@ name="ids[]">@contact_name@
+            <span class="x" onclick=id_delete(@contact_id@)>&nbsp;</span>
+        </span></span></span></span>
+    </span>
+</if>
+<if @option@ eq "reply_all">
+    <multiple name="contact">
+        <span id=span_@contact.party_id@ class="token">
+            <span><span><span><span>
+                <input type="hidden" value=@contact.party_id@ name="ids[]"/>@contact.party_name@
+                <span class="x" onclick=id_delete(@contact.party_id@)>&nbsp;</span>
+            </span></span></span></span>
+        </span>
+    </multiple>
+</if>
+<if @option@ eq "forward">forward</if>
\ No newline at end of file
Index: openacs-4/packages/messages/www/contacts.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/contacts.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/contacts.tcl	31 Aug 2010 21:30:45 -0000	1.1
@@ -0,0 +1,27 @@
+ad_page_contract {
+    Update numbers of new messages in the menu
+
+    @author pedro@viaro.net
+    @creation-date 2009-11-17
+    @cvs-id $Id: contacts.tcl,v 1.1 2010/08/31 21:30:45 alvaror Exp $
+} {
+    msg_id
+    {option ""}
+}
+
+set community_id [dotlrn_community::get_community_id]
+
+if { [string equal $option "reply"]} {
+    db_1row owner_info {}
+}
+
+if { [string equal $option "reply_all"]} {
+    #gets all the recipients included in the message
+    db_multirow contact recipient_ids { *SQL* } { }
+}
+
+if { [string equal $option "forward"]} {
+    #when select forward, return only forward because in the javascript,
+    #this option is write nothing in the recipients.
+    set contacts_reply "forward"
+}
\ No newline at end of file
Index: openacs-4/packages/messages/www/contacts.xql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/contacts.xql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/contacts.xql	31 Aug 2010 21:30:45 -0000	1.1
@@ -0,0 +1,28 @@
+<?xml version="1.0"?>
+<queryset>
+
+    <fullquery name="owner_info">
+        <querytext>
+            select acs_users_all.first_names||' '||acs_users_all.last_name  as contact_name,
+                   acs_users_all.party_id as contact_id
+            from acs_users_all,
+                    dotlrn_member_rels_approved
+            where dotlrn_member_rels_approved.community_id = :community_id
+            and dotlrn_member_rels_approved.user_id = acs_users_all.user_id
+            and acs_users_all.user_id in (select owner_id from messages_messages where msg_id = :msg_id)
+        </querytext>
+    </fullquery> 
+
+    <fullquery name="recipient_ids">
+        <querytext>
+            select acs_users_all.first_names||' '||acs_users_all.last_name  as party_name,
+                   acs_users_all.party_id
+            from acs_users_all,
+                    dotlrn_member_rels_approved
+            where dotlrn_member_rels_approved.community_id = :community_id
+            and dotlrn_member_rels_approved.user_id = acs_users_all.user_id
+            and acs_users_all.user_id in (select party_id from messages_recipients where msg_id = :msg_id)
+        </querytext>
+    </fullquery> 
+
+</queryset>
\ No newline at end of file
Index: openacs-4/packages/messages/www/create-folder.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/create-folder.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/create-folder.adp	31 Aug 2010 21:30:42 -0000	1.1
@@ -0,0 +1 @@
+@response@
\ No newline at end of file
Index: openacs-4/packages/messages/www/create-folder.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/create-folder.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/create-folder.tcl	31 Aug 2010 21:30:42 -0000	1.1
@@ -0,0 +1,26 @@
+ad_page_contract {
+    Create a New folder.
+
+    @author pedro@viaro.net
+    @creation-date 2009-11-17
+    @cvs-id $Id: create-folder.tcl,v 1.1 2010/08/31 21:30:42 alvaror Exp $
+} {
+    folder_name
+} 
+
+set user_id [ad_conn user_id]
+set folder_id [db_nextval messages_folders_sequence]
+set folder_name [string trim "$folder_name"]
+#create a new folder but not is create when folder_name is empty or greater than 20 characteres.
+if { [empty_string_p $folder_name]  || [string length $folder_name] > 20} {
+    if { [empty_string_p $folder_name] } {
+        set response "[_ messages.new_folder_name_error]"
+    } else {
+        set response "[_ messages.lt_folder_name_error]"
+    }
+} else {
+    db_transaction {
+        db_dml create_new_folder {}
+    }
+    set response [_ messages.sucsessful_create_folder]
+}
\ No newline at end of file
Index: openacs-4/packages/messages/www/create-folder.xql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/create-folder.xql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/create-folder.xql	31 Aug 2010 21:30:42 -0000	1.1
@@ -0,0 +1,12 @@
+<?xml version="1.0"?>
+<queryset>
+
+    <fullquery name="create_new_folder">
+        <querytext>
+            insert into 
+            messages_folders (user_id,folder_id,folder_name)
+            values(:user_id,:folder_id,:folder_name)
+        </querytext>
+    </fullquery>
+
+</queryset>
\ No newline at end of file
Index: openacs-4/packages/messages/www/delete-message.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/delete-message.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/delete-message.adp	31 Aug 2010 21:30:43 -0000	1.1
@@ -0,0 +1 @@
+complete 
Index: openacs-4/packages/messages/www/delete-message.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/delete-message.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/delete-message.tcl	31 Aug 2010 21:30:43 -0000	1.1
@@ -0,0 +1,17 @@
+ad_page_contract {
+    Delete selected messages in the trash folder
+
+    @author pedro@viaro.net
+    @creation-date 2009-11-24
+    @cvs-id $Id: delete-message.tcl,v 1.1 2010/08/31 21:30:43 alvaror Exp $
+} {
+   msg_ids
+}
+
+set folder_id [messages::get_folder_id -folder_name "trash"]
+set community_id [dotlrn_community::get_community_id]
+set user_id [ad_conn user_id]
+
+db_transaction {
+    db_dml delete_messages {}
+} 
Index: openacs-4/packages/messages/www/delete-message.xql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/delete-message.xql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/delete-message.xql	31 Aug 2010 21:30:43 -0000	1.1
@@ -0,0 +1,14 @@
+<?xml version="1.0"?>
+<queryset>
+
+    <fullquery name="delete_messages">
+        <querytext>
+            delete from messages_user_messages 
+            where folder_id = :folder_id 
+                and user_id = :user_id
+                and msg_id in ($msg_ids)
+            
+        </querytext>
+    </fullquery>
+
+</queryset>
\ No newline at end of file
Index: openacs-4/packages/messages/www/empty-trash.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/empty-trash.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/empty-trash.adp	31 Aug 2010 21:30:43 -0000	1.1
@@ -0,0 +1 @@
+@folder_id@
\ No newline at end of file
Index: openacs-4/packages/messages/www/empty-trash.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/empty-trash.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/empty-trash.tcl	31 Aug 2010 21:30:43 -0000	1.1
@@ -0,0 +1,16 @@
+ad_page_contract {
+    Empty Trash
+
+    @author pedro@viaro.net
+    @creation-date 2010-01-22
+    @cvs-id $Id: empty-trash.tcl,v 1.1 2010/08/31 21:30:43 alvaror Exp $
+} {
+
+}
+
+set user_id [ad_conn user_id]
+set community_id [dotlrn_community::get_community_id]
+set folder_id [messages::get_folder_id -folder_name "trash"]
+db_transaction {
+    db_dml empty_trash {}
+}
\ No newline at end of file
Index: openacs-4/packages/messages/www/empty-trash.xql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/empty-trash.xql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/empty-trash.xql	31 Aug 2010 21:30:43 -0000	1.1
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<queryset>
+
+    <fullquery name="empty_trash">
+        <querytext>
+            delete from messages_user_messages 
+            where folder_id = :folder_id and user_id = :user_id
+        </querytext>
+    </fullquery>
+
+</queryset>
\ No newline at end of file
Index: openacs-4/packages/messages/www/inbox-oracle.xql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/inbox-oracle.xql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/inbox-oracle.xql	31 Aug 2010 21:30:44 -0000	1.1
@@ -0,0 +1,52 @@
+<?xml version="1.0"?>
+
+<queryset>
+   <rdbms><type>oracle</type><version>8.1.6</version></rdbms>
+   
+<fullquery name="get_messages">
+    <querytext>
+    select mm.last_msg_id, mm.msg_id, mm.number_conversation, mm.subject, wm_concat(distinct(p.first_names||' '||p.last_name)) as contacts, count(distinct(p.first_names)) as total_contacs, mm.owner_id
+    from persons p,
+        (select max(um.msg_id) as last_msg_id, um.parent_id as msg_id, count(um.msg_id) as number_conversation, m.subject, m.owner_id
+        from (select parent_id,msg_id from messages_user_messages where user_id = :user_id and folder_id = :folder_id) um,
+             (select msg_id,subject,owner_id from messages_messages where community_id = :community_id and msg_id in ($msg_ids)) m
+        where um.parent_id = m.msg_id
+        group by um.parent_id, m.subject, m.owner_id) mm
+    where p.person_id = mm.owner_id
+    group by mm.msg_id, mm.subject, mm.last_msg_id, mm.number_conversation, mm.owner_id
+    order by mm.last_msg_id desc 
+    </querytext>
+</fullquery>
+
+<fullquery name="get_contacts">
+    <querytext>
+        select distinct(owner_id)
+        from messages_messages
+        where parent_id = :msg_id
+            or msg_id = :msg_id
+            and rownum < 4
+    </querytext>
+</fullquery>
+
+<fullquery name="get_msg_ids">
+    <querytext>
+        select msg_id, last_msg_id
+        from (select paginate.*, rownum rowsub 
+            from (select max(m.msg_id) as last_msg_id, m.parent_id as msg_id, count(m.msg_id) as number_conversation
+                    from (select msg_id, coalesce(parent_id,msg_id) as parent_id 
+                          from messages_messages 
+                          where community_id = :community_id 
+                              and msg_id in (select parent_id 
+                                             from messages_user_messages 
+                                             where user_id = :user_id 
+                                                 and folder_id = :folder_id)) m
+                        group by m.parent_id
+                        order by last_msg_id desc
+                   ) paginate
+          where rownum <= :last_row)
+    where rowsub >= :first_row
+
+    </querytext>
+</fullquery> 
+
+</queryset>
Index: openacs-4/packages/messages/www/inbox-postgresql.xql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/inbox-postgresql.xql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/inbox-postgresql.xql	31 Aug 2010 21:30:45 -0000	1.1
@@ -0,0 +1,51 @@
+<?xml version="1.0"?>
+<queryset>
+   <rdbms><type>postgresql</type><version>7.3</version></rdbms>    
+
+<fullquery name="get_messages">
+    <querytext>
+    select mm.last_msg_id, mm.msg_id, mm.number_conversation, mm.subject, ARRAY_TO_STRING(ARRAY(select first_names||' '||last_name from persons where person_id = mm.owner_id),',') as contacts, count(distinct p.first_names) as total_contacs, mm.owner_id
+    from persons p,
+        (select max(um.msg_id) as last_msg_id, um.parent_id as msg_id, count(um.msg_id) as number_conversation, m.subject, m.owner_id
+        from (select parent_id,msg_id from messages_user_messages where user_id = :user_id and folder_id = :folder_id) um,
+             (select msg_id,subject,owner_id from messages_messages where community_id = :community_id and msg_id in ($msg_ids)) m
+        where um.parent_id = m.msg_id
+        group by um.parent_id, m.subject, m.owner_id) mm
+    where p.person_id = mm.owner_id
+    group by mm.msg_id, mm.subject, mm.last_msg_id, mm.number_conversation, mm.owner_id
+    order by mm.last_msg_id desc 
+    </querytext>
+</fullquery>
+
+<fullquery name="get_contacts">
+    <querytext>
+        select distinct(owner_id)
+        from messages_messages
+        where parent_id = :msg_id
+            or msg_id = :msg_id
+        LIMIT 4
+    </querytext>
+</fullquery>
+
+<fullquery name="get_msg_ids">
+    <querytext>
+        select messages.msg_id, messages.last_msg_id
+        from (select paginate.*
+            from (select max(m.msg_id) as last_msg_id, m.parent_id as msg_id, count(m.msg_id) as number_conversation
+                    from (select msg_id, coalesce(parent_id,msg_id) as parent_id 
+                          from messages_messages 
+                          where community_id = :community_id 
+                              and msg_id in (select parent_id 
+                                             from messages_user_messages 
+                                             where user_id = :user_id 
+                                                 and folder_id = :folder_id)) m
+                        group by m.parent_id
+                        order by last_msg_id desc
+                   ) paginate
+          LIMIT :last_row) messages
+    OFFSET :first_row - 1
+
+    </querytext>
+</fullquery> 
+
+</queryset>
Index: openacs-4/packages/messages/www/inbox.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/inbox.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/inbox.adp	31 Aug 2010 21:30:43 -0000	1.1
@@ -0,0 +1,44 @@
+<input type="hidden" id="page_number" value=@page_number@>
+<input type="hidden" id="folder_id" value=@folder_id@>
+<input type="hidden" id="folder_id_state" value="@folder_id@">
+<input type="hidden" id="msg_continue_empty" value="#messages.msg_continue_empty#">
+<input type="hidden" id="number_messages" value=@total_messages@>
+
+<div align="right">
+    <if @total_messages@ gt @show_messages@>
+        <if @page_number@ eq 2>
+            <a href="#" onClick="paginate(@folder_id@,@page_number@,'newer');update_numbers(@folder_id@,@user_id@)">#messages.newer# </a>
+        </if>
+        <if @page_number@ ge 3>
+            <a href="#" onClick="paginate(@folder_id@,1,'newest');update_numbers(@folder_id@,@user_id@)">#messages.newest# </a>
+            <a href="#" onClick="paginate(@folder_id@,@page_number@,'newer');update_numbers(@folder_id@,@user_id@)">#messages.newer# </a>
+        </if>
+        #messages.range_messages_show#
+        <if @page_number@ lt @total_pages@>
+            <a href="#" onClick="paginate(@folder_id@,@page_number@,'older');update_numbers(@folder_id@,@user_id@)">#messages.older# </a>
+            <if @page_number@ ne @oldest_page@>
+                <if @page_number@ lt @oldest_page@>
+                    <a href="#" onClick="paginate(@folder_id@,@total_pages@,'oldest');update_numbers(@folder_id@,@user_id@)">#messages.oldest# </a>
+                </if>
+            </if>
+        </if>
+    </if>
+    <else>
+        <if @total_messages@ ne 0>  #messages.range_messages_show# </if>
+    </else>
+</div>
+<if @folder_id@ eq 3>
+    <div id="folder_trash_msg" style="font-size:12px;" align="center">
+        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+        <if @total_messages@ ne 0>
+            <a href="#" onclick="empty_trash()">#messages.empty_trash#</a>
+        </if>
+         #messages.folder_trash_msg# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+    </div>
+</if>
+
+<form name="messages_list" method="GET">
+    <div id="list_messages" style="weight:100%">
+        <listtemplate name="messages"></listtemplate>
+    </div>
+</form>
\ No newline at end of file
Index: openacs-4/packages/messages/www/inbox.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/inbox.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/inbox.tcl	31 Aug 2010 21:30:43 -0000	1.1
@@ -0,0 +1,201 @@
+ad_page_contract {
+    Inbox Page
+
+    @author pedro@viaro.net
+    @creation-date 2009-11-11
+    @cvs-id $Id: inbox.tcl,v 1.1 2010/08/31 21:30:43 alvaror Exp $
+} {
+    {folder_id ""}
+    {page_number ""}
+} 
+
+set user_id [ad_conn user_id]
+set community_id [dotlrn_community::get_community_id]
+if {[empty_string_p $page_number]} {
+    set page_number 1
+}
+
+if {[empty_string_p $folder_id]} {
+    set folder_id 0
+}
+
+#####################################################################
+#get the total messages and calculate how many pages have in folder show
+#First and last row are calculate based on the total messages show per page
+set total_messages [db_string get_total_messages {} -default 0]
+set show_messages [db_string get_max_show_msg {} -default 25]
+#Round to the next integer because the las page include some messages.
+set total_pages [expr ceil([expr [expr $total_messages + 0.0] / [expr $show_messages + 0.0]])]
+set oldest_page [expr $total_pages - 1]
+set count_messages 0
+if { $page_number != 1} {
+    set first_row [expr [expr [expr $page_number - 1] * $show_messages] + 1]
+    set last_row [expr [expr $first_row + $show_messages] - 1]
+} else {
+    set first_row [expr [expr $page_number - 1] * $show_messages]
+    set last_row [expr $first_row + $show_messages]
+    set first_row [expr $first_row + 1]
+}
+######################################################################
+
+set elements [list  msg_ids \
+                [list label { <input type="checkbox" id="checkbox_general" name="_dummy" onclick="select_all(this.form,this.checked);" title="[_ acs-templating.lt_Checkuncheck_all_rows]">} \
+                display_template {<input type="checkbox" id="check_box_msg.@messages.msg_id@" name="subject_id" value="@messages.msg_id@" id="msg_ids.@messages.msg_id@" title="[_ acs-templating.lt_Checkuncheck_this_row]" onclick="more_actions_select();">} \
+                sub_class {narrow} \
+                html { align center }] \
+            ]
+
+lappend elements contacts \
+            [list html { align left style "font-size: smaller; width:20%"} \
+                label [_ messages.from] \
+                display_template { <a href="#@messages.msg_id@a${folder_id}a${page_number}" onclick="read_message(@messages.msg_id@,$folder_id,$page_number)"> <if @messages.new_p@>\
+                                     <span id="new_p.@messages.msg_id@"><span id="new_p_style.@messages.msg_id@"  style="font-weight: bold;">@messages.contacts@ \
+                                        <if $folder_id ne 1> (@messages.number_conversation@) </if></span>\
+                                    </if><else><span id="new_p_style.@messages.msg_id@" style="font-weight:;">@messages.contacts@ (@messages.number_conversation@)</span></else></a>} \
+            ] \
+    rel_type \
+        [list html { align left style "font-size: smaller; width:5%;"} \
+            label "" \
+                display_template { <if @messages.rel_type@ ne ""> \
+                        <a href="#@messages.msg_id@a${folder_id}a${page_number}" style="background-color:#F06040;color:white;padding:2px;" onclick="read_message(@messages.msg_id@,$folder_id,$page_number)">@messages.rel_type@</a></if> } \
+        ] \
+    attachmet_mail \
+        [list html { align center style "font-size: smaller; width:3%"} \
+            label "" \
+                display_template { <a href="#@messages.msg_id@a${folder_id}a${page_number}" onclick="read_message(@messages.msg_id@,$folder_id,$page_number)">\
+                                    <if @messages.attachment_p@ gt 0><span style="font-weight: bold;"><img src="/resources/messages/attachment.png" width="20" height="20"></span> \
+                                   </if></a>} \
+        ] \
+    subject_mail \
+        [list html { align left style "font-size: smaller; width:62%"} \
+            label [_ messages.subject] \
+                display_template { <a href="#@messages.msg_id@a${folder_id}a${page_number}" onclick="read_message(@messages.msg_id@,$folder_id,$page_number)">\
+                                    <if @messages.new_p@><span id="new_p_sub.@messages.msg_id@" style="font-weight: bold;">@messages.subject_mail@</span> \
+                                   </if><else><span id="new_p_sub.@messages.msg_id@" style="font-weight:;">@messages.subject_mail@</span></else></a>} \
+        ] \
+    date \
+        [list html { align left style "font-size: smaller;width:12%"} \
+            label [_ messages.date] \
+                display_template { <a href="#@messages.msg_id@a${folder_id}a${page_number}" onclick="read_message(@messages.msg_id@,$folder_id,$page_number)">\
+                                    <if @messages.new_p@><span id="new_p_date.@messages.msg_id@" style="font-weight: bold;">@messages.date@</span> \
+                                    </if><else><span id="new_p_date.@messages.msg_id@" style="font-weight:;">@messages.date@</span></else></a>} \
+        ]
+
+template::list::create \
+    -name messages \
+    -multirow messages \
+    -main_class "list-table" \
+    -html { width=100% } \
+    -orderby_name orderby \
+    -no_data "[_ messages.no_messages_found]" \
+    -elements $elements 
+
+
+set msg_id_list [list]
+set last_msg_ids_list [list]
+db_foreach get_msg_ids { *SQL* } {
+    set last_msg_ids_array($msg_id) $last_msg_id
+    lappend last_msg_ids_list $last_msg_id
+    lappend msg_id_list $msg_id
+}
+
+set msg_ids [join $msg_id_list ","]
+set last_msg_ids [join $last_msg_ids_list ","]
+
+if { [empty_string_p $msg_ids] } {
+    set msg_ids 0
+}
+
+if { [empty_string_p $last_msg_ids] } {
+    set last_msg_ids 0
+}
+
+db_foreach get_attachment_p { } {
+   set get_attachment_array($parent_id) $count_attachment
+}
+
+db_foreach get_new_p {} {
+   set new_p_array($parent_id) $new_p
+}
+
+db_foreach get_msg_info {} {
+    set msg_content_array($parent_id) $msg_content
+    set date_sent_array($parent_id) $sent_date_ansi
+}
+
+##Roles: se estable los roles que existen y se obtienen lo usuarios y sus roles
+set roles [dotlrn_community::get_roles -community_id $community_id]
+set rel_types [list]
+foreach role $roles {
+    lappend rel_types [lindex $role 0]
+}
+set rel_types [join $rel_types "','"]
+set rel_type_case "case "
+foreach {rel_type role pretty_name pretty_plural} [eval concat $roles] {
+    if { [string equal $rel_type "dotlrn_student_rel"] } {
+        append rel_type_case "when rel_type = '$rel_type' then '' "
+    } else {
+        append rel_type_case "when rel_type = '$rel_type' then '$pretty_name' "
+    }
+}
+append rel_type_case " end"
+
+###
+
+foreach msg_id $msg_id_list {
+    set contacts_ids [db_list get_contacts {}]
+    set owner_id [db_string get_owner_id {}]
+    set member_ids_rel_type($owner_id) [db_string get_members_ids_rel_type {}]
+    if { [llength $contacts_ids] > 1} {
+        set contacts ""
+        foreach contact_id $contacts_ids {
+            append contacts [db_string get_name {}] ","
+        }
+        set contacts [string range $contacts 0 end-1]
+        set msg_contacts($msg_id) $contacts
+    }     
+}
+
+db_multirow -extend { rel_type subject_mail date attachment_p new_p} messages get_messages { *SQL* } {
+    if { [info exists get_attachment_array($msg_id)]} {
+        set attachment_p $get_attachment_array($msg_id)
+    } else {
+        set attachment_p 0
+    }
+    if { [info exists member_ids_rel_type($owner_id)]} {
+        set rel_type [lang::util::localize $member_ids_rel_type($owner_id)] 
+    } else {
+        set rel_type ""
+    }
+
+    if { [info exists msg_contacts($msg_id)]} {
+        set contacts $msg_contacts($msg_id)
+    }
+    set msg_content $msg_content_array($msg_id)
+    set sent_date_ansi $date_sent_array($msg_id)
+    set content [ad_html_to_text [template::util::richtext::get_property contents $msg_content]]
+    if { [empty_string_p $subject] } {
+        set subject "[_ messages.no_subject] "
+    }
+    append subject_mail $subject "-" [string_truncate -len 80 -ellipsis "..." " $content"]
+    set cur_time [clock format [clock seconds] -format "%Y %m %d"]
+    set sent_time_seconds [clock format [clock scan $sent_date_ansi] -format "%Y %m %d"]
+    #compare the sent date whith the current date and display the message according to the date.
+    if { [lindex $cur_time 0] > [lindex $sent_time_seconds 0]} {
+        set date [lc_time_fmt $sent_date_ansi "%D"]
+    } else {
+        if {[lindex $cur_time 1] >= [lindex $sent_time_seconds 1] && [lindex $cur_time 2] == [lindex $sent_time_seconds 2]} {
+            set date [_ messages.today]
+        } else {
+            set date [lc_time_fmt $sent_date_ansi "%d,%b,%Y %H:%M"]
+        }
+    }
+    if { [info exists new_p_array($msg_id)]} {
+        set new_p $new_p_array($msg_id)
+    } else {
+        set new_p 0
+    }
+    incr count_messages
+}
+
+set last_number [expr [expr $count_messages + $first_row] - 1]
Index: openacs-4/packages/messages/www/inbox.xql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/inbox.xql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/inbox.xql	31 Aug 2010 21:30:43 -0000	1.1
@@ -0,0 +1,96 @@
+<?xml version="1.0"?>
+<queryset>
+
+    <fullquery name="get_name">
+        <querytext>
+            select acs_users_all.last_name||' '||acs_users_all.first_names
+            from acs_users_all,
+                    dotlrn_member_rels_approved
+            where dotlrn_member_rels_approved.community_id = :community_id
+                  and dotlrn_member_rels_approved.user_id = acs_users_all.user_id
+                  and acs_users_all.user_id = :party_id 
+        </querytext>
+    </fullquery>
+
+    <fullquery name="get_msg_info">
+        <querytext>
+            select coalesce(parent_id,msg_id) as parent_id, msg_content, to_char(sent_date, 'YYYY-MM-DD HH24:MI:SS') as sent_date_ansi
+            from messages_messages
+            where msg_id in ($last_msg_ids)
+        </querytext>
+    </fullquery>
+
+    <fullquery name="get_total_messages">
+        <querytext>
+                select count(um.msg_id) as total
+                from (select distinct(mum.parent_id) as msg_id
+                        from (select * from messages_user_messages where user_id = :user_id) as mum
+                        where user_id = :user_id
+                        and msg_id in (select msg_id 
+                        from messages_messages 
+                        where community_id = :community_id)
+                        and folder_id = :folder_id) um
+        </querytext>
+    </fullquery>
+
+    <fullquery name="get_max_show_msg">
+        <querytext>
+            select max_messages
+            from messages_options
+            where user_id = :user_id
+        </querytext>
+    </fullquery>
+
+    <fullquery name="get_attachment_p">
+        <querytext>
+            select um.parent_id, count(*) as count_attachment
+            from messages_attachments ma,
+                (select msg_id, parent_id
+                             from messages_user_messages
+                             where user_id = :user_id
+                                    and folder_id = :folder_id
+                                    and parent_id in ($msg_ids)
+                            ) um
+             where ma.msg_id = um.msg_id
+            group by um.parent_id
+        </querytext>
+    </fullquery>
+
+    <fullquery name="get_new_p">
+        <querytext>
+            select parent_id, new_p
+            from messages_user_messages
+            where parent_id in ($last_msg_ids)
+                    and folder_id = :folder_id
+                    and user_id = :user_id
+        </querytext>
+    </fullquery>
+
+    <fullquery name="get_owner_id">
+        <querytext>
+            select owner_id
+            from messages_messages
+            where msg_id = :msg_id
+            and community_id = :community_id
+        </querytext>
+    </fullquery>
+
+    <fullquery name="get_members_ids_rel_type">
+        <querytext>
+            select $rel_type_case as rel_type 
+            from acs_rels map
+            where map.object_id_one = :community_id
+                and map.rel_type in ('$rel_types')  
+                and object_id_two = :owner_id
+        </querytext>
+    </fullquery>
+
+    <fullquery name="get_name">
+        <querytext>
+            select first_names
+            from persons
+            where person_id = :contact_id
+        </querytext>
+    </fullquery>
+
+</queryset> 
\ No newline at end of file
Index: openacs-4/packages/messages/www/index.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/index.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/index.adp	31 Aug 2010 21:30:43 -0000	1.1
@@ -0,0 +1 @@
+redirect
\ No newline at end of file
Index: openacs-4/packages/messages/www/index.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/index.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/index.tcl	31 Aug 2010 21:30:43 -0000	1.1
@@ -0,0 +1,11 @@
+ad_page_contract {
+    Redirect to messages 
+
+    @author pedro@viaro.net
+    @creation-date 2009-11-17
+    @cvs-id $Id: index.tcl,v 1.1 2010/08/31 21:30:43 alvaror Exp $
+} {
+
+}
+
+ad_returnredirect "messages"
\ No newline at end of file
Index: openacs-4/packages/messages/www/members-rol.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/members-rol.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/members-rol.adp	31 Aug 2010 21:30:45 -0000	1.1
@@ -0,0 +1,13 @@
+<if @data_search@ eq "">
+    <input type="text" id="search_name" >
+    <input type="button" id="search_button" value="#messages.search#" onclick="search_members_rel_type();">
+    <input type="hidden" id="rel_type" value="@rel_type@">
+</if>
+<div id="members_list_div">
+    <form name="members_list" method="GET">
+        <div id="list_members" style="weight:100%">
+            <listtemplate name="members"></listtemplate>
+        </div>
+        <input type="hidden" id="total_rel_members" value="@rowscount@">
+    </form>
+</div>
Index: openacs-4/packages/messages/www/members-rol.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/members-rol.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/members-rol.tcl	31 Aug 2010 21:30:45 -0000	1.1
@@ -0,0 +1,59 @@
+ad_page_contract {
+    Inbox Page
+
+    @author pedro@viaro.net
+    @creation-date 2010-05-17
+    @cvs-id $Id: members-rol.tcl,v 1.1 2010/08/31 21:30:45 alvaror Exp $
+} {
+    {rel_type ""}
+    {data_search ""}
+} 
+
+set user_id [ad_conn user_id]
+set community_id [dotlrn_community::get_community_id]
+
+
+if { [exists_and_not_null data_search] && ![string equal $data_search "none"]} {
+    set data_search_list [string trim [split $data_search " "]]
+    set count 0
+    foreach data_search $data_search_list {
+        set sql_keyword "%[string tolower $data_search]%"
+        if { $count == 0 } {
+            append where_clause_user "lower(first_names || ' ' || last_name) like '$sql_keyword'"
+        } else {
+            append where_clause_user " or lower(first_names || ' ' || last_name) like '$sql_keyword'"        
+        }
+        incr count
+    }
+    set where_clause_user "and ($where_clause_user)"
+} else {
+    set where_clause_user ""
+    set search_clause ""
+}
+
+set elements [list  msg_ids \
+                [list label { <input type="checkbox" id="checkbox_general" name="_dummy" onclick="select_all(this.form,this.checked);" title="[_ acs-templating.lt_Checkuncheck_all_rows]">} \
+                display_template {<input type="checkbox" id="check_box_msg.@members.person_id@" name="subject_id" value="@members.person_id@" id="person_id.@members.person_id@" title="[_ acs-templating.lt_Checkuncheck_this_row]">} \
+                sub_class {narrow} \
+                html { align center }] \
+            ]
+
+lappend elements party_name \
+            [list html { align left style "font-size: smaller; width:95%"} \
+                label [_ messages.from] \
+                display_template {<input type="hidden" value="@members.party_name@" id="party_name.@members.person_id@"> @members.party_name@} \
+            ] 
+
+
+
+template::list::create \
+    -name members \
+    -multirow members \
+    -main_class "list-table" \
+    -html { width=100% } \
+    -orderby_name orderby \
+    -no_data "[_ messages.no_members_found]" \
+    -elements $elements 
+    
+set rowscount 0    
+db_multirow -extend { } members get_members { *SQL* } { incr rowscount}
Index: openacs-4/packages/messages/www/members-rol.xql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/members-rol.xql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/members-rol.xql	31 Aug 2010 21:30:45 -0000	1.1
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<queryset>
+
+    <fullquery name="get_members">
+        <querytext>
+            select p.first_names||' '||p.last_name  as party_name,
+                   p.person_id
+            from persons p,
+                acs_rels map
+            where map.object_id_one = :community_id
+                and map.object_id_two = p.person_id
+                and map.rel_type = :rel_type
+                $where_clause_user
+            order by party_name
+        </querytext>
+    </fullquery>
+
+</queryset>
+
Index: openacs-4/packages/messages/www/message-option.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/message-option.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/message-option.adp	31 Aug 2010 21:30:43 -0000	1.1
@@ -0,0 +1,42 @@
+<master>
+<property name="title">@page_title;noquote@</property>
+<property name="context">@context;noquote@</property>
+<include src="messages-actions">
+<br>
+<div class="yui-skin-sam">
+    <div>
+        <a class="button" id="show" style="cursor:pointer">#messages.new_folder#</a> 
+    </div>
+    <div id="dialog1" class="yui-pe-content">
+        <div class="hd">#messages.name_new_folder#</div>
+        <div class="bd">
+            <form method="GET" action="create-folder">
+                <label for="folder_name">#messages.folder_name#:</label><input type="text" name="folder_name" />
+            </form>
+        </div>
+    </div>
+</div>
+<br>
+<if @folder:rowcount@ gt 0>
+    <strong> #messages.folders_created# </strong><br>#messages.edit_folder#<br><br>
+</if>
+
+<multiple name="folder">
+    <span id="span_folder_@folder.folder_id@" onclick="convert_input('@folder.folder_id@')">
+        <img src="/resources/messages/folder.png" width="20" height="20">
+        <span id="folder_@folder.folder_id@">
+             @folder.folder_name@
+        </span>
+    </span>
+    <span id="span_folder_delete_@folder.folder_id@">
+        <a href="javascript:delete_folder();" title="Borrar">
+            <img src="/resources/messages/Delete16-on.gif">
+            <span class="class="button" onclick="update_folder(@folder.folder_id@,'delete')">
+                #messages.delete#
+            </span>
+        </a>
+    </span>
+    <br>
+</multiple>
+<br><br>
+<formtemplate id="options_mail"></formtemplate>
\ No newline at end of file
Index: openacs-4/packages/messages/www/message-option.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/message-option.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/message-option.tcl	31 Aug 2010 21:30:43 -0000	1.1
@@ -0,0 +1,87 @@
+ad_page_contract {
+    Message Options
+
+    @author pedro@viaro.net
+    @creation-date 2009-11-13
+    @cvs-id $Id: message-option.tcl,v 1.1 2010/08/31 21:30:43 alvaror Exp $
+} {
+    {return_url "messages"}
+} 
+
+set page_title "[_ messages.configuration]"
+set context [list "[_ messages.configuration]"]
+set owner_id [ad_conn user_id]
+set user_id [ad_conn user_id]
+set community_id [dotlrn_community::get_community_id]
+set save_sent_p 1
+set signature_p 0
+set signature ""
+set max_messages "25"
+
+append folders_id_standar [messages::get_folder_id -folder_name "inbox"] ","\
+                          [messages::get_folder_id -folder_name "sent"] ","\
+                          [messages::get_folder_id -folder_name "save"] ","\
+                          [messages::get_folder_id -folder_name "trash"]
+
+db_multirow -extend { folder_p } folder get_folders_data { *SQL* } { }
+
+if { ![db_0or1row select_options {} ] } {
+    db_dml create_options {}
+}
+
+set signature [ad_html_to_text $signature]
+
+if { [empty_string_p $signature] } {
+    set signature ""
+} else {
+    set signature [string trim $signature]
+}
+
+ad_form -name options_mail -cancel_url "messages" -form {
+    {max_messages:naturalnum {label "[_ messages.max_show_messages]:"} 
+        {html {size 3}} {value $max_messages}
+    }
+    {save_sent_p:text(radio) {label "[_ messages.save_sent_message]:"} 
+        {options {{[_ messages.yes] 1} {[_ messages.no] 0}}} {value $save_sent_p}
+    }
+    {signature_p:text(radio) {label "[_ messages.insert_signature]:"} 
+        {options {{[_ messages.yes] 1} {No 0}}} {value $signature_p}
+    }
+    {signature_text:richtext(richtext),optional,nospell {label "[_ messages.signature]:"}
+        {options {editor xinha }}
+        {value "$signature"} {html {id "signature" cols 80 rows 3 onload "loadEditors('advancedMessagesEditor'); return false;"}} 
+    }
+} -on_submit {
+    set signature [string trim [ad_convert_to_html $signature_text]]
+    db_dml update_options {}
+} -after_submit {
+    ad_returnredirect "$return_url"
+    ad_script_abort
+}
+
+template::head::add_javascript -src "/resources/ajaxhelper/yui/yahoo/yahoo-min.js" -order "-3"
+template::head::add_javascript -src "/resources/ajaxhelper/yui/yahoo-dom-event/yahoo-dom-event.js" -order "-1"
+template::head::add_javascript -src "/resources/ajaxhelper/yui/element/element-beta-min.js" -order "-2"
+template::head::add_javascript -src "/resources/ajaxhelper/yui/event/event-min.js" -order "-4"
+template::head::add_javascript -src "/resources/ajaxhelper/yui/dom/dom-min.js" -order "-5"
+template::head::add_javascript -src "/resources/ajaxhelper/yui/button/button-beta-min.js" -order "-7"
+template::head::add_javascript -src "/resources/ajaxhelper/yui/utilities/utilities.js" -order "-8"
+template::head::add_javascript -src "/resources/ajaxhelper/yui/connection/connection-min.js" -order "-9"
+template::head::add_javascript -src "/resources/ajaxhelper/yui/button/button-min.js"
+template::head::add_javascript -src "/resources/ajaxhelper/yui/dragdrop/dragdrop-min.js"
+template::head::add_javascript -src "/resources/ajaxhelper/yui/container/container-min.js"
+template::head::add_javascript -src "/resources/ajaxhelper/yui/yuiloader/yuiloader-min.js"
+template::head::add_javascript -src "/resources/messages/folder-new.js"
+template::head::add_css -href "/resources/ajaxhelper/yui/button/assets/skins/sam/button.css" -order "-3"
+template::head::add_css -href "/resources/ajaxhelper/yui/container/assets/skins/sam/container.css" -order "-4"
+template::head::add_css -href "/resources/ajaxhelper/yui/fonts/fonts-min.css"
+template::head::add_css -href "/resources/ajaxhelper/yui/button/assets/skins/sam/button.css"
+template::head::add_css -href "/resources/ajaxhelper/yui/assets/skins/sam/container.css"
+template::head::add_javascript -src "/resources/messages/messages.js" -order "3"
+template::head::add_javascript -src "/resources/messages/message-option.js" -order "4"
+
+
+set ::acs_blank_master(xinha) 1
+set ::acs_blank_master(xinha.plugins) "'OacsFs'"
+set ::acs_blank_master(xinha.options) ""
+set ::acs_blank_master__htmlareas ""
\ No newline at end of file
Index: openacs-4/packages/messages/www/message-option.xql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/message-option.xql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/message-option.xql	31 Aug 2010 21:30:43 -0000	1.1
@@ -0,0 +1,37 @@
+<?xml version="1.0"?>
+<queryset>
+
+    <fullquery name="get_folders_data">
+        <querytext>
+            select folder_id, folder_name
+            from messages_folders 
+            where user_id = :user_id
+            or user_id = 0
+            and folder_id not in ($folders_id_standar)
+            order by folder_id asc
+        </querytext>
+    </fullquery>
+
+    <fullquery name="select_options">
+        <querytext>
+            select max_messages, save_sent_p, signature_p, signature
+            from messages_options
+            where user_id = :user_id
+        </querytext>
+    </fullquery>
+
+    <fullquery name="create_options">
+        <querytext>
+            insert into messages_options values (:user_id,25,:save_sent_p,:signature_p,:signature) 
+        </querytext>
+    </fullquery>
+
+    <fullquery name="update_options">
+        <querytext>
+            update messages_options 
+            set max_messages = :max_messages,save_sent_p = :save_sent_p, signature_p = :signature_p, signature = :signature
+            where user_id = :user_id
+        </querytext>
+    </fullquery>
+
+</queryset>
\ No newline at end of file
Index: openacs-4/packages/messages/www/messages-actions.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/messages-actions.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/messages-actions.adp	31 Aug 2010 21:30:42 -0000	1.1
@@ -0,0 +1,52 @@
+<input type="hidden" id="new_folder_name_error" value="#messages.new_folder_name_error#">
+<input type="hidden" id="delete_confirm" value="#messages.delete_confirm#">
+<input type="hidden" id="subject_empty" value="#messages.subject_empty#">
+<input type="hidden" id="confirm_trash_empty" value="#messages.confirm_trash_empty#">
+<input type="hidden" id="recipients_empty" value="#messages.recipients_empty#">
+<input type="hidden" id="msg_read" value="#messages.msg_read#">
+<input type="hidden" id="msg_unread" value="#messages.msg_unread#">
+<input type="hidden" id="msg_delete" value="#messages.msg_delete#">
+<input type="hidden" id="more_msg_unread" value="#messages.more_msg_unread#">
+<input type="hidden" id="more_msg_read" value="#messages.more_msg_read#">
+<input type="hidden" id="msg_moved_to" value="#messages.has_been_moved_to#">
+<input type="hidden" id="msg_cancel" value="#messages.msg_cancel#">
+<input type="hidden" id="cancel" value="#messages.cancel#">
+<input type="hidden" id="other_msg_reply" value="#messages.other_msg_reply#">
+<input type="hidden" id="send_msg" value="#messages.send#">
+<input type="hidden" id="msg_invalid_user" value="#messages.msg_invalid_user#">
+<input type="hidden" id="more_msg_moved_to" value="#messages.more_msg_moved_to#">
+<input type="hidden" id="msg_select_msg" value="#messages.msg_select_msg#">
+<input type="hidden" id="msg_loading" value="#messages.msg_loading#">
+<input type="hidden" id="msg_yes" value="#messages.yes#">
+<input type="hidden" id="msg_create" value="#messages.create#">
+<input type="hidden" id="msg_id_reply" value="">
+<input type="hidden" id="option_reply" value="">
+<input type="hidden" id="msg_action_send_p" value="f">
+<input type="hidden" id="msg_id_new_send" value="">
+<input type="hidden" id="msg_active_reply" value="">
+<input type="hidden" id="msg_action_p" value="f">
+<input type="hidden" id="more_actions_option" value="f">
+<input type="hidden" id="more_actions_p" value="f">
+<input type="hidden" id="ids_length" value=0>
+<input type="hidden" id="folder_active" value=0>
+<input type="hidden" id="option_move_to" value=>
+<input type="hidden" id="move_to_p" value="f">
+<input type="hidden" id="inbox_select_p" value="f">
+<input type="hidden" id="option_action" value=>
+<input type="hidden" id="option" value=0>
+<input type="hidden" id="message_action_p" value="t">
+<input type="hidden" id="location_href" value="t">
+<input type="hidden" id="reply_dialog_active" value="f">
+<input type="hidden" id="count_msg_ids" value=0>
+<input type="hidden" id="folder_name" value=0>
+<input type="hidden" id="ids_more_actions" value=""> 
+<input type="hidden" id="msg_invalid_number"  value="#messages.msg_invalid_number#">
+<input type="hidden" id="save_info" value="#messages.save_info#">
+<input type="hidden" id="msg_delete_folder" value="#messages.msg_delete_folder#">
+<input type="hidden" id="active" value="f">
+<input type="hidden" id="name_cancel" value="">
+<input type="hidden" id="cancel_p" value="f">
+<input type="hidden" id="delete_p" value="f">
+<input type="hidden" id="page_number" value="1">
+<input type="hidden" id="total_messages" value="0">
+<input type="hidden" id="msg_id_action_reply" value="0">
\ No newline at end of file
Index: openacs-4/packages/messages/www/messages-folders.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/messages-folders.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/messages-folders.adp	31 Aug 2010 21:30:44 -0000	1.1
@@ -0,0 +1,153 @@
+<input type="hidden" id="add" value="#messages.add#">
+<input type="hidden" id="contacts" value="@contacts_list;noquote@">
+<input type="hidden" id="owner_id" value="@user_id;noquote@">
+<input type="hidden" id="not_permision_read" value="#messages.not_permision_read#">
+<span id="scroll_top"></span>
+
+<div id="autocomplete_loding" style="display:none">
+    <div tabindex="-1" id="ids" class="clearfix tokenizer" onclick="$('autocomplete_input').focus()">
+        <span class="tokenizer_stretcher">^_^</span>
+        <span class="tab_stop"><input type="text" id="hidden_input" tabindex="-1"></span>
+            <span id=contact_reply>
+            </span>
+        <div id="autocomplete_display" class="tokenizer_input">
+            <input type="text" size="1" tabindex="" id="autocomplete_input" />            
+        </div>
+    </div>
+    <div id="autocomplete_populate" class="clearfix autocomplete typeahead_list" style="width: 358px; height: auto; overflow-y: hidden;display:none">
+        <div class="typeahead_message">Type the name of a member</div>
+    </div>
+</div>
+
+<script type="text/javascript">
+    update_numbers();
+    var contacts = [@contacts_list;noquote@];
+    var typeahead = new Autocompleter.LocalAdvanced('autocomplete_input', 'autocomplete_populate', contacts, {
+        frequency: 0.1,
+        updateElement: addContactToList,
+        search_field: "name"
+    });
+    var hidden_input = new HiddenInput('hidden_input',typeahead);
+    function contact_users() {
+        var typeahead = new Autocompleter.LocalAdvanced('autocomplete_input', 'autocomplete_populate', contacts, {
+            frequency: 0.1,
+            updateElement: addContactToList,
+            search_field: "name"
+        });
+        var hidden_input = new HiddenInput('hidden_input',typeahead);
+    }
+
+</script>
+
+<div style="height:auto;text-align:center">
+    <span id="msg_system">
+    </span>
+    <span id="msg_action" style="display:none">
+        <span class="alert">
+            <strong>
+                <span id="msg_actions"></span>
+            </strong>
+        </span>
+    </span>
+</div>
+<br>
+<table cellspacing="0" cellpadding="0" border="0" width="100%">
+    <tr>
+        <th></th>
+        <th valign="top">
+            <div id="top_menu">
+                <div class="menuBar">
+                    <span id="return" style="display:none">
+                        <span onclick="return_msg(@user_id@);" style="cursor:pointer;">#messages.back#</span>
+                    </span>
+                    <span id="delete">
+                        <span class="yui-skin-sam">
+                            <span id="container">
+                                <input id="show_delete" type="button" value="#messages.delete#"> 
+                            </span>
+                        </span>
+                    </span>
+                    <span id="delete_message_trash" style="display:none">
+                        <span class="yui-skin-sam">
+                            <span id="container">
+                                <input id="show_delete_trash" type="button" value="#messages.delete#" onclick="delete_select_msg()"> 
+                            </span>
+                        </span>
+                    </span>
+                    <span id="delete_message" style="display:none">
+                        <a class="menuButton" href="" onclick="return move_to(3);"></a>
+                    </span>
+                    <span id="select_msg" style="position:absolute;right:0;display:none;align:right">
+                        <span id="msg_newer" style="text-align:right;cursor:pointer;">
+                            <span class="menuButton" onclick="select_message('newer');">#messages.newer#  </span>
+                        </span>
+                        <span id="msg_older" style="text-align:right;cursor:pointer;">
+                            <span class="menuButton" onclick="select_message('older');"> #messages.older#</span>
+                        </span>
+                    </span> 
+                    <span>
+                        <span id="move_to" sytle="display:true;">
+                            <input  type="button" href="" onclick="show_div('fileMenu');return buttonClick(event, 'fileMenu');show_div('fileMenu');" value="#messages.move_to#  &darr;">
+                    </span>
+             </span>
+                    <span id="more_actions_span">
+                        <input type="button"  href="" onclick="show_div('more_actions');return buttonClick(event, 'more_actions');show_div('more_actions');" value="#messages.more_actions# &darr;">
+                    </span>
+                    <span id="search" class="menu">
+                        <input type="text" size="40" id="search_data" onKeyUp="valid_search(this.value);" onKeyPress="checkKey(event,this.value);" title="#messages.search_tooltip_text#">
+                        <input type="button" id="search_button" href="" onclick="search();" value="#messages.search#" disabled>
+                    </span>
+                </div>
+                <div id="fileMenu" class="menu" onmouseover="menuMouseover(event);">
+                    <multiple name="folders">
+                        <if @folders.folder_id@ ne 1  and @folders.folder_id@ ne 4>
+                            <a class="menuItem" href="javascript:onClick=move_to(@folders.folder_id@)" id="@folders.folder_id@">@folders.folder_name@</a>
+                        </if>
+                    </multiple>
+                </div>
+                <div id="more_actions" class="menu" onmouseover="menuMouseover(event);">
+                    <span id="action_as_unread" align="left">
+                        <a class="menuItem" href="javascript:more_actions('t',@folder_id@,@user_id@);" id="as_unread">#messages.as_unread#</a>
+                    </span>
+                    <span id="action_as_read">
+                        <a class="menuItem" href="javascript:more_actions('f',@folder_id@,@user_id@);" id="as_read">#messages.as_read#</a>
+                    </span>
+                </div>
+            </div>
+        </th>
+    </tr>
+</table>
+<table cellspacing="0" cellpadding="0" border="0" width="100%">
+    <tr>
+        <td valign="top">
+            <br><br>
+            <b><u><a href="send" >#messages.compose_mail#</a></u></b><br><br>
+                <multiple name="folders">
+                    <div id="background_@folders.folder_id@"  style="background-color:@folders.color@;weigth=100%;">
+                        <a href="javascript:get_messages_folder(@folders.folder_id@);update_numbers(@folders.folder_id@,@user_id@);"> 
+                            @folders.folder_name@ 
+                            <input type="hidden" value="@folders.total_new_msg@" id="new_msg_folder_@folders.folder_id@">
+                            <span id="number_@folders.folder_id@">
+                            </span>
+                        </a>
+                    </div>
+                </multiple>
+            <br>
+             &nbsp;<a class="button" href="message-option" >#messages.configuration#</a><br><br>
+        </td>
+        <td width="88%" valign="top" style="padding-left:10px">
+            <script type="text/javascript">
+                if (window.location.hash == "") {
+                    get_messages_folder(0);
+                } else {
+                    var url_msg = window.location.hash.split('a');
+                    var msg_id = url_msg[0].substring(1,url_msg[0].length);
+                    read_message(msg_id,url_msg[1],url_msg[2]);
+                }
+            </script>
+            <span id="folder_section_display">
+            </span>
+        </td>
+    </tr>
+</table>
+
Index: openacs-4/packages/messages/www/messages-folders.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/messages-folders.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/messages-folders.tcl	31 Aug 2010 21:30:44 -0000	1.1
@@ -0,0 +1,26 @@
+ad_page_contract {
+    General page, list inbox, sent, draft, trash.
+
+    @author pedro@viaro.net
+    @creation-date 2009-11-12
+    @cvs-id $Id: messages-folders.tcl,v 1.1 2010/08/31 21:30:44 alvaror Exp $
+} {
+    {community_id ""}
+    {page_number ""}
+}
+
+set page_title "[_ messages.folders]"
+set context [list $page_title]
+set user_id [ad_conn user_id]
+set community_id [dotlrn_community::get_community_id]
+
+set contacts_list [util_memoize [list messages::get_community_users_fb -community_id $community_id] 86400]
+
+db_multirow -extend { color total_new_msg} folders get_folders_user { *SQL* } {
+    if { $folder_id == 0 } {
+        set color "#C0C0C0"
+    } else {
+        set color "#FFFFFF"
+    }
+}
+
Index: openacs-4/packages/messages/www/messages-folders.xql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/messages-folders.xql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/messages-folders.xql	31 Aug 2010 21:30:45 -0000	1.1
@@ -0,0 +1,14 @@
+<?xml version="1.0"?>
+<queryset>
+
+    <fullquery name="get_folders_user">
+        <querytext>
+            select folder_id, folder_name
+            from messages_folders 
+            where (user_id = :user_id
+            or user_id = 0)
+            order by folder_id asc
+        </querytext>
+    </fullquery>
+
+</queryset>   
Index: openacs-4/packages/messages/www/messages.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/messages.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/messages.adp	31 Aug 2010 21:30:45 -0000	1.1
@@ -0,0 +1,43 @@
+<master>
+<property name="title">@page_title;noquote@</property>
+<property name="context">@context;noquote@</property>
+<script type="text/javascript">
+document.documentElement.className = "yui-pe";
+document.body.className += 'yui-skin-sam';
+</script>
+<input type="hidden" id="dialog_rol_set" value="0">
+<script type="text/javascript" src="/resources/messages/dispatcher.js"></script>
+<script type="text/javascript" src="/resources/messages/rol-dialog.js"></script>
+<script type="text/javascript" src="/resources/messages/message-dialog-roles.js"></script>
+
+<include src="messages-actions">
+<input type="hidden" id="add" value="#messages.add#">
+<input type="hidden" id="owner_id" value="@user_id;noquote@">
+<input type="hidden" id="not_permision_read" value="#messages.not_permision_read#">
+<span id="scroll_top"></span>
+
+
+<div id="mydynamicarea">
+    <div style="height:auto;text-align:center">
+        <span id="msg_action" style="display:none">
+            <span class="alert">
+                <strong>
+                    <span id="msg_actions"></span>
+                </strong>
+            </span>
+        </span>
+    </div>
+</div>
+
+<script type="text/javascript">
+    loading();
+    YAHOO.plugin.Dispatcher.fetch ( 'mydynamicarea', 'messages-folders' );
+</script>
+
+<div id="dialog1" class="yui-pe-content">
+    <div class="bd">
+        <div id="info_members_rols" "style="width:28em;height:30em;overflow:auto"> 
+        </div>
+    </div>
+</div>
+ 
\ No newline at end of file
Index: openacs-4/packages/messages/www/messages.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/messages.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/messages.tcl	31 Aug 2010 21:30:45 -0000	1.1
@@ -0,0 +1,55 @@
+ad_page_contract {
+    General page, list inbox, sent, draft, trash.
+
+    @author pedro@viaro.net
+    @creation-date 2009-11-12
+    @cvs-id $Id: messages.tcl,v 1.1 2010/08/31 21:30:45 alvaror Exp $
+} {
+    {community_id ""}
+    {page_number ""}
+}
+
+set page_title "[_ messages.folders]"
+set context [list $page_title]
+set user_id [ad_conn user_id]
+set community_id [dotlrn_community::get_community_id]
+
+set ::acs_blank_master(xinha) 1
+set ::acs_blank_master(xinha.plugins) "'OacsFs'"
+set ::acs_blank_master(xinha.options) ""
+set ::acs_blank_master__htmlareas ""
+
+template::head::add_javascript -src "/resources/ajaxhelper/yui/yahoo-dom-event/yahoo-dom-event.js" -order "-1"
+template::head::add_javascript -src "/resources/ajaxhelper/yui/yahoo/yahoo-min.js" -order "-14"
+template::head::add_javascript -src "/resources/ajaxhelper/yui/element/element-beta-min.js" -order "-13"
+template::head::add_javascript -src "/resources/ajaxhelper/yui/event/event-min.js" -order "-15"
+template::head::add_javascript -src "/resources/ajaxhelper/yui/dom/dom-min.js" -order "-16"
+template::head::add_javascript -src "/resources/ajaxhelper/yui/button/button-beta-min.js" -order "-17"
+template::head::add_javascript -src "/resources/ajaxhelper/yui/utilities/utilities.js" -order "-18"
+template::head::add_javascript -src "/resources/ajaxhelper/yui/history/history-beta-min.js" -order "-19"
+template::head::add_javascript -src "/resources/ajaxhelper/yui/button/button-min.js" -order "-2"
+template::head::add_javascript -src "/resources/ajaxhelper/yui/dragdrop/dragdrop-min.js" -order "2"
+template::head::add_javascript -src "/resources/ajaxhelper/yui/container/container-min.js" -order "3"
+template::head::add_javascript -src "/resources/messages/prototype.js" -order "4"
+template::head::add_javascript -src "/resources/messages/effects.js" -order "5"
+template::head::add_javascript -src "/resources/ajaxhelper/yui/dragdrop/dragdrop.js" -order "6"
+template::head::add_javascript -src "/resources/messages/controls.js" -order "7"
+template::head::add_javascript -src "/resources/messages/builder.js" -order "8"
+template::head::add_javascript -src "/resources/messages/application.js" -order "9"
+template::head::add_javascript -src "/resources/messages/messages.js" -order "10"
+template::head::add_javascript -src "/resources/messages/dispatcher.js"
+template::head::add_javascript -src "/resources/messages/dispatcher-min.js"
+template::head::add_javascript -src "/resources/messages/menu.js" -order "11"
+template::head::add_javascript -src "/resources/messages/delete-dialog.js" -order "12"
+template::head::add_javascript -src "/resources/messages/message-attachment.js" -order "13"
+template::head::add_css -href "/resources/messages/menu.css" -order "-2"
+template::head::add_css -href "/resources/ajaxhelper/yui/fonts/fonts-min.css" -order "-3"
+template::head::add_css -href "/resources/ajaxhelper/yui/button/assets/skins/sam/button.css" -order "-4"
+template::head::add_css -href "/resources/ajaxhelper/yui/container/assets/skins/sam/container.css" -order "-5"
+template::head::add_css -href "/resources/messages/messages.css" -order "-6"
+template::head::add_css -href "/resources/messages/send.css" -order "1"
+template::head::add_javascript -src "/resources/ajaxhelper/yui/connection/connection-min.js"
+
+
+
+
Index: openacs-4/packages/messages/www/messages.xql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/messages.xql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/messages.xql	31 Aug 2010 21:30:45 -0000	1.1
@@ -0,0 +1,14 @@
+<?xml version="1.0"?>
+<queryset>
+
+    <fullquery name="get_folders_user">
+        <querytext>
+            select folder_id, folder_name
+            from messages_folders 
+            where (user_id = :user_id
+            or user_id = 0)
+            order by folder_id asc
+        </querytext>
+    </fullquery>
+
+</queryset>  
\ No newline at end of file
Index: openacs-4/packages/messages/www/more-actions.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/more-actions.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/more-actions.adp	31 Aug 2010 21:30:45 -0000	1.1
@@ -0,0 +1 @@
+@option_select@
\ No newline at end of file
Index: openacs-4/packages/messages/www/more-actions.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/more-actions.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/more-actions.tcl	31 Aug 2010 21:30:45 -0000	1.1
@@ -0,0 +1,16 @@
+ad_page_contract {
+    Update numbers of new messages in the menu
+
+    @author pedro@viaro.net
+    @creation-date 2009-11-24
+    @cvs-id $Id: more-actions.tcl,v 1.1 2010/08/31 21:30:45 alvaror Exp $
+} {
+   msg_ids
+   option_select
+}
+
+set community_id [dotlrn_community::get_community_id]
+set user_id [ad_conn user_id]
+db_transaction {
+    db_dml update_status_msg {}
+}
\ No newline at end of file
Index: openacs-4/packages/messages/www/more-actions.xql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/more-actions.xql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/more-actions.xql	31 Aug 2010 21:30:45 -0000	1.1
@@ -0,0 +1,12 @@
+<?xml version="1.0"?>
+<queryset>
+
+    <fullquery name="update_status_msg">
+        <querytext>
+            update messages_user_messages 
+            set new_p = :option_select
+            where parent_id in ($msg_ids) and user_id = :user_id
+        </querytext>
+    </fullquery>
+
+</queryset>
\ No newline at end of file
Index: openacs-4/packages/messages/www/move-to.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/move-to.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/move-to.adp	31 Aug 2010 21:30:43 -0000	1.1
@@ -0,0 +1 @@
+@folder_id;noquote@
\ No newline at end of file
Index: openacs-4/packages/messages/www/move-to.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/move-to.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/move-to.tcl	31 Aug 2010 21:30:43 -0000	1.1
@@ -0,0 +1,17 @@
+ad_page_contract {
+    General page, list inbox, sent, draft, trash.
+
+    @author pedro@viaro.net
+    @creation-date 2009-11-13
+    @cvs-id $Id: move-to.tcl,v 1.1 2010/08/31 21:30:43 alvaror Exp $
+} {
+    {msg_ids ""}
+    {folder_id ""}
+} 
+
+set user_id [ad_conn user_id]
+set folder_id_sent [messages::get_folder_id -folder_name "sent"]
+set community_id [dotlrn_community::get_community_id]
+db_transaction {
+    db_dml update_folder {}
+}
\ No newline at end of file
Index: openacs-4/packages/messages/www/move-to.xql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/move-to.xql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/move-to.xql	31 Aug 2010 21:30:43 -0000	1.1
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<queryset>
+    <fullquery name="update_folder">
+        <querytext>
+            update messages_user_messages set folder_id = :folder_id 
+            where parent_id in ($msg_ids)
+                and user_id = :user_id
+                and folder_id != :folder_id_sent
+        </querytext>
+    </fullquery>
+</queryset>
\ No newline at end of file
Index: openacs-4/packages/messages/www/new-folder.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/new-folder.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/new-folder.adp	31 Aug 2010 21:30:42 -0000	1.1
@@ -0,0 +1,26 @@
+<body bgcolor="#E0ECF8"> 
+
+<script type="text/javascript" src="/resources/ajaxhelper/yui/yahoo-dom-event/yahoo-dom-event.js"></script>
+<script type="text/javascript" src="/resources/ajaxhelper/yui/container/container_core.js"></script>
+<script type="text/javascript" src="/resources/ajaxhelper/yui/menu/menu.js"></script>
+<script type="text/javascript" src="/resources/ajaxhelper/yui/element/element-beta-min.js"></script>
+<script type="text/javascript" src="/resources/ajaxhelper/yui/yahoo/yahoo-min.js"></script>
+<script type="text/javascript" src="/resources/ajaxhelper/yui/event/event-min.js"></script>
+<script type="text/javascript" src="/resources/ajaxhelper/yui/dom/dom-min.js"></script>
+<script type="text/javascript" src="/resources/ajaxhelper/yui/button/button-beta-min.js"></script>
+
+<script type="text/javascript" src="/resources/ajaxhelper/yui/utilities/utilities.js"></script>
+<script type="text/javascript" src="/resources/ajaxhelper/yui/connection/connection-min.js"></script>
+
+
+
+<script src="/resources/messages/messages.js" type="text/javascript"></script>
+<input type=hidden id=new_folder_name_error value="#messages.new_folder_name_error#">
+<div align=center>
+    <form>
+        <label>#messages.name_new_folder#:</label><br>
+        <input type=text id=new_folder_name><br><br>
+        <input  class="button" name="send_button" value="#messages.create#" type="button" onclick=create_new_folder(); >
+        <input  class="button" name="cancel_button" value="#messages.cancel#" type="button" onclick=window.close()>
+    </form>
+</div>  
\ No newline at end of file
Index: openacs-4/packages/messages/www/print-message.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/print-message.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/print-message.adp	31 Aug 2010 21:30:42 -0000	1.1
@@ -0,0 +1,31 @@
+<script type="text/javascript">
+    window.print();
+</script>
+
+<property name="title">@page_title;noquote@</property>
+<property name="context">@context;noquote@</property>
+
+<span style="float:left;text-align:left;"> </span>
+    <span style="float:right;font-size:small;"><a class=button href="javascript:window.close()">#messages.close_window#</a></span><br>
+    <span style="float: right;text-align:right;">@owner_name@</span>
+<br><br><br>
+<hr>
+    <span style="float:left;text-align:left;font-size: large;">@subject@</span>
+<br><br>
+    <span style="float:left;text-align:left;font-size: x-small;">#messages.messages#:@print:rowcount@</span>
+<br>
+<hr>
+<multiple name="print">
+    <span style="float:left;text-align:left;font-size: small;">
+        <strong> @print.contact_names@ </strong> 
+    </span>
+    <span style="float:right;text-align:left;font-size: small;"> 
+        @print.ansi_time@
+    </span>
+    <br><br> 
+        @print.content;noquote@
+    <br><hr>
+</multiple>
+
+
+
Index: openacs-4/packages/messages/www/print-message.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/print-message.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/print-message.tcl	31 Aug 2010 21:30:42 -0000	1.1
@@ -0,0 +1,40 @@
+ad_page_contract {
+    Print the messages.
+
+    @author pedro@viaro.net
+    @creation-date 2009-11-13
+    @cvs-id $Id: print-message.tcl,v 1.1 2010/08/31 21:30:42 alvaror Exp $
+} {
+    {msg_id ""}
+} 
+
+set page_title "[_ messages.print]"
+set context [list $page_title]
+set user_id [ad_conn user_id]
+set community_id [dotlrn_community::get_community_id]
+set contact_names [db_list_of_lists get_contact_names {}]
+
+foreach contact $contact_names {
+    if { [lsearch -exact $contact $user_id] } {
+        set owner_name "[lindex $contact 0] <[lindex $contact 2]>"
+        break
+    }
+}
+
+db_0or1row data_msg { *SQL* }
+
+set data_msg [db_list_of_lists get_data_msg {}]
+set total_msg [llength $data_msg]
+
+db_multirow -extend { contact_names ansi_time content} print get_data_msg { *SQL* } {
+    set contact_list [db_list_of_lists get_contacts {}]
+    set contact_names [list]
+    foreach party_id $contact_list {
+    	set contact_name [acs_user::get_element -user_id $party_id -element first_names]
+        lappend contact_names $contact_name
+    }
+    set contact_names [join $contact_names ","]
+    set day_recive [lc_time_fmt $sent_date "%d %b, %Y" ]
+    set ansi_time "$day_recive - $time"
+    set content [template::util::richtext::get_property content $msg_content]
+}
\ No newline at end of file
Index: openacs-4/packages/messages/www/print-message.xql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/print-message.xql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/print-message.xql	31 Aug 2010 21:30:42 -0000	1.1
@@ -0,0 +1,56 @@
+<?xml version="1.0"?>
+<queryset>
+
+    <fullquery name="get_contact_names">
+        <querytext>
+            select acs_users_all.last_name||' '||acs_users_all.first_names  as party_name,
+                   acs_users_all.party_id, acs_users_all.email
+            from acs_users_all,
+                    dotlrn_member_rels_approved
+            where dotlrn_member_rels_approved.community_id = :community_id
+                  and dotlrn_member_rels_approved.user_id = acs_users_all.user_id
+                  and acs_users_all.user_id = :user_id 
+        </querytext>
+    </fullquery> 
+
+    <fullquery name="data_msg">
+        <querytext>
+            select subject
+            from messages_messages 
+            where msg_id = :msg_id
+        </querytext>
+    </fullquery>
+
+    <fullquery name="get_data_msg">
+        <querytext>
+            select msg_content, owner_id, community_id,parent_id,
+                   sent_date, to_char(sent_date, 'HH24:MI:SS') as time,msg_id
+            from messages_messages
+            where msg_id in (select msg_id
+				from messages_user_messages
+				where user_id = :user_id
+				      and parent_id = :msg_id)
+            order by msg_id asc
+        </querytext>
+    </fullquery>
+
+    <fullquery name="get_contacts">
+        <querytext>
+            select party_id
+            from messages_recipients
+            where msg_id = :msg_id
+        </querytext>
+    </fullquery> 
+
+    <fullquery name="get_name">
+        <querytext>
+            select acs_users_all.last_name||' '||acs_users_all.first_names, acs_users_all.email
+            from acs_users_all,
+                    dotlrn_member_rels_approved
+            where dotlrn_member_rels_approved.community_id = :community_id
+                  and dotlrn_member_rels_approved.user_id = acs_users_all.user_id
+                  and acs_users_all.user_id = :party_id 
+        </querytext>
+    </fullquery>
+
+</queryset>
Index: openacs-4/packages/messages/www/read-message.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/read-message.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/read-message.adp	31 Aug 2010 21:30:43 -0000	1.1
@@ -0,0 +1,36 @@
+<if @permition_read_p@ eq "f">false</if>
+<else>
+    <input type="hidden" id="folder_id" value="@folder_id@">
+    <input type="hidden" id="msg_id" value="@msg_id@">
+    <input type="hidden" id="older_msg_id" value="@older_msg_id@">
+    <input type="hidden" id="oldest_msg_id" value="@oldest_msg_id@">
+    <input type="hidden" id="newer_msg_id" value="@newer_msg_id@">
+    <input type="hidden" id="newest_msg_id" value="@newest_msg_id@">
+    <input type="hidden" id="new_page_number" value="@new_page_number@">
+    <br>
+    <div style="margin-left:10px">
+        <input type="hidden" id="subject_field" value="@subject@">
+        <table width="100%">
+            <tr>
+                <th align="left">
+                    <strong style="font-size:15px">
+                        @subject@ 
+                        <input type="hidden" id="subject_fw" value="@subject@">
+                        <br>
+                    </strong>
+                </th>
+                <th>
+                    <span style="float: right;text-align:right;">
+                        <a href="print-message?msg_id=@msg_id@" target="_blank">
+                            &nbsp;&nbsp;<img src="/resources/messages/icono_imprimir.png" width="20" height="20">#messages.print#&nbsp;&nbsp;&nbsp;&nbsp;
+                        </a>
+                    </span>
+                </th>
+            </tr>
+        </table>
+        <span style="margin-right:10px;">
+            <include src="read" folder_id="@folder_id@" page_number="@page_number@">
+        </span>
+    </div>
+    <br>
+</else>
\ No newline at end of file
Index: openacs-4/packages/messages/www/read-message.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/read-message.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/read-message.tcl	31 Aug 2010 21:30:43 -0000	1.1
@@ -0,0 +1,62 @@
+ad_page_contract {
+    Read the messages.
+
+    @author pedro@viaro.net
+    @creation-date 2009-11-13
+    @cvs-id $Id: read-message.tcl,v 1.1 2010/08/31 21:30:43 alvaror Exp $
+} {
+    {msg_id ""}
+    {folder_id ""}
+    {page_number ""}
+    {total_messages ""}
+}
+
+set user_id [ad_conn user_id]
+set community_id [dotlrn_community::get_community_id]
+set parents_ids [db_list_of_lists get_parents_ids {}]
+set get_max_messages [db_string get_max_messages {} -default 25]
+set permition_read [db_string exist_msg {} -default 0]
+
+if { $permition_read == 0 } {
+    set permition_read_p "f"
+} else {
+    set permition_read_p "t"
+}
+
+###########################################
+#get message ids around the message view
+#and set range messages, before or after message display acording to message view.
+set msg_before [expr [lsearch $parents_ids $msg_id] - 2]
+set msg_after [expr [lsearch $parents_ids $msg_id] + 2]
+set parents_ids [lrange $parents_ids $msg_before $msg_after]
+set newer_msg_id [lindex $parents_ids [expr [lsearch $parents_ids $msg_id] - 1]]
+############################################
+if {[empty_string_p $newer_msg_id]} {
+    set newer_msg_id "-1"
+}
+set newest_msg_id [lindex $parents_ids [expr [lsearch $parents_ids $msg_id] - 2]]
+if {[empty_string_p $newest_msg_id]} {
+    set newest_msg_id "-1"
+}
+set older_msg_id [lindex $parents_ids [expr [lsearch $parents_ids $msg_id] + 1]]
+if {[empty_string_p $older_msg_id]} {
+    set older_msg_id "-1"
+}
+set oldest_msg_id [lindex $parents_ids [expr [lsearch $parents_ids $msg_id] + 2]]
+if {[empty_string_p $oldest_msg_id]} {
+    set oldest_msg_id "-1"
+}
+
+set number_total_pages [expr [expr ceil([expr [expr $total_messages + 0.0] / [expr $get_max_messages + 0.0]])] - 1]
+
+if { !$number_total_pages } {
+    set new_page_number 1
+} else {
+    set new_page_number [expr [format %0.0f [expr ceil([lsearch $parents_ids $msg_id]/ $get_max_messages)]] + 1]
+}
+
+db_0or1row data_msg { *SQL* }
+
+if { [empty_string_p $subject] } {
+    set subject "[_ messages.no_subject] "
+}
\ No newline at end of file
Index: openacs-4/packages/messages/www/read-message.xql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/read-message.xql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/read-message.xql	31 Aug 2010 21:30:43 -0000	1.1
@@ -0,0 +1,81 @@
+<?xml version="1.0"?>
+<queryset>
+
+    <fullquery name="data_msg">
+        <querytext>
+            select subject, owner_id
+            from messages_messages 
+            where msg_id = :msg_id
+                and community_id = :community_id
+        </querytext>
+    </fullquery>
+
+    <fullquery name="get_parents_ids">
+        <querytext>
+            select messages.msg_id
+            from (select max(m.msg_id) as last_msg_id, m.parent_id as msg_id, count(m.msg_id) as number_conversation
+                    from (select msg_id, coalesce(parent_id,msg_id) as parent_id 
+                          from messages_messages 
+                          where community_id = :community_id 
+                              and msg_id in (select parent_id 
+                                             from messages_user_messages 
+                                             where user_id = :user_id 
+                                                 and folder_id = :folder_id)) m
+                        group by m.parent_id
+                        order by last_msg_id desc) messages
+        </querytext>
+    </fullquery>
+
+    <fullquery name="get_max_messages">
+        <querytext>
+            select max_messages
+            from messages_options
+            where user_id = :user_id
+        </querytext>
+    </fullquery>
+
+    <fullquery name="get_attachment_id">
+        <querytext>
+            select attachment_id
+            from messages_attachments
+            where msg_id in (select msg_id from messages_messages where community_id = :community_id and parent_id = :msg_id or msg_id = :msg_id)
+        </querytext>
+    </fullquery>
+
+    <fullquery name="get_sol_info">
+        <querytext>
+            select crr.title as content, 
+            crr.title,
+            crr.item_id,
+            cri.storage_type,
+            crr.revision_id,
+            crr.content_length,
+            crr.mime_type,
+            crr.title
+            from cr_items cri, cr_revisions crr
+            where crr.revision_id = :attachment_id
+            and crr.item_id = cri.item_id
+        </querytext>
+    </fullquery>
+
+    <fullquery name="exist_msg">
+        <querytext>
+            select count(*)
+            from messages_user_messages
+            where user_id = :user_id
+                  and parent_id = :msg_id
+        </querytext>
+    </fullquery>
+
+    <fullquery name="get_members_ids_rel_type">
+        <querytext>
+            select $rel_type_case as rel_type 
+            from acs_rels 
+            where object_id_one = :community_id
+                and object_id_two = :owner_id
+        </querytext>
+    </fullquery>
+
+
+</queryset>  
+ 
Index: openacs-4/packages/messages/www/read-oracle.xql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/read-oracle.xql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/read-oracle.xql	31 Aug 2010 21:30:44 -0000	1.1
@@ -0,0 +1,41 @@
+<?xml version="1.0"?>
+
+<queryset>
+   <rdbms><type>oracle</type><version>8.1.6</version></rdbms>
+   
+    <fullquery name="get_messages_info">
+        <querytext>
+            select msg_content, owner_id, community_id,parent_id, subject,
+                   sent_date, to_char(sent_date, 'HH24:MI:SS') as time, msg_id
+            from messages_messages
+            where msg_id in ($msg_ids)
+            order by msg_id asc
+        </querytext>
+    </fullquery>
+
+    <fullquery name="get_contacts_ids">
+        <querytext>
+        select  msg_id,wm_concat(party_id) as contacts
+            from messages_recipients
+            where msg_id in ($msg_ids)
+            group by msg_id
+        </querytext>
+    </fullquery> 
+    
+    <fullquery name="get_sol_info">
+        <querytext>
+            select crr.title as content, 
+            crr.title,
+            crr.item_id,
+            cri.storage_type,
+            crr.revision_id,
+            crr.content_length,
+            crr.mime_type,
+            crr.filename
+            from cr_items cri, cr_revisions crr
+            where crr.revision_id = :attachment_id
+            and crr.item_id = cri.item_id
+        </querytext>
+    </fullquery>
+
+</queryset>
Index: openacs-4/packages/messages/www/read-postgresql.xql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/read-postgresql.xql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/read-postgresql.xql	31 Aug 2010 21:30:42 -0000	1.1
@@ -0,0 +1,48 @@
+<?xml version="1.0"?>
+<queryset>
+   <rdbms><type>postgresql</type><version>7.3</version></rdbms>    
+
+    <fullquery name="get_contacts_ids">
+        <querytext>
+            select  msg_id, concat(distinct(party_id)||',') as contacts
+            from messages_recipients
+            where msg_id in ($msg_ids)
+            group by msg_id
+        </querytext>
+    </fullquery>
+    
+    <fullquery name="get_contacts_ids_roles">
+        <querytext>
+        select  msg_id, concat(distinct(rel_id)||',') as contacts
+            from messages_recipients_roles
+            where msg_id in ($msg_ids)
+            group by msg_id
+        </querytext>
+    </fullquery> 
+
+    <fullquery name="get_messages_info">
+        <querytext>
+            select msg_content, owner_id, community_id,parent_id, subject,
+                   to_char(sent_date, 'YYYY-MM-DD HH24:MI:SS') as sent_date, to_char(sent_date, 'HH24:MI:SS') as time, msg_id
+            from messages_messages
+            where msg_id in ($msg_ids)
+            order by msg_id asc
+        </querytext>
+    </fullquery>
+
+    <fullquery name="get_sol_info">
+        <querytext>
+            select crr.title as content, 
+            crr.title,
+            crr.item_id,
+            cri.storage_type,
+            crr.revision_id,
+            crr.content_length,
+            crr.mime_type
+            from cr_items cri, cr_revisions crr
+            where crr.revision_id = :attachment_id
+            and crr.item_id = cri.item_id
+        </querytext>
+    </fullquery>
+
+</queryset>
Index: openacs-4/packages/messages/www/read.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/read.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/read.adp	31 Aug 2010 21:30:45 -0000	1.1
@@ -0,0 +1,88 @@
+<script src="/resources/messages/messages.js" type="text/javascript"></script>
+<input type="hidden" id="folder_id" value="@folder_id@">
+<input type="hidden" id="read" value="true">
+<input type="hidden" id="msg_reply" value="">
+<input type="hidden" id="parent_id_reply" value="">
+<input type="hidden" id="msg_action_id" value="">
+<multiple name="messages">
+    <span id="msg_action_@messages.msg_id@" style="display:none">
+        <span id=alert-message> 
+            <span class="alert">
+                <strong>#messages.msg_has_been_sent#</strong>
+            </span>
+        </span>
+    </span>
+    <div style="border-style:ridge;border-width:1px;">
+        <table width="98%" valign="top" style="margin-left:10px;margin-left:15px;">
+            <tr>
+                <th valign="top" align="left">
+                    <span id="from_@messages.msg_id@" style="float: left;text-align:left;display:none;"> <font color="#888888">#messages.from#: &nbsp;</font></span>
+                    <strong>@messages.name_user@</strong>
+                    <if @messages.rel_type_user@ ne "">
+                        <span style="background-color:#F06040;color:white;padding:2px;font-size:10px;"> #messages.message_sent_by#: @messages.rel_type_user@ </span>
+                    </if>
+                    <div style="overflow:hidden;width:100%;height:20px;" id="contact_list_@messages.msg_id@">
+                        <font color="#888888">#messages.to# @messages.first_name_contacts@ </font>
+                    </div>
+                    <span style="text-align:left;display:none;font-size: x-small;" id="details_@messages.msg_id@" > 
+                        <font color="#888888">
+                            <br>
+                            #messages.to#: @messages.contact_names;noquote@ <br>
+                            #messages.date#: @messages.ansi_time@ <br>
+                            #messages.subject#: @messages.subject@
+                        </font>
+                    </span>
+                    <span style="margin-left:20px;font-size: 12px;color:#6E6E6E">
+                        <br><br>
+                            @messages.msg_content;noquote@
+                        <br>
+                    </span>
+                </th>
+                <th width="20%" valign="top" align="left">
+                    <div style="margin-left:20px;margin-left:18px;">
+                        <span class="margin-form">
+                            <span style="float: right;text-align:right;">
+                                &nbsp;&nbsp;@date@ &nbsp;&nbsp;
+                            </span>
+                            <span style="float: right;text-align:right;" id="show_details_@messages.msg_id@">
+                                <a href="javascript:mostrardiv(@messages.msg_id@);">  #messages.show_details#</a>&nbsp;&nbsp;
+                            </span>
+                            <span style="float: right;text-align:right;display:none;" id="close_details_@messages.msg_id@">
+                                <br>
+                                <a href="javascript:close_dialog(@messages.msg_id@);">#messages.hide_details#</a>&nbsp;&nbsp;
+                            </span>
+                        </span>
+                        <br><br>
+                        <span>
+                            @messages.download_file;noquote@
+                        </span>
+                    </div>
+                </th>
+            </tr>
+        </table>
+        <br>
+        <span id="scroll_message_@messages.msg_id@"></span>
+        <div style="background-color:@color@">
+            <table>
+                <th>
+                    <td id="reply_@messages.msg_id@" style="background-color:@color@;">
+                        <a href="javascript:reply_dialog('reply',@messages.msg_id@,@general_parent_id@);update_numbers(@folder_id@,@user_id@);"><img src="/resources/messages/reply.png" width="15px" height="15px">#messages.reply#&nbsp;&nbsp;</a>
+                    </td>
+                    <if @messages.total_recipients@ gt 1>
+                        <td id="reply_all_@messages.msg_id@" style="background-color:@color@;">
+                            <a href="javascript:reply_dialog('reply_all',@messages.msg_id@,@general_parent_id@);update_numbers(@folder_id@,@user_id@);"><img src="/resources/messages/reply-all.png" width="15px" height="15px">#messages.reply_all#</a>
+                        </td>
+                    </if>
+                    <td id="forward_@messages.msg_id@" style=background-color:@color@;>
+                        <a href="javascript:reply_dialog('forward',@messages.msg_id@,@general_parent_id@);update_numbers(@folder_id@,@user_id@);"><img src="/resources/messages/forward.png" width="15px" height="15px">#messages.forward#</a>
+                    </td>
+                </th>
+            </table>
+        </div>
+        <span id=response_reply_mail_@messages.msg_id@ style="display:;">
+        </span>
+    </div>
+    <br>
+</multiple>
+
+<input type="hidden" id="rel_types_checked" value="@rel_types_checked@">
Index: openacs-4/packages/messages/www/read.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/read.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/read.tcl	31 Aug 2010 21:30:45 -0000	1.1
@@ -0,0 +1,131 @@
+ad_page_contract {
+    Read the messages.
+
+    @author pedro@viaro.net
+    @creation-date 2009-11-13
+    @cvs-id $Id: read.tcl,v 1.1 2010/08/31 21:30:45 alvaror Exp $
+} {
+    {msg_id ""}
+}
+
+set general_parent_id $msg_id
+set community_id [dotlrn_community::get_community_id]
+set user_id [ad_conn user_id]
+set color "#C0C0C0"
+set msg_id_list [list]
+db_dml update_status {}
+
+db_foreach get_msg_ids {} {
+    lappend msg_id_list $msg_id
+}
+
+if { [llength $msg_id_list] > 0 } {
+    set msg_ids [join $msg_id_list ","]
+} else {
+    set msg_ids ""
+}
+
+db_foreach get_contacts_ids {} {
+   set contacts_ids_array($msg_id) $contacts
+}
+
+db_foreach get_contacts_ids_roles {} {
+   set contacts_ids_array_roles($msg_id) $contacts
+}
+
+##Roles: se estable los roles que existen y se obtienen lo usuarios y sus roles
+set roles [dotlrn_community::get_roles -community_id $community_id]
+set rel_types [list]
+foreach role $roles {
+    lappend rel_types [lindex $role 0]
+}
+set rel_types [join $rel_types "','"]
+set rel_type_case "case "
+foreach {rel_type role pretty_name pretty_plural} [eval concat $roles] {
+    append rel_type_case "when rel_type = '$rel_type' then '$pretty_name' "
+}
+append rel_type_case " end"
+###
+
+db_multirow -extend { rel_type_user to first_name_contacts name_user total_recipients contact_names download_file ansi_time } messages get_messages_info { *SQL* } {
+    ##Roles: se estable los roles que existen y se obtienen lo usuarios y sus roles
+    set rel_type_user [lang::util::localize [db_string get_members_ids_rel_type {}]]
+
+    ###
+    set name_user [acs_user::get_element -user_id $owner_id -element name]
+    set download_file ""
+    set attachment [db_list_of_lists get_attachment_id {}]
+
+    if { [llength $attachment] != 0} {
+        foreach attachment_info $attachment {
+            set attachment_id [lindex $attachment_info 0]
+            set attachment_item_id [lindex $attachment_info 1]
+            db_1row get_sol_info { *SQL* }
+            set position [string first . "$content" 1]
+            if { $position == "-1" } {
+                set position [string length $content]
+            }
+            append download_file "<img width=20 height=20 src=\"/resources/messages/attachment.png\"/><a style=\"font-size: 8pt\" target=\"_blank\"  href=\"view/$title?file%5fid=$attachment_item_id\"> \
+                                    [messages::string_truncate_middle -len 15 -string $title]</a><br>"
+        }
+
+    } else {
+        set download_file ""
+    }
+
+    set contact_names ""
+    set first_name_contacts ""
+    if { [array exists contacts_ids_array] != -1 } {
+        if { [lsearch [array name contacts_ids_array] $msg_id] == -1 }  {
+            set contact_list ""
+        } else {
+            set contact_list [split $contacts_ids_array($msg_id) ","]
+        }
+    } else {
+        set contact_list ""
+    }
+
+
+    set total_recipients [llength $contact_list]
+    set name_to_p 0
+    foreach party_id $contact_list {
+        if { ![empty_string_p $party_id] } {
+            set name [acs_user::get_element -user_id $party_id -element name] 
+            if {$party_id == $owner_id}  {
+                     append contact_names $name ",<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"
+            } else {
+                set name_to_p 1
+                append first_name_contacts [string range $name 0 [expr [string first " " $name 0] - 1]] ", "
+                append contact_names $name ",<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"
+            }
+        }
+    }
+    set rel_types [db_list get_rel_types {}]
+    set rel_types_checked [join $rel_types ","]
+    foreach rel_type $rel_types { 
+        set pretty_name [dotlrn_community::get_role_pretty_plural -rel_type $rel_type]
+        append first_name_contacts $pretty_name ", "
+        append contact_names $pretty_name ",<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"
+    }
+    if { $name_to_p } {
+        set to [string tolower [_ messages.to] 0 1]
+    } else {
+        set to ""
+    }
+    set first_name_contacts [string range $first_name_contacts 0 [expr [string length $first_name_contacts] - 3]]
+    set contact_names [string range $contact_names 0 [expr [string length $contact_names] - 60]]
+    set cur_time [clock format [clock seconds] -format "%Y %m %d"]
+    set sent_time_seconds [clock format [clock scan $sent_date] -format "%Y %m %d"]]
+    set day_recive [lc_time_fmt $sent_date "%d %b, %Y" ]
+    set ansi_time "$day_recive - $time"
+    set msg_content [template::util::richtext::get_property contents $msg_content]
+    if { [lindex $cur_time 0] > [lindex $sent_time_seconds 0]} {
+        set date [lc_time_fmt $sent_date "%D"]
+    } else {
+        if { ([lindex $cur_time 1] >= [lindex $sent_time_seconds 1]) && ([lindex $cur_time 2] == [lindex $sent_time_seconds 2] ) } {
+            set date [_ messages.today]
+        } else {
+            set date [lc_time_fmt $sent_date "%d %b" ]
+        }
+    }
+}
\ No newline at end of file
Index: openacs-4/packages/messages/www/read.xql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/read.xql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/read.xql	31 Aug 2010 21:30:45 -0000	1.1
@@ -0,0 +1,95 @@
+<?xml version="1.0"?>
+<queryset>
+
+    <fullquery name="get_msg_ids">
+        <querytext>
+            select distinct(msg_id)
+            from messages_user_messages
+            where user_id = :user_id
+                  and parent_id = :msg_id
+        </querytext>
+    </fullquery>
+
+    <fullquery name="count_recipients">
+        <querytext>
+            select count(party_id)
+            from messages_recipients
+            where msg_id = :msg_id
+        </querytext>
+    </fullquery>
+
+    <fullquery name="update_status">
+        <querytext>
+            update messages_user_messages
+            set new_p = 'f'
+            where parent_id = :msg_id
+                and user_id = :user_id
+                and new_p != 'f'
+        </querytext>
+    </fullquery>
+
+    <fullquery name="get_name">
+        <querytext>
+            select acs_users_all.last_name||' '||acs_users_all.first_names
+            from acs_users_all,
+                    dotlrn_member_rels_approved
+            where dotlrn_member_rels_approved.community_id = :community_id
+                  and dotlrn_member_rels_approved.user_id = acs_users_all.user_id
+                  and acs_users_all.user_id = :party_id 
+        </querytext>
+    </fullquery>
+
+    <fullquery name="get_attachment_id">
+        <querytext>
+            select attachment_id, attachment_item_id
+            from messages_attachments
+            where msg_id = :msg_id
+        </querytext>
+    </fullquery>
+
+    <fullquery name="get_rel_types">
+        <querytext>
+            select rel_id
+            from messages_recipients_roles
+            where msg_id = :msg_id
+        </querytext>
+    </fullquery>
+
+    <fullquery name="get_messages_info">
+        <querytext>
+            select msg_content, owner_id, community_id,parent_id, subject,
+                   to_char(sent_date, 'YYYY-MM-DD HH24:MI:SS') as sent_date, to_char(sent_date, 'HH24:MI:SS') as time, msg_id
+            from messages_messages
+            where msg_id in ($msg_ids)
+            order by msg_id asc
+        </querytext>
+    </fullquery>
+
+    <fullquery name="get_contacts_ids">
+        <querytext>
+            select  msg_id,wm_concat(party_id) as contacts
+            from messages_recipients
+            where msg_id in ($msg_ids)
+            group by msg_id
+        </querytext>
+    </fullquery>
+
+    <fullquery name="get_contacts_ids_roles">
+        <querytext>
+        select  msg_id, wm_concat(distinct(rel_id)) as contacts
+            from messages_recipients_roles
+            where msg_id in ($msg_ids)
+            group by msg_id
+        </querytext>
+    </fullquery> 
+
+    <fullquery name="get_members_ids_rel_type">
+        <querytext>
+            select $rel_type_case as rel_type 
+            from acs_rels 
+            where object_id_one = :community_id
+                and object_id_two = :owner_id
+        </querytext>
+    </fullquery>
+
+</queryset>
Index: openacs-4/packages/messages/www/search-oracle.xql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/search-oracle.xql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/search-oracle.xql	31 Aug 2010 21:30:42 -0000	1.1
@@ -0,0 +1,53 @@
+<?xml version="1.0"?>
+
+<queryset>
+   <rdbms><type>oracle</type><version>8.1.6</version></rdbms>
+   
+ <fullquery name="get_msg_ids">
+        <querytext>
+            select msg_id, last_msg_id
+            from (select paginate.*, rownum rowsub 
+                from (  select max(msg_id) as last_msg_id, parent_id as msg_id, folder_id, count(msg_id) as number_conversation
+                        from messages_user_messages
+                        where user_id = :user_id
+                         and msg_id in (select msg_id
+                                        from messages_messages
+                                        where community_id = :community_id
+                                              and (owner_id in (select person_id
+                                                                from persons
+                                                                $where_clause_user)
+                                                   or msg_id in (select msg_id
+                                                                 from messages_recipients
+                                                                 where party_id in (select person_id
+                                                                                    from   persons
+                                                                                    $where_clause_user))
+                                                  $search_clause))
+                        group by parent_id, folder_id
+                        order by last_msg_id desc
+                       ) paginate
+              where rownum <= :last_row)
+        where rowsub >= :first_row
+        </querytext>
+    </fullquery>
+    
+    
+        <fullquery name="get_messages">
+        <querytext>
+        select mm.last_msg_id, mm.msg_id, mm.number_conversation, mm.subject, wm_concat(distinct(p.first_names)) as contacts, mm.folder_id
+        from persons p,
+            (select max(um.msg_id) as last_msg_id, um.parent_id as msg_id, count(um.msg_id) as number_conversation, m.subject, m.owner_id, um.folder_id
+            from (select parent_id,msg_id, folder_id 
+                  from messages_user_messages 
+                  where user_id = :user_id) um,
+                 (select msg_id,subject,owner_id 
+                  from messages_messages 
+                  where community_id = :community_id 
+                        and msg_id in ($msg_ids)) m
+            where um.parent_id = m.msg_id
+            group by um.parent_id, m.subject, m.owner_id, um.folder_id) mm
+        where p.person_id = mm.owner_id
+        group by mm.msg_id, mm.subject, mm.last_msg_id, mm.number_conversation, mm.folder_id
+        order by mm.last_msg_id desc 
+        </querytext>
+    </fullquery>
+</queryset> 
Index: openacs-4/packages/messages/www/search-postgresql.xql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/search-postgresql.xql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/search-postgresql.xql	31 Aug 2010 21:30:42 -0000	1.1
@@ -0,0 +1,54 @@
+<?xml version="1.0"?>
+<queryset>
+   <rdbms><type>postgresql</type><version>7.3</version></rdbms>    
+
+ <fullquery name="get_msg_ids">
+        <querytext>
+            select msg_id, last_msg_id
+            from (select paginate.*
+                from (  select max(msg_id) as last_msg_id, parent_id as msg_id, folder_id, count(msg_id) as number_conversation
+                        from messages_user_messages
+                        where user_id = :user_id
+                         and msg_id in (select msg_id
+                                        from messages_messages
+                                        where community_id = :community_id
+                                              and (owner_id in (select person_id
+                                                                from persons
+                                                                $where_clause_user)
+                                                   or msg_id in (select msg_id
+                                                                 from messages_recipients
+                                                                 where party_id in (select person_id
+                                                                                    from   persons
+                                                                                    $where_clause_user))
+                                                  $search_clause))
+                        group by parent_id, folder_id
+                        order by last_msg_id desc
+                       ) paginate
+          LIMIT :last_row) messages
+    OFFSET :first_row - 1
+        </querytext>
+    </fullquery>
+    
+        <fullquery name="get_messages">
+        <querytext>
+        select mm.last_msg_id, mm.msg_id, mm.number_conversation, mm.subject, concat(distinct(p.first_names)||',') as contacts, mm.folder_id
+        from persons p,
+            (select max(um.msg_id) as last_msg_id, um.parent_id as msg_id, count(um.msg_id) as number_conversation, m.subject, m.owner_id, um.folder_id
+            from (select parent_id,msg_id, folder_id 
+                  from messages_user_messages 
+                  where user_id = :user_id) um,
+                 (select msg_id,subject,owner_id 
+                  from messages_messages 
+                  where community_id = :community_id 
+                        and msg_id in ($msg_ids)) m
+            where um.parent_id = m.msg_id
+            group by um.parent_id, m.subject, m.owner_id, um.folder_id) mm
+        where p.person_id = mm.owner_id
+        group by mm.msg_id, mm.subject, mm.last_msg_id, mm.number_conversation, mm.folder_id
+        order by mm.last_msg_id desc 
+        </querytext>
+    </fullquery>
+    
+
+</queryset>
+ 
Index: openacs-4/packages/messages/www/search.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/search.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/search.adp	31 Aug 2010 21:30:44 -0000	1.1
@@ -0,0 +1,44 @@
+<input type="hidden" id="page_number" value=@page_number@>
+<input type="hidden" id="folder_id" value=@folder_id@>
+<input type="hidden" id="folder_id_state" value="@folder_id@">
+<input type="hidden" id="msg_continue_empty" value="#messages.msg_continue_empty#">
+<input type="hidden" id="number_messages" value=@total_messages@>
+
+<div align="right">
+    <if @total_messages@ gt @show_messages@>
+        <if @page_number@ eq 2>
+            <a href="#" onClick="paginate(@folder_id@,@page_number@,'newer');update_numbers(@folder_id@,@user_id@)">#messages.newer# </a>
+        </if>
+        <if @page_number@ ge 3>
+            <a href="#" onClick="paginate(@folder_id@,1,'newest');update_numbers(@folder_id@,@user_id@)">#messages.newest# </a>
+            <a href="#" onClick="paginate(@folder_id@,@page_number@,'newer');update_numbers(@folder_id@,@user_id@)">#messages.newer# </a>
+        </if>
+        #messages.range_messages_show#
+        <if @page_number@ lt @total_pages@>
+            <a href="#" onClick="paginate(@folder_id@,@page_number@,'older');update_numbers(@folder_id@,@user_id@)">#messages.older# </a>
+            <if @page_number@ ne @oldest_page@>
+                <if @page_number@ lt @oldest_page@>
+                    <a href="#" onClick="paginate(@folder_id@,@total_pages@,'oldest');update_numbers(@folder_id@,@user_id@)">#messages.oldest# </a>
+                </if>
+            </if>
+        </if>
+    </if>
+    <else>
+        <if @total_messages@ ne 0>  #messages.range_messages_show# </if>
+    </else>
+</div>
+<if @folder_id@ eq 3>
+    <div id="folder_trash_msg" style="font-size:12px;" align="center">
+        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+        <if @total_messages@ ne 0>
+            <a href="#" onclick="empty_trash()">#messages.empty_trash#</a>
+        </if>
+         #messages.folder_trash_msg# &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+    </div>
+</if>
+
+<form name="messages_list" method="GET">
+    <div id="list_messages" style="weight:100%">
+        <listtemplate name="messages"></listtemplate>
+    </div>
+</form> 
Index: openacs-4/packages/messages/www/search.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/search.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/search.tcl	31 Aug 2010 21:30:44 -0000	1.1
@@ -0,0 +1,176 @@
+ad_page_contract {
+    Inbox Page
+
+    @author pedro@viaro.net
+    @creation-date 2009-11-11
+    @cvs-id $Id: search.tcl,v 1.1 2010/08/31 21:30:44 alvaror Exp $
+} {
+    {page_number ""}
+    {data_search ""}
+} 
+
+set user_id [ad_conn user_id]
+set community_id [dotlrn_community::get_community_id]
+
+if {[empty_string_p $page_number]} {
+    set page_number 1
+}
+
+if { [exists_and_not_null data_search] } {
+    set data_search_list [string trim [split $data_search " "]]
+    set count 0
+    foreach data_search $data_search_list {
+        set sql_keyword "%[string tolower $data_search]%"
+        if { $count == 0 } {
+            append where_clause_user "lower(first_names || ' ' || last_name) like '$sql_keyword'"
+        } else {
+            append where_clause_user " or lower(first_names || ' ' || last_name) like '$sql_keyword'"        
+        }
+        incr count
+        append search_clause "or lower(msg_content) like '%'||lower('$data_search')||'%'"
+        append search_clause " or lower(subject) like '%'||lower('$data_search')||'%'"
+    }
+    set where_clause_user "where $where_clause_user"
+} else {
+    set where_clause_user ""
+    set search_clause ""
+}
+
+#####################################################################
+#get the total messages and calculate how many pages have in folder show
+#First and last row are calculate based on the total messages show per page
+set total_messages [db_string get_total_messages {}]
+set show_messages [db_string get_max_show_msg {} -default 25]
+#Round to the next integer because the las page include some messages.
+set total_pages [expr ceil([expr [expr $total_messages + 0.0] / [expr $show_messages + 0.0]])]
+set oldest_page [expr $total_pages - 1]
+set count_messages 0
+if { $page_number != 1} {
+    set first_row [expr [expr [expr $page_number - 1] * $show_messages] + 1]
+    set last_row [expr [expr $first_row + $show_messages] - 1]
+} else {
+    set first_row [expr [expr $page_number - 1] * $show_messages]
+    set last_row [expr $first_row + $show_messages]
+    set first_row [expr $first_row + 1]
+}
+######################################################################
+
+set elements [list  msg_ids \
+                [list label { <input type="checkbox" id="checkbox_general" name="_dummy" onclick="select_all(this.form,this.checked);" title="[_ acs-templating.lt_Checkuncheck_all_rows]">} \
+                display_template {<input type="checkbox" id="check_box_msg.@messages.msg_id@" name="subject_id" value="@messages.msg_id@" id="msg_ids.@messages.msg_id@" title="[_ acs-templating.lt_Checkuncheck_this_row]" onclick="more_actions_select();">} \
+                sub_class {narrow} \
+                html { align center }] \
+            ]
+
+lappend elements contacts \
+            [list html { align left style "font-size: smaller; width:20%"} \
+                label [_ messages.from] \
+                display_template { <a href="#" onclick="read_message(@messages.msg_id@,@messages.folder_id@,$page_number)"> <if @messages.new_p@>\
+                                     <span id="new_p.@messages.msg_id@"><span id="new_p_style.@messages.msg_id@"  style="font-weight: bold;">@messages.contacts@ \
+                                        <if @messages.folder_id@ ne 1> (@messages.number_conversation@) </if></span>\
+                                    </if><else><span id="new_p_style.@messages.msg_id@" style="font-weight:;">@messages.contacts@ (@messages.number_conversation@)</span></else></a>} \
+            ] \
+    folder_id \
+        [list html { align center style "font-size: smaller; width:5%"} \
+            label "" \
+                display_template { <input type="button" value="@messages.folder_name@" href="#" onclick="read_message(@messages.msg_id@,@messages.folder_id@,$page_number)"> }] \
+    attachmet_mail \
+        [list html { align center style "font-size: smaller; width:5%"} \
+            label "" \
+                display_template { <a href="#" onclick="read_message(@messages.msg_id@,@messages.folder_id@,$page_number)">\
+                                    <if @messages.attachment_p@ gt 0><span style="font-weight: bold;"><img src="/resources/messages/attachment.png" width="20" height="20"></span> \
+                                   </if></a>} \
+        ] \
+    subject_mail \
+        [list html { align left style "font-size: smaller; width:65%"} \
+            label [_ messages.subject] \
+                display_template { <a href="#" onclick="read_message(@messages.msg_id@,@messages.folder_id@,$page_number)">\
+                                    <if @messages.new_p@><span id="new_p_sub.@messages.msg_id@" style="font-weight: bold;">@messages.subject_mail@</span> \
+                                   </if><else><span id="new_p_sub.@messages.msg_id@" style="font-weight:;">@messages.subject_mail@</span></else></a>} \
+        ] \
+    date \
+        [list html { align left style "font-size: smaller;width:10%"} \
+            label [_ messages.date] \
+                display_template { <a href="#" onclick="read_message(@messages.msg_id@,@messages.folder_id@,$page_number)">\
+                                    <if @messages.new_p@><span id="new_p_date.@messages.msg_id@" style="font-weight: bold;">@messages.date@</span> \
+                                    </if><else><span id="new_p_date.@messages.msg_id@" style="font-weight:;">@messages.date@</span></else></a>} \
+        ]
+
+
+template::list::create \
+    -name messages \
+    -multirow messages \
+    -main_class "list-table" \
+    -html { width=100% } \
+    -orderby_name orderby \
+    -no_data "[_ messages.no_messages_found]" \
+    -elements $elements 
+
+set msg_id_list [list]
+set last_msg_ids_list [list]
+db_foreach get_msg_ids {} {
+    set last_msg_ids_array($msg_id) $last_msg_id
+    lappend last_msg_ids_list $last_msg_id
+    lappend msg_id_list $msg_id
+}
+
+set msg_ids [join $msg_id_list ","]
+set last_msg_ids [join $last_msg_ids_list ","]
+
+if { ![exists_and_not_null $msg_ids] } {
+    set folder_id 0
+}
+
+if { [empty_string_p $msg_ids] } {
+    set msg_ids 0
+}
+
+if { [empty_string_p $last_msg_ids] } {
+    set last_msg_ids 0
+}
+
+db_foreach get_attachment_p { } {
+   set get_attachment_array($parent_id) $count_attachment
+}
+
+db_foreach get_new_p {} {
+   set new_p_array($parent_id) $new_p
+}
+
+db_foreach get_msg_info {} {
+    set msg_content_array($parent_id) $msg_content
+    set date_sent_array($parent_id) $sent_date_ansi
+}
+
+db_multirow -extend { subject_mail date attachment_p new_p folder_name} messages get_messages { *SQL* } {
+    set folder_name [db_string get_folder_name {}]
+    if { [info exists get_attachment_array($msg_id)]} {
+        set attachment_p $get_attachment_array($msg_id)
+    } else {
+        set attachment_p 0
+    }
+    set msg_content $msg_content_array($msg_id)
+    set sent_date_ansi $date_sent_array($msg_id)
+    set content [ad_html_to_text [template::util::richtext::get_property contents $msg_content]]
+    append subject_mail $subject "-" [string_truncate -len 80 -ellipsis "..." " $content"]
+    set cur_time [clock format [clock seconds] -format "%Y %m %d"]
+    set sent_time_seconds [clock format [clock scan $sent_date_ansi] -format "%Y %m %d"]
+    #compare the sent date whith the current date and display the message according to the date.
+    if { [lindex $cur_time 0] > [lindex $sent_time_seconds 0]} {
+        set date [lc_time_fmt $sent_date_ansi "%D"]
+    } else {
+        if {[lindex $cur_time 1] >= [lindex $sent_time_seconds 1] && [lindex $cur_time 2] == [lindex $sent_time_seconds 2]} {
+            set date [_ messages.today]
+        } else {
+            set date [lc_time_fmt $sent_date_ansi "%d,%b,%Y %H:%M"]
+        }
+    }
+    if { [info exists new_p_array($msg_id)]} {
+        set new_p $new_p_array($msg_id)
+    } else {
+        set new_p 0
+    }
+    incr count_messages
+}
+
+set last_number [expr [expr $count_messages + $first_row] - 1]
\ No newline at end of file
Index: openacs-4/packages/messages/www/search.xql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/search.xql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/search.xql	31 Aug 2010 21:30:44 -0000	1.1
@@ -0,0 +1,93 @@
+<?xml version="1.0"?>
+<queryset>
+
+    <fullquery name="get_name">
+        <querytext>
+            select acs_users_all.last_name||' '||acs_users_all.first_names
+            from acs_users_all,
+                    dotlrn_member_rels_approved
+            where dotlrn_member_rels_approved.community_id = :community_id
+                  and dotlrn_member_rels_approved.user_id = acs_users_all.user_id
+                  and acs_users_all.user_id = :party_id 
+        </querytext>
+    </fullquery>
+
+    <fullquery name="get_msg_info">
+        <querytext>
+            select coalesce(parent_id,msg_id) as parent_id, msg_content, to_char(sent_date, 'YYYY-MM-DD HH24:MI:SS') as sent_date_ansi
+            from messages_messages
+            where msg_id in ($last_msg_ids)
+        </querytext>
+    </fullquery>
+
+    <fullquery name="get_total_messages">
+        <querytext>
+        select count(*) 
+        from (select parent_id, folder_id
+            from messages_user_messages
+            where user_id = :user_id
+             and msg_id in (select msg_id
+                            from messages_messages
+                            where community_id = :community_id
+                                  and (owner_id in (select person_id
+                                                    from persons
+                                                    $where_clause_user)
+                                       or msg_id in (select msg_id
+                                                     from messages_recipients
+                                                     where party_id in (select person_id
+                                                                        from   persons
+                                                                        $where_clause_user))
+                                      $search_clause))
+              group by parent_id,folder_id) as messages
+        </querytext>
+    </fullquery>
+
+    <fullquery name="get_max_show_msg">
+        <querytext>
+            select max_messages
+            from messages_options
+            where user_id = :user_id
+        </querytext>
+    </fullquery>
+
+    <fullquery name="get_attachment_p">
+        <querytext>
+            select um.parent_id, count(*) as count_attachment
+            from messages_attachments ma,
+                (select msg_id, parent_id
+                             from messages_user_messages
+                             where user_id = :user_id
+                                    and parent_id in ($msg_ids)
+                            ) um
+             where ma.msg_id = um.msg_id
+            group by um.parent_id
+        </querytext>
+    </fullquery>
+
+    <fullquery name="get_new_p">
+        <querytext>
+            select parent_id, new_p
+            from messages_user_messages
+            where msg_id in ($last_msg_ids)
+                    and user_id = :user_id
+        </querytext>
+    </fullquery>
+
+    <fullquery name="get_owner_id">
+        <querytext>
+            select owner_id
+            from messages_messages
+            where msg_id = :msg_id
+            and community_id = :community_id
+        </querytext>
+    </fullquery>
+
+    <fullquery name="get_folder_name">
+        <querytext>
+            select folder_name
+            from messages_folders
+            where folder_id = :folder_id
+        </querytext>
+    </fullquery>
+
+</queryset>  
\ No newline at end of file
Index: openacs-4/packages/messages/www/send-mail-oracle.xql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/send-mail-oracle.xql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/send-mail-oracle.xql	31 Aug 2010 21:30:44 -0000	1.1
@@ -0,0 +1,49 @@
+<?xml version="1.0"?>
+
+<queryset>
+   <rdbms><type>oracle</type><version>8.1.6</version></rdbms>
+   
+        <fullquery name="set_file_content">
+              <querytext>
+
+                update cr_revisions
+                set filename = :file_name,
+                mime_type = :mime_type,
+                content_length = :content_length
+                where revision_id = :revision_id
+                    
+              </querytext>
+        </fullquery>
+              
+            <fullquery name="lob_content">
+                <querytext>
+                
+                        update cr_revisions 
+                        set content = empty_blob(),
+                        filename = :filename
+                        where revision_id = :revision_id
+                        returning content into :1   
+                        
+                  </querytext>
+            </fullquery>
+        
+        
+        <fullquery name="get_sol_info">      
+             <querytext>
+
+                select crr.title as content, 
+                crr.title,
+                crr.item_id,
+                cri.storage_type,
+                crr.revision_id,
+                crr.content_length,
+                crr.mime_type
+                crr.filename
+                from cr_items cri, cr_revisions crr
+                where crr.revision_id = :attachment_id
+                        and crr.item_id = cri.item_id
+            
+              </querytext>
+        </fullquery>
+        
+</queryset> 
Index: openacs-4/packages/messages/www/send-mail-postgresql.xql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/send-mail-postgresql.xql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/send-mail-postgresql.xql	31 Aug 2010 21:30:44 -0000	1.1
@@ -0,0 +1,45 @@
+<?xml version="1.0"?>
+
+<queryset>
+   <rdbms><type>postgresql</type><version>7.3</version></rdbms>
+   
+<fullquery name="set_file_content">      
+      <querytext>
+
+        update cr_revisions
+        set content = :file_name,
+        mime_type = :mime_type,
+        content_length = :content_length
+        where revision_id = :revision_id
+            
+      </querytext>
+</fullquery>   
+
+<fullquery name="lob_content">      
+      <querytext>
+
+        update cr_revisions 
+        set lob = [set __lob_id [db_string get_lob_id "select empty_lob()"]]
+        where revision_id = :revision_id
+    
+      </querytext>
+</fullquery>
+
+<fullquery name="get_sol_info">      
+      <querytext>
+
+        select crr.title as content, 
+        crr.title,
+        crr.item_id,
+        cri.storage_type,
+        crr.revision_id,
+        crr.content_length,
+        crr.mime_type
+        from cr_items cri, cr_revisions crr
+        where crr.revision_id = :attachment_id
+          and crr.item_id = cri.item_id
+    
+      </querytext>
+</fullquery>
+   
+</queryset>
\ No newline at end of file
Index: openacs-4/packages/messages/www/send-mail.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/send-mail.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/send-mail.adp	31 Aug 2010 21:30:44 -0000	1.1
@@ -0,0 +1 @@
+@msg_id;noquote@
\ No newline at end of file
Index: openacs-4/packages/messages/www/send-mail.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/send-mail.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/send-mail.tcl	31 Aug 2010 21:30:44 -0000	1.1
@@ -0,0 +1,114 @@
+ad_page_contract {
+    Page send mail
+
+    @author pedro@viaro.net
+    @creation-date 2009-11-11
+    @cvs-id $Id: send-mail.tcl,v 1.1 2010/08/31 21:30:44 alvaror Exp $
+} {
+    {msg_id ""}
+    {subject ""}
+    {parent_id ""}
+    {contacts_ids ""}
+    {total_attachment 0}
+    {reply ""}
+    {content_msg ""}
+    {message:html ""}
+    {attachment_item_id ""}
+    {general_parent_id ""}
+    {msg_id_old ""}
+    {rel_types:multiple "" }
+    {attachment_ids ""}
+}
+
+set community_id [dotlrn_community::get_community_id]
+set owner_id [ad_conn user_id]
+set community_name [dotlrn_community::get_community_name $community_id]
+set contact_info ""
+set rel_types_html ""
+set fs_package_id [dotlrn_community::get_package_id_from_package_key -package_key file-storage -community_id $community_id]
+set fs_folder_id [fs::get_root_folder -package_id $fs_package_id]
+set attachment_item_id ""
+set package_id [ad_conn package_id]
+set admin_p [permission::permission_p -party_id $owner_id -object_id $package_id -privilege admin]
+
+if { $admin_p } {
+    set roles [dotlrn_community::get_roles -community_id $community_id]
+    foreach {rel_type role pretty_name pretty_plural} [eval concat $roles] {
+        append rel_types_html "<input type=checkbox value=\"$rel_type\" name=\"rel_types\" id=\"$rel_type\">  [lang::util::localize $pretty_plural] "
+    }
+    append rel_types_html "<input type=checkbox value=[_ messages.send_to_all] name=rel_types_all onclick=select_all_rel_types(this.checked)> [_ messages.send_to_all]"
+}
+
+ad_form -html { enctype multipart/form-data } -name compose_message -form { }
+
+ad_form -extend -name compose_message -form  {
+
+} -on_submit {
+    set count_attachment [expr $total_attachment + 1]
+    if { ![exists_and_not_null parent_id] }  {
+        set parent_id ""
+    }
+    set base_url [dotlrn_community::get_community_url $community_id]
+    set message_url [export_vars -base "${base_url}messages/messages"]
+    set msg_id [messages::reply_mail -parent_id $parent_id -count_attachment $count_attachment \
+	-contacts_ids $contacts_ids -community_id $community_id \
+	-owner_id $owner_id -subject $subject -message $message -general_parent_id $general_parent_id \
+        -message_url $message_url -msg_id_old $msg_id_old -attachment_ids $attachment_ids -rel_types $rel_types]
+
+    for {set x 1} {$x < $count_attachment} {incr x} {
+        set name_attachment "upload_file_${x}"
+        ad_form -extend -name compose_message -form {
+            {upload_file_$x:file,optional
+                {label ""} 
+            }
+        } 
+        set upload_file [set $name_attachment]
+        if { ![empty_string_p $upload_file] }  {
+        db_transaction {
+                set attachment_item_id ""
+                if { ![regexp {[^//\\]+$} $upload_file filename] } {
+                    # no match
+                    set filename $upload_file
+                }
+                set community_id [dotlrn_community::get_community_id]
+                set package_id [dotlrn_community::get_package_id_from_package_key -package_key messages -community_id $community_id]
+                set title [template::util::file::get_property filename $upload_file]
+                set item_name "[clock seconds]-${title}"
+                set mime_type [cr_filename_to_mime_type -create $title]
+                set folder_id [content::item::get_id -item_path "messages_attachments_${package_id}" -resolve_index f]
+                set creation_user [ad_conn user_id]
+                set creation_ip [ad_conn peeraddr]
+                # set storage_type to its default value according to a db constraint
+                set storage_type file
+                set attachment_item_id [content::item::new \
+                                -parent_id $folder_id \
+                                -name $item_name \
+                                -context_id $package_id \
+                                -content_type messages_attachments \
+                                -mime_type $mime_type \
+                                -storage_type $storage_type \
+                                -creation_user $creation_user \
+                                -creation_ip $creation_ip]
+
+                set revision_id [content::revision::new  -item_id $attachment_item_id \
+                                    -mime_type $mime_type  \
+                                    -title $title \
+                                    -content_type messages_attachments \
+                                    -creation_user $creation_user  \
+                                    -creation_ip $creation_ip \
+                                    -attributes [list [list msg_id $msg_id]\
+                                                        [list attachment_item_id $attachment_item_id] \
+                                                ] ]
+
+                content::item::set_live_revision -revision_id $revision_id
+                if { ![empty_string_p $upload_file] }  {
+                    set tmp_file [template::util::file::get_property tmp_filename $upload_file]
+                    set content_length [file size $tmp_file]
+                    # create the new item
+                    set file_name [cr_create_content_file $attachment_item_id $revision_id $tmp_file]
+                    db_dml set_file_content { *SQL* }
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
Index: openacs-4/packages/messages/www/send-mail.xql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/send-mail.xql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/send-mail.xql	31 Aug 2010 21:30:44 -0000	1.1
@@ -0,0 +1,13 @@
+<?xml version="1.0"?>
+
+<queryset>
+
+<fullquery name="lob_size">
+    <querytext>
+        update cr_revisions
+        set content_length = :content_length
+        where revision_id = :revision_id
+    </querytext>
+</fullquery>
+
+</queryset>
\ No newline at end of file
Index: openacs-4/packages/messages/www/send-oracle.xql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/send-oracle.xql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/send-oracle.xql	31 Aug 2010 21:30:42 -0000	1.1
@@ -0,0 +1,49 @@
+<?xml version="1.0"?>
+
+<queryset>
+   <rdbms><type>oracle</type><version>8.1.6</version></rdbms>
+   
+        <fullquery name="set_file_content">
+              <querytext>
+
+                update cr_revisions
+                set filename = :file_name,
+                mime_type = :mime_type,
+                content_length = :content_length
+                where revision_id = :revision_id
+                    
+              </querytext>
+        </fullquery>
+              
+            <fullquery name="lob_content">
+                <querytext>
+                
+                        update cr_revisions 
+                        set content = empty_blob(),
+                        filename = :filename
+                        where revision_id = :revision_id
+                        returning content into :1   
+                        
+                  </querytext>
+            </fullquery>
+        
+        
+        <fullquery name="get_sol_info">      
+             <querytext>
+
+                select crr.title as content, 
+                crr.title,
+                crr.item_id,
+                cri.storage_type,
+                crr.revision_id,
+                crr.content_length,
+                crr.mime_type
+                crr.filename
+                from cr_items cri, cr_revisions crr
+                where crr.revision_id = :attachment_id
+                        and crr.item_id = cri.item_id
+            
+              </querytext>
+        </fullquery>
+        
+</queryset> 
Index: openacs-4/packages/messages/www/send-postgresql.xql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/send-postgresql.xql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/send-postgresql.xql	31 Aug 2010 21:30:44 -0000	1.1
@@ -0,0 +1,45 @@
+<?xml version="1.0"?>
+
+<queryset>
+   <rdbms><type>postgresql</type><version>7.3</version></rdbms>
+   
+<fullquery name="set_file_content">      
+      <querytext>
+
+        update cr_revisions
+        set content = :file_name,
+        mime_type = :mime_type,
+        content_length = :content_length
+        where revision_id = :revision_id
+            
+      </querytext>
+</fullquery>   
+
+<fullquery name="lob_content">      
+      <querytext>
+
+        update cr_revisions 
+        set lob = [set __lob_id [db_string get_lob_id "select empty_lob()"]]
+        where revision_id = :revision_id
+    
+      </querytext>
+</fullquery>
+
+<fullquery name="get_sol_info">      
+      <querytext>
+
+        select crr.title as content, 
+        crr.title,
+        crr.item_id,
+        cri.storage_type,
+        crr.revision_id,
+        crr.content_length,
+        crr.mime_type
+        from cr_items cri, cr_revisions crr
+        where crr.revision_id = :attachment_id
+          and crr.item_id = cri.item_id
+    
+      </querytext>
+</fullquery>
+   
+</queryset>
\ No newline at end of file
Index: openacs-4/packages/messages/www/send.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/send.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/send.adp	31 Aug 2010 21:30:43 -0000	1.1
@@ -0,0 +1,150 @@
+<if @reply_p@>
+    <master src="/packages/messages/www/xinha-master">
+    <input type="hidden" id="dialog_rol_set" value="0">    
+</if><else>
+    <master>
+        <property name="title">@page_title;noquote@</property>
+        <property name="context">@context;noquote@</property>
+        <input type="hidden" id="dialog_rol_set" value="1">        
+        <script type="text/javascript" src="/resources/ajaxhelper/yui/button/button-min.js"></script>
+        <script type="text/javascript" src="/resources/messages/rol-dialog.js"></script>
+    <style>
+        .composer{padding-top:10px;padding-left:10px;padding:10px 0 10px 10px;background:#FFFFFF;}
+    </style>
+    <a class="button" href="messages">#messages.back_folders#</a>
+</else>
+
+
+<span id="send_mail_loading" style="display:none">
+    <span id=alert-message> 
+        <span class="alert">
+            <strong>
+                <span id=send_mail_wait style=text-align:center></span>
+            </strong>
+        </span>
+    </span>
+</span>
+
+<input type=hidden id="sending" value="#messages.sending#">
+<input type=hidden id="add" value="#messages.add#">
+<input type=hidden id="delete_confirm" value="#messages.delete_confirm#">
+<input type="hidden" id="attachment_delete" value="#messages.delete#">
+<input type="hidden" id="contacts_list" value="">
+<input type=hidden id="subject_empty" value="#messages.subject_empty#">
+<input type=hidden id="body_empty" value="#messages.body_empty#">
+<span style='display:none' id="history_conversation">@history_conversation;noquote@</span>
+<span style='display:none' id="signature">@signature;noquote@</span>
+<if @reply_p@>
+    <div id=background style="background-color:#FFFFFF">
+</if>
+<else>
+    <div id=background style="background-color:#E0F8F7">
+</else>
+    <div id="autocomplete">
+        <div class="composer">
+        <form id="compose_message" name="compose_message" enctype="multipart/form-data" action="@action_send@" method="POST">
+            <formtemplate id="compose_message">
+                @rel_types_html;noquote@
+                <if @reply_p@ eq 0>
+                    <input type="hidden" id="total_attachment" name="total_attachment" value="0">
+                    <input type="hidden" id="contacts_ids" name="contacts_ids" value="">
+                    <input type="hidden" id="subject" name="subject" value="">
+                </if>                
+                <br><br>
+                <dl class="clearfix">
+                        <table width="100%" style="font-size:10px">
+                            <tr>
+                                <th width="5%" align="left">
+                                    <label>#messages.to#:</label>
+                                </th>
+                                <th align="left">
+                                    <div tabindex="-1" id="ids" class="clearfix tokenizer" onclick="$('autocomplete_input').focus()" width="600px">
+                                        <span class="tokenizer_stretcher">^_^</span>
+                                        <span class="tab_stop"><input type="text" id="hidden_input" tabindex="-1"></span>
+                                        <span id=contact_reply></span>
+                                        <div id="autocomplete_display" class="tokenizer_input">
+                                            <input type="text" size="1" tabindex="" id="autocomplete_input" onblur="delete_text_input(this.name)" />
+                                        </div>
+                                    </div>
+                                </th>
+                            </tr>
+                        </table>
+                        <div id="autocomplete_populate" class="clearfix autocomplete typeahead_list" style="width: 358px; height: auto; overflow-y: hidden;display:none; z-index: 1000;">
+                            <div class="typeahead_message">Type the name of a member</div>
+                        </div> 
+                        <script type="text/javascript">
+                            (new Image()).src='inbox/images/token.gif';
+                            (new Image()).src='inbox/images/token_selected.gif';
+                            (new Image()).src='inbox/images/token_hover.gif';
+                            (new Image()).src='inbox/images/token_x.gif';
+                            <if @reply_p@ eq 0>
+                                var contacts = [@contacts_list;noquote@];
+                            </if>
+                            var typeahead = new Autocompleter.LocalAdvanced('autocomplete_input', 'autocomplete_populate', contacts, {
+                                frequency: 0.1,
+                                updateElement: addContactToList,
+                                search_field: "name"
+                            });
+                            var hidden_input = new HiddenInput('hidden_input',typeahead);
+                        </script>
+                        <label style="font-size:8pt">#messages.to_help#</label>
+                        <br>
+                    <if @reply_p@ eq 0>
+                        <br>
+                        <input type=hidden id="recipients_empty" value="#messages.recipients_empty#">
+                        <table width="100%" style="font-size:12px">
+                            <tr>
+                                <th width="5%" align="left">
+                                    <label>#messages.subject#:</label>
+                                </th>
+                                <th align="left" width="95%">
+                                    <input width="600px" class="subject" id="subject_field">
+                                </th>
+                            </tr>
+                        </table>
+                    </if>
+                    <else>
+                        <input type=hidden id=@msg_id@_recipients_empty value="#messages.recipients_empty#">
+                    </else>
+                        <br>
+                        <label>#messages.message#:</label>
+                    <dd class="field">
+                            <formwidget id=message>
+                    </dd>
+                    <dd>
+                        <label>#messages.attachments#:</label>
+                        <div id=attachment_forward style="display:none">
+                            @download_file;noquote@
+                        </div>
+                        <a onclick="addField()" class="button">#messages.add_attachment#</a>
+                    </dd>
+                    <dd><div id="files"></div></dd><br>
+                    <dd class="submits">
+                        <div class="submits_wrap">
+                            <if @reply_p@ ne 0>
+                                <span id=submit_reply>
+                                    <a  class="button" name="send_button" value="Send" type="button" onclick="submit_reply(@msg_id@,@general_parent_id@);Effect.ScrollTo('reply_@msg_id@');" >#messages.send#</a>
+                                </span>
+                                <a  href="javascript:cancel(@msg_id@);" class="button" name="cancel_button" value="Cancel" type="button" >#messages.discard#</a>
+                            </if>
+                            <else>
+                                <a  class="button" name="send_button" value="Send" type="button" href="#" onclick="submit_normal();Effect.ScrollTo('send_mail_loading');" >#messages.send#</a>
+                                <a  href="messages" class="button" name="cancel_button" value="Cancel" type="button" >#messages.discard#</a>
+                            </else>
+                        </div>
+                        <div class="sub_controls"></div>
+                    </dd>
+                </dl>
+           </formtemplate>
+        </form>
+        </div>
+    </div> 
+    
+<if @reply_p@ eq 0>    
+    <div id="dialog2" class="yui-pe-content">
+        <div class="bd">
+            <div id="info_members_rols" "style="width:28em;height:30em;overflow:auto"> 
+            </div>
+        </div>
+    </div>
+</if>
\ No newline at end of file
Index: openacs-4/packages/messages/www/send.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/send.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/send.tcl	31 Aug 2010 21:30:43 -0000	1.1
@@ -0,0 +1,216 @@
+ad_page_contract {
+    Send the message with attachment to users in the community.
+
+} {
+    {msg_id "" }
+    {subject ""}
+    {contacts_ids ""}
+    {total_attachment 0}
+    {reply ""}
+    {reply_p 0}
+    {general_parent_id ""}
+    {rel_types:multiple "" }
+    {reply_option ""}
+}
+
+set page_title "[_ messages.messages]"
+set context [list "[_ messages.compose_mail]"]
+set community_id [dotlrn_community::get_community_id]
+set rel_types_html ""
+set owner_id [ad_conn user_id]
+set package_id [ad_conn package_id]
+set admin_p [permission::permission_p -party_id $owner_id -object_id $package_id -privilege admin]
+#Set file javascript to members functions
+if { $reply_p == 1 } {
+    set action_send "send-mail"
+    #when send message reply
+    set member_js "show_members_rol_index"
+} else {
+    set action_send "send"
+    #When send a new message
+    set member_js "show_members_rol"
+}
+
+set roles [dotlrn_community::get_roles -community_id $community_id]
+if { $admin_p } {
+    foreach {rel_type role pretty_name pretty_plural} [eval concat $roles] {
+        if {![string equal $rel_type "dotlrn_student_rel"]} {
+            append rel_types_html "<input type=\"checkbox\" value=\"$rel_type\" name=\"rel_types\" id=\"$rel_type\"> <a onclick=\"${member_js}('$rel_type')\"><img src=\"/resources/messages/add.png\"></a> [lang::util::localize $pretty_plural] |"
+        } else {
+            append rel_types_html "<input type=\"checkbox\" value=\"$rel_type\" name=\"rel_types\" id=\"$rel_type\" onclick=\"${member_js}(this.id,this.checked)\">  [lang::util::localize $pretty_plural] |"
+        }
+    }
+    append rel_types_html "<input type=\"checkbox\" value=\"[_ messages.send_to_all]\" name=\"rel_types_all\" onclick=\"select_all_rel_types(this.checked);\"> [_ messages.send_to_all]"
+} else {
+    foreach {rel_type role pretty_name pretty_plural} [eval concat $roles] {
+        if {[string equal $rel_type "dotlrn_ca_rel"] || [string equal $rel_type "dotlrn_instructor_rel"] } {
+            if {![string equal $rel_type "dotlrn_student_rel"]} {
+                append rel_types_html "<input type=\"checkbox\" value=\"$rel_type\" name=\"rel_types\" id=\"$rel_type\"> <a onclick=\"${member_js}('$rel_type')\"><img src=\"/resources/messages/add.png\"></a> [lang::util::localize $pretty_plural] |"
+            } else {
+                append rel_types_html "<input type=\"checkbox\" value=\"$rel_type\" name=\"rel_types\" id=\"$rel_type\" onclick=\"${member_js}(this.id,this.checked)\">  [lang::util::localize $pretty_plural] |"
+            }
+        }
+    }
+}
+
+if {![exists_and_not_null show_master_p]} {
+    set show_master_p 1
+    set reply_send 0
+} else {
+    set reply_send 1
+}
+
+if {![exists_and_not_null option]} {
+    set option ""
+}
+
+set community_id [dotlrn_community::get_community_id]
+if { !$reply_p } {
+    set contacts_list [util_memoize [list messages::get_community_users_fb -community_id $community_id] 86400]
+}
+
+set signature_p [db_string signature_p {} -default 0]
+
+if { $signature_p } {
+    set signature [db_string get_signature {}]
+    set signature [ad_html_to_text [lindex $signature 0]]
+    set signature "<div id=signature><font color=\"#888888\"> &#45;&#45; <br> $signature</font></div>"
+} else {
+    set signature ""
+}
+
+if { [empty_string_p $signature] } {
+    set signature ""
+} else {
+    set signature [string trim $signature]
+}
+
+set attachment [db_list_of_lists get_attachment_id {}]
+if { [llength $attachment] != 0} {
+    foreach attachment_info $attachment {
+        set attachment_id [lindex $attachment_info 0]
+        set attachment_item_id [lindex $attachment_info 1]
+        db_1row get_sol_info { *SQL* }
+        set position [string first . "$content" 1]
+        if { $position == "-1" } {
+            set position [string length $content]
+        }
+        append download_file "<input type=checkbox checked name=attachment_checkbox id=${attachment_id}>[string range $title 0 [expr $position - 1]]<br>"
+    }
+} else {
+    set download_file ""
+}
+
+set fs_package_id [dotlrn_community::get_package_id_from_package_key -package_key file-storage -community_id $community_id]
+set fs_folder_id [fs::get_root_folder -package_id $fs_package_id]
+set attachment_item_id ""
+
+set history_conversation [messages::get_history_conversation -parent_id $general_parent_id -msg_id $msg_id -user_id $owner_id]
+set content_message_conversation "<br>$history_conversation $signature"
+
+if { ![string equal $reply_option "forward"] } {
+    set content_message_conversation "<p>&nbsp;</p><p>&nbsp;</p> $signature"
+}
+
+ad_form -html { enctype multipart/form-data } -export { signature } -name compose_message -form {
+    {message:richtext(richtext),optional,nospell {label ""} {options {width "99%"}} {value "[list \"$content_message_conversation\" \"text/html\"]"}
+        {html {id "comments" rows 25 cols 50 onload "loadEditors('advancedMessagesEditor'); return false;"}} 
+    }
+} -on_submit {
+    set count_attachment [expr $total_attachment + 1]
+    if { ![exists_and_not_null parent_id] }  {
+        set parent_id ""
+    }
+
+    set base_url [dotlrn_community::get_community_url $community_id]
+    set message_url [export_vars -base "${base_url}messages/messages"]
+    set msg_id [messages::send_mail -parent_id $parent_id -count_attachment $count_attachment \
+    -contacts_ids $contacts_ids -community_id $community_id \
+    -owner_id $owner_id  -subject $subject -message $message -message_url $message_url -rel_types $rel_types]
+
+    for {set x 1} {$x < $count_attachment} {incr x} {
+        set name_attachment "upload_file_${x}"
+        ad_form -extend -name compose_message -form {
+            {upload_file_$x:file,optional
+                {label ""} 
+            }
+        } 
+        set upload_file [set $name_attachment]
+        if { ![empty_string_p $upload_file] }  {
+        db_transaction {
+                set attachment_item_id ""
+                if { ![regexp {[^//\\]+$} $upload_file filename] } {
+                    # no match
+                    set filename ${x}_$upload_file
+                }
+                set community_id [dotlrn_community::get_community_id]
+                set package_id [dotlrn_community::get_package_id_from_package_key -package_key messages -community_id $community_id]
+                set title [template::util::file::get_property filename $upload_file]
+                set item_id [db_nextval acs_object_id_seq]
+                set item_name "${item_id}-${title}"
+                set mime_type [cr_filename_to_mime_type -create $title]
+                set folder_id [content::item::get_id -item_path "messages_attachments_${package_id}" -resolve_index f]
+                set creation_user [ad_conn user_id]
+                set creation_ip [ad_conn peeraddr]
+                # set storage_type to its default value according to a db constraint
+                set storage_type file
+                set attachment_item_id [content::item::new \
+                                -item_id $item_id \
+                                -parent_id $folder_id \
+                                -name $item_name \
+                                -context_id $package_id \
+                                -content_type messages_attachments \
+                                -mime_type $mime_type \
+                                -storage_type $storage_type \
+                                -creation_user $creation_user \
+                                -creation_ip $creation_ip]
+    
+                set revision_id [content::revision::new  -item_id $attachment_item_id \
+                                    -mime_type $mime_type  \
+                                    -title $title \
+                                    -content_type messages_attachments \
+                                    -creation_user $creation_user  \
+                                    -creation_ip $creation_ip \
+                                    -attributes [list [list msg_id $msg_id]\
+                                                        [list attachment_item_id $attachment_item_id] \
+                                                ] ]
+    
+                content::item::set_live_revision -revision_id $revision_id
+                if { ![empty_string_p $upload_file] }  {
+                    set tmp_file [template::util::file::get_property tmp_filename $upload_file]
+                    set content_length [file size $tmp_file]
+                    # create the new item
+                    set file_name [cr_create_content_file $attachment_item_id $revision_id $tmp_file]
+                    db_dml set_file_content { *SQL* }
+                }
+            }
+        }
+    }
+} -after_submit {
+    ad_returnredirect "messages"
+    ad_script_abort
+}
+
+template::head::add_javascript -src "/resources/ajaxhelper/yui/yahoo-dom-event/yahoo-dom-event.js" -order "-1"
+template::head::add_javascript -src "/resources/ajaxhelper/yui/yahoo/yahoo-min.js" -order "-14"
+template::head::add_javascript -src "/resources/ajaxhelper/yui/element/element-beta-min.js" -order "-13"
+template::head::add_javascript -src "/resources/ajaxhelper/yui/event/event-min.js" -order "-15"
+template::head::add_javascript -src "/resources/ajaxhelper/yui/dom/dom-min.js" -order "-16"
+template::head::add_javascript -src "/resources/ajaxhelper/yui/button/button-beta-min.js" -order "-17"
+template::head::add_javascript -src "/resources/ajaxhelper/yui/utilities/utilities.js" -order "-18"
+template::head::add_javascript -src "/resources/ajaxhelper/yui/history/history-beta-min.js" -order "-19"
+template::head::add_javascript -src "/resources/messages/prototype.js" -order "2"
+template::head::add_javascript -src "/resources/messages/effects.js" -order "3"
+template::head::add_javascript -src "/resources/ajaxhelper/yui/dragdrop/dragdrop.js" -order "4"
+template::head::add_javascript -src "/resources/messages/controls.js" -order "5"
+template::head::add_javascript -src "/resources/messages/builder.js" -order "7"
+template::head::add_javascript -src "/resources/messages/application.js" -order "8"
+template::head::add_javascript -src "/resources/messages/messages.js" -order "10"
+template::head::add_javascript -src "/resources/messages/message-attachment.js" -order "11"
+template::head::add_css -href "/resources/messages/send.css"
+template::head::add_javascript -src "/resources/ajaxhelper/yui/container/container-min.js"
+template::head::add_css -href "/resources/ajaxhelper/yui/fonts/fonts-min.css"
+template::head::add_css -href "/resources/ajaxhelper/yui/button/assets/skins/sam/button.css"
+template::head::add_css -href "/resources/ajaxhelper/yui/container/assets/skins/sam/container.css"
+
+template::head::add_javascript -src "/resources/ajaxhelper/yui/connection/connection-min.js"
\ No newline at end of file
Index: openacs-4/packages/messages/www/send.xql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/send.xql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/send.xql	31 Aug 2010 21:30:44 -0000	1.1
@@ -0,0 +1,62 @@
+<?xml version="1.0"?>
+
+<queryset>
+
+    <fullquery name="members">
+        <querytext>
+            select persons.first_names||' '||persons.last_name  as party_name,
+                   persons.person_id
+            from persons,
+                    dotlrn_member_rels_approved
+            where dotlrn_member_rels_approved.community_id = :community_id
+            and dotlrn_member_rels_approved.user_id = persons.person_id
+            order by party_name
+        </querytext>
+    </fullquery> 
+
+    <fullquery name="signature_p">
+        <querytext>
+            select signature_p
+            from messages_options
+            where user_id = :owner_id
+        </querytext>
+    </fullquery> 
+
+    <fullquery name="get_signature">
+        <querytext>
+            select signature
+            from messages_options
+            where user_id = :owner_id
+        </querytext>
+    </fullquery>
+
+
+    <fullquery name="get_attachment_id">
+        <querytext>
+            select attachment_id, attachment_item_id
+            from messages_attachments
+            where msg_id = :msg_id
+        </querytext>
+    </fullquery>
+
+    <fullquery name="lob_size">
+        <querytext>
+            update cr_revisions
+            set content_length = :content_length
+            where revision_id = :revision_id
+        </querytext>
+    </fullquery>
+
+<fullquery name="set_file_content">
+      <querytext>
+
+		update cr_revisions
+		set title = :title,
+		mime_type = :mime_type,
+		content_length = :content_length
+		where revision_id = :revision_id
+			
+      </querytext>
+</fullquery>
+
+</queryset>
Index: openacs-4/packages/messages/www/test.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/test.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/test.adp	31 Aug 2010 21:30:42 -0000	1.1
@@ -0,0 +1,2 @@
+<master>
+@contacts_list@
Index: openacs-4/packages/messages/www/test.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/test.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/test.tcl	31 Aug 2010 21:30:42 -0000	1.1
@@ -0,0 +1,26 @@
+ad_page_contract {
+    General page, list inbox, sent, draft, trash.
+
+    @author pedro@viaro.net
+    @creation-date 2009-11-12
+    @cvs-id $Id: test.tcl,v 1.1 2010/08/31 21:30:42 alvaror Exp $
+} {
+    {community_id ""}
+    {page_number ""}
+}
+
+set page_title "[_ messages.folders]"
+set context [list $page_title]
+set user_id [ad_conn user_id]
+set community_id [dotlrn_community::get_community_id]
+
+set count_members [db_string count_members {}]
+
+for {set i 0} { $i < $count_members} {set i [expr $i + 1500] } {
+    ns_log notice ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ciclo for $i"
+    set limit [expr $i + 1500]
+    append contacts_list [join [db_list members { *SQL* }] ","]
+}
+
+ns_log notice ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> count_members $count_members"
+
Index: openacs-4/packages/messages/www/test.xql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/test.xql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/test.xql	31 Aug 2010 21:30:42 -0000	1.1
@@ -0,0 +1,45 @@
+<?xml version="1.0"?>
+<queryset>
+
+    <fullquery name="get_folders_user">
+        <querytext>
+            select folder_id, folder_name
+            from messages_folders 
+            where (user_id = :user_id
+            or user_id = 0)
+            order by folder_id asc
+        </querytext>
+    </fullquery>
+
+    <fullquery name="members">
+        <querytext>
+            select concat('{n:"'||to_clob(party_name)||'", p:"'||to_clob(person_id)||'"}') as contact
+            from (select persons.first_names||' '||persons.last_name as party_name, persons.person_id
+                  from persons,dotlrn_member_rels_approved
+                  where dotlrn_member_rels_approved.community_id = :community_id
+                        and dotlrn_member_rels_approved.user_id = persons.person_id and rownum <= $limit and rownum > $i
+                   order by party_name)
+        </querytext>
+    </fullquery>
+    
+    <fullquery name="members2">
+        <querytext>
+            select '{n:"'||party_name||'", p:"'||person_id||'"}' as contact
+            from (select persons.first_names||' '||persons.last_name as party_name, persons.person_id
+                  from persons,dotlrn_member_rels_approved
+                  where dotlrn_member_rels_approved.community_id = :community_id
+                        and dotlrn_member_rels_approved.user_id = persons.person_id and rownum > 1500
+                   order by party_name)
+        </querytext>
+    </fullquery> 
+
+    <fullquery name="count_members">
+        <querytext>
+           select count(*)
+              from persons,dotlrn_member_rels_approved
+              where dotlrn_member_rels_approved.community_id = :community_id
+                        and dotlrn_member_rels_approved.user_id = persons.person_id
+        </querytext>
+    </fullquery>     
+
+</queryset>   
Index: openacs-4/packages/messages/www/update-name.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/update-name.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/update-name.adp	31 Aug 2010 21:30:43 -0000	1.1
@@ -0,0 +1 @@
+@folder_id;noquote@
\ No newline at end of file
Index: openacs-4/packages/messages/www/update-name.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/update-name.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/update-name.tcl	31 Aug 2010 21:30:43 -0000	1.1
@@ -0,0 +1,24 @@
+ad_page_contract {
+    Update the folder name
+
+    @author pedro@viaro.net
+    @creation-date 2010-01-19
+    @cvs-id $Id: update-name.tcl,v 1.1 2010/08/31 21:30:43 alvaror Exp $
+} {
+    {name ""}
+    {delete "f"}
+    folder_id
+}
+
+set user_id [ad_conn user_id]
+set folder_id_inbox [messages::get_folder_id -folder_name "inbox"]
+if { [string equal $delete "t"] } {
+    db_transaction {
+        db_dml update_messages { *SQL* }
+        db_dml delete_folder { *SQL* }
+    }
+} elseif {![empty_string_p $name]} {
+    db_transaction {
+        db_dml update_name { *SQL* }
+    }
+}
\ No newline at end of file
Index: openacs-4/packages/messages/www/update-name.xql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/update-name.xql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/update-name.xql	31 Aug 2010 21:30:43 -0000	1.1
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+<queryset>
+
+    <fullquery name="update_name">
+        <querytext>
+            update messages_folders set folder_name = :name where folder_id = :folder_id
+        </querytext>
+    </fullquery>
+
+    <fullquery name="delete_folder">
+        <querytext>
+            delete from messages_folders where folder_id = :folder_id
+        </querytext>
+    </fullquery>
+
+    <fullquery name="update_messages">
+        <querytext>
+            update messages_user_messages set folder_id = :folder_id_inbox 
+            where folder_id = :folder_id and user_id = :user_id
+        </querytext>
+    </fullquery>
+
+</queryset> 
\ No newline at end of file
Index: openacs-4/packages/messages/www/update-numbers.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/update-numbers.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/update-numbers.adp	31 Aug 2010 21:30:42 -0000	1.1
@@ -0,0 +1 @@
+@numbers;noquote@
\ No newline at end of file
Index: openacs-4/packages/messages/www/update-numbers.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/update-numbers.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/update-numbers.tcl	31 Aug 2010 21:30:42 -0000	1.1
@@ -0,0 +1,28 @@
+ad_page_contract {
+    Update numbers of new messages in the menu
+
+    @author pedro@viaro.net
+    @creation-date 2009-11-17
+    @cvs-id $Id: update-numbers.tcl,v 1.1 2010/08/31 21:30:42 alvaror Exp $
+} {
+
+}
+
+append not_folder_ids [messages::get_folder_id -folder_name "sent"] "," \
+                      [messages::get_folder_id -folder_name "trash"]
+
+set user_id [ad_conn user_id]
+set community_id [dotlrn_community::get_community_id]
+set folders_info [db_list_of_lists count_new_messages {}]
+set numbers [list]
+foreach folder $folders_info {
+    lappend numbers [lindex $folder 0]
+    lappend numbers [lindex $folder 1]
+    lappend numbers [lindex $folder 2]
+}
+
+if { [llength $numbers] } {
+    set numbers [join $numbers ","] 
+} else {
+    set numbers " "
+}
Index: openacs-4/packages/messages/www/update-numbers.xql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/update-numbers.xql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/update-numbers.xql	31 Aug 2010 21:30:43 -0000	1.1
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>
+<queryset>
+
+    <fullquery name="count_new_messages">
+        <querytext>
+                select count (case when um.new_p = 't' then 1 end ) as new_t, um.folder_id, 
+                    count (um.new_p) as total
+                from (select distinct(parent_id) as msg_id,folder_id,new_p
+                        from messages_user_messages 
+                        where user_id = :user_id
+                        and msg_id in (select msg_id 
+                        from messages_messages 
+                        where community_id = :community_id)
+                        and folder_id not in ($not_folder_ids)) um
+                group by um.folder_id
+                order by folder_id
+        </querytext>
+    </fullquery>
+
+</queryset>
\ No newline at end of file
Index: openacs-4/packages/messages/www/xinha-master.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/xinha-master.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/xinha-master.adp	31 Aug 2010 21:30:44 -0000	1.1
@@ -0,0 +1,18 @@
+<slave>
+<p id="advancedMessagesEditor" style="display: none;">
+xinha_editors = null;
+xinha_init = null;
+xinha_config = null;
+xinha_plugins = [@xinha_plugins@];
+Xinha.loadPlugins(xinha_plugins,null);
+xinha_editors = xinha_editors ? xinha_editors :[ @htmlarea_ids@ ];
+xinha_config = xinha_config ? xinha_config() : new Xinha.Config();
+@xinha_params@
+@xinha_options@
+xinha_editors =
+Xinha.makeEditors(xinha_editors, xinha_config, xinha_plugins);
+Xinha.startEditors(xinha_editors);
+</p>
+<p id="advancedMessagesIds" style="display: none;">
+@htmlarea_ids@
+</p>
\ No newline at end of file
Index: openacs-4/packages/messages/www/xinha-master.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/xinha-master.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/xinha-master.tcl	31 Aug 2010 21:30:44 -0000	1.1
@@ -0,0 +1,11 @@
+  set ::xinha_dir /resources/acs-templating/xinha-nightly/
+  set ::xinha_lang [lang::conn::language]
+
+  # We could add site wide Xinha configurations (.js code) into xinha_params
+  set xinha_params ""
+
+  # Per call configuration
+  set xinha_plugins $::acs_blank_master(xinha.plugins)
+  set xinha_options $::acs_blank_master(xinha.options)
+  # HTML ids of the textareas used for Xinha
+  set htmlarea_ids '[join $::acs_blank_master__htmlareas "','"]'
Index: openacs-4/packages/messages/www/resources/Delete16-on.gif
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/resources/Delete16-on.gif,v
diff -u
Binary files differ
Index: openacs-4/packages/messages/www/resources/add.png
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/resources/add.png,v
diff -u
Binary files differ
Index: openacs-4/packages/messages/www/resources/application.js
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/resources/application.js,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/resources/application.js	31 Aug 2010 21:30:54 -0000	1.1
@@ -0,0 +1,338 @@
+validate_party_id = function(party_id) {
+    var regexparty_id = /^(("[\w-\s]+")|([\w-]+(?:\.[\w-]+)*)|("[\w-\s]+")([\w-]+(?:\.[\w-]+)*))(@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$)|(@\[?((25[0-5]\.|2[0-4][0-9]\.|1[0-9]{2}\.|[0-9]{1,2}\.))((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[0-9]{1,2})\.){2}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[0-9]{1,2})\]?$)/i
+    return regexparty_id.test(party_id);
+
+}
+
+Autocompleter.LocalAdvanced = Class.create(Autocompleter.Base, {
+    initialize: function(element, update, array, options) {
+        this.baseInitialize(element, update, options);
+        this.options.array = array;
+        this.wrapper = $(this.element.parentNode);
+
+	    if (!this.element.hacks) {
+	        this.element.should_use_borderless_hack = Prototype.Browser.WebKit;
+	        this.element.should_use_shadow_hack = Prototype.Browser.IE || Prototype.Browser.Opera;
+	        this.element.hacks = true;
+	    }
+		if (this.element.should_use_borderless_hack  || this.element.should_use_shadow_hack) { this.wrapper.addClassName('tokenizer_input_borderless'); }
+		
+		this.options.onShow = function(element,update) {
+		 	Position.clone(element.parentNode.parentNode, update, {
+		            setHeight: false, 
+					setWidth: false,
+		            offsetTop: element.parentNode.parentNode.offsetHeight
+		     });		
+			update.show(); 
+			
+		}  
+		this.options.onHide = function(element, update){ update.hide() };
+	    
+
+    },
+getUpdatedChoices: function() {
+        this.updateChoices(this.options.selector(this));
+
+    },
+
+onBlur: function($super, event) {
+        $super();
+        //move itself back to the end on blur
+        if (this.wrapper.nextSiblings().length > 0) {
+            this.wrapper.nextSiblings().last().insert({
+                after: this.wrapper
+            });
+
+        }
+
+    },
+set_input_size: function(size) {
+	size = size || 20;
+	this.element.setStyle({width: size + "px"});	
+},
+onKeyPress: function(event) {
+        //dynamically resize the input field
+		var new_size = 20 + (this.element.value.length * 7);
+        if (new_size <= 340) {
+			this.set_input_size(new_size);
+        } else {
+			this.set_input_size(340);
+        }
+        //active is when there's suggesitons found
+        if (this.active)
+        switch (event.keyCode) {
+        case Event.KEY_TAB:
+        case Event.KEY_RETURN:
+            this.selectEntry();
+            Event.stop(event);
+            case Event.KEY_ESC:
+            this.hide();
+            this.active = false;
+            Event.stop(event);
+            return;
+            case Event.KEY_LEFT:
+        case Event.KEY_RIGHT:
+            return;
+            case Event.KEY_UP:
+            this.markPrevious();
+            this.render();
+            Event.stop(event);
+            return;
+            case Event.KEY_DOWN:
+            this.markNext();
+            this.render();
+            Event.stop(event);
+            return;
+
+        }
+        else {
+            if (event.keyCode == Event.KEY_TAB || event.keyCode == Event.KEY_RETURN || 
+            (Prototype.Browser.WebKit > 0 && event.keyCode == 0) || event.keyCode == 44 /* , comma */ ||  event.keyCode == 188 ) {
+                var party_id_addr = this.element.value.strip().sub(',', '')
+                //recognise party_id format
+                if (validate_party_id(party_id_addr)) {
+                    addparty_idToList(party_id_addr);
+                    Event.stop(event);
+                } 
+                this.element.value = "";
+				this.set_input_size();
+                return false;
+
+            }
+            switch (event.keyCode) {
+                //jump left to token
+                case Event.KEY_LEFT:
+            case Event.KEY_BACKSPACE:
+                if (this.element.value == "" && typeof this.wrapper.previous().token != "undefined") {
+                    this.wrapper.previous().token.select();
+
+                }
+                return;
+                //jump right to token
+                case Event.KEY_RIGHT:
+                if (this.element.value == "" && this.wrapper.next() && typeof this.wrapper.next().token != "undefined") {
+                    this.wrapper.next().token.select();
+
+                }
+
+            }
+
+        }
+
+        this.changed = true;
+        this.hasFocus = true;
+
+        if (this.observer) clearTimeout(this.observer);
+        this.observer = 
+        setTimeout(this.onObserverEvent.bind(this), this.options.frequency * 1000);
+
+    },
+
+setOptions: function(options) {
+        this.options = Object.extend({
+            choices: 10,
+            partialSearch: true,
+            partialChars: 2,
+            ignoreCase: true,
+            fullSearch: false,
+            selector: function(instance) {
+                var ret = [];
+                // Beginning matches
+                var partial = [];
+                // Inside matches
+                var entry = instance.getToken();
+                var count = 0;
+
+                for (var i = 0; i < instance.options.array.length && 
+                ret.length < instance.options.choices; i++) {
+
+                    var elem = instance.options.array[i];
+                    var elem_name = elem[instance.options.search_field];
+                    var foundPos = instance.options.ignoreCase ? 
+                    elem_name.toLowerCase().indexOf(entry.toLowerCase()) : 
+                    elem_name.indexOf(entry);
+
+                    while (foundPos != -1) {
+
+                        if (foundPos == 0 && elem_name.length != entry.length) {
+                            var value = "<strong>" + elem_name.substr(0, entry.length) + "</strong>" + elem_name.substr(entry.length);
+                            ret.push(
+                            "<li value='" + i + "'>" + "<div>" + value + "</div>"
+                            + "<div style=display:none>" + elem.p + "</div>" + "</li>"
+                            );
+                            break;
+
+                        } else if (entry.length >= instance.options.partialChars && instance.options.partialSearch && foundPos != -1) {
+                            if (instance.options.fullSearch || /\s/.test(elem_name.substr(foundPos - 1, 1))) {
+                                var value = elem_name.substr(0, foundPos) + "<strong>" + 
+                                elem_name.substr(foundPos, entry.length) + "</strong>" + elem_name.substr(
+                                foundPos + entry.length)
+
+                                partial.push(
+                                "<li value='" + i + "'>" + "<div>" + value + "</div>"
+                                + "<div style=display:none>" + elem.p + "</div>" + "</li>"
+                                );
+                                break;
+
+                            }
+
+                        }
+                        foundPos = instance.options.ignoreCase ? 
+                        elem_name.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) : 
+                        elem_name.indexOf(entry, foundPos + 1);
+
+
+                    }
+
+                }
+                if (partial.length)
+                ret = ret.concat(partial.slice(0, instance.options.choices - ret.length));
+                return "<ul>" + ret.join('') + "</ul>";
+
+            }
+
+        },
+        options || {});
+
+    }
+
+});
+HiddenInput = Class.create({
+    initialize: function(element, auto_complete) {
+        this.element = $(element);
+        this.auto_complete = auto_complete;
+        this.token;
+        Event.observe(this.element, 'keydown', this.onKeyPress.bindAsEventListener(this));
+
+    },
+    onKeyPress: function(event) {
+        if (this.token.selected) {
+            switch (event.keyCode) {
+                case Event.KEY_LEFT:
+                this.token.element.insert({
+                    before:
+                    this.auto_complete.wrapper
+                })
+                this.token.deselect();
+                this.auto_complete.element.focus();
+                return false;
+                case Event.KEY_RIGHT:
+                this.token.element.insert({
+                    after:
+                    this.auto_complete.wrapper
+                })
+                this.token.deselect();
+                this.auto_complete.element.focus();
+                return false;
+                case Event.KEY_BACKSPACE:
+            case Event.KEY_DELETE:
+                this.token.element.remove();
+                this.auto_complete.element.focus();
+                return false;
+
+            }
+
+        }
+
+    }
+
+
+})
+ Token = Class.create({
+    initialize: function(element, hidden_input) {
+        this.element = $(element);
+        this.hidden_input = hidden_input;
+        this.element.token = this;
+        this.selected = false;
+        Event.observe(document, 'click', this.onclick.bindAsEventListener(this));
+
+    },
+    select: function() {
+        this.hidden_input.token = this;
+        this.hidden_input.element.activate();
+        this.selected = true;
+        this.element.addClassName('token_selected');
+
+    },
+    deselect: function() {
+        this.hidden_input.token = "undefined";
+        this.selected = false;
+        this.element.removeClassName('token_selected')
+
+    },
+    onclick: function(event) {
+        if (this.detect(event) && !this.selected) {
+            this.select();
+
+        } else {
+            this.deselect();
+
+        }
+
+    },
+    detect: function(e) {
+        //find the event object
+        var eventTarget = e.target ? e.target: e.srcElement;
+        var token = eventTarget.token;
+        var candidate = eventTarget;
+        while (token == null && candidate.parentNode) {
+            candidate = candidate.parentNode;
+            token = candidate.token;
+
+        }
+        return token != null && token.element == this.element;
+
+    }
+
+});
+
+
+addContactToList = function(item) {
+    $('autocomplete_input').value = "";
+    var token = Builder.node('a', {
+        "class": 'token',
+        href: "#",
+        tabindex: "-1"
+    },
+    Builder.node('span', 
+    Builder.node('span', 
+    Builder.node('span', 
+    Builder.node('span', {},
+    [Builder.node('input', { type: "hidden", name: "ids[]",
+        value: item.lastChild.innerHTML
+    }), 
+	contacts[Element.readAttribute(item,'value')].name,
+        Builder.node('span',{"class":'x',onmouseout:"this.className='x'",onmouseover:"this.className='x_hover'",
+        onclick:"this.parentNode.parentNode.parentNode.parentNode.parentNode.remove(true); return false;"}," ")
+        ]
+    )
+    )
+    )   
+    )
+	);  
+	$(token).down(4).next().innerHTML = "&nbsp;";
+ 	new Token(token,hidden_input);
+   $('autocomplete_display').insert({before:token});
+}
+addparty_idToList = function(party_id) {
+/*   $('autocomplete_input').value = "";*/
+   var token = Builder.node('a',{"class":'token',href:"#",tabindex:"-1"},
+       Builder.node('span',
+       Builder.node('span',
+       Builder.node('span',
+       Builder.node('span',{},[
+           Builder.node('input',{type:"hidden",name:"party_ids[]",value: party_id} ) ,
+           party_id,
+           Builder.node('span',{"class":'x',onmouseout:"this.className='x'",onmouseover:"this.className='x_hover'",
+           onclick:"this.parentNode.parentNode.parentNode.parentNode.parentNode.remove(true); return false;"}," ")
+           ]
+       )
+       )
+       )   
+       )
+   );  
+	$(token).down(4).next().innerHTML = "&nbsp;";
+   new Token(token,hidden_input);
+   $('autocomplete_display').insert({before:token});
+}
+
Index: openacs-4/packages/messages/www/resources/arrow_down.gif
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/resources/arrow_down.gif,v
diff -u
Binary files differ
Index: openacs-4/packages/messages/www/resources/arrow_down.png
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/resources/arrow_down.png,v
diff -u
Binary files differ
Index: openacs-4/packages/messages/www/resources/attachment.png
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/resources/attachment.png,v
diff -u
Binary files differ
Index: openacs-4/packages/messages/www/resources/before_arr.gif
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/resources/before_arr.gif,v
diff -u
Binary files differ
Index: openacs-4/packages/messages/www/resources/builder.js
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/resources/builder.js,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/resources/builder.js	31 Aug 2010 21:30:50 -0000	1.1
@@ -0,0 +1,129 @@
+var Builder = {
+  NODEMAP: {
+    AREA: 'map',
+    CAPTION: 'table',
+    COL: 'table',
+    COLGROUP: 'table',
+    LEGEND: 'fieldset',
+    OPTGROUP: 'select',
+    OPTION: 'select',
+    PARAM: 'object',
+    TBODY: 'table',
+    TD: 'table',
+    TFOOT: 'table',
+    TH: 'table',
+    THEAD: 'table',
+    TR: 'table'
+  },
+  // note: For Firefox < 1.5, OPTION and OPTGROUP tags are currently broken,
+  //       due to a Firefox bug
+  node: function(elementName) {
+    elementName = elementName.toUpperCase();
+    
+    // try innerHTML approach
+    var parentTag = this.NODEMAP[elementName] || 'div';
+    var parentElement = document.createElement(parentTag);
+    try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
+      parentElement.innerHTML = "<" + elementName + "></" + elementName + ">";
+    } catch(e) {}
+    var element = parentElement.firstChild || null;
+      
+    // see if browser added wrapping tags
+    if(element && (element.tagName.toUpperCase() != elementName))
+      element = element.getElementsByTagName(elementName)[0];
+    
+    // fallback to createElement approach
+    if(!element) element = document.createElement(elementName);
+    
+    // abort if nothing could be created
+    if(!element) return;
+
+    // attributes (or text)
+    if(arguments[1])
+      if(this._isStringOrNumber(arguments[1]) ||
+        (arguments[1] instanceof Array) ||
+        arguments[1].tagName) {
+          this._children(element, arguments[1]);
+        } else {
+          var attrs = this._attributes(arguments[1]);
+          if(attrs.length) {
+            try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
+              parentElement.innerHTML = "<" +elementName + " " +
+                attrs + "></" + elementName + ">";
+            } catch(e) {}
+            element = parentElement.firstChild || null;
+            // workaround firefox 1.0.X bug
+            if(!element) {
+              element = document.createElement(elementName);
+              for(attr in arguments[1]) 
+                element[attr == 'class' ? 'className' : attr] = arguments[1][attr];
+            }
+            if(element.tagName.toUpperCase() != elementName)
+              element = parentElement.getElementsByTagName(elementName)[0];
+          }
+        } 
+
+    // text, or array of children
+    if(arguments[2])
+      this._children(element, arguments[2]);
+
+     return element;
+  },
+  _text: function(text) {
+     return document.createTextNode(text);
+  },
+
+  ATTR_MAP: {
+    'className': 'class',
+    'htmlFor': 'for'
+  },
+
+  _attributes: function(attributes) {
+    var attrs = [];
+    for(attribute in attributes)
+      attrs.push((attribute in this.ATTR_MAP ? this.ATTR_MAP[attribute] : attribute) +
+          '="' + attributes[attribute].toString().escapeHTML().gsub(/"/,'&quot;') + '"');
+    return attrs.join(" ");
+  },
+  _children: function(element, children) {
+    if(children.tagName) {
+      element.appendChild(children);
+      return;
+    }
+    if(typeof children=='object') { // array can hold nodes and text
+      children.flatten().each( function(e) {
+        if(typeof e=='object')
+          element.appendChild(e);
+        else
+          if(Builder._isStringOrNumber(e))
+            element.appendChild(Builder._text(e));
+      });
+    } else
+      if(Builder._isStringOrNumber(children))
+        element.appendChild(Builder._text(children));
+  },
+  _isStringOrNumber: function(param) {
+    return(typeof param=='string' || typeof param=='number');
+  },
+  build: function(html) {
+    var element = this.node('div');
+    $(element).update(html.strip());
+    return element.down();
+  },
+  dump: function(scope) { 
+    if(typeof scope != 'object' && typeof scope != 'function') scope = window; //global scope 
+  
+    var tags = ("A ABBR ACRONYM ADDRESS APPLET AREA B BASE BASEFONT BDO BIG BLOCKQUOTE BODY " +
+      "BR BUTTON CAPTION CENTER CITE CODE COL COLGROUP DD DEL DFN DIR DIV DL DT EM FIELDSET " +
+      "FONT FORM FRAME FRAMESET H1 H2 H3 H4 H5 H6 HEAD HR HTML I IFRAME IMG INPUT INS ISINDEX "+
+      "KBD LABEL LEGEND LI LINK MAP MENU META NOFRAMES NOSCRIPT OBJECT OL OPTGROUP OPTION P "+
+      "PARAM PRE Q S SAMP SCRIPT SELECT SMALL SPAN STRIKE STRONG STYLE SUB SUP TABLE TBODY TD "+
+      "TEXTAREA TFOOT TH THEAD TITLE TR TT U UL VAR").split(/\s+/);
+  
+    tags.each( function(tag){ 
+      scope[tag] = function() { 
+        return Builder.node.apply(Builder, [tag].concat($A(arguments)));  
+      };
+    });
+  }
+};
Index: openacs-4/packages/messages/www/resources/controls.js
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/resources/controls.js,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/resources/controls.js	31 Aug 2010 21:30:50 -0000	1.1
@@ -0,0 +1,926 @@
+if(typeof Effect == 'undefined')
+  throw("controls.js requires including script.aculo.us' effects.js library");
+
+var Autocompleter = { };
+Autocompleter.Base = Class.create({
+  baseInitialize: function(element, update, options) {
+    element          = $(element);
+    this.element     = element; 
+    this.update      = $(update);  
+    this.hasFocus    = false; 
+    this.changed     = false; 
+    this.active      = false; 
+    this.index       = 0;     
+    this.entryCount  = 0;
+    this.oldElementValue = this.element.value;
+    if(this.setOptions)
+      this.setOptions(options);
+    else
+      this.options = options || { };
+
+    this.options.paramName    = this.options.paramName || this.element.name;
+    this.options.tokens       = this.options.tokens || [];
+    this.options.frequency    = this.options.frequency || 0.4;
+    this.options.minChars     = this.options.minChars || 1;
+    this.options.onShow       = this.options.onShow || 
+      function(element, update){ 
+        if(!$(update).getStyle('position') || $(update).getStyle('position')=='absolute') {
+          $(update).setStyle({position : 'absolute'});
+          Position.clone(element, update, {
+            setHeight: false, 
+            offsetTop: element.offsetHeight
+          });
+        }
+        Effect.Appear(update,{duration:0.15});
+      };
+    this.options.onHide = this.options.onHide || 
+      function(element, update){ new Effect.Fade(update,{duration:0.15}) };
+
+    if(typeof(this.options.tokens) == 'string') 
+      this.options.tokens = new Array(this.options.tokens);
+    // Force carriage returns as token delimiters anyway
+    if (!this.options.tokens.include('\n'))
+      this.options.tokens.push('\n');
+
+    this.observer = null;
+    
+    this.element.setAttribute('autocomplete','off');
+
+    Element.hide(this.update);
+
+    Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this));
+    Event.observe(this.element, 'keydown', this.onKeyPress.bindAsEventListener(this));
+  },
+
+  show: function() {
+    if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
+    if(!this.iefix && 
+      (Prototype.Browser.IE) &&
+      (Element.getStyle(this.update, 'position')=='absolute')) {
+      new Insertion.After(this.update, 
+       '<iframe id="' + this.update.id + '_iefix" '+
+       'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
+       'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
+      this.iefix = $(this.update.id+'_iefix');
+    }
+    if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
+  },
+  
+  fixIEOverlapping: function() {
+    Position.clone(this.update, this.iefix, {setTop:(!this.update.getStyle("height"))});
+    $(this.iefix).setStyle({zIndex : 1});
+    this.update.setStyle({zIndex:2});
+    Element.show(this.iefix);
+  },
+
+  hide: function() {
+    this.stopIndicator();
+    if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);
+    if(this.iefix) Element.hide(this.iefix);
+  },
+
+  startIndicator: function() {
+    if(this.options.indicator) Element.show(this.options.indicator);
+  },
+
+  stopIndicator: function() {
+    if(this.options.indicator) Element.hide(this.options.indicator);
+  },
+
+  onKeyPress: function(event) {
+    if(this.active)
+      switch(event.keyCode) {
+       case Event.KEY_TAB:
+       case Event.KEY_RETURN:
+         this.selectEntry();
+         Event.stop(event);
+       case Event.KEY_ESC:
+         this.hide();
+         this.active = false;
+         Event.stop(event);
+         return;
+       case Event.KEY_LEFT:
+       case Event.KEY_RIGHT:
+         return;
+       case Event.KEY_UP:
+         this.markPrevious();
+         this.render();
+         Event.stop(event);
+         return;
+       case Event.KEY_DOWN:
+         this.markNext();
+         this.render();
+         Event.stop(event);
+         return;
+      }
+     else 
+       if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN || 
+         (Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return;
+
+    this.changed = true;
+    this.hasFocus = true;
+
+    if(this.observer) clearTimeout(this.observer);
+      this.observer = 
+        setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
+  },
+
+  activate: function() {
+    this.changed = false;
+    this.hasFocus = true;
+    this.getUpdatedChoices();
+  },
+
+  onHover: function(event) {
+    var element = Event.findElement(event, 'LI');
+    if(this.index != element.autocompleteIndex) 
+    {
+        this.index = element.autocompleteIndex;
+        this.render();
+    }
+    Event.stop(event);
+  },
+  
+  onClick: function(event) {
+    var element = Event.findElement(event, 'LI');
+    this.index = element.autocompleteIndex;
+    this.selectEntry();
+    this.hide();
+  },
+  
+  onBlur: function(event) {
+    // needed to make click events working
+    setTimeout(this.hide.bind(this), 250);
+    this.hasFocus = false;
+    this.active = false;     
+  }, 
+  
+  render: function() {
+    if(this.entryCount > 0) {
+      for (var i = 0; i < this.entryCount; i++)
+        this.index==i ? 
+          Element.addClassName(this.getEntry(i),"selected") : 
+          Element.removeClassName(this.getEntry(i),"selected");
+      if(this.hasFocus) { 
+        this.show();
+        this.active = true;
+      }
+    } else {
+      this.active = false;
+      this.hide();
+    }
+  },
+  
+  markPrevious: function() {
+    if(this.index > 0) this.index--;
+      else this.index = this.entryCount-1;
+    this.getEntry(this.index).scrollIntoView(true);
+  },
+  
+  markNext: function() {
+    if(this.index < this.entryCount-1) this.index++;
+      else this.index = 0;
+    this.getEntry(this.index).scrollIntoView(false);
+  },
+  
+  getEntry: function(index) {
+    return this.update.firstChild.childNodes[index];
+  },
+  
+  getCurrentEntry: function() {
+    return this.getEntry(this.index);
+  },
+  
+  selectEntry: function() {
+    this.active = false;
+    this.updateElement(this.getCurrentEntry());
+  },
+
+  updateElement: function(selectedElement) {
+    if (this.options.updateElement) {
+      this.options.updateElement(selectedElement);
+      return;
+    }
+    var value = '';
+    if (this.options.select) {
+      var nodes = $(selectedElement).select('.' + this.options.select) || [];
+      if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
+    } else
+      value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
+    
+    var bounds = this.getTokenBounds();
+    if (bounds[0] != -1) {
+      var newValue = this.element.value.substr(0, bounds[0]);
+      var whitespace = this.element.value.substr(bounds[0]).match(/^\s+/);
+      if (whitespace)
+        newValue += whitespace[0];
+      this.element.value = newValue + value + this.element.value.substr(bounds[1]);
+    } else {
+      this.element.value = value;
+    }
+    this.oldElementValue = this.element.value;
+    this.element.focus();
+    
+    if (this.options.afterUpdateElement)
+      this.options.afterUpdateElement(this.element, selectedElement);
+  },
+
+  updateChoices: function(choices) {
+    if(!this.changed && this.hasFocus) {
+      this.update.innerHTML = choices;
+      Element.cleanWhitespace(this.update);
+      Element.cleanWhitespace(this.update.down());
+
+      if(this.update.firstChild && this.update.down().childNodes) {
+        this.entryCount = 
+          this.update.down().childNodes.length;
+        for (var i = 0; i < this.entryCount; i++) {
+          var entry = this.getEntry(i);
+          entry.autocompleteIndex = i;
+          this.addObservers(entry);
+        }
+      } else { 
+        this.entryCount = 0;
+      }
+
+      this.stopIndicator();
+      this.index = 0;
+      
+      if(this.entryCount==1 && this.options.autoSelect) {
+        this.selectEntry();
+        this.hide();
+      } else {
+        this.render();
+      }
+    }
+  },
+
+  addObservers: function(element) {
+    Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
+    Event.observe(element, "click", this.onClick.bindAsEventListener(this));
+  },
+
+  onObserverEvent: function() {
+    this.changed = false;   
+    this.tokenBounds = null;
+    if(this.getToken().length>=this.options.minChars) {
+      this.getUpdatedChoices();
+    } else {
+      this.active = false;
+      this.hide();
+    }
+    this.oldElementValue = this.element.value;
+  },
+
+  getToken: function() {
+    var bounds = this.getTokenBounds();
+    return this.element.value.substring(bounds[0], bounds[1]).strip();
+  },
+
+  getTokenBounds: function() {
+    if (null != this.tokenBounds) return this.tokenBounds;
+    var value = this.element.value;
+    if (value.strip().empty()) return [-1, 0];
+    var diff = arguments.callee.getFirstDifferencePos(value, this.oldElementValue);
+    var offset = (diff == this.oldElementValue.length ? 1 : 0);
+    var prevTokenPos = -1, nextTokenPos = value.length;
+    var tp;
+    for (var index = 0, l = this.options.tokens.length; index < l; ++index) {
+      tp = value.lastIndexOf(this.options.tokens[index], diff + offset - 1);
+      if (tp > prevTokenPos) prevTokenPos = tp;
+      tp = value.indexOf(this.options.tokens[index], diff + offset);
+      if (-1 != tp && tp < nextTokenPos) nextTokenPos = tp;
+    }
+    return (this.tokenBounds = [prevTokenPos + 1, nextTokenPos]);
+  }
+});
+
+Autocompleter.Base.prototype.getTokenBounds.getFirstDifferencePos = function(newS, oldS) {
+  var boundary = Math.min(newS.length, oldS.length);
+  for (var index = 0; index < boundary; ++index)
+    if (newS[index] != oldS[index])
+      return index;
+  return boundary;
+};
+
+Ajax.Autocompleter = Class.create(Autocompleter.Base, {
+  initialize: function(element, update, url, options) {
+    this.baseInitialize(element, update, options);
+    this.options.asynchronous  = true;
+    this.options.onComplete    = this.onComplete.bind(this);
+    this.options.defaultParams = this.options.parameters || null;
+    this.url                   = url;
+  },
+
+  getUpdatedChoices: function() {
+    this.startIndicator();
+    
+    var entry = encodeURIComponent(this.options.paramName) + '=' + 
+      encodeURIComponent(this.getToken());
+
+    this.options.parameters = this.options.callback ?
+      this.options.callback(this.element, entry) : entry;
+
+    if(this.options.defaultParams) 
+      this.options.parameters += '&' + this.options.defaultParams;
+    
+    new Ajax.Request(this.url, this.options);
+  },
+
+  onComplete: function(request) {
+    this.updateChoices(request.responseText);
+  }
+});
+
+// The local array autocompleter. Used when you'd prefer to
+// inject an array of autocompletion options into the page, rather
+// than sending out Ajax queries, which can be quite slow sometimes.
+//
+// The constructor takes four parameters. The first two are, as usual,
+// the id of the monitored textbox, and id of the autocompletion menu.
+// The third is the array you want to autocomplete from, and the fourth
+// is the options block.
+//
+// Extra local autocompletion options:
+// - choices - How many autocompletion choices to offer
+//
+// - partialSearch - If false, the autocompleter will match entered
+//                    text only at the beginning of strings in the 
+//                    autocomplete array. Defaults to true, which will
+//                    match text at the beginning of any *word* in the
+//                    strings in the autocomplete array. If you want to
+//                    search anywhere in the string, additionally set
+//                    the option fullSearch to true (default: off).
+//
+// - fullSsearch - Search anywhere in autocomplete array strings.
+//
+// - partialChars - How many characters to enter before triggering
+//                   a partial match (unlike minChars, which defines
+//                   how many characters are required to do any match
+//                   at all). Defaults to 2.
+//
+// - ignoreCase - Whether to ignore case when autocompleting.
+//                 Defaults to true.
+//
+// It's possible to pass in a custom function as the 'selector' 
+// option, if you prefer to write your own autocompletion logic.
+// In that case, the other options above will not apply unless
+// you support them.
+
+Autocompleter.Local = Class.create(Autocompleter.Base, {
+  initialize: function(element, update, array, options) {
+    this.baseInitialize(element, update, options);
+    this.options.array = array;
+  },
+
+  getUpdatedChoices: function() {
+    this.updateChoices(this.options.selector(this));
+  },
+
+  setOptions: function(options) {
+    this.options = Object.extend({
+      choices: 10,
+      partialSearch: true,
+      partialChars: 2,
+      ignoreCase: true,
+      fullSearch: false,
+      selector: function(instance) {
+        var ret       = []; // Beginning matches
+        var partial   = []; // Inside matches
+        var entry     = instance.getToken();
+        var count     = 0;
+
+        for (var i = 0; i < instance.options.array.length &&  
+          ret.length < instance.options.choices ; i++) { 
+
+          var elem = instance.options.array[i];
+          var foundPos = instance.options.ignoreCase ? 
+            elem.toLowerCase().indexOf(entry.toLowerCase()) : 
+            elem.indexOf(entry);
+
+          while (foundPos != -1) {
+            if (foundPos == 0 && elem.length != entry.length) { 
+              ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" + 
+                elem.substr(entry.length) + "</li>");
+              break;
+            } else if (entry.length >= instance.options.partialChars && 
+              instance.options.partialSearch && foundPos != -1) {
+              if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
+                partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
+                  elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
+                  foundPos + entry.length) + "</li>");
+                break;
+              }
+            }
+
+            foundPos = instance.options.ignoreCase ? 
+              elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) : 
+              elem.indexOf(entry, foundPos + 1);
+
+          }
+        }
+        if (partial.length)
+          ret = ret.concat(partial.slice(0, instance.options.choices - ret.length));
+        return "<ul>" + ret.join('') + "</ul>";
+      }
+    }, options || { });
+  }
+});
+
+// AJAX in-place editor and collection editor
+// Full rewrite by Christophe Porteneuve <tdd@tddsworld.com> (April 2007).
+
+// Use this if you notice weird scrolling problems on some browsers,
+// the DOM might be a bit confused when this gets called so do this
+// waits 1 ms (with setTimeout) until it does the activation
+Field.scrollFreeActivate = function(field) {
+  setTimeout(function() {
+    Field.activate(field);
+  }, 1);
+};
+
+Ajax.InPlaceEditor = Class.create({
+  initialize: function(element, url, options) {
+    this.url = url;
+    this.element = element = $(element);
+    this.prepareOptions();
+    this._controls = { };
+    arguments.callee.dealWithDeprecatedOptions(options); // DEPRECATION LAYER!!!
+    Object.extend(this.options, options || { });
+    if (!this.options.formId && this.element.id) {
+      this.options.formId = this.element.id + '-inplaceeditor';
+      if ($(this.options.formId))
+        this.options.formId = '';
+    }
+    if (this.options.externalControl)
+      this.options.externalControl = $(this.options.externalControl);
+    if (!this.options.externalControl)
+      this.options.externalControlOnly = false;
+    this._originalBackground = this.element.getStyle('background-color') || 'transparent';
+    this.element.title = this.options.clickToEditText;
+    this._boundCancelHandler = this.handleFormCancellation.bind(this);
+    this._boundComplete = (this.options.onComplete || Prototype.emptyFunction).bind(this);
+    this._boundFailureHandler = this.handleAJAXFailure.bind(this);
+    this._boundSubmitHandler = this.handleFormSubmission.bind(this);
+    this._boundWrapperHandler = this.wrapUp.bind(this);
+    this.registerListeners();
+  },
+  checkForEscapeOrReturn: function(e) {
+    if (!this._editing || e.ctrlKey || e.altKey || e.shiftKey) return;
+    if (Event.KEY_ESC == e.keyCode)
+      this.handleFormCancellation(e);
+    else if (Event.KEY_RETURN == e.keyCode)
+      this.handleFormSubmission(e);
+  },
+  createControl: function(mode, handler, extraClasses) {
+    var control = this.options[mode + 'Control'];
+    var text = this.options[mode + 'Text'];
+    if ('button' == control) {
+      var btn = document.createElement('input');
+      btn.type = 'submit';
+      btn.value = text;
+      btn.className = 'editor_' + mode + '_button';
+      if ('cancel' == mode)
+        btn.onclick = this._boundCancelHandler;
+      this._form.appendChild(btn);
+      this._controls[mode] = btn;
+    } else if ('link' == control) {
+      var link = document.createElement('a');
+      link.href = '#';
+      link.appendChild(document.createTextNode(text));
+      link.onclick = 'cancel' == mode ? this._boundCancelHandler : this._boundSubmitHandler;
+      link.className = 'editor_' + mode + '_link';
+      if (extraClasses)
+        link.className += ' ' + extraClasses;
+      this._form.appendChild(link);
+      this._controls[mode] = link;
+    }
+  },
+  createEditField: function() {
+    var text = (this.options.loadTextURL ? this.options.loadingText : this.getText());
+    var fld;
+    if (1 >= this.options.rows && !/\r|\n/.test(this.getText())) {
+      fld = document.createElement('input');
+      fld.type = 'text';
+      var size = this.options.size || this.options.cols || 0;
+      if (0 < size) fld.size = size;
+    } else {
+      fld = document.createElement('textarea');
+      fld.rows = (1 >= this.options.rows ? this.options.autoRows : this.options.rows);
+      fld.cols = this.options.cols || 40;
+    }
+    fld.name = this.options.paramName;
+    fld.value = text; // No HTML breaks conversion anymore
+    fld.className = 'editor_field';
+    if (this.options.submitOnBlur)
+      fld.onblur = this._boundSubmitHandler;
+    this._controls.editor = fld;
+    if (this.options.loadTextURL)
+      this.loadExternalText();
+    this._form.appendChild(this._controls.editor);
+  },
+  createForm: function() {
+    var ipe = this;
+    function addText(mode, condition) {
+      var text = ipe.options['text' + mode + 'Controls'];
+      if (!text || condition === false) return;
+      ipe._form.appendChild(document.createTextNode(text));
+    };
+    this._form = $(document.createElement('form'));
+    this._form.id = this.options.formId;
+    this._form.addClassName(this.options.formClassName);
+    this._form.onsubmit = this._boundSubmitHandler;
+    this.createEditField();
+    if ('textarea' == this._controls.editor.tagName.toLowerCase())
+      this._form.appendChild(document.createElement('br'));
+    if (this.options.onFormCustomization)
+      this.options.onFormCustomization(this, this._form);
+    addText('Before', this.options.okControl || this.options.cancelControl);
+    this.createControl('ok', this._boundSubmitHandler);
+    addText('Between', this.options.okControl && this.options.cancelControl);
+    this.createControl('cancel', this._boundCancelHandler, 'editor_cancel');
+    addText('After', this.options.okControl || this.options.cancelControl);
+  },
+  destroy: function() {
+    if (this._oldInnerHTML)
+      this.element.innerHTML = this._oldInnerHTML;
+    this.leaveEditMode();
+    this.unregisterListeners();
+  },
+  enterEditMode: function(e) {
+    if (this._saving || this._editing) return;
+    this._editing = true;
+    this.triggerCallback('onEnterEditMode');
+    if (this.options.externalControl)
+      this.options.externalControl.hide();
+    this.element.hide();
+    this.createForm();
+    this.element.parentNode.insertBefore(this._form, this.element);
+    if (!this.options.loadTextURL)
+      this.postProcessEditField();
+    if (e) Event.stop(e);
+  },
+  enterHover: function(e) {
+    if (this.options.hoverClassName)
+      this.element.addClassName(this.options.hoverClassName);
+    if (this._saving) return;
+    this.triggerCallback('onEnterHover');
+  },
+  getText: function() {
+    return this.element.innerHTML;
+  },
+  handleAJAXFailure: function(transport) {
+    this.triggerCallback('onFailure', transport);
+    if (this._oldInnerHTML) {
+      this.element.innerHTML = this._oldInnerHTML;
+      this._oldInnerHTML = null;
+    }
+  },
+  handleFormCancellation: function(e) {
+    this.wrapUp();
+    if (e) Event.stop(e);
+  },
+  handleFormSubmission: function(e) {
+    var form = this._form;
+    var value = $F(this._controls.editor);
+    this.prepareSubmission();
+    var params = this.options.callback(form, value) || '';
+    if (Object.isString(params))
+      params = params.toQueryParams();
+    params.editorId = this.element.id;
+    if (this.options.htmlResponse) {
+      var options = Object.extend({ evalScripts: true }, this.options.ajaxOptions);
+      Object.extend(options, {
+        parameters: params,
+        onComplete: this._boundWrapperHandler,
+        onFailure: this._boundFailureHandler
+      });
+      new Ajax.Updater({ success: this.element }, this.url, options);
+    } else {
+      var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
+      Object.extend(options, {
+        parameters: params,
+        onComplete: this._boundWrapperHandler,
+        onFailure: this._boundFailureHandler
+      });
+      new Ajax.Request(this.url, options);
+    }
+    if (e) Event.stop(e);
+  },
+  leaveEditMode: function() {
+    this.element.removeClassName(this.options.savingClassName);
+    this.removeForm();
+    this.leaveHover();
+    this.element.setStyle({backgroundColor : this._originalBackground});
+    this.element.show();
+    if (this.options.externalControl)
+      this.options.externalControl.show();
+    this._saving = false;
+    this._editing = false;
+    this._oldInnerHTML = null;
+    this.triggerCallback('onLeaveEditMode');
+  },
+  leaveHover: function(e) {
+    if (this.options.hoverClassName)
+      this.element.removeClassName(this.options.hoverClassName);
+    if (this._saving) return;
+    this.triggerCallback('onLeaveHover');
+  },
+  loadExternalText: function() {
+    this._form.addClassName(this.options.loadingClassName);
+    this._controls.editor.disabled = true;
+    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
+    Object.extend(options, {
+      parameters: 'editorId=' + encodeURIComponent(this.element.id),
+      onComplete: Prototype.emptyFunction,
+      onSuccess: function(transport) {
+        this._form.removeClassName(this.options.loadingClassName);
+        var text = transport.responseText;
+        if (this.options.stripLoadedTextTags)
+          text = text.stripTags();
+        this._controls.editor.value = text;
+        this._controls.editor.disabled = false;
+        this.postProcessEditField();
+      }.bind(this),
+      onFailure: this._boundFailureHandler
+    });
+    new Ajax.Request(this.options.loadTextURL, options);
+  },
+  postProcessEditField: function() {
+    var fpc = this.options.fieldPostCreation;
+    if (fpc)
+      $(this._controls.editor)['focus' == fpc ? 'focus' : 'activate']();
+  },
+  prepareOptions: function() {
+    this.options = Object.clone(Ajax.InPlaceEditor.DefaultOptions);
+    Object.extend(this.options, Ajax.InPlaceEditor.DefaultCallbacks);
+    [this._extraDefaultOptions].flatten().compact().each(function(defs) {
+      Object.extend(this.options, defs);
+    }.bind(this));
+  },
+  prepareSubmission: function() {
+    this._saving = true;
+    this.removeForm();
+    this.leaveHover();
+    this.showSaving();
+  },
+  registerListeners: function() {
+    this._listeners = { };
+    var listener;
+    $H(Ajax.InPlaceEditor.Listeners).each(function(pair) {
+      listener = this[pair.value].bind(this);
+      this._listeners[pair.key] = listener;
+      if (!this.options.externalControlOnly)
+        this.element.observe(pair.key, listener);
+      if (this.options.externalControl)
+        this.options.externalControl.observe(pair.key, listener);
+    }.bind(this));
+  },
+  removeForm: function() {
+    if (!this._form) return;
+    this._form.remove();
+    this._form = null;
+    this._controls = { };
+  },
+  showSaving: function() {
+    this._oldInnerHTML = this.element.innerHTML;
+    this.element.innerHTML = this.options.savingText;
+    this.element.addClassName(this.options.savingClassName);
+    this.element.setStyle({backgroundColor: this._originalBackground});
+    this.element.show();
+  },
+  triggerCallback: function(cbName, arg) {
+    if ('function' == typeof this.options[cbName]) {
+      this.options[cbName](this, arg);
+    }
+  },
+  unregisterListeners: function() {
+    $H(this._listeners).each(function(pair) {
+      if (!this.options.externalControlOnly)
+        this.element.stopObserving(pair.key, pair.value);
+      if (this.options.externalControl)
+        this.options.externalControl.stopObserving(pair.key, pair.value);
+    }.bind(this));
+  },
+  wrapUp: function(transport) {
+    this.leaveEditMode();
+    // Can't use triggerCallback due to backward compatibility: requires
+    // binding + direct element
+    this._boundComplete(transport, this.element);
+  }
+});
+
+Object.extend(Ajax.InPlaceEditor.prototype, {
+  dispose: Ajax.InPlaceEditor.prototype.destroy
+});
+
+Ajax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, {
+  initialize: function($super, element, url, options) {
+    this._extraDefaultOptions = Ajax.InPlaceCollectionEditor.DefaultOptions;
+    $super(element, url, options);
+  },
+
+  createEditField: function() {
+    var list = document.createElement('select');
+    list.name = this.options.paramName;
+    list.size = 1;
+    this._controls.editor = list;
+    this._collection = this.options.collection || [];
+    if (this.options.loadCollectionURL)
+      this.loadCollection();
+    else
+      this.checkForExternalText();
+    this._form.appendChild(this._controls.editor);
+  },
+
+  loadCollection: function() {
+    this._form.addClassName(this.options.loadingClassName);
+    this.showLoadingText(this.options.loadingCollectionText);
+    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
+    Object.extend(options, {
+      parameters: 'editorId=' + encodeURIComponent(this.element.id),
+      onComplete: Prototype.emptyFunction,
+      onSuccess: function(transport) {
+        var js = transport.responseText.strip();
+        if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check
+          throw('Server returned an invalid collection representation.');
+        this._collection = eval(js);
+        this.checkForExternalText();
+      }.bind(this),
+      onFailure: this.onFailure
+    });
+    new Ajax.Request(this.options.loadCollectionURL, options);
+  },
+
+  showLoadingText: function(text) {
+    this._controls.editor.disabled = true;
+    var tempOption = this._controls.editor.firstChild;
+    if (!tempOption) {
+      tempOption = document.createElement('option');
+      tempOption.value = '';
+      this._controls.editor.appendChild(tempOption);
+      tempOption.selected = true;
+    }
+    tempOption.update((text || '').stripScripts().stripTags());
+  },
+
+  checkForExternalText: function() {
+    this._text = this.getText();
+    if (this.options.loadTextURL)
+      this.loadExternalText();
+    else
+      this.buildOptionList();
+  },
+
+  loadExternalText: function() {
+    this.showLoadingText(this.options.loadingText);
+    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
+    Object.extend(options, {
+      parameters: 'editorId=' + encodeURIComponent(this.element.id),
+      onComplete: Prototype.emptyFunction,
+      onSuccess: function(transport) {
+        this._text = transport.responseText.strip();
+        this.buildOptionList();
+      }.bind(this),
+      onFailure: this.onFailure
+    });
+    new Ajax.Request(this.options.loadTextURL, options);
+  },
+
+  buildOptionList: function() {
+    this._form.removeClassName(this.options.loadingClassName);
+    this._collection = this._collection.map(function(entry) {
+      return 2 === entry.length ? entry : [entry, entry].flatten();
+    });
+    var marker = ('value' in this.options) ? this.options.value : this._text;
+    var textFound = this._collection.any(function(entry) {
+      return entry[0] == marker;
+    }.bind(this));
+    this._controls.editor.update('');
+    var option;
+    this._collection.each(function(entry, index) {
+      option = document.createElement('option');
+      option.value = entry[0];
+      option.selected = textFound ? entry[0] == marker : 0 == index;
+      option.appendChild(document.createTextNode(entry[1]));
+      this._controls.editor.appendChild(option);
+    }.bind(this));
+    this._controls.editor.disabled = false;
+    Field.scrollFreeActivate(this._controls.editor);
+  }
+});
+
+//**** DEPRECATION LAYER FOR InPlace[Collection]Editor! ****
+//**** This only  exists for a while,  in order to  let ****
+//**** users adapt to  the new API.  Read up on the new ****
+//**** API and convert your code to it ASAP!            ****
+
+Ajax.InPlaceEditor.prototype.initialize.dealWithDeprecatedOptions = function(options) {
+  if (!options) return;
+  function fallback(name, expr) {
+    if (name in options || expr === undefined) return;
+    options[name] = expr;
+  };
+  fallback('cancelControl', (options.cancelLink ? 'link' : (options.cancelButton ? 'button' :
+    options.cancelLink == options.cancelButton == false ? false : undefined)));
+  fallback('okControl', (options.okLink ? 'link' : (options.okButton ? 'button' :
+    options.okLink == options.okButton == false ? false : undefined)));
+  fallback('highlightColor', options.highlightcolor);
+  fallback('highlightEndColor', options.highlightendcolor);
+};
+
+Object.extend(Ajax.InPlaceEditor, {
+  DefaultOptions: {
+    ajaxOptions: { },
+    autoRows: 3,                                // Use when multi-line w/ rows == 1
+    cancelControl: 'link',                      // 'link'|'button'|false
+    cancelText: 'cancel',
+    clickToEditText: 'Click to edit',
+    externalControl: null,                      // id|elt
+    externalControlOnly: false,
+    fieldPostCreation: 'activate',              // 'activate'|'focus'|false
+    formClassName: 'inplaceeditor-form',
+    formId: null,                               // id|elt
+    highlightColor: '#ffff99',
+    highlightEndColor: '#ffffff',
+    hoverClassName: '',
+    htmlResponse: true,
+    loadingClassName: 'inplaceeditor-loading',
+    loadingText: 'Loading...',
+    okControl: 'button',                        // 'link'|'button'|false
+    okText: 'ok',
+    paramName: 'value',
+    rows: 1,                                    // If 1 and multi-line, uses autoRows
+    savingClassName: 'inplaceeditor-saving',
+    savingText: 'Saving...',
+    size: 0,
+    stripLoadedTextTags: false,
+    submitOnBlur: false,
+    textAfterControls: '',
+    textBeforeControls: '',
+    textBetweenControls: ''
+  },
+  DefaultCallbacks: {
+    callback: function(form) {
+      return Form.serialize(form);
+    },
+    onComplete: function(transport, element) {
+      // For backward compatibility, this one is bound to the IPE, and passes
+      // the element directly.  It was too often customized, so we don't break it.
+      new Effect.Highlight(element, {
+        startcolor: this.options.highlightColor, keepBackgroundImage: true });
+    },
+    onEnterEditMode: null,
+    onEnterHover: function(ipe) {
+      $(ipe.element).setStyle({backgroundColor : ipe.options.highlightColor});
+      if (ipe._effect)
+        ipe._effect.cancel();
+    },
+    onFailure: function(transport, ipe) {
+      alert('Error communication with the server: ' + transport.responseText.stripTags());
+    },
+    onFormCustomization: null, // Takes the IPE and its generated form, after editor, before controls.
+    onLeaveEditMode: null,
+    onLeaveHover: function(ipe) {
+      ipe._effect = new Effect.Highlight(ipe.element, {
+        startcolor: ipe.options.highlightColor, endcolor: ipe.options.highlightEndColor,
+        restorecolor: ipe._originalBackground, keepBackgroundImage: true
+      });
+    }
+  },
+  Listeners: {
+    click: 'enterEditMode',
+    keydown: 'checkForEscapeOrReturn',
+    mouseover: 'enterHover',
+    mouseout: 'leaveHover'
+  }
+});
+
+Ajax.InPlaceCollectionEditor.DefaultOptions = {
+  loadingCollectionText: 'Loading options...'
+};
+
+// Delayed observer, like Form.Element.Observer, 
+// but waits for delay after last key input
+// Ideal for live-search fields
+
+Form.Element.DelayedObserver = Class.create({
+  initialize: function(element, delay, callback) {
+    this.delay     = delay || 0.5;
+    this.element   = $(element);
+    this.callback  = callback;
+    this.timer     = null;
+    this.lastValue = $F(this.element); 
+    Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
+  },
+  delayedListener: function(event) {
+    if(this.lastValue == $F(this.element)) return;
+    if(this.timer) clearTimeout(this.timer);
+    this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);
+    this.lastValue = $F(this.element);
+  },
+  onTimerEvent: function() {
+    this.timer = null;
+    this.callback(this.element, $F(this.element));
+  }
+});
Index: openacs-4/packages/messages/www/resources/delete-dialog.js
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/resources/delete-dialog.js,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/resources/delete-dialog.js	31 Aug 2010 21:30:50 -0000	1.1
@@ -0,0 +1,57 @@
+//----------------- dialog delete
+
+YAHOO.namespace("delete_msg.container");
+function init_delete() {
+	// Define various event handlers for Dialog
+	var handleYes = function() {
+            document.getElementById('msg_system').style.display = 'none';
+            var formObject = document.getElementsByName('messages_list');
+            var form = formObject[0];
+            var elements = form.elements;
+            var break_p = 0;
+            for ( var i = 0; i < elements.length ; i++){
+                if (elements[i].name == 'subject_id' && elements[i].checked) {
+                    break_p = 1;
+                    break;
+                }
+            }
+            if (break_p) {
+                document.getElementById('msg_action_p').value = 't';
+                move_to(3);
+                this.hide();
+            } else {
+                document.getElementById('msg_action_p').value = 'f';
+                document.getElementById('msg_action').style.display = '';
+                document.getElementById('msg_actions').innerHTML = document.getElementById('msg_select_msg').value;
+                var folder_id_state = document.getElementById('folder_id').value;
+                if (document.getElementById('folder_id_state') != null ) {
+                    var folder_id_state = document.getElementById('folder_id_state').value;
+                }
+                get_messages_folder('more_actions');
+                this.hide();
+            }
+	};
+	var handleNo = function() {
+            document.getElementById('msg_system').style.display = 'none';
+            this.hide();
+	};
+
+	// Instantiate the Dialog
+	YAHOO.delete_msg.container.delete_dialog = new YAHOO.widget.SimpleDialog("delete_dialog", 
+                                        { modal: true,
+				          visible: false,
+				          fixedcenter: true,
+				          constraintoviewport: true,
+                                          iframe: true,
+                                          close: false,
+                                          width: "300px",
+               				  text: document.getElementById('delete_confirm').value,
+					  buttons: [ { text:document.getElementById('msg_yes').value, handler:handleYes, isDefault:true },
+                                                      { text:"No",  handler:handleNo } ]
+					 } );
+	// Render the Dialog
+	YAHOO.delete_msg.container.delete_dialog.render("container");
+	YAHOO.util.Event.addListener("show_delete", "click", YAHOO.delete_msg.container.delete_dialog.show, null,YAHOO.delete_msg.container.delete_dialog);
+}
+
+YAHOO.util.Event.addListener(window, "load", init_delete);
\ No newline at end of file
Index: openacs-4/packages/messages/www/resources/dispatcher-min.js
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/resources/dispatcher-min.js,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/resources/dispatcher-min.js	31 Aug 2010 21:30:52 -0000	1.1
@@ -0,0 +1,8 @@
+/*
+Copyright (c) 2008, Bubbling Library Team. All rights reserved.
+Portions Copyright (c) 2008, Yahoo!, Inc. All rights reserved.
+Code licensed under the BSD License:
+http://www.bubbling-library.com/eng/licence
+version: 2.1 
+*/
+YAHOO.namespace("plugin");(function(){var I=YAHOO.util.Connect,D=YAHOO.lang,F=YAHOO.util.Event,H=YAHOO.util.Dom,E=YAHOO.util.Dom.get;var N={LOADING:1,DISPATCHED:2,ERROR:3,EMPTY:4,proxy:"/dispatcher.php?uri=",CSSNODE:1,JSNODE:2};var L=/<script([^>]*)>([\s\S]*?)<\/script>/igm,A=/src=(['"]?)([^"']*)\1/i,M=/rel=(['"]?)([^"']*)\1/i,C=/<link([^>]*)(>[\s]*<\/link>|>)/igm,K=/href=(['"]?)([^"']*)\1/i,O=/<style([^>]*)>([\s\S]*?)<\/style>/igm,B=new RegExp("([\\w-.]+)\\s*=\\s*(\".*?\"|'.*?'|\\w+)*","im");var J=new RegExp("url\\s*\\(([^\\)]*)","igm");var G=new RegExp("^((?:http|https)://)((?:\\w+[.|-]?)*\\w+)(/.*)$","i");YAHOO.plugin.Dispatcher=function(){var W={},S={},j=[],P={relative:false,baseURI:document.location.toString()},b="loading",c="yui-dispatchable",T={key:["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],q:{name:"queryKey",parser:/(?:^|&)([^&=]*)=?([^&]*)/g},parser:{strict:/^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,loose:/^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/}},a=/^\s+|\s+$/g;function h(s,p){var r=T,l=r.parser[p?"strict":"loose"].exec(s),q={},n=14;while(n--){q[r.key[n]]=l[n]||""}q[r.q.name]={};q[r.key[12]].replace(r.q.parser,function(o,m,t){if(m){q[r.q.name][m]=t}});return q}function e(l){var n=document.getElementsByTagName("head")[0]||document.documentElement,m=document.createElement("script");l=l.replace(a,"");if(l){m.type="text/javascript";if(YAHOO.env.ua.ie){m.text=l}else{m.appendChild(document.createTextNode(l))}n.insertBefore(m,n.firstChild);n.removeChild(m)}}function Y(l){if(D.isString(l)){l=l.replace(/^\s*(\S*(\s+\S+)*)\s*$/,"$1");l=l.replace(/^(['|"])*(\S*(?:\s+\S+)*)\1$/,"$2")}return l}function Q(o,l){var n=null,m={};l=l||{};if(D.isString(o)){while(n=B.exec(o)){n[2]=(l.hasOwnProperty(n[1])?l[n[1]]:n[2]);if(n[2]){m[n[1]]=Y(n[2])}o=o.replace(B,"")}}return m}function U(l){l=((D.isString(l)&&(l.indexOf("/")>-1))?l:P.baseURI)+"";return l.substr(0,l.lastIndexOf("/")+1)}function k(m,l){if(l&&!G.test(l)&&(l.indexOf("/")!==0)){l=m+l}return l}function R(l){if(l.element&&l.element.setAttribute){l.element.setAttribute("aria-live","polite");l.element.setAttribute("aria-atomic","true");l.element.setAttribute("aria-relevant","all");l.element.setAttribute("aria-busy","true")}l.onStart=l.before||l.onStart;if(D.isFunction(l.onStart)){l.onStart.apply(l,[l.element]);l.onStart=null}if(!l.underground&&YAHOO.Bubbling){YAHOO.Bubbling.fire("onAsyncRequestStart",{element:l.element})}}function g(l,m){if(D.isFunction(l.onChange)){m=m||l.element;l.onChange.apply(l,[m])}}function V(l){l.onLoad=l.after||l.onLoad;if(D.isFunction(l.onLoad)){l.onLoad.apply(l,[l.element])}if(!l.underground&&YAHOO.Bubbling){YAHOO.Bubbling.fire("onAsyncRequestEnd",{element:l.element})}if(l.element&&l.element.setAttribute){l.element.setAttribute("aria-busy","false")}}function i(q,m){var r=null,l=true,p=null,o="",n=0;m=m||{};if(W.isAlive(q)){p=S[q].chunks.shift();if(D.isObject(p)&&p.src){m.hash=j.length;for(n=0;n<j.length;n++){if(j[n].uri==p.src){if((j[n].status==N.DISPATCHED)&&!m.override){l=false}else{}m.hash=n;break}}if(l){o=W.firewall(p.src,m,true);if(D.isString(o)&&(o!=="")){j[m.hash]={uri:p.src,proxy:o,status:N.LOADING};if(p.type===N.JSNODE){W.area=q;W.destroyer=S[q].destroyer;m.handle=YAHOO.util.Get.script(o,{onSuccess:function(){j[m.hash].status=N.DISPATCHED;if(m.rel&&YAHOO.Bubbling){YAHOO.Bubbling.fire("onScriptReady",{module:p.rel,src:p.src,uri:o,hash:m.hash})}m.hash=null;i(q,m)},onFailure:function(){i(q,m)},scope:W,data:m})}else{if(p.type===N.CSSNODE){YAHOO.util.Get.css(o,{});j[m.hash].status=N.DISPATCHED;i(q,m)}}}}else{i(q,m)}}else{m.hash=null;Z(q,p.content,m)}}else{W.kill(q);V(m)}}function Z(o,p,m){var l=N.EMPTY;if(p&&(p!=="")){try{l=N.DISPATCHED;if(!m.hash||(j[m.hash].status!=N.DISPATCHED)){W.area=o;W.destroyer=S[o].destroyer;if(D.isFunction(m.evalRoutine)){m.evalRoutine(p,m)}else{e(p)}}}catch(n){l=N.ERROR;if(D.isFunction(m.error)){m.error.apply(m,[o,p,j,n])}else{throw new Error("Dispacher: Script Execution Error ("+n+")")}}}if(D.isNumber(m.hash)){j[m.hash].status=l;m.hash=null}i(o,m)}function d(m,q,l){l.action=(l.action?l.action:"replace");switch(l.action){case"tabview":f(m.get("contentEl"),l);try{m.set("content",q)}catch(p){return false}g(l,m.get("contentEl"));break;case"update":q=m.innerHTML+q;try{m.innerHTML=q}catch(o){return false}g(l,m);break;case"replace":case"layout":default:f(m,l);try{m.innerHTML=q}catch(n){return false}g(l,m);break}return true}function f(n,l){var o=l.guid,m=0;if(D.isObject(S[o].destroyer)){S[o].destroyer.fire(n,l)}if(H.inDocument(n)){for(m=0;m<n.childNodes.length;m++){F.purgeElement(n.childNodes[m],true)}H.addClass(n,c)}S[o].destroyer=new YAHOO.util.CustomEvent("destroyer");if(D.isFunction(l.onDestroy)){S[o].destroyer.subscribe(l.onDestroy)}}function X(r,p,o){o=o||{};o.uri=o.uri||null;o.relative=o.relative||P.relative;var n=true,l=false,q=U(o.uri);p=p.replace(O,function(w,v,t,u,m){if(t){W.applyCSS(t,Q(v),o)}return""});p=p.replace(C,function(w,v,t,u,m){if(v){l=v.match(K);if(l){if(o.relative){l[2]=k(q,l[2])}S[r].chunks.push({src:l[2],content:"",type:N.CSSNODE,params:Q(v)})}}return""});p=p.replace(L,function(x,w,u,v,t){if(w){l=w.match(A);if(l){var m=w.match(M);m=(m?m[2]:null);if(o.relative){l[2]=k(q,l[2])}S[r].chunks.push({src:l[2],content:"",type:N.JSNODE,rel:m,params:Q(w)})}}if(u){S[r].chunks.push({src:null,content:u,type:N.JSNODE,params:Q(w)})}return""});return p}if(YAHOO.Bubbling){YAHOO.Bubbling.on("onScriptReady",function(){if(this.src&&!this.hash){j[this.hash].status=N.DISPATCHED}})}W.area=null;W.strictMode=true;W.destroyer=null;W.fetch=function(n,o,m){m=m||{};m.uri=o;var q=null,l=null,p={success:function(r){if(r.responseText!="undefined"){W.process(n,r.responseText,m,true)}H.removeClass(n,b)},failure:function(r){if(D.isFunction(m.onError)){m.onError.apply(m,[m.element,r])}H.removeClass(n,b)}};if((D.isObject(n)||(n=E(n)))&&o){l=W.firewall(o,m);H.addClass(n,b);m.handle=I.asyncRequest("GET",l,p);m.element=n;R(m);if(YAHOO.Bubbling){q=m.guid||F.generateId(n);YAHOO.Bubbling.fire("onNavigate",{state:q+escape(o),control:"dispatcher",element:n,uri:o,config:m,restore:function(){W.fetch(n,o,m)}})}return m.handle}return null};W.process=function(n,o,m,l){var p=null;m=m||{};if(D.isObject(n)||(n=E(n))){p=m.guid||F.generateId(n);this.kill(p);m.element=n;m.content=o;m.guid=p;if(!l){R(m)}if(d(n,X(p,o,m),m)){i(p,m)}}return p};W.delegate=function(m,n,l){l=l||{};l.action="tabview";l.uri=m.get("dataSrc")||null;l.tab=m;m.loadHandler.success=function(q){var p=m.get("contentEl");l.tab=p;l.underground=true;W.process(m,q.responseText,l);if(YAHOO.Bubbling){YAHOO.Bubbling.fire("onAsyncRequestEnd",{element:p})}};m.on("activeChange",function(){if(YAHOO.Bubbling&&this.get("active")&&m.get("dataSrc")&&!this.get("cacheData")){YAHOO.Bubbling.fire("onAsyncRequestStart",{element:this.get("contentEl")})}});if(D.isObject(n)){n.addTab(m)}};W.addUnit=function(o,n,l){var p=l||{},m;p.action="layout";if(!o||!n){return false}if(D.isString(o)){o=n.getUnitByPosition(o)}else{if(o.position&&!n.getUnitByPosition(o.position)){o=n.addUnit(o)}}if(p.uri){o.set("dataSrc",p.uri)}if((p.unit=o)&&(p.uri=p.unit.get("dataSrc"))&&(m=p.unit.body)){p.underground=true;p._dispatcherConfig=p;p.unit.loadHandler.success=function(q){W.process(this.body,q.responseText,this._dispatcherConfig);if(YAHOO.Bubbling){YAHOO.Bubbling.fire("onAsyncRequestEnd",{element:this.body})}};if(p.unit.loadContent()&&YAHOO.Bubbling){YAHOO.Bubbling.fire("onAsyncRequestStart",{element:m})}}return p.unit};W.applyCSS=function(m,r,n){r=r||{};var l=document.createElement("style"),p=r.href||"";n=n||{};var o=n.uri||P.baseURI;n.relative=n.relative||P.relative;if(n.relative){o=W.firewall(o,n,true);p=U(o);p=k(p,r.href)}p=U(p);m=m.replace(J,function(w,v,u,t){v=Y(v);v="url("+k(p,v);return v});l.type="text/css";if(D.isObject(l.styleSheet)){l.styleSheet.cssText=m}else{l.appendChild(document.createTextNode(m))}try{document.getElementsByTagName("head")[0].appendChild(l)}catch(q){throw new Error("Dispacher: CSS Processing Error ("+q+")");return false}return true};W.jsLoader=function(m,l){if(D.isString(m)&&(m!=="")){l=l||{};F.generateId(l);W.kill(l.id);S[l.id].chunks=[{src:m,content:"",type:N.JSNODE,params:{href:m}}];l.underground=true;R(l);i(l.id,l);return l.id}return null};W.cssLoader=function(m,l){if(D.isString(m)&&(m!=="")){l=l||{};F.generateId(l);W.kill(l.id);S[l.id].chunks=[{src:m,content:"",type:N.CSSNODE,params:{href:m}}];l.underground=true;R(l);i(l.id,l);return l.id}return null};W.isAlive=function(l){return(l&&D.isObject(S[l])&&(S[l].chunks.length>0))};W.kill=function(l){if(l&&!D.isObject(S[l])){S[l]={chunks:[],destroyer:null}}else{if(this.isAlive(l)){S[l].chunks=[]}}};W.destroy=function(l){this.kill(l);if(l&&!D.isObject(S[l])){S[l].destroyer.fire(E(l),{})}};W.onDestroy=function(n,l,m){var o=(m?[l,m,true]:[l]);if(D.isObject(S[n])&&D.isObject(S[n].destroyer)){if(D.isObject(m)){S[n].destroyer.subscribe(l,m,true)}else{S[n].destroyer.subscribe(l)}return true}return false};W.init=function(l){l=l||{};l.relative=l.relative||false;P=l};W.firewall=function(p,o,r){var n=null,q=null,l=null;while(p.indexOf("&amp;")>-1){p=p.replace("&amp;","&")}o.proxy=o.proxy||N.proxy;if(D.isFunction(o.firewall)){p=o.firewall.apply(o,[p])}else{if(!o.monolithic&&!r&&o.proxy){l=p.match(G);if(l&&(l[2]!==document.domain)){p=o.proxy+escape(p)}}}return p};W.obj2query=function(n){var l="",m;if(D.isObject(n)){for(key in n){if(n.hasOwnProperty(key)){l+=(l==""?"":"&");l+=key+"="+n[key]}}}return l};W.augmentURI=function(p,l){l=l||{};var q=h(p,this.strictMode),n="";q.queryKey=q.queryKey||{};D.augmentObject(q.queryKey,l,true);if(q.protocol){n+=q.protocol+":"}if(this.strictMode){if(/^(?:[^:\/?#]+:)?\/\//.test(q.source)){n+="//"}}else{if(/^(?:(?![^:@]+:[^:@\/]*@)[^:\/?#.]+:)?\/\//.test(q.source)){n+="//"}}if(q.authority){if(q.userInfo){if(q.user){n+=q.user}if(q.userInfo.indexOf(":")>-1){n+=":"}if(q.password){n+=q.password}n+="@"}if(q.host){n+=q.host}if(q.port){n+=":"+q.port}}if(q.relative){if(q.path){if(q.directory){n+=q.directory}if(q.file){n+=q.file}}n+="?";for(sName in q.queryKey){if(q.queryKey.hasOwnProperty(sName)){n+=sName+"="+q.queryKey[sName]+"&"}}if(q.anchor){n+="#"+q.anchor}}return n};W.toString=function(){return("YUI Dispatcher Plugin")};return W}()})();YAHOO.util.Dispatcher=YAHOO.plugin.Dispatcher;YAHOO.register("dispatcher",YAHOO.plugin.Dispatcher,{version:"2.1",build:"237"});
\ No newline at end of file
Index: openacs-4/packages/messages/www/resources/dispatcher.js
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/resources/dispatcher.js,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/resources/dispatcher.js	31 Aug 2010 21:30:53 -0000	1.1
@@ -0,0 +1,877 @@
+YAHOO.namespace("plugin");
+(function() {
+  var $C = YAHOO.util.Connect,
+      $L = YAHOO.lang,
+      $E = YAHOO.util.Event,
+      $D = YAHOO.util.Dom,
+      $  = YAHOO.util.Dom.get;
+
+  /**
+  * Constants
+  */
+  var constants = { LOADING: 1, DISPATCHED: 2, ERROR: 3, EMPTY: 4, proxy: '/dispatcher.php?uri=', CSSNODE: 1, JSNODE: 2 };
+
+  var reScriptTag = /<script([^>]*)>([\s\S]*?)<\/script>/igm,
+      reScriptTagSrc = /src=(['"]?)([^"']*)\1/i,
+      reScriptTagRel = /rel=(['"]?)([^"']*)\1/i,
+      reLinkTag = /<link([^>]*)(>[\s]*<\/link>|>)/igm,
+      reLinkTagSrc = /href=(['"]?)([^"']*)\1/i,
+      reStyleTag = /<style([^>]*)>([\s\S]*?)<\/style>/igm,
+      reTagParams = new RegExp('([\\w-\.]+)\\s*=\\s*(".*?"|\'.*?\'|\\w+)*', 'im');
+
+  var reCSS3rdFile    = new RegExp('url\\s*\\(([^\\)]*)', 'igm');                // [url(image.gif] - also can include quotes
+
+  var reURI = new RegExp('^((?:http|https)://)((?:\\w+[\.|-]?)*\\w+)(/.*)$', 'i');  // full url: [http://www.domain.com/path/file.html]
+
+  /**
+  * @class Dispatcher
+  */
+  YAHOO.plugin.Dispatcher = function () {
+    var obj = {},
+        _threads = {}, // each thread represent an area...
+        _hashtable = [],
+        _oDefaultConfig = {relative: false, baseURI:document.location.toString()},
+        _loadingClass = 'loading',
+        _classname = 'yui-dispatchable',
+        _reURI = {
+                key: ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],
+                q:   {
+                    name:   "queryKey",
+                    parser: /(?:^|&)([^&=]*)=?([^&]*)/g
+                },
+                parser: {
+                    strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,
+                    loose:  /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/
+                }
+        },
+        _reTrim = /^\s+|\s+$/g;
+
+    function _parseUri (str, strictMode) {
+        var o   = _reURI,
+            m   = o.parser[strictMode?"strict":"loose"].exec(str),
+            uri = {},
+            i   = 14;
+    
+        while (i--) uri[o.key[i]] = m[i] || "";
+        uri[o.q.name] = {};
+        uri[o.key[12]].replace(o.q.parser, function ($0, $1, $2) {
+            if ($1) uri[o.q.name][$1] = $2;
+        });
+        return uri;
+    }; 
+
+    function _globalEval (scriptString) {
+        var h = document.getElementsByTagName('head')[0] || document.documentElement,
+            s = document.createElement('script');
+        scriptString = scriptString.replace (_reTrim, '');
+        if (scriptString) {
+            s.type = "text/javascript";
+            /* hack: IE doesn't support appendChild+document.createTextNode, using .text instead */
+            if (YAHOO.env.ua.ie) {
+                s.text = scriptString;
+            } else {
+                s.appendChild( document.createTextNode( scriptString ) );
+            }
+            h.insertBefore(s, h.firstChild);
+            h.removeChild(s);
+        }
+    }
+
+    // utilities
+    function _eraseQuotes( str ) {
+      if ($L.isString(str)) {
+        str = str.replace(/^\s*(\S*(\s+\S+)*)\s*$/, "$1");  // trim
+        str = str.replace(/^(['|"])*(\S*(?:\s+\S+)*)\1$/, "$2"); // un-quotes
+      }
+      return str;
+    }
+    function _getParams ( str, validator ) {
+        var p = null, r = {};
+        validator = validator || {};
+        // capturing the params into an object literal (r)
+        if($L.isString(str)){
+          while(p = reTagParams.exec(str)){
+            // apply validation if exists, if the value is null will be discarted
+            p[2] = (validator.hasOwnProperty(p[1])?validator[p[1]]:p[2]);
+            if (p[2]) {
+               r[p[1]] = _eraseQuotes(p[2]);
+            }
+            str = str.replace(reTagParams, '');
+          }
+        }
+        return r;
+    }
+    function _baseURI ( uri ) {
+        uri = (($L.isString(uri) && (uri.indexOf('/') > -1))?uri:_oDefaultConfig.baseURI) + ''; // the default base is the current document uri (hack to convert to string)
+        return uri.substr (0, uri.lastIndexOf('/')+1); // forget the file name
+    }
+    function _relativeURI( base, uri ) {
+        // is the url is relative (not http..., not / at the begining)
+        if(uri && !reURI.test(uri) && (uri.indexOf('/') !== 0)){
+          uri = base+uri;
+        }
+        return uri;
+    }
+    function _onStart (config) {
+         /* ARIA: set as a live area */
+         if (config.element && config.element.setAttribute) {
+             config.element.setAttribute ('aria-live', 'polite');
+             config.element.setAttribute ('aria-atomic', 'true'); // the whole area will be updated
+             config.element.setAttribute ('aria-relevant', 'all'); // the whole area will be updated
+             config.element.setAttribute ('aria-busy', 'true'); // starting the loading process
+         }
+         // onStart (before the loading)
+         config.onStart = config.before || config.onStart;
+         if ($L.isFunction(config.onStart)) {
+           config.onStart.apply ( config, [config.element] );
+           config.onStart = null; // because the process method will try to executed again...
+         }
+         // broadcasting the message
+         if (!config.underground && YAHOO.Bubbling) {
+             YAHOO.Bubbling.fire ('onAsyncRequestStart', {
+                element: config.element
+             });
+         }
+    }
+    function _onChange (config, el) {
+         if ($L.isFunction(config.onChange)) {
+           el = el || config.element;
+           config.onChange.apply ( config, [el] );
+         }
+    }
+    function _onLoad (config) {
+         // onLoad (after the execution)
+         config.onLoad = config.after || config.onLoad;
+         if ($L.isFunction(config.onLoad)) {
+           config.onLoad.apply ( config, [config.element] );
+         }
+         //console.log ("config: ", config);
+         if (!config.underground && YAHOO.Bubbling) {
+         //console.log ('el:', (config.tab || config.element));
+             YAHOO.Bubbling.fire ('onAsyncRequestEnd', {
+                element: config.element
+             });
+         }
+         /* ARIA: set as a live area */
+         if (config.element && config.element.setAttribute) {
+            config.element.setAttribute ('aria-busy', 'false'); // ending the loading process
+         }
+    }
+    // private stuff
+    /**
+    * Dispatching the next node of the handle
+    * @public
+    * @param {Object} hd            Thread's handle
+    * @param {Object} config    Used to pass the user configuration thru the chain of execution
+    * @return boolean
+    */
+    function dispatch( hd, config ) {
+      var callback = null, flag = true, node = null, uri = '', i = 0;
+      config = config || {};
+      if (obj.isAlive(hd)) {
+        node = _threads[hd].chunks.shift ();
+        if ($L.isObject(node) && node.src) {
+          // cheching the uri in the hashtable
+          config.hash = _hashtable.length; // default hash (at the end of the table)
+          for (i=0; i<_hashtable.length; i++) {
+            if (_hashtable[i].uri == node.src) {
+                if ((_hashtable[i].status == constants.DISPATCHED) && !config.override) {
+                    // this uri was already dispatched by this plugin and will be discarted...
+                    flag = false;
+                } else {
+                    // the uri already exists in the table
+                }
+                config.hash = i;
+                break;
+            }
+          }
+          if (flag) {
+            // fetching the remote script using the YUI Get Utility
+            uri = obj.firewall (node.src, config, true);
+            if ($L.isString(uri) && (uri !== '')) {
+                _hashtable[config.hash] = {uri: node.src, proxy: uri, status: constants.LOADING};
+                if (node.type === constants.JSNODE) {
+                    obj.area = hd;
+                    obj.destroyer = _threads[hd].destroyer;
+                    config.handle = YAHOO.util.Get.script(uri, { 
+                        onSuccess: function() {
+                            _hashtable[config.hash].status = constants.DISPATCHED;
+                            // broadcasting the message
+                            if (config.rel && YAHOO.Bubbling) {
+                                 YAHOO.Bubbling.fire ('onScriptReady', {
+                                    module: node.rel,
+                                    src: node.src,
+                                    uri: uri,
+                                    hash: config.hash
+                                 });
+                            }
+                            config.hash = null; // resetting the config.hash for the next iteration...
+                            // continue with the thread
+                            dispatch( hd, config );
+                        },
+                        onFailure: function() {
+                            // continue with the thread
+                            dispatch( hd, config );
+                        },
+                        scope: obj,
+                        data: config
+                    });
+                } else if (node.type === constants.CSSNODE) {
+                    YAHOO.util.Get.css(uri, {});
+                    _hashtable[config.hash].status = constants.DISPATCHED;
+                    // continue with the thread
+                    dispatch( hd, config );
+                }
+            }
+          } else {
+            // continue with the thread execution
+            dispatch( hd, config );
+          }
+        }
+        else {
+          // the node represent an inline script (don't have hash value)
+          config.hash = null;
+          exec (hd, node.content, config);
+        }
+      } else {
+        // ending the execution thread
+        obj.kill(hd);
+        _onLoad (config);
+      }
+    }
+
+    /**
+    * Executing a javascript script segment
+    * @public
+    * @param {Object} hd            Thread's handle
+    * @param {string} c             Content to execute
+    * @param {Object} config    User configuration (useful for future implementations)
+    * @return boolean
+    */
+    function exec( hd, c, config ) {
+      var status = constants.EMPTY;
+      if (c && (c !== '')) {
+        try{
+          status = constants.DISPATCHED;
+          // instead send in a variable that points to this object...
+          if (!config.hash || (_hashtable[config.hash].status != constants.DISPATCHED)) {
+             obj.area = hd;
+             obj.destroyer = _threads[hd].destroyer;
+             // you can define your own evaluation routine for inline scripts
+             if ($L.isFunction(config.evalRoutine)) {
+                 config.evalRoutine(c, config);
+             } else {
+                 /* using the new routine to evaluate inline script, no longer using eval */
+                 _globalEval (c);
+             }           
+          }
+        }catch(e){
+          status = constants.ERROR;
+          if ($L.isFunction(config.error)) {
+              config.error.apply ( config, [hd, c, _hashtable, e] );
+          } else {
+              throw new
+                Error ("Dispacher: Script Execution Error ("+e+")");
+          }
+        }
+      }
+      // updating the status of the remote script in the hashtable
+      if ($L.isNumber(config.hash)) {
+        _hashtable[config.hash].status = status;
+        config.hash = null; // resetting the config.hash for the next iteration...
+      }
+      dispatch( hd, config );
+    }
+
+    /**
+    * Display the content inside the element
+    * @public
+    * @param {Object} el            Element reference
+    * @param {string} c             Content to display
+    * @param {Object} config    User configuration (useful for future implementations)
+    * @return boolean
+    */
+    function display( el, c, config ) {
+        config.action = (config.action?config.action:'replace');
+        switch (config.action)
+        {
+            case 'tabview':
+                // onDestroy event... used to release the memory inside the tab...
+                destroy (el.get('contentEl'), config);
+                try { el.set('content', c); } catch (e1) {return false;}
+                _onChange (config, el.get('contentEl'));
+                break;
+            case 'update':
+                c = el.innerHTML + c;
+                try { el.innerHTML = c; } catch (e2) {return false;}
+                _onChange (config, el);
+                break;
+            case 'replace':
+            case 'layout':
+            default:
+                // onDestroy event... used to release the memory inside the element...
+                destroy (el, config);
+                // changing the content
+                try { el.innerHTML = c; } catch (e3) {return false;}
+                _onChange (config, el);
+                break;
+        }
+        return true;
+    }
+    /**
+    * "Destroy Custom Event" will be fired before remove the innerHTML in the displaying process
+    * @public
+    * @param {Object} el        DOM Element reference
+    * @param {Object} config    User configuration (useful for future implementations)
+    * @return void
+    */
+    function destroy( el, config ) {
+        var hd = config.guid, i = 0;
+        if ($L.isObject(_threads[hd].destroyer)) {
+            _threads[hd].destroyer.fire (el, config);
+        }
+        if ($D.inDocument(el)) {
+            // purge the child elements and the events attached...
+            for (i=0;i<el.childNodes.length;i++) {
+              $E.purgeElement ( el.childNodes[i], true );
+            }
+            $D.addClass (el, _classname);
+        }
+        _threads[hd].destroyer = new YAHOO.util.CustomEvent('destroyer');
+        // checking for default onDestroy (usually passed to the methods fetch, delegate or process)
+        if ($L.isFunction(config.onDestroy)) {
+            _threads[hd].destroyer.subscribe (config.onDestroy);
+        }
+        // other subscribers can be add using: YAHOO.plugin.Dispatcher.destroyer.subscribe ( mySubscriber );
+    }
+    /**
+    * Parse the string, remove the script tags, and create the execution thread...
+    * @public
+    * @param {Object} hd            Element reference
+    * @param {string} s             String with the script tags inside...
+    * @return string
+    */
+    function parse( hd, s, config ) {
+        config = config || {};
+        config.uri = config.uri || null;
+        config.relative = config.relative || _oDefaultConfig.relative;
+        var m = true, attr = false,
+            base = _baseURI(config.uri); // calculation of the base path...
+        // searching and cut out all style tags, push them into thread
+        // ej. <style title="" type="text/css"></style>
+        // not supported yet. <!--@import url("http://us.js2.yimg.com/us.js.yimg.com/lib/hdr/ygma_2.19.css");body{margin:0px 4px;}-->
+        s = s.replace(reStyleTag,
+          function (str,p1,p2,offset,s) {
+            // apply the inline style automatically
+            if (p2) {
+                obj.applyCSS(p2, _getParams(p1), config);
+            }
+            return "";
+          }
+        );
+
+        // searching and cut out all Link tags, push them into thread
+        // ej. <link rel="stylesheet" type="text/css" href='/themes/bubbling/css/common.css' />
+        s = s.replace(reLinkTag,
+          function (str,p1,p2,offset,s) {
+            // add a remote style to buffer
+            if(p1){
+              attr = p1.match(reLinkTagSrc);
+              if(attr) {
+                if (config.relative) {
+                    attr[2] = _relativeURI(base, attr[2]); // path correction process...
+                }
+                _threads[hd].chunks.push ({
+                    src: attr[2],
+                    content: '',
+                    type: constants.CSSNODE,
+                    params: _getParams(p1)
+                });
+              }
+            }
+            return "";
+          }
+        );
+
+        // searching and cut out all script tags, push them into thread
+        s = s.replace(reScriptTag,
+          function (str,p1,p2,offset,s) {
+            // add a remote script to buffer
+            if(p1){
+              attr = p1.match(reScriptTagSrc);
+              if(attr) {
+                var rel = p1.match(reScriptTagRel);
+                rel = (rel?rel[2]:null);
+                if (config.relative) {
+                    attr[2] = _relativeURI(base, attr[2]); // path correction process...
+                }
+                _threads[hd].chunks.push ({
+                    src: attr[2],
+                    content: '',
+                    type: constants.JSNODE,
+                    rel: rel,
+                    params: _getParams(p1)
+                });
+              }
+            }
+            // add a inline script to buffer
+            if (p2) {
+                _threads[hd].chunks.push ({
+                    src: null,
+                    content: p2,
+                    type: constants.JSNODE,
+                    params: _getParams(p1)
+                });
+            }
+            return "";
+          }
+        );
+        return s;
+    }
+    
+    // is the bubbling is available, we will define the cache listener...
+    if (YAHOO.Bubbling) {
+        YAHOO.Bubbling.on('onScriptReady', function() {
+            if (this.src && !this.hash) {
+                _hashtable[this.hash].status = constants.DISPATCHED;
+            }
+        });     
+    }
+
+    // public vars
+    obj.area = null; // current thread, useful to get the current DOM element...
+    obj.strictMode = true;
+    obj.destroyer = null; // current destroyer, useful to add new subscriber during the execution...
+    // public methods
+    /**
+    * * Fetching a remote file that will be processed thru this object...
+    * @public
+    * @param {object} el        {HTMLElement | String | Object} The html element that represents the dynamic area.
+    * @param {object} uri       Remote file that will be loaded using AJAX
+    * @param {object} config    Literal object with the user configuration vars
+    * @return object  Reference to the connection handle
+    */
+    obj.fetch = function( el, uri, config ){
+       config = config || {};
+       config.uri = uri;
+       var id = null,
+           u = null,
+           callback = {
+            success: function (o) {
+                if (o.responseText != 'undefined') {
+                    obj.process( el, o.responseText, config, true );
+                }
+                $D.removeClass(el, _loadingClass);
+            },
+            failure:function (o) {
+                if ($L.isFunction(config.onError)) {
+                   config.onError.apply ( config, [config.element, o] );
+                }
+                $D.removeClass(el, _loadingClass);
+            }
+       };
+       if (($L.isObject(el) || (el = $( el ))) && uri) {
+         u = obj.firewall (uri, config);
+         $D.addClass(el, _loadingClass);
+         config.handle = $C.asyncRequest('GET', u, callback);
+         config.element = el;
+         _onStart (config);
+         // broadcasting the message to log the navigation action into the history manager
+         if (YAHOO.Bubbling) {
+            id = config.guid || $E.generateId (el);
+            YAHOO.Bubbling.fire ('onNavigate', {
+                state: id+escape(uri),              
+                control: 'dispatcher',
+                element: el,
+                uri: uri,
+                config: config,
+                restore: function() { 
+                   obj.fetch (el, uri, config);
+                }
+            });
+         }
+         return config.handle;
+       }
+       return null;
+    };
+    /**
+    * * Starting the process for a content...
+    * @public
+    * @param {object} el            {HTMLElement | String | Object} The html element that represents the dynamic area.
+    * @param {string} content       Content to be processed
+    * @param {object} config        Literal object with the user configuration vars
+    * @param {boolean} flag         If the call was internal or external
+    * @return object  reference to the thread handle...
+    */
+    obj.process = function( el, content, config, flag ){
+        var hd = null;
+        config = config || {};
+        if ($L.isObject(el) || (el = $( el ))) {
+            hd = config.guid || $E.generateId (el); // by default, one thread by element, use the GUID to discard this rule...
+            this.kill(hd); // kill the previous process for this handle...
+            config.element = el;
+            config.content = content;
+            config.guid = hd;
+            if (!flag) {
+                _onStart(config);
+            }
+            // processing
+            if (display(el, parse (hd, content, config), config)) {
+                dispatch (hd, config); // starting the execution chain
+            }
+        }
+        return hd;
+    };
+    /**
+    * * TABVIEW: delegate the set content method...
+    * @public
+    * @param {object} tab       reference to the tab...
+    * @param {object} tabview   reference to the tabview...
+    * @param {object} config    Literal object with the user configuration vars
+    * @return void
+    */
+    obj.delegate = function ( tab, tabview, config ) {
+        config = config || {};
+        config.action = 'tabview';
+        config.uri = tab.get ('dataSrc') || null; // getting the base url for the execution...
+        config.tab = tab;
+        tab.loadHandler.success = function(o) {
+            var el = tab.get('contentEl');
+            config.tab = el;
+            config.underground = true;
+            obj.process( tab, o.responseText, config );
+            // broadcasting the message
+            if (YAHOO.Bubbling) {
+                 YAHOO.Bubbling.fire ('onAsyncRequestEnd', {
+                    element: el
+                 });
+            }
+        };
+        tab.on("activeChange", function() {
+            // broadcasting the message
+            if (YAHOO.Bubbling && this.get('active') && tab.get ('dataSrc') && !this.get('cacheData')) {
+                 YAHOO.Bubbling.fire ('onAsyncRequestStart', {
+                    element: this.get('contentEl')
+                 });
+            }                                           
+        });
+        if ($L.isObject(tabview)) {
+            tabview.addTab(tab);
+        }
+    };
+    /**
+    * * addUnit: delegate the addUnit layout method...
+    * @public
+    * @param {object} unit         reference to the unit...
+    * @param {object} layout       reference to the layout...
+    * @param {object} config       Literal object with the user configuration vars
+    * @return object    reference to the unit
+    */
+    obj.addUnit = function ( unit, layout, config ) {
+        var c = config || {}, el;
+        c.action = 'layout';
+        if (!unit || !layout) {
+            return false;
+        }
+        if ($L.isString(unit)) {
+            unit = layout.getUnitByPosition (unit);
+        } else if (unit.position && !layout.getUnitByPosition (unit.position)) {
+            // creating the new unit if the object is a simple literal with the position and 
+            // the position is not filled yet in the layout manager...
+            unit = layout.addUnit(unit);
+        }
+        if (c.uri) {
+            unit.set('dataSrc', c.uri);
+        }
+        if ((c.unit = unit) && (c.uri = c.unit.get ('dataSrc')) && (el = c.unit.body)) {
+            c.underground = true;
+            c._dispatcherConfig = c;
+            c.unit.loadHandler.success = function(o) {
+                obj.process( this.body, o.responseText, this._dispatcherConfig );
+                // broadcasting the message
+                if (YAHOO.Bubbling) {
+                     YAHOO.Bubbling.fire ('onAsyncRequestEnd', {
+                        element: this.body
+                     });
+                }
+            };
+            /* starting the loading process */
+            if (c.unit.loadContent() && YAHOO.Bubbling) {
+                 // broadcasting the message
+                 YAHOO.Bubbling.fire ('onAsyncRequestStart', {
+                    element: el
+                 });
+            }
+        }
+        return c.unit;
+    };
+    /**
+    * * Injecting CSS in the current page
+    * @public
+    * @param {string} cssCode   CSS content that will be injected
+    * @param {object} params    Literal object with the tag configuration params
+    * @param {object} config    Literal object with the dispatcher configuration values
+    * @return boolean  the operation result
+    */
+    obj.applyCSS = function( cssCode, params, config ) {
+        params = params || {};
+        var styleElement = document.createElement("style"), base = params.href || '';
+        // calculation of the base path...
+        config = config || {};
+        var uri = config.uri || _oDefaultConfig.baseURI;
+        config.relative = config.relative || _oDefaultConfig.relative;
+        if (config.relative) {
+            // applying the firewall routine to get the real path and load the css internal resources correctly.
+            uri = obj.firewall (uri, config, true);
+            base = _baseURI(uri); // calculation of the base path...
+            base = _relativeURI(base, params.href); // path correction process...
+        }
+        base = _baseURI(base);
+        // path correction process...
+        // ej: (image.gif) or ("image.gif") or ('image.gif') or (http://fullpath/image.gif)
+        /*  In the case of DXImageTransform.Microsoft.AlphaImageLoader(src='image.png',sizingMethod='scale');
+            The use of the AlphaImageLoader ultimately proved to be
+            an unreliable solution for IE 6 as it never handled relative paths well.
+            It seems like the AlphaImageLoader always expects the paths to be relative
+            to the page the CSS is included in, rather than relative to the CSS file itself. */
+        cssCode = cssCode.replace(reCSS3rdFile,
+          function (str,p1,offset,s) {
+            p1 = _eraseQuotes (p1);
+            p1 = 'url('+_relativeURI(base, p1);
+            return p1;
+          }
+        );
+
+        // the CSS is ready...
+        styleElement.type = "text/css";
+        if ($L.isObject(styleElement.styleSheet)) {
+          styleElement.styleSheet.cssText = cssCode;
+        } else {
+          styleElement.appendChild(document.createTextNode(cssCode));
+        }
+        try{
+          document.getElementsByTagName("head")[0].appendChild(styleElement);
+        }catch(e){
+          throw new
+            Error ("Dispacher: CSS Processing Error ("+e+")");
+          return false;
+        }
+        return true;
+    };
+    /**
+    * * Fetching a remote javascript file that will be processed thru this object...
+    * @public
+    * @param {object} uri       Remote js file that will be loaded using AJAX
+    * @param {object} config    Literal object with the user configuration vars
+    * @return string  ID reference to the new dispatcher's thread
+    */
+    obj.jsLoader = function( uri, config ){
+       if ($L.isString(uri) && (uri !== '')) {
+         config = config || {};
+         $E.generateId ( config ); // generating an unique ID for the thread (config.id)
+         obj.kill (config.id);
+         // add a remote script to buffer
+         _threads[config.id].chunks = [{
+            src: uri,
+            content: '',
+            type: constants.JSNODE,
+            params: {href: uri}
+         }];
+         config.underground = true;
+         _onStart(config);
+         dispatch (config.id, config); // starting the execution chain
+         return config.id;
+       }
+       return null;
+    };
+    /**
+    * * Fetching a remote CSS file that will be processed thru this object...
+    * @public
+    * @param {object} uri       Remote CSS file that will be loaded using AJAX
+    * @param {object} config    Literal object with the user configuration vars
+    * @return string  ID reference to the new dispatcher's thread
+    */
+    obj.cssLoader = function( uri, config ){
+       if ($L.isString(uri) && (uri !== '')) {
+         config = config || {};
+         $E.generateId ( config ); // generating an unique ID for the thread (config.id)
+         obj.kill (config.id);
+         // add a remote script to buffer
+         _threads[config.id].chunks = [{
+            src: uri,
+            content: '',
+            type: constants.CSSNODE,
+            params: {href: uri}
+         }];
+         config.underground = true;
+         _onStart(config);
+         dispatch (config.id, config); // starting the execution chain
+         return config.id;
+       }
+       return null;
+    };
+    /**
+    * * Verify if the a process is still alive
+    * @public
+    * @param {object} hd   Process handle
+    * @return boolean
+    */
+    obj.isAlive = function ( hd ) {
+        return (hd && $L.isObject(_threads[hd]) && (_threads[hd].chunks.length > 0));
+    };
+    /**
+    * * Kill a process...
+    * @public
+    * @param {object} handle   Process handle
+    * @return void
+    */
+    obj.kill = function ( hd ) {
+        if (hd && !$L.isObject(_threads[hd])) {
+            _threads[hd] = {chunks: [], destroyer: null};
+        } else if (this.isAlive (hd)) {
+            _threads[hd].chunks = []; // discarding the handle...
+        }
+    };
+    /**
+    * * destroy an area...
+    * @public
+    * @param {object} handle   Process handle
+    * @return void
+    */
+    obj.destroy = function ( hd ) {
+        this.kill(hd);
+        if (hd && !$L.isObject(_threads[hd])) {
+            _threads[hd].destroyer.fire( $(hd), {} );
+        }
+    };
+    /* * onDestroy - subscribe a new destroyer for a certain area...
+    * @public
+    * @param {object} handle   Process handle
+    * @return boolean
+    */
+    obj.onDestroy = function ( hd, bh, scope ) {
+        var params = (scope?[bh, scope, true]:[bh]); // params for scope corrections
+        if ($L.isObject(_threads[hd]) && $L.isObject(_threads[hd].destroyer)) {
+            if ($L.isObject(scope)) {
+              _threads[hd].destroyer.subscribe(bh, scope, true);  // correcting the default scope
+            } else {
+              _threads[hd].destroyer.subscribe(bh);  // use the default scope
+            }
+            return true;
+        }
+        return false;
+    };
+    obj.init = function (c) {
+        c = c || {};
+        c.relative = c.relative || false;
+        _oDefaultConfig = c;
+    };
+    /**
+    * Analyze the uri before start the downloading process (if the uri isn't in over the current domain name, the dispatcher can use a proxy)
+    * @public
+    * @param {String} uri         Remote URL
+    * @param {Object} config      Used to pass the user configuration thru the chain of execution
+    * @param {Boolean} monolitic  Do not use proxy
+    * @return String
+    */
+    obj.firewall = function( uri, config, monolitic ) {
+        var sDomain = null,
+            sProtocol = null,
+            m = null;
+        // AJAX url don't work with &amp;
+        while (uri.indexOf ( '&amp;' ) > -1) {
+            uri = uri.replace ( '&amp;', '&' );
+        }
+        // defining the proxy for cross-domain scripting...
+        config.proxy = config.proxy || constants.proxy;
+        if ($L.isFunction(config.firewall)) {
+          // external verification only...
+          uri = config.firewall.apply ( config, [uri] );
+        } else {
+            // internal verification only...
+            // monolithic execution discard the cross-domain capabilities
+            if (!config.monolithic && !monolitic && config.proxy) {
+                m = uri.match(reURI); // checking the RE to verify if the url is external...
+                if (m && (m[2] !== document.domain)) {
+                    // the uri is external, escaping especial chars and use a proxy
+                    uri = config.proxy + escape(uri);
+                }
+            }
+        }
+        return uri;
+    };
+  /**
+    * converting a literal object into a query string
+    * @public
+    * @param {object} params       Literal object to create the url querystring {'param1':'value1','param2':'value2'}
+    * @return string
+    */
+    obj.obj2query = function( params ) {
+        var u = '', indx;
+        if ($L.isObject(params)) {
+            for (key in params) {
+                if (params.hasOwnProperty(key)) {
+                    u += (u==''?'':'&');
+                    u += key+'='+params[key];
+                }
+            }
+        }
+        return u;
+    };
+  /**
+    * augment an url with more parameters, overriding...
+    * @public
+    * @param {string} url 
+    * @param {string|array} m   MoreParams: string: param1=value1&param2=value2 or array: {'param1':'value1','param2':'value2'}
+    * @return string
+    */
+    obj.augmentURI = function( url, m ) {
+        m = m || {};
+        var o = _parseUri(url, this.strictMode),
+            u = '';
+        o.queryKey = o.queryKey || {};
+        $L.augmentObject(o.queryKey, m, true);
+        if (o.protocol) u += o.protocol + ':';
+        if (this.strictMode) {
+            if (/^(?:[^:\/?#]+:)?\/\//.test(o.source)) u += '//';
+        } else {
+            if (/^(?:(?![^:@]+:[^:@\/]*@)[^:\/?#.]+:)?\/\//.test(o.source)) u += '//';
+        }
+        if (o.authority) {
+            if (o.userInfo) {
+                if (o.user) u += o.user;
+                if (o.userInfo.indexOf(':') > -1) u += ':';
+                if (o.password) u += o.password;
+                u += '@';
+            }
+            if (o.host) u += o.host;
+            if (o.port) u += ':' + o.port;
+        }
+        if (o.relative) {
+            if (o.path) {
+                if (o.directory) u += o.directory;
+                if (o.file) u += o.file;
+            }
+            u += '?';// + o.query;
+            for (sName in o.queryKey) {
+                if (o.queryKey.hasOwnProperty(sName)) {
+                    u += sName+'='+o.queryKey[sName]+'&';
+                }
+            }
+            if (o.anchor) u += '#' + o.anchor;
+        }
+        return u;
+    };
+    /**
+    * @method toString
+    * @description Returns a string representing the dispatcher plugin.
+    * @return {String}
+    */
+    obj.toString = function() {
+        return ("YUI Dispatcher Plugin");
+    };
+    return obj;
+  }();
+})();
+YAHOO.util.Dispatcher = YAHOO.plugin.Dispatcher; // deprecated: backward compatibility issue...
+YAHOO.register("dispatcher", YAHOO.plugin.Dispatcher, {version: "2.1", build: "237"}); 
Index: openacs-4/packages/messages/www/resources/dragdrop.js
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/resources/dragdrop.js,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/resources/dragdrop.js	31 Aug 2010 21:30:54 -0000	1.1
@@ -0,0 +1,967 @@
+if(Object.isUndefined(Effect))
+  throw("dragdrop.js requires including script.aculo.us' effects.js library");
+
+var Droppables = {
+  drops: [],
+
+  remove: function(element) {
+    this.drops = this.drops.reject(function(d) { return d.element==$(element) });
+  },
+
+  add: function(element) {
+    element = $(element);
+    var options = Object.extend({
+      greedy:     true,
+      hoverclass: null,
+      tree:       false
+    }, arguments[1] || { });
+
+    // cache containers
+    if(options.containment) {
+      options._containers = [];
+      var containment = options.containment;
+      if(Object.isArray(containment)) {
+        containment.each( function(c) { options._containers.push($(c)) });
+      } else {
+        options._containers.push($(containment));
+      }
+    }
+    
+    if(options.accept) options.accept = [options.accept].flatten();
+
+    Element.makePositioned(element); // fix IE
+    options.element = element;
+
+    this.drops.push(options);
+  },
+  
+  findDeepestChild: function(drops) {
+    deepest = drops[0];
+      
+    for (i = 1; i < drops.length; ++i)
+      if (Element.isParent(drops[i].element, deepest.element))
+        deepest = drops[i];
+    
+    return deepest;
+  },
+
+  isContained: function(element, drop) {
+    var containmentNode;
+    if(drop.tree) {
+      containmentNode = element.treeNode; 
+    } else {
+      containmentNode = element.parentNode;
+    }
+    return drop._containers.detect(function(c) { return containmentNode == c });
+  },
+  
+  isAffected: function(point, element, drop) {
+    return (
+      (drop.element!=element) &&
+      ((!drop._containers) ||
+        this.isContained(element, drop)) &&
+      ((!drop.accept) ||
+        (Element.classNames(element).detect( 
+          function(v) { return drop.accept.include(v) } ) )) &&
+      Position.within(drop.element, point[0], point[1]) );
+  },
+
+  deactivate: function(drop) {
+    if(drop.hoverclass)
+      Element.removeClassName(drop.element, drop.hoverclass);
+    this.last_active = null;
+  },
+
+  activate: function(drop) {
+    if(drop.hoverclass)
+      Element.addClassName(drop.element, drop.hoverclass);
+    this.last_active = drop;
+  },
+
+  show: function(point, element) {
+    if(!this.drops.length) return;
+    var drop, affected = [];
+    
+    this.drops.each( function(drop) {
+      if(Droppables.isAffected(point, element, drop))
+        affected.push(drop);
+    });
+        
+    if(affected.length>0)
+      drop = Droppables.findDeepestChild(affected);
+
+    if(this.last_active && this.last_active != drop) this.deactivate(this.last_active);
+    if (drop) {
+      Position.within(drop.element, point[0], point[1]);
+      if(drop.onHover)
+        drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
+      
+      if (drop != this.last_active) Droppables.activate(drop);
+    }
+  },
+
+  fire: function(event, element) {
+    if(!this.last_active) return;
+    Position.prepare();
+
+    if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
+      if (this.last_active.onDrop) {
+        this.last_active.onDrop(element, this.last_active.element, event); 
+        return true; 
+      }
+  },
+
+  reset: function() {
+    if(this.last_active)
+      this.deactivate(this.last_active);
+  }
+};
+
+var Draggables = {
+  drags: [],
+  observers: [],
+  
+  register: function(draggable) {
+    if(this.drags.length == 0) {
+      this.eventMouseUp   = this.endDrag.bindAsEventListener(this);
+      this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
+      this.eventKeypress  = this.keyPress.bindAsEventListener(this);
+      
+      Event.observe(document, "mouseup", this.eventMouseUp);
+      Event.observe(document, "mousemove", this.eventMouseMove);
+      Event.observe(document, "keypress", this.eventKeypress);
+    }
+    this.drags.push(draggable);
+  },
+  
+  unregister: function(draggable) {
+    this.drags = this.drags.reject(function(d) { return d==draggable });
+    if(this.drags.length == 0) {
+      Event.stopObserving(document, "mouseup", this.eventMouseUp);
+      Event.stopObserving(document, "mousemove", this.eventMouseMove);
+      Event.stopObserving(document, "keypress", this.eventKeypress);
+    }
+  },
+  
+  activate: function(draggable) {
+    if(draggable.options.delay) { 
+      this._timeout = setTimeout(function() { 
+        Draggables._timeout = null; 
+        window.focus(); 
+        Draggables.activeDraggable = draggable; 
+      }.bind(this), draggable.options.delay); 
+    } else {
+      window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
+      this.activeDraggable = draggable;
+    }
+  },
+  
+  deactivate: function() {
+    this.activeDraggable = null;
+  },
+  
+  updateDrag: function(event) {
+    if(!this.activeDraggable) return;
+    var pointer = [Event.pointerX(event), Event.pointerY(event)];
+    // Mozilla-based browsers fire successive mousemove events with
+    // the same coordinates, prevent needless redrawing (moz bug?)
+    if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
+    this._lastPointer = pointer;
+    
+    this.activeDraggable.updateDrag(event, pointer);
+  },
+  
+  endDrag: function(event) {
+    if(this._timeout) { 
+      clearTimeout(this._timeout); 
+      this._timeout = null; 
+    }
+    if(!this.activeDraggable) return;
+    this._lastPointer = null;
+    this.activeDraggable.endDrag(event);
+    this.activeDraggable = null;
+  },
+  
+  keyPress: function(event) {
+    if(this.activeDraggable)
+      this.activeDraggable.keyPress(event);
+  },
+  
+  addObserver: function(observer) {
+    this.observers.push(observer);
+    this._cacheObserverCallbacks();
+  },
+  
+  removeObserver: function(element) {  // element instead of observer fixes mem leaks
+    this.observers = this.observers.reject( function(o) { return o.element==element });
+    this._cacheObserverCallbacks();
+  },
+  
+  notify: function(eventName, draggable, event) {  // 'onStart', 'onEnd', 'onDrag'
+    if(this[eventName+'Count'] > 0)
+      this.observers.each( function(o) {
+        if(o[eventName]) o[eventName](eventName, draggable, event);
+      });
+    if(draggable.options[eventName]) draggable.options[eventName](draggable, event);
+  },
+  
+  _cacheObserverCallbacks: function() {
+    ['onStart','onEnd','onDrag'].each( function(eventName) {
+      Draggables[eventName+'Count'] = Draggables.observers.select(
+        function(o) { return o[eventName]; }
+      ).length;
+    });
+  }
+};
+
+/*--------------------------------------------------------------------------*/
+
+var Draggable = Class.create({
+  initialize: function(element) {
+    var defaults = {
+      handle: false,
+      reverteffect: function(element, top_offset, left_offset) {
+        var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
+        new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur,
+          queue: {scope:'_draggable', position:'end'}
+        });
+      },
+      endeffect: function(element) {
+        var toOpacity = Object.isNumber(element._opacity) ? element._opacity : 1.0;
+        new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity, 
+          queue: {scope:'_draggable', position:'end'},
+          afterFinish: function(){ 
+            Draggable._dragging[element] = false 
+          }
+        }); 
+      },
+      zindex: 1000,
+      revert: false,
+      quiet: false,
+      scroll: false,
+      scrollSensitivity: 20,
+      scrollSpeed: 15,
+      snap: false,  // false, or xy or [x,y] or function(x,y){ return [x,y] }
+      delay: 0
+    };
+    
+    if(!arguments[1] || Object.isUndefined(arguments[1].endeffect))
+      Object.extend(defaults, {
+        starteffect: function(element) {
+          element._opacity = Element.getOpacity(element);
+          Draggable._dragging[element] = true;
+          new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7}); 
+        }
+      });
+    
+    var options = Object.extend(defaults, arguments[1] || { });
+
+    this.element = $(element);
+    
+    if(options.handle && Object.isString(options.handle))
+      this.handle = this.element.down('.'+options.handle, 0);
+    
+    if(!this.handle) this.handle = $(options.handle);
+    if(!this.handle) this.handle = this.element;
+    
+    if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) {
+      options.scroll = $(options.scroll);
+      this._isScrollChild = Element.childOf(this.element, options.scroll);
+    }
+
+    Element.makePositioned(this.element); // fix IE    
+
+    this.options  = options;
+    this.dragging = false;   
+
+    this.eventMouseDown = this.initDrag.bindAsEventListener(this);
+    Event.observe(this.handle, "mousedown", this.eventMouseDown);
+    
+    Draggables.register(this);
+  },
+  
+  destroy: function() {
+    Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
+    Draggables.unregister(this);
+  },
+  
+  currentDelta: function() {
+    return([
+      parseInt(Element.getStyle(this.element,'left') || '0'),
+      parseInt(Element.getStyle(this.element,'top') || '0')]);
+  },
+  
+  initDrag: function(event) {
+    if(!Object.isUndefined(Draggable._dragging[this.element]) &&
+      Draggable._dragging[this.element]) return;
+    if(Event.isLeftClick(event)) {    
+      // abort on form elements, fixes a Firefox issue
+      var src = Event.element(event);
+      if((tag_name = src.tagName.toUpperCase()) && (
+        tag_name=='INPUT' ||
+        tag_name=='SELECT' ||
+        tag_name=='OPTION' ||
+        tag_name=='BUTTON' ||
+        tag_name=='TEXTAREA')) return;
+        
+      var pointer = [Event.pointerX(event), Event.pointerY(event)];
+      var pos     = Position.cumulativeOffset(this.element);
+      this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });
+      
+      Draggables.activate(this);
+      Event.stop(event);
+    }
+  },
+  
+  startDrag: function(event) {
+    this.dragging = true;
+    if(!this.delta)
+      this.delta = this.currentDelta();
+    
+    if(this.options.zindex) {
+      this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
+      this.element.style.zIndex = this.options.zindex;
+    }
+    
+    if(this.options.ghosting) {
+      this._clone = this.element.cloneNode(true);
+      this._originallyAbsolute = (this.element.getStyle('position') == 'absolute');
+      if (!this._originallyAbsolute)
+        Position.absolutize(this.element);
+      this.element.parentNode.insertBefore(this._clone, this.element);
+    }
+    
+    if(this.options.scroll) {
+      if (this.options.scroll == window) {
+        var where = this._getWindowScroll(this.options.scroll);
+        this.originalScrollLeft = where.left;
+        this.originalScrollTop = where.top;
+      } else {
+        this.originalScrollLeft = this.options.scroll.scrollLeft;
+        this.originalScrollTop = this.options.scroll.scrollTop;
+      }
+    }
+    
+    Draggables.notify('onStart', this, event);
+        
+    if(this.options.starteffect) this.options.starteffect(this.element);
+  },
+  
+  updateDrag: function(event, pointer) {
+    if(!this.dragging) this.startDrag(event);
+    
+    if(!this.options.quiet){
+      Position.prepare();
+      Droppables.show(pointer, this.element);
+    }
+    
+    Draggables.notify('onDrag', this, event);
+    
+    this.draw(pointer);
+    if(this.options.change) this.options.change(this);
+    
+    if(this.options.scroll) {
+      this.stopScrolling();
+      
+      var p;
+      if (this.options.scroll == window) {
+        with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; }
+      } else {
+        p = Position.page(this.options.scroll);
+        p[0] += this.options.scroll.scrollLeft + Position.deltaX;
+        p[1] += this.options.scroll.scrollTop + Position.deltaY;
+        p.push(p[0]+this.options.scroll.offsetWidth);
+        p.push(p[1]+this.options.scroll.offsetHeight);
+      }
+      var speed = [0,0];
+      if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity);
+      if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity);
+      if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity);
+      if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity);
+      this.startScrolling(speed);
+    }
+    
+    // fix AppleWebKit rendering
+    if(Prototype.Browser.WebKit) window.scrollBy(0,0);
+    
+    Event.stop(event);
+  },
+  
+  finishDrag: function(event, success) {
+    this.dragging = false;
+    
+    if(this.options.quiet){
+      Position.prepare();
+      var pointer = [Event.pointerX(event), Event.pointerY(event)];
+      Droppables.show(pointer, this.element);
+    }
+
+    if(this.options.ghosting) {
+      if (!this._originallyAbsolute)
+        Position.relativize(this.element);
+      delete this._originallyAbsolute;
+      Element.remove(this._clone);
+      this._clone = null;
+    }
+
+    var dropped = false; 
+    if(success) { 
+      dropped = Droppables.fire(event, this.element); 
+      if (!dropped) dropped = false; 
+    }
+    if(dropped && this.options.onDropped) this.options.onDropped(this.element);
+    Draggables.notify('onEnd', this, event);
+
+    var revert = this.options.revert;
+    if(revert && Object.isFunction(revert)) revert = revert(this.element);
+    
+    var d = this.currentDelta();
+    if(revert && this.options.reverteffect) {
+      if (dropped == 0 || revert != 'failure')
+        this.options.reverteffect(this.element,
+          d[1]-this.delta[1], d[0]-this.delta[0]);
+    } else {
+      this.delta = d;
+    }
+
+    if(this.options.zindex)
+      this.element.style.zIndex = this.originalZ;
+
+    if(this.options.endeffect) 
+      this.options.endeffect(this.element);
+      
+    Draggables.deactivate(this);
+    Droppables.reset();
+  },
+  
+  keyPress: function(event) {
+    if(event.keyCode!=Event.KEY_ESC) return;
+    this.finishDrag(event, false);
+    Event.stop(event);
+  },
+  
+  endDrag: function(event) {
+    if(!this.dragging) return;
+    this.stopScrolling();
+    this.finishDrag(event, true);
+    Event.stop(event);
+  },
+  
+  draw: function(point) {
+    var pos = Position.cumulativeOffset(this.element);
+    if(this.options.ghosting) {
+      var r   = Position.realOffset(this.element);
+      pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY;
+    }
+    
+    var d = this.currentDelta();
+    pos[0] -= d[0]; pos[1] -= d[1];
+    
+    if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) {
+      pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;
+      pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
+    }
+    
+    var p = [0,1].map(function(i){ 
+      return (point[i]-pos[i]-this.offset[i]) 
+    }.bind(this));
+    
+    if(this.options.snap) {
+      if(Object.isFunction(this.options.snap)) {
+        p = this.options.snap(p[0],p[1],this);
+      } else {
+      if(Object.isArray(this.options.snap)) {
+        p = p.map( function(v, i) {
+          return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this));
+      } else {
+        p = p.map( function(v) {
+          return (v/this.options.snap).round()*this.options.snap }.bind(this));
+      }
+    }}
+    
+    var style = this.element.style;
+    if((!this.options.constraint) || (this.options.constraint=='horizontal'))
+      style.left = p[0] + "px";
+    if((!this.options.constraint) || (this.options.constraint=='vertical'))
+      style.top  = p[1] + "px";
+    
+    if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
+  },
+  
+  stopScrolling: function() {
+    if(this.scrollInterval) {
+      clearInterval(this.scrollInterval);
+      this.scrollInterval = null;
+      Draggables._lastScrollPointer = null;
+    }
+  },
+  
+  startScrolling: function(speed) {
+    if(!(speed[0] || speed[1])) return;
+    this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed];
+    this.lastScrolled = new Date();
+    this.scrollInterval = setInterval(this.scroll.bind(this), 10);
+  },
+  
+  scroll: function() {
+    var current = new Date();
+    var delta = current - this.lastScrolled;
+    this.lastScrolled = current;
+    if(this.options.scroll == window) {
+      with (this._getWindowScroll(this.options.scroll)) {
+        if (this.scrollSpeed[0] || this.scrollSpeed[1]) {
+          var d = delta / 1000;
+          this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] );
+        }
+      }
+    } else {
+      this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;
+      this.options.scroll.scrollTop  += this.scrollSpeed[1] * delta / 1000;
+    }
+    
+    Position.prepare();
+    Droppables.show(Draggables._lastPointer, this.element);
+    Draggables.notify('onDrag', this);
+    if (this._isScrollChild) {
+      Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);
+      Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;
+      Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;
+      if (Draggables._lastScrollPointer[0] < 0)
+        Draggables._lastScrollPointer[0] = 0;
+      if (Draggables._lastScrollPointer[1] < 0)
+        Draggables._lastScrollPointer[1] = 0;
+      this.draw(Draggables._lastScrollPointer);
+    }
+    
+    if(this.options.change) this.options.change(this);
+  },
+  
+  _getWindowScroll: function(w) {
+    var T, L, W, H;
+    with (w.document) {
+      if (w.document.documentElement && documentElement.scrollTop) {
+        T = documentElement.scrollTop;
+        L = documentElement.scrollLeft;
+      } else if (w.document.body) {
+        T = body.scrollTop;
+        L = body.scrollLeft;
+      }
+      if (w.innerWidth) {
+        W = w.innerWidth;
+        H = w.innerHeight;
+      } else if (w.document.documentElement && documentElement.clientWidth) {
+        W = documentElement.clientWidth;
+        H = documentElement.clientHeight;
+      } else {
+        W = body.offsetWidth;
+        H = body.offsetHeight;
+      }
+    }
+    return { top: T, left: L, width: W, height: H };
+  }
+});
+
+Draggable._dragging = { };
+
+/*--------------------------------------------------------------------------*/
+
+var SortableObserver = Class.create({
+  initialize: function(element, observer) {
+    this.element   = $(element);
+    this.observer  = observer;
+    this.lastValue = Sortable.serialize(this.element);
+  },
+  
+  onStart: function() {
+    this.lastValue = Sortable.serialize(this.element);
+  },
+  
+  onEnd: function() {
+    Sortable.unmark();
+    if(this.lastValue != Sortable.serialize(this.element))
+      this.observer(this.element)
+  }
+});
+
+var Sortable = {
+  SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/,
+  
+  sortables: { },
+  
+  _findRootElement: function(element) {
+    while (element.tagName.toUpperCase() != "BODY") {  
+      if(element.id && Sortable.sortables[element.id]) return element;
+      element = element.parentNode;
+    }
+  },
+
+  options: function(element) {
+    element = Sortable._findRootElement($(element));
+    if(!element) return;
+    return Sortable.sortables[element.id];
+  },
+  
+  destroy: function(element){
+    element = $(element);
+    var s = Sortable.sortables[element.id];
+    
+    if(s) {
+      Draggables.removeObserver(s.element);
+      s.droppables.each(function(d){ Droppables.remove(d) });
+      s.draggables.invoke('destroy');
+      
+      delete Sortable.sortables[s.element.id];
+    }
+  },
+
+  create: function(element) {
+    element = $(element);
+    var options = Object.extend({ 
+      element:     element,
+      tag:         'li',       // assumes li children, override with tag: 'tagname'
+      dropOnEmpty: false,
+      tree:        false,
+      treeTag:     'ul',
+      overlap:     'vertical', // one of 'vertical', 'horizontal'
+      constraint:  'vertical', // one of 'vertical', 'horizontal', false
+      containment: element,    // also takes array of elements (or id's); or false
+      handle:      false,      // or a CSS class
+      only:        false,
+      delay:       0,
+      hoverclass:  null,
+      ghosting:    false,
+      quiet:       false, 
+      scroll:      false,
+      scrollSensitivity: 20,
+      scrollSpeed: 15,
+      format:      this.SERIALIZE_RULE,
+      
+      // these take arrays of elements or ids and can be 
+      // used for better initialization performance
+      elements:    false,
+      handles:     false,
+      
+      onChange:    Prototype.emptyFunction,
+      onUpdate:    Prototype.emptyFunction
+    }, arguments[1] || { });
+
+    // clear any old sortable with same element
+    this.destroy(element);
+
+    // build options for the draggables
+    var options_for_draggable = {
+      revert:      true,
+      quiet:       options.quiet,
+      scroll:      options.scroll,
+      scrollSpeed: options.scrollSpeed,
+      scrollSensitivity: options.scrollSensitivity,
+      delay:       options.delay,
+      ghosting:    options.ghosting,
+      constraint:  options.constraint,
+      handle:      options.handle };
+
+    if(options.starteffect)
+      options_for_draggable.starteffect = options.starteffect;
+
+    if(options.reverteffect)
+      options_for_draggable.reverteffect = options.reverteffect;
+    else
+      if(options.ghosting) options_for_draggable.reverteffect = function(element) {
+        element.style.top  = 0;
+        element.style.left = 0;
+      };
+
+    if(options.endeffect)
+      options_for_draggable.endeffect = options.endeffect;
+
+    if(options.zindex)
+      options_for_draggable.zindex = options.zindex;
+
+    // build options for the droppables  
+    var options_for_droppable = {
+      overlap:     options.overlap,
+      containment: options.containment,
+      tree:        options.tree,
+      hoverclass:  options.hoverclass,
+      onHover:     Sortable.onHover
+    };
+    
+    var options_for_tree = {
+      onHover:      Sortable.onEmptyHover,
+      overlap:      options.overlap,
+      containment:  options.containment,
+      hoverclass:   options.hoverclass
+    };
+
+    // fix for gecko engine
+    Element.cleanWhitespace(element); 
+
+    options.draggables = [];
+    options.droppables = [];
+
+    // drop on empty handling
+    if(options.dropOnEmpty || options.tree) {
+      Droppables.add(element, options_for_tree);
+      options.droppables.push(element);
+    }
+
+    (options.elements || this.findElements(element, options) || []).each( function(e,i) {
+      var handle = options.handles ? $(options.handles[i]) :
+        (options.handle ? $(e).select('.' + options.handle)[0] : e); 
+      options.draggables.push(
+        new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
+      Droppables.add(e, options_for_droppable);
+      if(options.tree) e.treeNode = element;
+      options.droppables.push(e);      
+    });
+    
+    if(options.tree) {
+      (Sortable.findTreeElements(element, options) || []).each( function(e) {
+        Droppables.add(e, options_for_tree);
+        e.treeNode = element;
+        options.droppables.push(e);
+      });
+    }
+
+    // keep reference
+    this.sortables[element.id] = options;
+
+    // for onupdate
+    Draggables.addObserver(new SortableObserver(element, options.onUpdate));
+
+  },
+
+  // return all suitable-for-sortable elements in a guaranteed order
+  findElements: function(element, options) {
+    return Element.findChildren(
+      element, options.only, options.tree ? true : false, options.tag);
+  },
+  
+  findTreeElements: function(element, options) {
+    return Element.findChildren(
+      element, options.only, options.tree ? true : false, options.treeTag);
+  },
+
+  onHover: function(element, dropon, overlap) {
+    if(Element.isParent(dropon, element)) return;
+
+    if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) {
+      return;
+    } else if(overlap>0.5) {
+      Sortable.mark(dropon, 'before');
+      if(dropon.previousSibling != element) {
+        var oldParentNode = element.parentNode;
+        element.style.visibility = "hidden"; // fix gecko rendering
+        dropon.parentNode.insertBefore(element, dropon);
+        if(dropon.parentNode!=oldParentNode) 
+          Sortable.options(oldParentNode).onChange(element);
+        Sortable.options(dropon.parentNode).onChange(element);
+      }
+    } else {
+      Sortable.mark(dropon, 'after');
+      var nextElement = dropon.nextSibling || null;
+      if(nextElement != element) {
+        var oldParentNode = element.parentNode;
+        element.style.visibility = "hidden"; // fix gecko rendering
+        dropon.parentNode.insertBefore(element, nextElement);
+        if(dropon.parentNode!=oldParentNode) 
+          Sortable.options(oldParentNode).onChange(element);
+        Sortable.options(dropon.parentNode).onChange(element);
+      }
+    }
+  },
+  
+  onEmptyHover: function(element, dropon, overlap) {
+    var oldParentNode = element.parentNode;
+    var droponOptions = Sortable.options(dropon);
+        
+    if(!Element.isParent(dropon, element)) {
+      var index;
+      
+      var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only});
+      var child = null;
+            
+      if(children) {
+        var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);
+        
+        for (index = 0; index < children.length; index += 1) {
+          if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) {
+            offset -= Element.offsetSize (children[index], droponOptions.overlap);
+          } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) {
+            child = index + 1 < children.length ? children[index + 1] : null;
+            break;
+          } else {
+            child = children[index];
+            break;
+          }
+        }
+      }
+      
+      dropon.insertBefore(element, child);
+      
+      Sortable.options(oldParentNode).onChange(element);
+      droponOptions.onChange(element);
+    }
+  },
+
+  unmark: function() {
+    if(Sortable._marker) Sortable._marker.hide();
+  },
+
+  mark: function(dropon, position) {
+    // mark on ghosting only
+    var sortable = Sortable.options(dropon.parentNode);
+    if(sortable && !sortable.ghosting) return; 
+
+    if(!Sortable._marker) {
+      Sortable._marker = 
+        ($('dropmarker') || Element.extend(document.createElement('DIV'))).
+          hide().addClassName('dropmarker').setStyle({position:'absolute'});
+      document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
+    }    
+    var offsets = Position.cumulativeOffset(dropon);
+    Sortable._marker.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'});
+    
+    if(position=='after')
+      if(sortable.overlap == 'horizontal') 
+        Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'});
+      else
+        Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'});
+    
+    Sortable._marker.show();
+  },
+  
+  _tree: function(element, options, parent) {
+    var children = Sortable.findElements(element, options) || [];
+  
+    for (var i = 0; i < children.length; ++i) {
+      var match = children[i].id.match(options.format);
+
+      if (!match) continue;
+      
+      var child = {
+        id: encodeURIComponent(match ? match[1] : null),
+        element: element,
+        parent: parent,
+        children: [],
+        position: parent.children.length,
+        container: $(children[i]).down(options.treeTag)
+      };
+      
+      /* Get the element containing the children and recurse over it */
+      if (child.container)
+        this._tree(child.container, options, child);
+      
+      parent.children.push (child);
+    }
+
+    return parent; 
+  },
+
+  tree: function(element) {
+    element = $(element);
+    var sortableOptions = this.options(element);
+    var options = Object.extend({
+      tag: sortableOptions.tag,
+      treeTag: sortableOptions.treeTag,
+      only: sortableOptions.only,
+      name: element.id,
+      format: sortableOptions.format
+    }, arguments[1] || { });
+    
+    var root = {
+      id: null,
+      parent: null,
+      children: [],
+      container: element,
+      position: 0
+    };
+    
+    return Sortable._tree(element, options, root);
+  },
+
+  /* Construct a [i] index for a particular node */
+  _constructIndex: function(node) {
+    var index = '';
+    do {
+      if (node.id) index = '[' + node.position + ']' + index;
+    } while ((node = node.parent) != null);
+    return index;
+  },
+
+  sequence: function(element) {
+    element = $(element);
+    var options = Object.extend(this.options(element), arguments[1] || { });
+    
+    return $(this.findElements(element, options) || []).map( function(item) {
+      return item.id.match(options.format) ? item.id.match(options.format)[1] : '';
+    });
+  },
+
+  setSequence: function(element, new_sequence) {
+    element = $(element);
+    var options = Object.extend(this.options(element), arguments[2] || { });
+    
+    var nodeMap = { };
+    this.findElements(element, options).each( function(n) {
+        if (n.id.match(options.format))
+            nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode];
+        n.parentNode.removeChild(n);
+    });
+   
+    new_sequence.each(function(ident) {
+      var n = nodeMap[ident];
+      if (n) {
+        n[1].appendChild(n[0]);
+        delete nodeMap[ident];
+      }
+    });
+  },
+  
+  serialize: function(element) {
+    element = $(element);
+    var options = Object.extend(Sortable.options(element), arguments[1] || { });
+    var name = encodeURIComponent(
+      (arguments[1] && arguments[1].name) ? arguments[1].name : element.id);
+    
+    if (options.tree) {
+      return Sortable.tree(element, arguments[1]).children.map( function (item) {
+        return [name + Sortable._constructIndex(item) + "[id]=" + 
+                encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
+      }).flatten().join('&');
+    } else {
+      return Sortable.sequence(element, arguments[1]).map( function(item) {
+        return name + "[]=" + encodeURIComponent(item);
+      }).join('&');
+    }
+  }
+};
+
+// Returns true if child is contained within element
+Element.isParent = function(child, element) {
+  if (!child.parentNode || child == element) return false;
+  if (child.parentNode == element) return true;
+  return Element.isParent(child.parentNode, element);
+};
+
+Element.findChildren = function(element, only, recursive, tagName) {   
+  if(!element.hasChildNodes()) return null;
+  tagName = tagName.toUpperCase();
+  if(only) only = [only].flatten();
+  var elements = [];
+  $A(element.childNodes).each( function(e) {
+    if(e.tagName && e.tagName.toUpperCase()==tagName &&
+      (!only || (Element.classNames(e).detect(function(v) { return only.include(v) }))))
+        elements.push(e);
+    if(recursive) {
+      var grandchildren = Element.findChildren(e, only, recursive, tagName);
+      if(grandchildren) elements.push(grandchildren);
+    }
+  });
+
+  return (elements.length>0 ? elements.flatten() : []);
+};
+
+Element.offsetSize = function (element, type) {
+  return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')];
+};
Index: openacs-4/packages/messages/www/resources/effects.js
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/resources/effects.js,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/resources/effects.js	31 Aug 2010 21:30:53 -0000	1.1
@@ -0,0 +1,1118 @@
+
+String.prototype.parseColor = function() {  
+  var color = '#';
+  if (this.slice(0,4) == 'rgb(') {  
+    var cols = this.slice(4,this.length-1).split(',');  
+    var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);  
+  } else {  
+    if (this.slice(0,1) == '#') {  
+      if (this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();  
+      if (this.length==7) color = this.toLowerCase();  
+    }  
+  }  
+  return (color.length==7 ? color : (arguments[0] || this));  
+};
+
+/*--------------------------------------------------------------------------*/
+
+Element.collectTextNodes = function(element) {  
+  return $A($(element).childNodes).collect( function(node) {
+    return (node.nodeType==3 ? node.nodeValue : 
+      (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
+  }).flatten().join('');
+};
+
+Element.collectTextNodesIgnoreClass = function(element, className) {  
+  return $A($(element).childNodes).collect( function(node) {
+    return (node.nodeType==3 ? node.nodeValue : 
+      ((node.hasChildNodes() && !Element.hasClassName(node,className)) ? 
+        Element.collectTextNodesIgnoreClass(node, className) : ''));
+  }).flatten().join('');
+};
+
+Element.setContentZoom = function(element, percent) {
+  element = $(element);  
+  element.setStyle({fontSize: (percent/100) + 'em'});   
+  if (Prototype.Browser.WebKit) window.scrollBy(0,0);
+  return element;
+};
+
+Element.getInlineOpacity = function(element){
+  return $(element).style.opacity || '';
+};
+
+Element.forceRerendering = function(element) {
+  try {
+    element = $(element);
+    var n = document.createTextNode(' ');
+    element.appendChild(n);
+    element.removeChild(n);
+  } catch(e) { }
+};
+
+/*--------------------------------------------------------------------------*/
+
+var Effect = {
+  _elementDoesNotExistError: {
+    name: 'ElementDoesNotExistError',
+    message: 'The specified DOM element does not exist, but is required for this effect to operate'
+  },
+  Transitions: {
+    linear: Prototype.K,
+    sinoidal: function(pos) {
+      return (-Math.cos(pos*Math.PI)/2) + .5;
+    },
+    reverse: function(pos) {
+      return 1-pos;
+    },
+    flicker: function(pos) {
+      var pos = ((-Math.cos(pos*Math.PI)/4) + .75) + Math.random()/4;
+      return pos > 1 ? 1 : pos;
+    },
+    wobble: function(pos) {
+      return (-Math.cos(pos*Math.PI*(9*pos))/2) + .5;
+    },
+    pulse: function(pos, pulses) { 
+      return (-Math.cos((pos*((pulses||5)-.5)*2)*Math.PI)/2) + .5;
+    },
+    spring: function(pos) { 
+      return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6));
+    },
+    none: function(pos) {
+      return 0;
+    },
+    full: function(pos) {
+      return 1;
+    }
+  },
+  DefaultOptions: {
+    duration:   1.0,   // seconds
+    fps:        100,   // 100= assume 66fps max.
+    sync:       false, // true for combining
+    from:       0.0,
+    to:         1.0,
+    delay:      0.0,
+    queue:      'parallel'
+  },
+  tagifyText: function(element) {
+    var tagifyStyle = 'position:relative';
+    if (Prototype.Browser.IE) tagifyStyle += ';zoom:1';
+    
+    element = $(element);
+    $A(element.childNodes).each( function(child) {
+      if (child.nodeType==3) {
+        child.nodeValue.toArray().each( function(character) {
+          element.insertBefore(
+            new Element('span', {style: tagifyStyle}).update(
+              character == ' ' ? String.fromCharCode(160) : character), 
+              child);
+        });
+        Element.remove(child);
+      }
+    });
+  },
+  multiple: function(element, effect) {
+    var elements;
+    if (((typeof element == 'object') || 
+        Object.isFunction(element)) && 
+       (element.length))
+      elements = element;
+    else
+      elements = $(element).childNodes;
+      
+    var options = Object.extend({
+      speed: 0.1,
+      delay: 0.0
+    }, arguments[2] || { });
+    var masterDelay = options.delay;
+
+    $A(elements).each( function(element, index) {
+      new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));
+    });
+  },
+  PAIRS: {
+    'slide':  ['SlideDown','SlideUp'],
+    'blind':  ['BlindDown','BlindUp'],
+    'appear': ['Appear','Fade']
+  },
+  toggle: function(element, effect) {
+    element = $(element);
+    effect = (effect || 'appear').toLowerCase();
+    var options = Object.extend({
+      queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
+    }, arguments[2] || { });
+    Effect[element.visible() ? 
+      Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options);
+  }
+};
+
+Effect.DefaultOptions.transition = Effect.Transitions.sinoidal;
+
+/* ------------- core effects ------------- */
+
+Effect.ScopedQueue = Class.create(Enumerable, {
+  initialize: function() {
+    this.effects  = [];
+    this.interval = null;    
+  },
+  _each: function(iterator) {
+    this.effects._each(iterator);
+  },
+  add: function(effect) {
+    var timestamp = new Date().getTime();
+    
+    var position = Object.isString(effect.options.queue) ? 
+      effect.options.queue : effect.options.queue.position;
+    
+    switch(position) {
+      case 'front':
+        // move unstarted effects after this effect  
+        this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
+            e.startOn  += effect.finishOn;
+            e.finishOn += effect.finishOn;
+          });
+        break;
+      case 'with-last':
+        timestamp = this.effects.pluck('startOn').max() || timestamp;
+        break;
+      case 'end':
+        // start effect after last queued effect has finished
+        timestamp = this.effects.pluck('finishOn').max() || timestamp;
+        break;
+    }
+    
+    effect.startOn  += timestamp;
+    effect.finishOn += timestamp;
+
+    if (!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
+      this.effects.push(effect);
+    
+    if (!this.interval)
+      this.interval = setInterval(this.loop.bind(this), 15);
+  },
+  remove: function(effect) {
+    this.effects = this.effects.reject(function(e) { return e==effect });
+    if (this.effects.length == 0) {
+      clearInterval(this.interval);
+      this.interval = null;
+    }
+  },
+  loop: function() {
+    var timePos = new Date().getTime();
+    for(var i=0, len=this.effects.length;i<len;i++) 
+      this.effects[i] && this.effects[i].loop(timePos);
+  }
+});
+
+Effect.Queues = {
+  instances: $H(),
+  get: function(queueName) {
+    if (!Object.isString(queueName)) return queueName;
+    
+    return this.instances.get(queueName) ||
+      this.instances.set(queueName, new Effect.ScopedQueue());
+  }
+};
+Effect.Queue = Effect.Queues.get('global');
+
+Effect.Base = Class.create({
+  position: null,
+  start: function(options) {
+    function codeForEvent(options,eventName){
+      return (
+        (options[eventName+'Internal'] ? 'this.options.'+eventName+'Internal(this);' : '') +
+        (options[eventName] ? 'this.options.'+eventName+'(this);' : '')
+      );
+    }
+    if (options && options.transition === false) options.transition = Effect.Transitions.linear;
+    this.options      = Object.extend(Object.extend({ },Effect.DefaultOptions), options || { });
+    this.currentFrame = 0;
+    this.state        = 'idle';
+    this.startOn      = this.options.delay*1000;
+    this.finishOn     = this.startOn+(this.options.duration*1000);
+    this.fromToDelta  = this.options.to-this.options.from;
+    this.totalTime    = this.finishOn-this.startOn;
+    this.totalFrames  = this.options.fps*this.options.duration;
+    
+    this.render = (function() {
+      function dispatch(effect, eventName) {
+        if (effect.options[eventName + 'Internal'])
+          effect.options[eventName + 'Internal'](effect);
+        if (effect.options[eventName])
+          effect.options[eventName](effect);
+      }
+
+      return function(pos) {
+        if (this.state === "idle") {
+          this.state = "running";
+          dispatch(this, 'beforeSetup');
+          if (this.setup) this.setup();
+          dispatch(this, 'afterSetup');
+        }
+        if (this.state === "running") {
+          pos = (this.options.transition(pos) * this.fromToDelta) + this.options.from;
+          this.position = pos;
+          dispatch(this, 'beforeUpdate');
+          if (this.update) this.update(pos);
+          dispatch(this, 'afterUpdate');
+        }
+      }
+    })();
+    
+    this.event('beforeStart');
+    if (!this.options.sync)
+      Effect.Queues.get(Object.isString(this.options.queue) ? 
+        'global' : this.options.queue.scope).add(this);
+  },
+  loop: function(timePos) {
+    if (timePos >= this.startOn) {
+      if (timePos >= this.finishOn) {
+        this.render(1.0);
+        this.cancel();
+        this.event('beforeFinish');
+        if (this.finish) this.finish(); 
+        this.event('afterFinish');
+        return;  
+      }
+      var pos   = (timePos - this.startOn) / this.totalTime,
+          frame = (pos * this.totalFrames).round();
+      if (frame > this.currentFrame) {
+        this.render(pos);
+        this.currentFrame = frame;
+      }
+    }
+  },
+  cancel: function() {
+    if (!this.options.sync)
+      Effect.Queues.get(Object.isString(this.options.queue) ? 
+        'global' : this.options.queue.scope).remove(this);
+    this.state = 'finished';
+  },
+  event: function(eventName) {
+    if (this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
+    if (this.options[eventName]) this.options[eventName](this);
+  },
+  inspect: function() {
+    var data = $H();
+    for(property in this)
+      if (!Object.isFunction(this[property])) data.set(property, this[property]);
+    return '#<Effect:' + data.inspect() + ',options:' + $H(this.options).inspect() + '>';
+  }
+});
+
+Effect.Parallel = Class.create(Effect.Base, {
+  initialize: function(effects) {
+    this.effects = effects || [];
+    this.start(arguments[1]);
+  },
+  update: function(position) {
+    this.effects.invoke('render', position);
+  },
+  finish: function(position) {
+    this.effects.each( function(effect) {
+      effect.render(1.0);
+      effect.cancel();
+      effect.event('beforeFinish');
+      if (effect.finish) effect.finish(position);
+      effect.event('afterFinish');
+    });
+  }
+});
+
+Effect.Tween = Class.create(Effect.Base, {
+  initialize: function(object, from, to) {
+    object = Object.isString(object) ? $(object) : object;
+    var args = $A(arguments), method = args.last(), 
+      options = args.length == 5 ? args[3] : null;
+    this.method = Object.isFunction(method) ? method.bind(object) :
+      Object.isFunction(object[method]) ? object[method].bind(object) : 
+      function(value) { object[method] = value };
+    this.start(Object.extend({ from: from, to: to }, options || { }));
+  },
+  update: function(position) {
+    this.method(position);
+  }
+});
+
+Effect.Event = Class.create(Effect.Base, {
+  initialize: function() {
+    this.start(Object.extend({ duration: 0 }, arguments[0] || { }));
+  },
+  update: Prototype.emptyFunction
+});
+
+Effect.Opacity = Class.create(Effect.Base, {
+  initialize: function(element) {
+    this.element = $(element);
+    if (!this.element) throw(Effect._elementDoesNotExistError);
+    // make this work on IE on elements without 'layout'
+    if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
+      this.element.setStyle({zoom: 1});
+    var options = Object.extend({
+      from: this.element.getOpacity() || 0.0,
+      to:   1.0
+    }, arguments[1] || { });
+    this.start(options);
+  },
+  update: function(position) {
+    this.element.setOpacity(position);
+  }
+});
+
+Effect.Move = Class.create(Effect.Base, {
+  initialize: function(element) {
+    this.element = $(element);
+    if (!this.element) throw(Effect._elementDoesNotExistError);
+    var options = Object.extend({
+      x:    0,
+      y:    0,
+      mode: 'relative'
+    }, arguments[1] || { });
+    this.start(options);
+  },
+  setup: function() {
+    this.element.makePositioned();
+    this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
+    this.originalTop  = parseFloat(this.element.getStyle('top')  || '0');
+    if (this.options.mode == 'absolute') {
+      this.options.x = this.options.x - this.originalLeft;
+      this.options.y = this.options.y - this.originalTop;
+    }
+  },
+  update: function(position) {
+    this.element.setStyle({
+      left: (this.options.x  * position + this.originalLeft).round() + 'px',
+      top:  (this.options.y  * position + this.originalTop).round()  + 'px'
+    });
+  }
+});
+
+// for backwards compatibility
+Effect.MoveBy = function(element, toTop, toLeft) {
+  return new Effect.Move(element, 
+    Object.extend({ x: toLeft, y: toTop }, arguments[3] || { }));
+};
+
+Effect.Scale = Class.create(Effect.Base, {
+  initialize: function(element, percent) {
+    this.element = $(element);
+    if (!this.element) throw(Effect._elementDoesNotExistError);
+    var options = Object.extend({
+      scaleX: true,
+      scaleY: true,
+      scaleContent: true,
+      scaleFromCenter: false,
+      scaleMode: 'box',        // 'box' or 'contents' or { } with provided values
+      scaleFrom: 100.0,
+      scaleTo:   percent
+    }, arguments[2] || { });
+    this.start(options);
+  },
+  setup: function() {
+    this.restoreAfterFinish = this.options.restoreAfterFinish || false;
+    this.elementPositioning = this.element.getStyle('position');
+    
+    this.originalStyle = { };
+    ['top','left','width','height','fontSize'].each( function(k) {
+      this.originalStyle[k] = this.element.style[k];
+    }.bind(this));
+      
+    this.originalTop  = this.element.offsetTop;
+    this.originalLeft = this.element.offsetLeft;
+    
+    var fontSize = this.element.getStyle('font-size') || '100%';
+    ['em','px','%','pt'].each( function(fontSizeType) {
+      if (fontSize.indexOf(fontSizeType)>0) {
+        this.fontSize     = parseFloat(fontSize);
+        this.fontSizeType = fontSizeType;
+      }
+    }.bind(this));
+    
+    this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
+    
+    this.dims = null;
+    if (this.options.scaleMode=='box')
+      this.dims = [this.element.offsetHeight, this.element.offsetWidth];
+    if (/^content/.test(this.options.scaleMode))
+      this.dims = [this.element.scrollHeight, this.element.scrollWidth];
+    if (!this.dims)
+      this.dims = [this.options.scaleMode.originalHeight,
+                   this.options.scaleMode.originalWidth];
+  },
+  update: function(position) {
+    var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
+    if (this.options.scaleContent && this.fontSize)
+      this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType });
+    this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
+  },
+  finish: function(position) {
+    if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
+  },
+  setDimensions: function(height, width) {
+    var d = { };
+    if (this.options.scaleX) d.width = width.round() + 'px';
+    if (this.options.scaleY) d.height = height.round() + 'px';
+    if (this.options.scaleFromCenter) {
+      var topd  = (height - this.dims[0])/2;
+      var leftd = (width  - this.dims[1])/2;
+      if (this.elementPositioning == 'absolute') {
+        if (this.options.scaleY) d.top = this.originalTop-topd + 'px';
+        if (this.options.scaleX) d.left = this.originalLeft-leftd + 'px';
+      } else {
+        if (this.options.scaleY) d.top = -topd + 'px';
+        if (this.options.scaleX) d.left = -leftd + 'px';
+      }
+    }
+    this.element.setStyle(d);
+  }
+});
+
+Effect.Highlight = Class.create(Effect.Base, {
+  initialize: function(element) {
+    this.element = $(element);
+    if (!this.element) throw(Effect._elementDoesNotExistError);
+    var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || { });
+    this.start(options);
+  },
+  setup: function() {
+    // Prevent executing on elements not in the layout flow
+    if (this.element.getStyle('display')=='none') { this.cancel(); return; }
+    // Disable background image during the effect
+    this.oldStyle = { };
+    if (!this.options.keepBackgroundImage) {
+      this.oldStyle.backgroundImage = this.element.getStyle('background-image');
+      this.element.setStyle({backgroundImage: 'none'});
+    }
+    if (!this.options.endcolor)
+      this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');
+    if (!this.options.restorecolor)
+      this.options.restorecolor = this.element.getStyle('background-color');
+    // init color calculations
+    this._base  = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));
+    this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this));
+  },
+  update: function(position) {
+    this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){
+      return m+((this._base[i]+(this._delta[i]*position)).round().toColorPart()); }.bind(this)) });
+  },
+  finish: function() {
+    this.element.setStyle(Object.extend(this.oldStyle, {
+      backgroundColor: this.options.restorecolor
+    }));
+  }
+});
+
+Effect.ScrollTo = function(element) {
+  var options = arguments[1] || { },
+  scrollOffsets = document.viewport.getScrollOffsets(),
+  elementOffsets = $(element).cumulativeOffset();
+
+  if (options.offset) elementOffsets[1] += options.offset;
+
+  return new Effect.Tween(null,
+    scrollOffsets.top,
+    elementOffsets[1],
+    options,
+    function(p){ scrollTo(scrollOffsets.left, p.round()) }
+  );
+};
+
+/* ------------- combination effects ------------- */
+
+Effect.Fade = function(element) {
+  element = $(element);
+  var oldOpacity = element.getInlineOpacity();
+  var options = Object.extend({
+    from: element.getOpacity() || 1.0,
+    to:   0.0,
+    afterFinishInternal: function(effect) { 
+      if (effect.options.to!=0) return;
+      effect.element.hide().setStyle({opacity: oldOpacity}); 
+    }
+  }, arguments[1] || { });
+  return new Effect.Opacity(element,options);
+};
+
+Effect.Appear = function(element) {
+  element = $(element);
+  var options = Object.extend({
+  from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0),
+  to:   1.0,
+  // force Safari to render floated elements properly
+  afterFinishInternal: function(effect) {
+    effect.element.forceRerendering();
+  },
+  beforeSetup: function(effect) {
+    effect.element.setOpacity(effect.options.from).show(); 
+  }}, arguments[1] || { });
+  return new Effect.Opacity(element,options);
+};
+
+Effect.Puff = function(element) {
+  element = $(element);
+  var oldStyle = { 
+    opacity: element.getInlineOpacity(), 
+    position: element.getStyle('position'),
+    top:  element.style.top,
+    left: element.style.left,
+    width: element.style.width,
+    height: element.style.height
+  };
+  return new Effect.Parallel(
+   [ new Effect.Scale(element, 200, 
+      { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }), 
+     new Effect.Opacity(element, { sync: true, to: 0.0 } ) ], 
+     Object.extend({ duration: 1.0, 
+      beforeSetupInternal: function(effect) {
+        Position.absolutize(effect.effects[0].element)
+      },
+      afterFinishInternal: function(effect) {
+         effect.effects[0].element.hide().setStyle(oldStyle); }
+     }, arguments[1] || { })
+   );
+};
+
+Effect.BlindUp = function(element) {
+  element = $(element);
+  element.makeClipping();
+  return new Effect.Scale(element, 0,
+    Object.extend({ scaleContent: false, 
+      scaleX: false, 
+      restoreAfterFinish: true,
+      afterFinishInternal: function(effect) {
+        effect.element.hide().undoClipping();
+      } 
+    }, arguments[1] || { })
+  );
+};
+
+Effect.BlindDown = function(element) {
+  element = $(element);
+  var elementDimensions = element.getDimensions();
+  return new Effect.Scale(element, 100, Object.extend({ 
+    scaleContent: false, 
+    scaleX: false,
+    scaleFrom: 0,
+    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
+    restoreAfterFinish: true,
+    afterSetup: function(effect) {
+      effect.element.makeClipping().setStyle({height: '0px'}).show(); 
+    },  
+    afterFinishInternal: function(effect) {
+      effect.element.undoClipping();
+    }
+  }, arguments[1] || { }));
+};
+
+Effect.SwitchOff = function(element) {
+  element = $(element);
+  var oldOpacity = element.getInlineOpacity();
+  return new Effect.Appear(element, Object.extend({
+    duration: 0.4,
+    from: 0,
+    transition: Effect.Transitions.flicker,
+    afterFinishInternal: function(effect) {
+      new Effect.Scale(effect.element, 1, { 
+        duration: 0.3, scaleFromCenter: true,
+        scaleX: false, scaleContent: false, restoreAfterFinish: true,
+        beforeSetup: function(effect) { 
+          effect.element.makePositioned().makeClipping();
+        },
+        afterFinishInternal: function(effect) {
+          effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity});
+        }
+      })
+    }
+  }, arguments[1] || { }));
+};
+
+Effect.DropOut = function(element) {
+  element = $(element);
+  var oldStyle = {
+    top: element.getStyle('top'),
+    left: element.getStyle('left'),
+    opacity: element.getInlineOpacity() };
+  return new Effect.Parallel(
+    [ new Effect.Move(element, {x: 0, y: 100, sync: true }), 
+      new Effect.Opacity(element, { sync: true, to: 0.0 }) ],
+    Object.extend(
+      { duration: 0.5,
+        beforeSetup: function(effect) {
+          effect.effects[0].element.makePositioned(); 
+        },
+        afterFinishInternal: function(effect) {
+          effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);
+        } 
+      }, arguments[1] || { }));
+};
+
+Effect.Shake = function(element) {
+  element = $(element);
+  var options = Object.extend({
+    distance: 20,
+    duration: 0.5
+  }, arguments[1] || {});
+  var distance = parseFloat(options.distance);
+  var split = parseFloat(options.duration) / 10.0;
+  var oldStyle = {
+    top: element.getStyle('top'),
+    left: element.getStyle('left') };
+    return new Effect.Move(element,
+      { x:  distance, y: 0, duration: split, afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x: -distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x:  distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x: -distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x:  distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
+    new Effect.Move(effect.element,
+      { x: -distance, y: 0, duration: split, afterFinishInternal: function(effect) {
+        effect.element.undoPositioned().setStyle(oldStyle);
+  }}) }}) }}) }}) }}) }});
+};
+
+Effect.SlideDown = function(element) {
+  element = $(element).cleanWhitespace();
+  // SlideDown need to have the content of the element wrapped in a container element with fixed height!
+  var oldInnerBottom = element.down().getStyle('bottom');
+  var elementDimensions = element.getDimensions();
+  return new Effect.Scale(element, 100, Object.extend({ 
+    scaleContent: false, 
+    scaleX: false, 
+    scaleFrom: window.opera ? 0 : 1,
+    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
+    restoreAfterFinish: true,
+    afterSetup: function(effect) {
+      effect.element.makePositioned();
+      effect.element.down().makePositioned();
+      if (window.opera) effect.element.setStyle({top: ''});
+      effect.element.makeClipping().setStyle({height: '0px'}).show(); 
+    },
+    afterUpdateInternal: function(effect) {
+      effect.element.down().setStyle({bottom:
+        (effect.dims[0] - effect.element.clientHeight) + 'px' }); 
+    },
+    afterFinishInternal: function(effect) {
+      effect.element.undoClipping().undoPositioned();
+      effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); }
+    }, arguments[1] || { })
+  );
+};
+
+Effect.SlideUp = function(element) {
+  element = $(element).cleanWhitespace();
+  var oldInnerBottom = element.down().getStyle('bottom');
+  var elementDimensions = element.getDimensions();
+  return new Effect.Scale(element, window.opera ? 0 : 1,
+   Object.extend({ scaleContent: false, 
+    scaleX: false, 
+    scaleMode: 'box',
+    scaleFrom: 100,
+    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
+    restoreAfterFinish: true,
+    afterSetup: function(effect) {
+      effect.element.makePositioned();
+      effect.element.down().makePositioned();
+      if (window.opera) effect.element.setStyle({top: ''});
+      effect.element.makeClipping().show();
+    },  
+    afterUpdateInternal: function(effect) {
+      effect.element.down().setStyle({bottom:
+        (effect.dims[0] - effect.element.clientHeight) + 'px' });
+    },
+    afterFinishInternal: function(effect) {
+      effect.element.hide().undoClipping().undoPositioned();
+      effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom});
+    }
+   }, arguments[1] || { })
+  );
+};
+
+// Bug in opera makes the TD containing this element expand for a instance after finish 
+Effect.Squish = function(element) {
+  return new Effect.Scale(element, window.opera ? 1 : 0, { 
+    restoreAfterFinish: true,
+    beforeSetup: function(effect) {
+      effect.element.makeClipping(); 
+    },  
+    afterFinishInternal: function(effect) {
+      effect.element.hide().undoClipping(); 
+    }
+  });
+};
+
+Effect.Grow = function(element) {
+  element = $(element);
+  var options = Object.extend({
+    direction: 'center',
+    moveTransition: Effect.Transitions.sinoidal,
+    scaleTransition: Effect.Transitions.sinoidal,
+    opacityTransition: Effect.Transitions.full
+  }, arguments[1] || { });
+  var oldStyle = {
+    top: element.style.top,
+    left: element.style.left,
+    height: element.style.height,
+    width: element.style.width,
+    opacity: element.getInlineOpacity() };
+
+  var dims = element.getDimensions();    
+  var initialMoveX, initialMoveY;
+  var moveX, moveY;
+  
+  switch (options.direction) {
+    case 'top-left':
+      initialMoveX = initialMoveY = moveX = moveY = 0; 
+      break;
+    case 'top-right':
+      initialMoveX = dims.width;
+      initialMoveY = moveY = 0;
+      moveX = -dims.width;
+      break;
+    case 'bottom-left':
+      initialMoveX = moveX = 0;
+      initialMoveY = dims.height;
+      moveY = -dims.height;
+      break;
+    case 'bottom-right':
+      initialMoveX = dims.width;
+      initialMoveY = dims.height;
+      moveX = -dims.width;
+      moveY = -dims.height;
+      break;
+    case 'center':
+      initialMoveX = dims.width / 2;
+      initialMoveY = dims.height / 2;
+      moveX = -dims.width / 2;
+      moveY = -dims.height / 2;
+      break;
+  }
+  
+  return new Effect.Move(element, {
+    x: initialMoveX,
+    y: initialMoveY,
+    duration: 0.01, 
+    beforeSetup: function(effect) {
+      effect.element.hide().makeClipping().makePositioned();
+    },
+    afterFinishInternal: function(effect) {
+      new Effect.Parallel(
+        [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),
+          new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }),
+          new Effect.Scale(effect.element, 100, {
+            scaleMode: { originalHeight: dims.height, originalWidth: dims.width }, 
+            sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
+        ], Object.extend({
+             beforeSetup: function(effect) {
+               effect.effects[0].element.setStyle({height: '0px'}).show(); 
+             },
+             afterFinishInternal: function(effect) {
+               effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle); 
+             }
+           }, options)
+      )
+    }
+  });
+};
+
+Effect.Shrink = function(element) {
+  element = $(element);
+  var options = Object.extend({
+    direction: 'center',
+    moveTransition: Effect.Transitions.sinoidal,
+    scaleTransition: Effect.Transitions.sinoidal,
+    opacityTransition: Effect.Transitions.none
+  }, arguments[1] || { });
+  var oldStyle = {
+    top: element.style.top,
+    left: element.style.left,
+    height: element.style.height,
+    width: element.style.width,
+    opacity: element.getInlineOpacity() };
+
+  var dims = element.getDimensions();
+  var moveX, moveY;
+  
+  switch (options.direction) {
+    case 'top-left':
+      moveX = moveY = 0;
+      break;
+    case 'top-right':
+      moveX = dims.width;
+      moveY = 0;
+      break;
+    case 'bottom-left':
+      moveX = 0;
+      moveY = dims.height;
+      break;
+    case 'bottom-right':
+      moveX = dims.width;
+      moveY = dims.height;
+      break;
+    case 'center':  
+      moveX = dims.width / 2;
+      moveY = dims.height / 2;
+      break;
+  }
+  
+  return new Effect.Parallel(
+    [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
+      new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
+      new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
+    ], Object.extend({            
+         beforeStartInternal: function(effect) {
+           effect.effects[0].element.makePositioned().makeClipping(); 
+         },
+         afterFinishInternal: function(effect) {
+           effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }
+       }, options)
+  );
+};
+
+Effect.Pulsate = function(element) {
+  element = $(element);
+  var options    = arguments[1] || { },
+    oldOpacity = element.getInlineOpacity(),
+    transition = options.transition || Effect.Transitions.linear,
+    reverser   = function(pos){ 
+      return 1 - transition((-Math.cos((pos*(options.pulses||5)*2)*Math.PI)/2) + .5);
+    };
+    
+  return new Effect.Opacity(element, 
+    Object.extend(Object.extend({  duration: 2.0, from: 0,
+      afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
+    }, options), {transition: reverser}));
+};
+
+Effect.Fold = function(element) {
+  element = $(element);
+  var oldStyle = {
+    top: element.style.top,
+    left: element.style.left,
+    width: element.style.width,
+    height: element.style.height };
+  element.makeClipping();
+  return new Effect.Scale(element, 5, Object.extend({   
+    scaleContent: false,
+    scaleX: false,
+    afterFinishInternal: function(effect) {
+    new Effect.Scale(element, 1, { 
+      scaleContent: false, 
+      scaleY: false,
+      afterFinishInternal: function(effect) {
+        effect.element.hide().undoClipping().setStyle(oldStyle);
+      } });
+  }}, arguments[1] || { }));
+};
+
+Effect.Morph = Class.create(Effect.Base, {
+  initialize: function(element) {
+    this.element = $(element);
+    if (!this.element) throw(Effect._elementDoesNotExistError);
+    var options = Object.extend({
+      style: { }
+    }, arguments[1] || { });
+    
+    if (!Object.isString(options.style)) this.style = $H(options.style);
+    else {
+      if (options.style.include(':'))
+        this.style = options.style.parseStyle();
+      else {
+        this.element.addClassName(options.style);
+        this.style = $H(this.element.getStyles());
+        this.element.removeClassName(options.style);
+        var css = this.element.getStyles();
+        this.style = this.style.reject(function(style) {
+          return style.value == css[style.key];
+        });
+        options.afterFinishInternal = function(effect) {
+          effect.element.addClassName(effect.options.style);
+          effect.transforms.each(function(transform) {
+            effect.element.style[transform.style] = '';
+          });
+        };
+      }
+    }
+    this.start(options);
+  },
+  
+  setup: function(){
+    function parseColor(color){
+      if (!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';
+      color = color.parseColor();
+      return $R(0,2).map(function(i){
+        return parseInt( color.slice(i*2+1,i*2+3), 16 ) 
+      });
+    }
+    this.transforms = this.style.map(function(pair){
+      var property = pair[0], value = pair[1], unit = null;
+
+      if (value.parseColor('#zzzzzz') != '#zzzzzz') {
+        value = value.parseColor();
+        unit  = 'color';
+      } else if (property == 'opacity') {
+        value = parseFloat(value);
+        if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
+          this.element.setStyle({zoom: 1});
+      } else if (Element.CSS_LENGTH.test(value)) {
+          var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/);
+          value = parseFloat(components[1]);
+          unit = (components.length == 3) ? components[2] : null;
+      }
+
+      var originalValue = this.element.getStyle(property);
+      return { 
+        style: property.camelize(), 
+        originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0), 
+        targetValue: unit=='color' ? parseColor(value) : value,
+        unit: unit
+      };
+    }.bind(this)).reject(function(transform){
+      return (
+        (transform.originalValue == transform.targetValue) ||
+        (
+          transform.unit != 'color' &&
+          (isNaN(transform.originalValue) || isNaN(transform.targetValue))
+        )
+      )
+    });
+  },
+  update: function(position) {
+    var style = { }, transform, i = this.transforms.length;
+    while(i--)
+      style[(transform = this.transforms[i]).style] = 
+        transform.unit=='color' ? '#'+
+          (Math.round(transform.originalValue[0]+
+            (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() +
+          (Math.round(transform.originalValue[1]+
+            (transform.targetValue[1]-transform.originalValue[1])*position)).toColorPart() +
+          (Math.round(transform.originalValue[2]+
+            (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() :
+        (transform.originalValue +
+          (transform.targetValue - transform.originalValue) * position).toFixed(3) + 
+            (transform.unit === null ? '' : transform.unit);
+    this.element.setStyle(style, true);
+  }
+});
+
+Effect.Transform = Class.create({
+  initialize: function(tracks){
+    this.tracks  = [];
+    this.options = arguments[1] || { };
+    this.addTracks(tracks);
+  },
+  addTracks: function(tracks){
+    tracks.each(function(track){
+      track = $H(track);
+      var data = track.values().first();
+      this.tracks.push($H({
+        ids:     track.keys().first(),
+        effect:  Effect.Morph,
+        options: { style: data }
+      }));
+    }.bind(this));
+    return this;
+  },
+  play: function(){
+    return new Effect.Parallel(
+      this.tracks.map(function(track){
+        var ids = track.get('ids'), effect = track.get('effect'), options = track.get('options');
+        var elements = [$(ids) || $$(ids)].flatten();
+        return elements.map(function(e){ return new effect(e, Object.extend({ sync:true }, options)) });
+      }).flatten(),
+      this.options
+    );
+  }
+});
+
+Element.CSS_PROPERTIES = $w(
+  'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' + 
+  'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' +
+  'borderRightColor borderRightStyle borderRightWidth borderSpacing ' +
+  'borderTopColor borderTopStyle borderTopWidth bottom clip color ' +
+  'fontSize fontWeight height left letterSpacing lineHeight ' +
+  'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+
+  'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' +
+  'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' +
+  'right textIndent top width wordSpacing zIndex');
+  
+Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;
+
+String.__parseStyleElement = document.createElement('div');
+String.prototype.parseStyle = function(){
+  var style, styleRules = $H();
+  if (Prototype.Browser.WebKit)
+    style = new Element('div',{style:this}).style;
+  else {
+    String.__parseStyleElement.innerHTML = '<div style="' + this + '"></div>';
+    style = String.__parseStyleElement.childNodes[0].style;
+  }
+  
+  Element.CSS_PROPERTIES.each(function(property){
+    if (style[property]) styleRules.set(property, style[property]); 
+  });
+  
+  if (Prototype.Browser.IE && this.include('opacity'))
+    styleRules.set('opacity', this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]);
+
+  return styleRules;
+};
+
+if (document.defaultView && document.defaultView.getComputedStyle) {
+  Element.getStyles = function(element) {
+    var css = document.defaultView.getComputedStyle($(element), null);
+    return Element.CSS_PROPERTIES.inject({ }, function(styles, property) {
+      styles[property] = css[property];
+      return styles;
+    });
+  };
+} else {
+  Element.getStyles = function(element) {
+    element = $(element);
+    var css = element.currentStyle, styles;
+    styles = Element.CSS_PROPERTIES.inject({ }, function(results, property) {
+      results[property] = css[property];
+      return results;
+    });
+    if (!styles.opacity) styles.opacity = element.getOpacity();
+    return styles;
+  };
+}
+
+Effect.Methods = {
+  morph: function(element, style) {
+    element = $(element);
+    new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || { }));
+    return element;
+  },
+  visualEffect: function(element, effect, options) {
+    element = $(element);
+    var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1);
+    new Effect[klass](element, options);
+    return element;
+  },
+  highlight: function(element, options) {
+    element = $(element);
+    new Effect.Highlight(element, options);
+    return element;
+  }
+};
+
+$w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+
+  'pulsate shake puff squish switchOff dropOut').each(
+  function(effect) { 
+    Effect.Methods[effect] = function(element, options){
+      element = $(element);
+      Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options);
+      return element;
+    }
+  }
+);
+
+$w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each( 
+  function(f) { Effect.Methods[f] = Element[f]; }
+);
+
+Element.addMethods(Effect.Methods);
Index: openacs-4/packages/messages/www/resources/folder-new.js
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/resources/folder-new.js,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/resources/folder-new.js	31 Aug 2010 21:30:46 -0000	1.1
@@ -0,0 +1,46 @@
+    YAHOO.namespace("new_folder.container");
+    YAHOO.util.Event.onDOMReady(function () {
+            // Define various event handlers for Dialog
+            var handleSubmit = function() {
+                    this.submit();
+            };
+            var handleCancel = function() {
+                    this.cancel();
+            };
+            var handleSuccess = function(o) {
+                    var response = o.responseText;
+                    response = response.split("<!")[0];
+                    var response_error = response.substring(0,5);
+                    if ( response_error == 'error' ) {
+                        alert(response.substring(5,response.length));
+                    } else {
+                        //alert(response);
+                        //document.getElementById("resp").innerHTML = response;
+                        window.location.reload();
+                    }
+            };
+            var handleFailure = function(o) {
+                    alert("Submission failed: " + o.status);
+            };
+        // Remove progressively enhanced content class, just before creating the module
+        YAHOO.util.Dom.removeClass("dialog1", "yui-pe-content");
+            // Instantiate the Dialog
+            YAHOO.new_folder.container.dialog1 = new YAHOO.widget.Dialog("dialog1", 
+                                        { modal: true,
+				          visible: false,
+				          fixedcenter: true,
+				          constraintoviewport: true,
+                                          iframe: true,
+                                          close: false,
+                                          width: "300px",
+                                          buttons : [ { text:document.getElementById('msg_create').value, handler:handleSubmit, isDefault:true },
+                                          { text:document.getElementById('cancel').value, handler:handleCancel } ]
+                                        });
+            // Validate the entries in the form to require that both first and last name are entered
+            // Wire up the success and failure handlers
+            YAHOO.new_folder.container.dialog1.callback = { success: handleSuccess,
+                                                        failure: handleFailure };
+            // Render the Dialog
+            YAHOO.new_folder.container.dialog1.render("container");
+            YAHOO.util.Event.addListener("show", "click", YAHOO.new_folder.container.dialog1.show, null, YAHOO.new_folder.container.dialog1);
+    }); 
Index: openacs-4/packages/messages/www/resources/folder.png
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/resources/folder.png,v
diff -u
Binary files differ
Index: openacs-4/packages/messages/www/resources/forward.png
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/resources/forward.png,v
diff -u
Binary files differ
Index: openacs-4/packages/messages/www/resources/icono_imprimir.png
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/resources/icono_imprimir.png,v
diff -u
Binary files differ
Index: openacs-4/packages/messages/www/resources/mail.jpg
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/resources/mail.jpg,v
diff -u
Binary files differ
Index: openacs-4/packages/messages/www/resources/menu.css
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/resources/menu.css,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/resources/menu.css	31 Aug 2010 21:30:45 -0000	1.1
@@ -0,0 +1,87 @@
+div.menuBar,
+div.menuBar a.menuButton,
+div.menu,
+div.menu a.menuItem {
+  font-family: "MS Sans Serif", Arial, sans-serif;
+  font-size: 10pt;
+  font-style: normal;
+  font-weight: normal;
+  font-align: left;
+  color: #000000;
+}
+
+div.menuBar {
+  background-color: #d0d0d0;
+  border: 2px solid;
+  border-color: #f0f0f0 #909090 #909090 #f0f0f0;
+  padding: 4px 2px 4px 2px;
+  text-align: left;
+}
+
+div.menuBar a.menuButton {
+  background-color: transparent;
+  border: 1px solid #d0d0d0;
+  color: #000000;
+  cursor: default;
+  left: 0px;
+  margin: 1px;
+  padding: 2px 6px 2px 6px;
+  position: relative;
+  text-decoration: none;
+  top: 0px;
+  z-index: 100;
+}
+
+div.menuBar a.menuButton:hover {
+  background-color: transparent;
+  border-color: #f0f0f0 #909090 #909090 #f0f0f0;
+  color: #000000;
+}
+
+div.menuBar a.menuButtonActive,
+div.menuBar a.menuButtonActive:hover {
+  background-color: #a0a0a0;
+  border-color: #909090 #f0f0f0 #f0f0f0 #909090;
+  color: #ffffff;
+  left: 1px;
+  top: 1px;
+}
+
+div.menu {
+  background-color: #d0d0d0;
+  border: 2px solid;
+  border-color: #f0f0f0 #909090 #909090 #f0f0f0;
+  left: 0px;
+  padding: 0px 1px 1px 0px;
+  position: absolute;
+  top: 0px;
+  visibility: hidden;
+  z-index: 101;
+}
+
+div.menu a.menuItem {
+  color: #000000;
+  cursor: default;
+  display: block;
+  padding: 3px 5px;
+  text-decoration: none;
+  white-space: nowrap;
+}
+
+div.menu a.menuItem:hover, div.menu a.menuItemHighlight {
+  background-color: #000080;
+  color: #ffffff;
+}
+
+div.menu a.menuItem span.menuItemText {}
+
+div.menu a.menuItem span.menuItemArrow {
+  margin-right: -.75em;
+}
+
+div.menu div.menuItemSep {
+  border-top: 1px solid #909090;
+  border-bottom: 1px solid #f0f0f0;
+  margin: 4px 2px;
+}
+ 
Index: openacs-4/packages/messages/www/resources/menu.js
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/resources/menu.js,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/resources/menu.js	31 Aug 2010 21:30:50 -0000	1.1
@@ -0,0 +1,311 @@
+function Browser() {
+  var ua, s, i;
+  this.isIE    = false;  
+  this.isNS    = false;  
+  this.version = null;
+
+  ua = navigator.userAgent;
+
+  s = "MSIE";
+  if ((i = ua.indexOf(s)) >= 0) {
+    this.isIE = true;
+    this.version = parseFloat(ua.substr(i + s.length));
+    return;
+  }
+
+  s = "Netscape6/";
+  if ((i = ua.indexOf(s)) >= 0) {
+    this.isNS = true;
+    this.version = parseFloat(ua.substr(i + s.length));
+    return;
+  }
+
+  // Treat any other "Gecko" browser as NS 6.1.
+
+  s = "Gecko";
+  if ((i = ua.indexOf(s)) >= 0) {
+    this.isNS = true;
+    this.version = 6.1;
+    return;
+  }
+}
+
+var browser = new Browser();
+
+//----------------------------------------------------------------------------
+// Code for handling the menu bar and active button.
+//----------------------------------------------------------------------------
+
+var activeButton = null;
+
+// Capture mouse clicks on the page so any active button can be
+// deactivated.
+
+if (browser.isIE)
+  document.onmousedown = pageMousedown;
+else
+  document.addEventListener("mousedown", pageMousedown, true);
+
+function pageMousedown(event) {
+  var el;
+  if (activeButton == null)
+    return;
+
+  if (browser.isIE)
+    el = window.event.srcElement;
+  else
+    el = (event.target.tagName ? event.target : event.target.parentNode);
+
+  if (el == activeButton)
+    return;
+
+  if (getContainerWith(el, "DIV", "menu") == null) {
+    resetButton(activeButton);
+    activeButton = null;
+  }
+}
+
+function buttonClick(event, menuId) {
+  var button;
+
+  if (browser.isIE)
+    button = window.event.srcElement;
+  else
+    button = event.currentTarget;
+
+  button.blur();
+
+  if (button.menu == null) {
+    button.menu = document.getElementById(menuId);
+    menuInit(button.menu);
+  }
+
+  if (activeButton != null)
+    resetButton(activeButton);
+
+  if (button != activeButton) {
+    depressButton(button);
+    activeButton = button;
+  }
+  else
+    activeButton = null;
+
+  return false;
+}
+
+function buttonMouseover(event, menuId) {
+  var button;
+
+  if (browser.isIE)
+    button = window.event.srcElement;
+  else
+    button = event.currentTarget;
+
+  if (activeButton != null && activeButton != button)
+    buttonClick(event, menuId);
+}
+
+function depressButton(button) {
+  var x, y;
+
+  button.className += " menuButtonActive";
+
+  x = getPageOffsetLeft(button);
+  y = getPageOffsetTop(button) + button.offsetHeight;
+
+  if (browser.isIE) {
+    x += button.offsetParent.clientLeft;
+    y += button.offsetParent.clientTop;
+  }
+
+  button.menu.style.left = x + "px";
+  button.menu.style.top  = y + "px";
+  button.menu.style.visibility = "visible";
+}
+
+function resetButton(button) {
+  removeClassName(button, "menuButtonActive");
+
+  if (button.menu != null) {
+    closeSubMenu(button.menu);
+    button.menu.style.visibility = "hidden";
+  }
+}
+
+
+function menuMouseover(event) {
+  var menu;
+  if (browser.isIE)
+    menu = getContainerWith(window.event.srcElement, "DIV", "menu");
+  else
+    menu = event.currentTarget;
+
+  if (menu.activeItem != null)
+    closeSubMenu(menu);
+}
+
+function menuItemMouseover(event, menuId) {
+  var item, menu, x, y;
+  if (browser.isIE)
+    item = getContainerWith(window.event.srcElement, "A", "menuItem");
+  else
+    item = event.currentTarget;
+  menu = getContainerWith(item, "DIV", "menu");
+
+  if (menu.activeItem != null)
+    closeSubMenu(menu);
+  menu.activeItem = item;
+
+  item.className += " menuItemHighlight";
+
+  if (item.subMenu == null) {
+    item.subMenu = document.getElementById(menuId);
+    menuInit(item.subMenu);
+  }
+
+  x = getPageOffsetLeft(item) + item.offsetWidth;
+  y = getPageOffsetTop(item);
+
+  var maxX, maxY;
+
+  if (browser.isNS) {
+    maxX = window.scrollX + window.innerWidth;
+    maxY = window.scrollY + window.innerHeight;
+  }
+  if (browser.isIE && browser.version < 6) {
+    maxX = document.body.scrollLeft + document.body.clientWidth;
+    maxY = document.body.scrollTop  + document.body.clientHeight;
+  }
+  if (browser.isIE && browser.version >= 6) {
+    maxX = document.documentElement.scrollLeft + document.documentElement.clientWidth;
+    maxY = document.documentElement.scrollTop  + document.documentElement.clientHeight;
+  }
+  maxX -= item.subMenu.offsetWidth;
+  maxY -= item.subMenu.offsetHeight;
+
+  if (x > maxX)
+    x = Math.max(0, x - item.offsetWidth - item.subMenu.offsetWidth
+      + (menu.offsetWidth - item.offsetWidth));
+  y = Math.max(0, Math.min(y, maxY));
+
+  item.subMenu.style.left = x + "px";
+  item.subMenu.style.top  = y + "px";
+  item.subMenu.style.visibility = "visible";
+
+  if (browser.isIE)
+    window.event.cancelBubble = true;
+  else
+    event.stopPropagation();
+}
+
+function closeSubMenu(menu) {
+  if (menu == null || menu.activeItem == null)
+    return;
+
+  if (menu.activeItem.subMenu != null) {
+    closeSubMenu(menu.activeItem.subMenu);
+    menu.activeItem.subMenu.style.visibility = "hidden";
+    menu.activeItem.subMenu = null;
+  }
+  removeClassName(menu.activeItem, "menuItemHighlight");
+  menu.activeItem = null;
+}
+
+function menuInit(menu) {
+  var itemList, spanList
+  var textEl, arrowEl;
+  var itemWidth;
+  var w, dw;
+  var i, j;
+
+  if (browser.isIE) {
+    menu.style.lineHeight = "2.5ex";
+    spanList = menu.getElementsByTagName("SPAN");
+    for (i = 0; i < spanList.length; i++)
+      if (hasClassName(spanList[i], "menuItemArrow")) {
+        spanList[i].style.fontFamily = "Webdings";
+        spanList[i].firstChild.nodeValue = "4";
+      }
+  }
+
+  itemList = menu.getElementsByTagName("A");
+  if (itemList.length > 0)
+    itemWidth = itemList[0].offsetWidth;
+  else
+    return;
+
+  for (i = 0; i < itemList.length; i++) {
+    spanList = itemList[i].getElementsByTagName("SPAN")
+    textEl  = null
+    arrowEl = null;
+    for (j = 0; j < spanList.length; j++) {
+      if (hasClassName(spanList[j], "menuItemText"))
+        textEl = spanList[j];
+      if (hasClassName(spanList[j], "menuItemArrow"))
+        arrowEl = spanList[j];
+    }
+    if (textEl != null && arrowEl != null)
+      textEl.style.paddingRight = (itemWidth 
+        - (textEl.offsetWidth + arrowEl.offsetWidth)) + "px";
+  }
+
+  if (browser.isIE) {
+    w = itemList[0].offsetWidth;
+    itemList[0].style.width = w + "px";
+    dw = itemList[0].offsetWidth - w;
+    w -= dw;
+    itemList[0].style.width = w + "px";
+  }
+}
+
+function getContainerWith(node, tagName, className) {
+  while (node != null) {
+    if (node.tagName != null && node.tagName == tagName &&
+        hasClassName(node, className))
+      return node;
+    node = node.parentNode;
+  }
+
+  return node;
+}
+
+function hasClassName(el, name) {
+  var i, list;
+  list = el.className.split(" ");
+  for (i = 0; i < list.length; i++)
+    if (list[i] == name)
+      return true;
+
+  return false;
+}
+
+function removeClassName(el, name) {
+  var i, curList, newList;
+  if (el.className == null)
+    return;
+  newList = new Array();
+  curList = el.className.split(" ");
+  for (i = 0; i < curList.length; i++)
+    if (curList[i] != name)
+      newList.push(curList[i]);
+  el.className = newList.join(" ");
+}
+
+function getPageOffsetLeft(el) {
+  var x;
+  x = el.offsetLeft;
+  if (el.offsetParent != null)
+    x += getPageOffsetLeft(el.offsetParent);
+
+  return x;
+}
+
+function getPageOffsetTop(el) {
+  var y;
+  y = el.offsetTop;
+  if (el.offsetParent != null)
+    y += getPageOffsetTop(el.offsetParent);
+
+  return y;
+}
+ 
Index: openacs-4/packages/messages/www/resources/message-attachment.js
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/resources/message-attachment.js,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/resources/message-attachment.js	31 Aug 2010 21:30:53 -0000	1.1
@@ -0,0 +1,51 @@
+var number = 0;
+
+tag= function (tag) {
+    return document.createElement(tag);
+}
+
+tag_id = function (id) {
+    return document.getElementById(id);
+}
+
+even = function (evt) {
+    return (!evt) ? event : evt;
+}
+
+evt_src = function (evt) {
+    return evt.srcElement ?  evt.srcElement : evt.target;
+}
+
+addField = function () {
+	var container = document.getElementById('files');
+    span = tag('SPAN');
+	span.className = 'file';
+	span.id = 'upload_file_' + (++number);
+
+	field = tag('input');   
+	field.name = 'upload_file_'+ number ;
+	field.id = 'upload_file_' + number ;
+	field.type = 'file';
+   
+	attachment = tag('A');
+	attachment.name = span.id;
+	attachment.onclick = function(){ removeField(this.name) };
+	attachment.innerHTML = "&nbsp;&nbsp;" + document.getElementById('attachment_delete').value;
+
+	img = tag('IMG');
+	img.src ="/resources/messages/Delete16-on.gif";
+
+    new_line = tag('br');
+
+	span.appendChild(field);
+	attachment.appendChild(img);
+	span.appendChild(attachment);
+    span.appendChild(new_line);    
+	container.appendChild(span);
+}
+
+removeField = function (evt) {
+    lnk = evt_src(even(evt));
+    span = document.getElementById(evt);
+    span.parentNode.removeChild(span);
+}
\ No newline at end of file
Index: openacs-4/packages/messages/www/resources/message-dialog-roles.js
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/resources/message-dialog-roles.js,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/resources/message-dialog-roles.js	31 Aug 2010 21:30:48 -0000	1.1
@@ -0,0 +1,82 @@
+YAHOO.namespace("example.container");
+
+YAHOO.util.Event.onDOMReady(function () {
+	
+	// Define various event handlers for Dialog
+    var handleSubmit = function() {
+        var formObject = document.getElementsByName('members_list');
+        var form = formObject[0];
+        var elements = form.elements;
+        var member_ids = "";
+        var count = 0;
+        for ( var i = 0; i < elements.length ; i++) {
+            if (elements[i].checked && elements[i].name != '_dummy')  {
+                    count = count + 2;
+                    member_ids = member_ids + elements[i].value + "," + elements[i+1].value + ",";
+            }
+        }
+        var total_members = document.getElementById('total_rel_members').value;
+        member_ids = member_ids.substring(0,member_ids.length-1);
+        member_ids = member_ids.split(',');
+        var recipients = "";
+        var recipients_exists = document.getElementsByName('ids[]');
+        var recipients_exists_ids = "";
+        for ( var i = 0; i < document.getElementsByName('ids[]').length; i++) {
+            recipients_exists_ids = recipients_exists_ids + document.getElementsByName('ids[]')[i].value + ",";
+        }
+        for ( var i = 0; i < count; i = i + 2 ) {
+            var party_id = member_ids[i];
+            var party_name = member_ids[i+1];
+            if (recipients_exists_ids.match(party_id) == null) {
+                recipients = recipients + "<span id=span_" + party_id + " class=\"token\"><span><span><span><span><input type=\"hidden\" value=" + party_id + " name=\"ids[]\"/>" + party_name + "<span class=\"x\" onclick=id_delete(" + party_id + ")>&nbsp;</span></span></span></span></span></span>";
+            }
+        }
+        var recipients_old = document.getElementById('contact_reply').innerHTML;
+        document.getElementById('contact_reply').innerHTML = recipients_old + recipients;
+        this.cancel();
+    };
+	var handleCancel = function() {
+		this.cancel();
+	};
+	var handleSuccess = function(o) {
+		var response = o.responseText;
+		response = response.split("<!")[0];
+		document.getElementById("resp").innerHTML = response;
+	};
+	var handleFailure = function(o) {
+		alert("Submission failed: " + o.status);
+	};
+
+    // Remove progressively enhanced content class, just before creating the module
+    YAHOO.util.Dom.removeClass("dialog1", "yui-pe-content");
+
+	// Instantiate the Dialog
+	YAHOO.example.container.dialog1 = new YAHOO.widget.Dialog("dialog1", 
+							{ width : "30em",
+							  fixedcenter : true,
+							  visible : false, 
+							  constraintoviewport : true,
+                                                          modal: true,
+							  buttons : [ { text:document.getElementById('add').value, handler:handleSubmit, isDefault:true },
+								      { text:"Cancel", handler:handleCancel } ]
+							});
+
+	// Validate the entries in the form to require that both first and last name are entered
+	YAHOO.example.container.dialog1.validate = function() {
+		var data = this.getData();
+		if (data.firstname == "" || data.lastname == "") {
+			alert("Please enter your first and last names.");
+			return false;
+		} else {
+			return true;
+		}
+	};
+
+	// Wire up the success and failure handlers
+	YAHOO.example.container.dialog1.callback = { success: handleSuccess,
+						     failure: handleFailure };
+	
+	// Render the Dialog
+	YAHOO.example.container.dialog1.render();
+
+}); 
Index: openacs-4/packages/messages/www/resources/message-option.js
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/resources/message-option.js,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/resources/message-option.js	31 Aug 2010 21:30:46 -0000	1.1
@@ -0,0 +1,83 @@
+var browser = navigator.appName;
+
+function convert_input(folder_id) {
+    if (document.getElementById('active').value == 'f') {
+        var msg_cancel = document.getElementById('cancel').value;
+        var msg_save = document.getElementById('save_info').value;
+        var name = document.getElementById('folder_' + folder_id).innerHTML;
+        document.getElementById('folder_' + folder_id).innerHTML = "<input id=input_folder_" + folder_id + " value=\"" + name +  "\"><a  class=button name=send_button value=Send type=button href=\"#\" onclick=\"update_folder(" + folder_id + ",'save');\" > " + msg_save + "</a>  <a  class=button name=cancel_button value=Cancel type=button href=\"#\" onclick=\"update_folder(" + folder_id + ",'cancel');\" > " + msg_cancel + "</a>";
+        document.getElementById('active').value = 't';
+        document.getElementById('name_cancel').value = document.getElementById('input_folder_' + folder_id).value;
+    }
+}
+
+function cancel_folder(folder_id) {
+    if (document.getElementById('delete_p').value == 't') {
+        document.getElementById('span_folder_' + folder_id).innerHTML = '';
+        document.getElementById('span_folder_delete_' + folder_id).innerHTML = '';
+        document.getElementById('delete_p').value == 'f';
+    } else {
+        if (document.getElementById('cancel_p').value == 't') {
+            document.getElementById('folder_' + folder_id).innerHTML = document.getElementById('name_cancel').value;
+        } else {
+            if (document.getElementById('input_folder_' + folder_id) != null) {
+                var name = document.getElementById('input_folder_' + folder_id).value;
+                document.getElementById('folder_' + folder_id).innerHTML = '';
+                document.getElementById('active').value = 'f';
+                document.getElementById('folder_' + folder_id).innerHTML = name;
+            }
+        }
+    }
+}
+
+function update_folder (folder_id,option) {
+    var url = "update-name"
+    if (option == 'save' ) {
+        document.getElementById('delete_p').value = 'f';
+        document.getElementById('cancel_p').value == 'f';
+        var postData = "name=" + document.getElementById('input_folder_' + folder_id).value + "&folder_id=" + folder_id;
+    } else if (option == 'cancel') {
+        document.getElementById('delete_p').value = 'f';
+        document.getElementById('cancel_p').value = 't';
+        var postData = "folder_id=" + folder_id;
+    } else if (option == 'delete') {
+        var continue_send = confirm(document.getElementById("msg_delete_folder").value);
+        if (!continue_send ) {
+		self.close
+	} else {
+            document.getElementById('delete_p').value = 't';
+            var postData = "delete=t&folder_id=" + folder_id;
+        }
+    }
+    var request = YAHOO.util.Connect.asyncRequest('POST', url, callback, postData);
+}
+
+var handleSuccess = function(o){
+    cancel_folder(o.responseText);
+};
+
+var handleFailure= function(o){
+};
+
+var callback = {success:handleSuccess,failure:handleFailure};
+
+function delete_folder(){
+    dialog1.show();
+}
+
+var handleYes1 = function() {
+    dialog1.hide();
+}
+var handleNo1 = function() { dialog1.hide(); return false; }
+
+var dialog1 = new YAHOO.widget.Dialog("confirm_dialog1",
+                    { width: "450px",
+                    fixedcenter: true,
+                    visible: true,
+                    draggable: false,
+                    close: false,
+                    modal: true,
+                    buttons: [ { text:'Confirm', handler:handleYes1 },
+                               { text:'Cancel',  handler:handleNo1 } ]
+                    } );
+dialog1.render();
Index: openacs-4/packages/messages/www/resources/messages.css
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/resources/messages.css,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/resources/messages.css	31 Aug 2010 21:30:54 -0000	1.1
@@ -0,0 +1,13 @@
+div.menuBar {
+    background-color: #C0C0C0;
+    border-color: #F0F0F0 #909090 #909090 #F0F0F0;
+}
+div.menubar a.menuButton {
+    background-color: solid;
+    }
+
+div.menu {
+    background-color: #C0C0C0;
+    border-color: #F0F0F0 #909090 #909090 #F0F0F0;
+}
+
Index: openacs-4/packages/messages/www/resources/messages.js
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/resources/messages.js,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/resources/messages.js	31 Aug 2010 21:30:50 -0000	1.1
@@ -0,0 +1,1262 @@
+//color azul #D4E4FF -- #C9D7F1
+var normal_color = '#C0C0C0';
+var select_color = '#F0F0F0';
+//hidden all messages action when the user make some action
+function hidden_msg_actions () {
+    document.getElementById('msg_system').style.display = 'none';
+    if (document.getElementById('message_action_p').value == 't' && document.getElementById('message_action') != null) {
+        document.getElementById('message_action').style.display = 'none';
+    }
+    if (document.getElementById('msg_action_send_p').value == 't') {
+    var msg_id = document.getElementById('msg_id_new_send').value;
+        if (document.getElementById('msg_action_'+msg_id) != null ) {
+            document.getElementById('msg_action_'+msg_id).style.display = '';
+        }
+        document.getElementById('msg_action_send_p').value = 'f';
+    }
+    if (document.getElementById('msg_action_p').value == 'f') {
+        document.getElementById('msg_action').style.display = 'none';
+    }
+}
+
+//this function make a request and loading the messages in folder option
+function get_messages_folder (option) {
+    document.getElementById('search_data').value = "";
+    document.getElementById('search_button').disabled = true;
+    document.getElementById('option').value = option;
+    if (option == 'more_actions') {
+        var option = document.getElementById('folder_id').value;
+    } else {
+        hidden_msg_actions();
+        loading();
+        document.getElementById('inbox_select_p').value = 't';
+    }
+    var folder_active_old = document.getElementById('folder_active').value;
+    document.getElementById('reply_dialog_active').value = 'f';
+    if (document.getElementById('background_' + folder_active_old).style.background != null ) {
+           document.getElementById('background_' + folder_active_old).style.background = '#FFFFFF'; 
+    }
+    if (document.getElementById('background_' + option) != null ) {
+        document.getElementById('background_' + option).style.background = normal_color;
+    }
+    document.getElementById('folder_active').value = option;
+    document.getElementById('delete_message').style.display = 'none';
+    document.getElementById('select_msg').style.display = 'none';
+    document.getElementById('top_menu').style.display = '';
+    if (document.getElementById('page_number') != null && folder_active_old == option) {
+        var page_number = document.getElementById('page_number').value;
+    } else {
+        var page_number = 1;
+    }
+    var url = "inbox?folder_id="+option + "&page_number=" + page_number;
+    var request = YAHOO.util.Connect.asyncRequest('GET', url, callback_get_messages_folder);
+}
+
+
+var handleSuccess_get_messages_folder = function(o){
+    window.location.hash = "#";
+    if (document.getElementById('inbox_select_p') != null && document.getElementById('inbox_select_p').value == 't') {
+        document.getElementById('inbox_select_p').value = 'f';
+        document.getElementById('msg_action').style.display = 'none';
+        document.getElementById('msg_actions').innerHTML = '';
+    }
+    document.getElementById('folder_section_display').innerHTML = o.responseText;
+    document.getElementById('return').style.display = 'none';
+    document.getElementById('move_to').style.display = '';
+    document.getElementById('more_actions_span').style.display = '';
+    document.getElementById('delete_message_trash').style.display = 'none';
+    if (document.getElementById('folder_id').value == 3) {
+        document.getElementById('delete').style.display = 'none';
+        document.getElementById('delete_message_trash').style.display = '';
+    } else if  (document.getElementById('option').value == 1) {
+        document.getElementById('move_to').style.display = 'none';
+        document.getElementById('delete').style.display = 'none';
+        document.getElementById('delete_message_trash').style.display = 'none';
+    } else {
+        document.getElementById('move_to').style.display = '';
+        document.getElementById('more_actions_span').style.display = '';
+        document.getElementById('delete').style.display = '';
+        document.getElementById('delete_message_trash').style.display = 'none';
+    }
+    if (document.getElementById('move_to_p').value == 't') {
+        move_to_action ()
+    }
+
+    if (document.getElementById('more_actions_p').value == 't') {
+        more_action_do ()
+    }
+};
+
+var handleFailure_get_messages_folder= function(o){ };
+
+var callback_get_messages_folder = {success:handleSuccess_get_messages_folder,failure:handleFailure_get_messages_folder};
+
+function show_members_rol (id) {
+    if (id != 'dotlrn_student_rel' || id == 'rel_types_all') {
+        var url_rols = "members-rol";
+        var postData = "rel_type=" + id
+        var request = YAHOO.util.Connect.asyncRequest('POST', url_rols, callback_rols,postData);
+    }
+}
+
+var handleSuccess_rols = function(o) {
+    document.getElementById('info_members_rols').innerHTML = o.responseText;
+    YAHOO.roles.container_roles.dialog2.show();
+}
+
+var handleFailure_rols = function(o) { };
+
+var callback_rols = {success:handleSuccess_rols,failure:handleFailure_rols};
+
+
+
+function show_members_rol_index (id) {
+    if (id != 'dotlrn_student_rel' || id == 'rel_types_all') {
+        var url_rols = "members-rol";
+        var postData = "rel_type=" + id
+        var request = YAHOO.util.Connect.asyncRequest('POST', url_rols, callback_rols_index,postData);
+    }
+}
+
+var handleSuccess_rols_index = function(o) {
+    document.getElementById('rols_info_area').innerHTML = o.responseText;
+    YAHOO.example.container.rols_container.show();
+}
+
+var handleFailure_rols_index = function(o) { };
+
+var callback_rols_index = {success:handleSuccess_rols_index,failure:handleFailure_rols_index};
+
+function move_to_action () {
+    var count_msg_ids = document.getElementById('count_msg_ids').value;
+    if ( count_msg_ids != 0 ) {
+        document.getElementById('msg_action_p').value = 'f';
+        document.getElementById('msg_action').style.display = '';
+    } else {
+        document.getElementById('msg_action_p').value = 't';
+    }
+    var folder_name = document.getElementById('folder_name').value;
+    if (count_msg_ids == 1) {
+        document.getElementById('msg_actions').innerHTML = document.getElementById('msg_moved_to').value + " \"" + folder_name + "\"";
+    } else {
+        document.getElementById('msg_actions').innerHTML = count_msg_ids + " "+ document.getElementById('more_msg_moved_to').value + " \"" + folder_name + "\"";
+    }
+    document.getElementById('move_to_p').value = 'f';
+    document.getElementById('more_actions_p').value = 'f';
+}
+
+function more_action_do () {
+    var ids_length = document.getElementById('ids_length').value;
+    if (document.getElementById('more_actions_option').value == "t") {
+        if (ids_length == 1) {
+        var msg_action =  document.getElementById('msg_read').value;
+        } else {
+        var msg_action =  ids_length + " " + document.getElementById('more_msg_read').value;
+        }
+    } else {
+        if (ids_length == 1) {
+        var msg_action = document.getElementById('msg_unread').value;
+        } else {
+        var msg_action = ids_length + " " + document.getElementById('more_msg_unread').value;
+        }
+    }
+    document.getElementById('msg_actions').innerHTML = msg_action;
+    document.getElementById('more_actions_p').value = 'f';
+}
+
+//show loading image up the message send.
+function showLoading(container) {
+    if (container == 'send_mail') {
+        var text_action = document.getElementById('sending').value;
+    }
+    document.getElementById(container+"_loading").style.display = '';
+    document.getElementById(container+"_wait").innerHTML =  text_action + " <image src=/resources/ajaxhelper/images/indicator.gif>";
+}
+
+//this function make de submit form when send massage
+function submit_normal() {
+        var alert_p = 0
+        var formObject = document.getElementsByName('compose_message');
+        var form = formObject[0];
+        var elements = form.elements;
+        var rel_types_checked_p = 0;
+        for ( var i = 0; i < elements.length ; i++) {
+            if (elements[i].name == 'rel_types' && elements[i].checked) {
+                rel_types_checked_p = 1;
+                break;
+            }
+        }
+        if (document.getElementsByName('ids[]').length == 0 && rel_types_checked_p == 0) {
+            var continue_send = false;
+            var msg_empty = document.getElementById("recipients_empty").value;
+            alert(msg_empty);
+            alert_p = 1;
+            var continue_send = false;
+        } else {
+            var subject = document.getElementById('subject_field').value;
+            if (subject.trim() == "") {
+                var alert_p = 1;
+                var continue_send = confirm(document.getElementById("subject_empty").value);
+            } else {
+                var continue_send = true;
+                var alert_p = 0;
+            }
+            if (alert_p == 0) {
+                var editor = Xinha.getEditor("message");
+                var message = editor.getEditorContent();
+                if (message.length == 0) {
+                    var continue_send =  confirm(document.getElementById("body_empty").value);
+                } else {
+                    var continue_send = true;
+                }
+            }
+        }
+    if (!continue_send ) {
+        self.close
+    } else {
+            showLoading('send_mail');
+            var contact_length = document.getElementsByName('ids[]').length;
+            var contact_ids = "";
+            for (i=0; i< contact_length; i++) {
+                    if ((i+1) == contact_length) {
+                    contact_ids = contact_ids + document.getElementsByName('ids[]')[i].value;
+                    } else {
+                    contact_ids = contact_ids + document.getElementsByName('ids[]')[i].value + ",";
+                    }
+            }
+            document.getElementById('total_attachment').value = number;
+            document.getElementById('contacts_ids').value = contact_ids;
+            document.getElementById('subject').value = subject;           
+            document.compose_message.submit();
+    }
+}
+
+var handleSuccess_send = function(o){
+    window.location = "messages";
+};
+
+var handleUpload_send = function(o){
+    window.location = "messages";
+}
+
+var handleFailure_send= function(o){
+};
+
+var callback_send={success:handleSuccess_send,failure:handleFailure_send,upload:handleUpload_send};
+//finish submit function, send message normal
+
+
+function show_div(divName) {
+    document.getElementById(divName).style.display = '';
+}
+
+//this function make the move option
+function move_to(option) {
+    document.getElementById('msg_system').style.display = 'none';
+    document.getElementById('option_move_to').value = option;
+    document.getElementById('fileMenu').style.display = 'none';
+    if (document.getElementById('message_action') != null) {
+        document.getElementById('message_action').innerHTML = '';
+        document.getElementById('message_action').style.display = '';
+    }
+    var formObject = document.getElementsByName('messages_list');
+    var form = formObject[0];
+    var elements = form.elements;
+    var break_p = 0;
+    for ( var i = 0; i < elements.length ; i++){
+        if (elements[i].name == 'subject_id' && elements[i].checked) {
+            break_p = 1;
+            break;
+        }
+    }
+    if (!break_p) {
+        document.getElementById('msg_action_p').value = 'f';
+        document.getElementById('msg_action').style.display = '';
+        document.getElementById('msg_actions').innerHTML = document.getElementById('msg_select_msg').value;
+        var folder_id_state = document.getElementById('folder_id').value;
+        if (document.getElementById('folder_id_state') != null ) {
+            var folder_id_state = document.getElementById('folder_id_state').value;
+        }
+    } else {
+        var msg_ids = "";
+        var count_msg_ids = 0;
+        for ( var i = 0; i < elements.length ; i++) {
+            if ( elements[i].name == 'subject_id' && elements[i].checked )  {
+                    msg_ids = msg_ids + elements[i].value + ",";
+                    count_msg_ids = count_msg_ids + 1;
+            }
+        }
+        msg_ids = msg_ids.substring(0,msg_ids.length-1);
+        document.getElementById('count_msg_ids').value = count_msg_ids;
+        document.getElementById('folder_name').value = document.getElementById(option).innerHTML;
+        loading();
+        var url_move = "move-to?msg_ids=" + msg_ids + "&folder_id=" + option;
+        var request = YAHOO.util.Connect.asyncRequest('GET', url_move, callback_move_to);
+    }
+}
+
+var handleSuccess_move_to = function(o) {
+    var owner_id = document.getElementById('owner_id').value;
+    var folder_id_state = document.getElementById('folder_id').value;
+    update_numbers();
+    document.getElementById('move_to_p').value = 't';
+    document.getElementById('checkbox_general').checked = false;
+    if (document.getElementById('search_data').value == "") {
+        get_messages_folder('more_actions');
+    } else {
+        var page_number = document.getElementById('page_number').value;
+        search(page_number)
+    }
+};
+
+var handleFailure_move_to = function(o){
+};
+
+var callback_move_to={success:handleSuccess_move_to,failure:handleFailure_move_to};
+
+
+function update_numbers() {
+    var formObject = document.getElementsByName('messages');
+    var url_update_numbers = "update-numbers";
+    var request = YAHOO.util.Connect.asyncRequest('GET', url_update_numbers, callback_update_numbers);
+}
+
+var handleSuccess_update_numbers = function(o){
+    var response = o.responseText.split(',');
+    var get_messages_folder_p = 0;
+    if (document.getElementById('folder_id') != null) {
+        var active_folder = document.getElementById('folder_id').value;
+    } else {
+        var active_folder = 0;
+    }
+    if (o.responseText != "" ) {
+        for (var i=0; i < response.length; i= i+3) {
+            if ( response[i+1] == active_folder) {
+                get_messages_folder_p = 1
+            }
+            if (response[i] != "" && document.getElementById('new_msg_folder_'+response[i+1]) != null) {
+                document.getElementById('new_msg_folder_'+response[i+1]).value = response[i];
+                //if (document.getElementById('new_msg_folder_'+response[i+1]).value > 0 ) {
+                    if ( response[i] > 0 ) {
+                        document.getElementById('number_'+response[i+1]).innerHTML = "(<strong>" + response[i] + "</strong>/" + response[i+2] + ")";
+                    } else {
+                        document.getElementById('number_'+response[i+1]).innerHTML = "(" + response[i] + "/" + response[i+2] + ")";
+                    }
+                //} else {
+                    //document.getElementById('number_'+response[i+1]).innerHTML = "";
+                //}
+            }
+        }
+    }
+    if (get_messages_folder_p == 0 ) {
+        if (document.getElementById('new_msg_folder_'+active_folder) != null) {
+            document.getElementById('new_msg_folder_'+active_folder).value = 0;
+        }
+        if (document.getElementById('number_'+active_folder) != null ) {
+            document.getElementById('number_'+active_folder).innerHTML = "";
+        }
+    }
+};
+
+var handleFailure_update_numbers= function(o){
+};
+
+var callback_update_numbers={success:handleSuccess_update_numbers,failure:handleFailure_update_numbers};
+
+
+function paginate(folder_id,page_number,option) {
+    document.getElementById('msg_action_p').value = 'f';
+    hidden_msg_actions();
+    loading();
+    document.getElementById('inbox_select_p').value = 't';
+    var formObject = document.getElementsByName('messages');
+    if (option == 'newer') {
+        var page_number = page_number - 1;
+    }
+    if (option == 'older') {
+        var page_number = page_number + 1;
+    }
+    document.getElementById('page_number').value = page_number;
+    if (document.getElementById('search_data').value == "") {
+        var url_paginate = "inbox?folder_id=" + folder_id + "&page_number=" + page_number;
+        var request = YAHOO.util.Connect.asyncRequest('GET', url_paginate, callback_paginate);
+    } else {
+        search(page_number);
+    }
+}
+
+var handleSuccess_paginate = function(o){
+    if (document.getElementById('inbox_select_p') != null && document.getElementById('inbox_select_p').value == 't') {
+        document.getElementById('inbox_select_p').value = 'f';
+        document.getElementById('msg_action').style.display = 'none';
+        document.getElementById('msg_actions').innerHTML = '';
+    }
+    document.getElementById('folder_section_display').innerHTML = o.responseText;
+};
+
+var handleFailure_paginate= function(o){
+};
+
+var callback_paginate={success:handleSuccess_paginate,failure:handleFailure_paginate};
+
+function read_message(msg_id,folder_id,page_number,action) {
+    if (YAHOO.env.ua.ie) {
+        scrollTo(0,document.getElementById('msg_action_'+msg_id).offsetTop);
+    } else {
+        if (document.getElementById('msg_action_'+msg_id) == null) {
+            document.getElementById('scroll_top').scrollTo();
+        } else {
+            var msg_id_reply_action = document.getElementById('msg_id_action_reply').value;
+            document.getElementById('reply_'+msg_id_reply_action).scrollTo();
+        }
+    }
+    if ( document.getElementById('number_messages') != null) {
+        var total_messages = document.getElementById('number_messages').value;
+        document.getElementById('total_messages').value = document.getElementById('number_messages').value;
+    } else {
+        var total_messages = document.getElementById('total_messages').value;
+    }
+    document.getElementById('more_actions_span').style.display = 'none';
+    document.getElementById('msg_action_p').value = 'f';
+    hidden_msg_actions();
+    document.getElementById('delete_message').style.display = 'none';
+    loading();
+    document.getElementById('inbox_select_p').value = 't';
+    document.getElementById('return').style.display = '';
+    document.getElementById('delete').style.display = 'none';
+    document.getElementById('move_to').style.display = 'none';
+    if (action == "new_msg") {
+        document.getElementById('msg_action_send_p').value = 't';
+    }
+    var url = "read-message";
+    var postData = "msg_id="+msg_id+"&folder_id="+folder_id+"&page_number="+page_number+"&total_messages="+total_messages;
+    var request = YAHOO.util.Connect.asyncRequest('POST', url, callback_read_message,postData);
+}
+
+
+var handleSuccess_read_message = function(o){
+    if (o.responseText.trim() == "false") {
+        alert(document.getElementById('not_permission_read').value);
+        get_messages_folder(0);
+    } else {
+        if (document.getElementById('msg_action_'+msg_id) != null) {
+            document.getElementById('msg_action_'+msg_id).style.display = 'none';
+        }
+        if (document.getElementById('inbox_select_p') != null && document.getElementById('inbox_select_p').value == 't') {
+            document.getElementById('inbox_select_p').value = 'f';
+            document.getElementById('msg_action').style.display = 'none';
+            document.getElementById('msg_actions').innerHTML = '';
+        }
+        document.getElementById('folder_section_display').innerHTML = o.responseText;
+        if (document.getElementById('option_reply').value == 'forward') {
+            document.getElementById('msg_action_send_p').value = 't';
+            var msg_id = document.getElementById('msg_id_reply').value;
+            document.getElementById('option_reply').value = '';
+        } else {
+            var msg_id = document.getElementById('msg_id_new_send').value;
+        }
+        if (document.getElementById('msg_action_send_p').value == 't') {
+            if (document.getElementById('msg_action_'+msg_id) != null) {
+                document.getElementById('msg_action_'+msg_id).style.display = '';
+                document.getElementById('msg_action_id').value = msg_id;
+                if (YAHOO.env.ua.ie) {
+                    scrollTo(0,document.getElementById('msg_action_'+msg_id).offsetTop);
+                } else {
+                    document.getElementById('msg_action_'+msg_id).scrollTo();
+                }
+            }
+        }
+        if (document.getElementById('older_msg_id').value == '-1') {
+            document.getElementById('msg_older').style.display = 'none';
+        } else {
+            document.getElementById('msg_older').style.display = '';
+        }
+        if (document.getElementById('newer_msg_id').value == '-1') {
+            document.getElementById('msg_newer').style.display = 'none';
+        } else {
+            document.getElementById('msg_newer').style.display = '';
+        }
+        document.getElementById('select_msg').style.display = '';
+        var owner_id = document.getElementById('owner_id').value;
+        var folder_id_state = document.getElementById('folder_id').value;
+        update_numbers();
+        (new Image()).src='inbox/images/token.gif';
+        (new Image()).src='inbox/images/token_selected.gif';
+        (new Image()).src='inbox/images/token_hover.gif';
+        (new Image()).src='inbox/images/token_x.gif';
+        contact_users();
+    }
+};
+
+var handleFailure_read_message = function(o){
+};
+
+var callback_read_message = {success:handleSuccess_read_message,failure:handleFailure_read_message};
+
+
+function mostrardiv(msg_id) {
+    document.getElementById('contact_list_'+msg_id).style.display = 'none';
+    document.getElementById('msg_action_p').value = 'f';
+    hidden_msg_actions();
+    if (msg_id == undefined) {
+        var msg_id = document.getElementById('msg_id_new_send').value;
+    }
+    if (document.getElementById('msg_action_'+msg_id) != null ) {
+        document.getElementById('msg_action_'+msg_id).style.display = 'none';
+    }
+    document.getElementById('details_'+msg_id).style.display = '';
+    document.getElementById('show_details_'+msg_id).style.display = 'none';
+    document.getElementById('from_'+msg_id).style.display = '';
+    document.getElementById('close_details_'+msg_id).style.display = '';
+}
+
+function close_dialog(msg_id) {
+    div = document.getElementById('details_'+msg_id);
+    div.style.display='none';
+    div = document.getElementById('close_details_'+msg_id);
+    div.style.display='none';
+    div = document.getElementById('show_details_'+msg_id);
+    div.style.display = '';
+    document.getElementById('from_'+msg_id).style.display = 'none';
+    document.getElementById('contact_list_'+msg_id).style.display = '';
+}
+
+function reply_message(option,msg_id) {
+    document.getElementById('msg_reply').value = msg_id;
+    if (option != "forward") {
+        var url = "contacts?msg_id="+msg_id+"&option=" + option;
+        var request = YAHOO.util.Connect.asyncRequest('GET', url, callback_reply);
+    } else if (option == "forward") {
+       document.getElementById('contact_reply').innerHTML = '';
+        var url = "contacts?msg_id="+msg_id+"&option=" + option;
+        var request = YAHOO.util.Connect.asyncRequest('GET', url, callback_reply);
+    }
+}
+
+
+var handleSuccess_reply = function(o){
+    var msg_id = document.getElementById('msg_reply').value;
+    var parent_id = document.getElementById('parent_id_reply').value;
+    if (o.responseText == "forward") {
+        html_xinha_editor('forward');
+        document.getElementById('submit_reply').innerHTML = "<a  class=button name=send_button value=Send type=button href=\"javascript:submit_reply(0,0)\" >"+document.getElementById('send_msg').value+"</a>";
+    } else {
+        html_xinha_editor('reply');
+        document.getElementById('contact_reply').innerHTML = o.responseText;
+        document.getElementById('submit_reply').innerHTML = "<a  class=button name=send_button value=Send type=button href=\"javascript:submit_reply("+msg_id+","+parent_id+");\" > " + document.getElementById('send_msg').value + "</a>";
+    }
+    initEditors();
+}
+
+var handleFailure_reply = function(o){
+};
+
+var callback_reply = {success:handleSuccess_reply,failure:handleFailure_reply};
+
+function return_msg(owner_id) {
+    document.getElementById('msg_action_p').value = 'f';
+    hidden_msg_actions();
+    var folder_id = document.getElementById('folder_id').value;
+    var page_number = document.getElementById('page_number').value;
+    div = document.getElementById('select_msg');
+    div.style.display = 'none';
+    if (document.getElementById('search_data').value == "") {
+        var url = "inbox?page_number="+page_number+"&folder_id=" + folder_id;
+        var request = YAHOO.util.Connect.asyncRequest('GET', url, callback_return_msg);
+    } else {
+        search(page_number);
+    }
+}
+
+var handleSuccess_return_msg = function(o){
+    var folder_id = document.getElementById('folder_id').value;
+    if (YAHOO.env.ua.ie) {
+        scrollTo(0,document.getElementById('scroll_top').offsetTop);
+    } else {
+        document.getElementById('scroll_top').scrollTo();
+    }
+    get_messages_folder(folder_id);
+}
+
+var handleFailure_return_msg = function(o){
+};
+
+var callback_return_msg = {success:handleSuccess_return_msg,failure:handleFailure_return_msg};
+
+function cancel(msg_id) {
+    var continue_cancel = confirm (document.getElementById('msg_cancel').value);
+    if ( continue_cancel ) {
+        document.getElementById('reply_dialog_active').value = 'f';
+        if (YAHOO.env.ua.ie) {
+            scrollTo(0,document.getElementById('show_details_'+msg_id).offsetTop);
+        } else {
+            document.getElementById('show_details_'+msg_id).scrollTo();
+        }
+        document.getElementById('response_reply_mail_' + msg_id).innerHTML = '';
+        document.getElementById('reply_' + msg_id).style.background = normal_color;
+        if ( document.getElementById('reply_all_' + msg_id) != null) { 
+            document.getElementById('reply_all_'+ msg_id).style.background = normal_color;
+        }
+        document.getElementById('forward_'+ msg_id).style.background = normal_color;
+    } else {
+        self.close;
+    }
+}
+
+function submit_reply(msg,parent_id) {
+    document.getElementById('msg_action_p').value = 't';
+    hidden_msg_actions();
+    document.getElementById('msg_id_action_reply').value = msg;
+    var subject = document.getElementById('subject_field').value;
+    var msg_id = document.getElementById('msg_id').value;
+    var editor = Xinha.getEditor("message");
+    var message = editor.getEditorContent(); 
+    var contact_length = document.getElementsByName('ids[]').length;
+    var formObject = document.getElementsByName('compose_message');
+    var form = formObject[0];
+    var elements = form.elements;
+    var rel_types_checked_p = 0;
+    for ( var i = 0; i < elements.length ; i++) {
+        if (elements[i].name == 'rel_types' && elements[i].checked) {
+            rel_types_checked_p = 1;
+            break;
+        }
+    }
+    var alert_p = 0
+    if (document.getElementsByName('ids[]').length == 0 && rel_types_checked_p == 0) {
+        var continue_send = false;
+        var msg_empty = document.getElementById("recipients_empty").value;
+        alert(msg_empty);
+        alert_p = 1;
+        var continue_send = false;
+    } else {
+        if (message.length == 0) {
+            var continue_send = false;
+            var continue_send =  confirm(document.getElementById("body_empty").value);
+        } else {
+            var continue_send = true;
+        }
+    }
+    if (!continue_send ) {
+            self.close
+    } else {
+        showLoading('send_mail');
+        document.getElementById('reply_dialog_active').value = 'f';
+        var contact_ids = "";
+        for (i=0; i< contact_length; i++) {
+            if ((i+1) == contact_length) {
+                contact_ids = contact_ids + document.getElementsByName('ids[]')[i].value;
+            } else {
+                contact_ids = contact_ids + document.getElementsByName('ids[]')[i].value + ",";
+            }
+        }
+        if (YAHOO.env.ua.ie) {
+                YAHOO.util.Connect.setForm(form,true,true);
+        } else {
+                YAHOO.util.Connect.setForm(form,true);
+        }
+        var elements = form.elements;
+        var attachment_ids = "";
+        for ( var i = 0; i < elements.length ; i++) {
+            if ( elements[i].name == 'attachment_checkbox' && elements[i].checked )  {
+                    attachment_ids = attachment_ids + elements[i].id + ",";
+            }
+        }
+        attachment_ids = attachment_ids.substring(0,attachment_ids.length-1);
+        var msg_id_old = document.getElementById('msg_id_reply').value;
+        var url_send_mail = "send-mail";
+        var postData= "parent_id=" + msg_id +  "&contacts_ids=" + contact_ids + "&total_attachment=" + number + "&subject=" + subject+"&reply=true&general_parent_id=" + parent_id + "&msg_id_old=" + msg_id_old + "&attachment_ids=" + attachment_ids;
+        var request = YAHOO.util.Connect.asyncRequest('POST', url_send_mail, callback_send_mail, postData);
+    }
+}
+
+var handleSuccess_send_mail = function(o){ };
+
+var handleUpload_send_mail = function(o){
+    document.getElementById('msg_id_new_send').value = o.responseText;
+    if (document.getElementById('read') == null) {
+        window.location = "messages";
+    } else {
+        document.getElementById('autocomplete_loding').innerHTML = "<div tabindex=\"-1\" id=ids class=\"clearfix tokenizer\" onclick=\"$('autocomplete_input').focus()\"> <span class=\"tokenizer_stretcher\">^_^</span> <span class=\"tab_stop\"><input type=\"text\" id=\"hidden_input\" tabindex=\"-1\"></span> <span id=contact_reply> </span> <div id=\"autocomplete_display\" class=\"tokenizer_input\"> <input type=\"text\" size=\"1\" tabindex=\"\" id=\"autocomplete_input\" /> </div> </div> <div id=\"autocomplete_populate\" class=\"clearfix autocomplete typeahead_list\" style=\"width: 358px; height: auto; overflow-y: hidden;display:none\"> <div class=\"typeahead_message\">Type the name of a member</div></div>"
+        var msg_id = document.getElementById('msg_id').value;
+        var folder_id = document.getElementById('folder_id').value;
+        var page_number = document.getElementById('page_number');
+        read_message(msg_id,folder_id,page_number,'new_msg');
+    }
+};
+
+var handleFailure_send_mail= function(o){ };
+
+var callback_send_mail={success:handleSuccess_send_mail,failure:handleFailure_send_mail,upload:handleUpload_send_mail};
+
+function select_message(option) {
+    document.getElementById('msg_action_p').value = 'f';
+    hidden_msg_actions();
+    var folder_id = document.getElementById('folder_id').value;
+    var page_number = document.getElementById('new_page_number').value;
+    var div = document.getElementById('return');
+    div.style.display='';
+    if (option == 'older') {
+        var older_msg_id = document.getElementById('older_msg_id').value;
+        var newer_msg_id = document.getElementById('msg_id').value;
+        var newest_msg_id = document.getElementById('newest_msg_id').value;
+        var oldest_msg_id = document.getElementById('oldest_msg_id').value;
+        var msg_id = older_msg_id;
+        if (oldest_msg_id == '-1') {
+            document.getElementById('msg_older').style.display = 'none';
+        } else {
+            document.getElementById('msg_older').style.display = '';
+        }
+        if (newer_msg_id == '-1') {
+            document.getElementById('msg_newer').style.display = 'none';
+        } else {
+            document.getElementById('msg_newer').style.display = '';
+        }
+
+    } else if (option == 'newer') {
+        var older_msg_id = document.getElementById('msg_id').value;
+        var newer_msg_id = document.getElementById('newer_msg_id').value;
+    var newest_msg_id = document.getElementById('newest_msg_id').value;
+    var oldest_msg_id = document.getElementById('oldest_msg_id').value;
+        var msg_id = newer_msg_id;
+        if (newest_msg_id == '-1') {
+            document.getElementById('msg_newer').style.display = 'none';
+        } else {
+            document.getElementById('msg_newer').style.display = '';
+        }
+    if (older_msg_id == '-1') {
+            document.getElementById('msg_older').style.display = 'none';
+        } else {
+            document.getElementById('msg_older').style.display = '';
+        }
+    }
+    loading();
+    var url = "read-message?msg_id="+msg_id+"&folder_id="+folder_id+"&page_number="+page_number;
+    var request = YAHOO.util.Connect.asyncRequest('GET', url, callback_select_message);
+}
+
+var handleSuccess_select_message = function(o){
+    document.getElementById('folder_section_display').innerHTML = o.responseText;
+    var owner_id = document.getElementById('owner_id').value;
+    var folder_id_state = document.getElementById('folder_id').value;
+    update_numbers();
+    (new Image()).src='inbox/images/token.gif';
+    (new Image()).src='inbox/images/token_selected.gif';
+    (new Image()).src='inbox/images/token_hover.gif';
+    (new Image()).src='inbox/images/token_x.gif';
+    contact_users();
+    loading_cancel();
+};
+
+var handleFailure_select_message = function(o){ };
+
+var callback_select_message = {success:handleSuccess_select_message,failure:handleFailure_select_message};
+
+function more_actions_select(){
+    document.getElementById('msg_action_p').value = 'f';
+    hidden_msg_actions();
+    var formObject = document.getElementsByName('messages_list');
+    var form = formObject[0];
+    var elements = form.elements;
+    var break_p = 0;
+    var new_p = 0;
+    for ( var i = 0; i < elements.length ; i++){
+        if (elements[i].name == 'subject_id' && elements[i].checked) {
+            break_p = 1;
+            if (document.getElementById('new_p.'+elements[i].value) != null) {
+                new_p = 1;
+                break;
+            }
+        }
+    }
+    if (break_p) {
+        if (document.getElementById('more_actions_options') != null) {
+            document.getElementById('action_as_unread').style.display = '';
+        }
+    } else {
+        if (document.getElementById('more_actions_options') != null) {
+            document.getElementById('more_actions_options').style.display = 'none';
+        }
+    }
+}
+
+function delete_select_msg () {
+    document.getElementById('msg_system').style.display = 'none';
+    document.getElementById('more_actions').style.display = 'none';
+    document.getElementById('msg_action_p').value = 'f';
+    var formObject = document.getElementsByName('messages_list');
+    var form = formObject[0];
+    var elements = form.elements;
+    var msg_ids = "";
+    var count_msg_ids_select = 0
+    for ( var i = 0; i < elements.length ; i++) {
+        if ( elements[i].name == 'subject_id' && elements[i].checked )  {
+                msg_ids = msg_ids + elements[i].value + ",";
+                count_msg_ids_select = count_msg_ids_select + 1;
+        }
+    }
+    if (!count_msg_ids_select) {
+        document.getElementById('msg_action_p').value = 'f';
+        document.getElementById('msg_action').style.display = '';
+        document.getElementById('msg_actions').innerHTML = document.getElementById('msg_select_msg').value;
+        var folder_id_state = document.getElementById('folder_id').value;
+        if (document.getElementById('folder_id_state') != null ) {
+            var folder_id_state = document.getElementById('folder_id_state').value;
+        }
+        alert("debe de seleccionar al menos un mensaje para borrar");
+    } else {
+        msg_ids = msg_ids.substring(0,msg_ids.length-1);
+        document.getElementById('ids_more_actions').value = msg_ids;
+        var url_delete_msg = "delete-message";
+        var postData = "msg_ids=" + msg_ids;
+        document.getElementById('msg_action').style.display = '';
+        loading();
+        document.getElementById('ids_length').value = count_msg_ids_select;
+        var request = YAHOO.util.Connect.asyncRequest('POST', url_delete_msg, callback_delete_msg,postData);
+    }
+}
+
+var handleSuccess_delete_msg = function(o){
+    get_messages_folder(3);
+    update_numbers();
+};
+
+var handleFailure_delete_msg = function(o){
+};
+
+var callback_delete_msg={success:handleSuccess_delete_msg,failure:handleFailure_delete_msg};
+
+function more_actions(option,folder_id,owner_id) {
+    document.getElementById('msg_system').style.display = 'none';
+    document.getElementById('more_actions').style.display = 'none';
+    document.getElementById('msg_action_p').value = 'f';
+    var formObject = document.getElementsByName('messages_list');
+    var form = formObject[0];
+    var elements = form.elements;
+    var msg_ids = "";
+    var count_msg_ids_select = 0
+    for ( var i = 0; i < elements.length ; i++) {
+        if ( elements[i].name == 'subject_id' && elements[i].checked )  {
+                msg_ids = msg_ids + elements[i].value + ",";
+                count_msg_ids_select = count_msg_ids_select + 1;
+        }
+    }
+    if (!count_msg_ids_select) {
+        document.getElementById('msg_action_p').value = 'f';
+        document.getElementById('msg_action').style.display = '';
+        document.getElementById('msg_actions').innerHTML = document.getElementById('msg_select_msg').value;
+        var folder_id_state = document.getElementById('folder_id').value;
+        if (document.getElementById('folder_id_state') != null ) {
+            var folder_id_state = document.getElementById('folder_id_state').value;
+        }
+    } else {
+        msg_ids = msg_ids.substring(0,msg_ids.length-1);
+        document.getElementById('ids_more_actions').value = msg_ids;
+        var url_more_actions = "more-actions";
+        var postData = "msg_ids=" + msg_ids + "&option_select=" + option;
+        document.getElementById('msg_action').style.display = '';
+        loading();
+        document.getElementById('ids_length').value = count_msg_ids_select;
+        var request = YAHOO.util.Connect.asyncRequest('POST', url_more_actions, callback_more_actions,postData);
+    }
+}
+
+
+var handleSuccess_more_actions = function(o){
+    var folder_id_state = document.getElementById('folder_id').value;
+    var owner_id = document.getElementById('owner_id').value;
+    document.getElementById('more_actions_option').value = o.responseText;
+    var ids = (document.getElementById('ids_more_actions').value).split(',');
+    if (o.responseText == 't') {
+        var option_style = 'bold';
+    } else {
+        var option_style = '';
+    }
+    var count_new_msg = 0;
+    var count_read_msg = 0;
+    for (var count=0; count < ids.length; count++) {
+        if (document.getElementById('new_p_style.' + ids[count]) != null) {
+            if (document.getElementById('new_p_style.' + ids[count]).style.fontWeight == '') {
+                if (o.responseText == 't') {
+                    count_new_msg = count_new_msg+1;
+                }
+            } else {
+                if (o.responseText == 'f') {
+                    count_read_msg = count_read_msg+1;
+                }
+            }
+            document.getElementById('new_p_style.' + ids[count]).style.fontWeight = option_style;
+            document.getElementById('new_p_sub.' + ids[count]).style.fontWeight = option_style;
+            if (document.getElementById('new_p_comm.' + ids[count]) != null ) {
+                document.getElementById('new_p_comm.' + ids[count]).style.fontWeight = option_style;
+            }
+            document.getElementById('new_p_date.' + ids[count]).style.fontWeight = option_style;
+        }
+        if (document.getElementById('check_box_msg.' + ids[count]) != null) {
+            document.getElementById('check_box_msg.' + ids[count]).checked = 0;
+        }
+    }
+    document.getElementById('msg_action').style.display = '';
+    if (ids == "") {
+        document.getElementById('msg_actions').innerHTML = document.getElementById('msg_select_msg').value;
+    } else {
+        if (o.responseText == 't') {
+            if (count_new_msg > 1) {
+                document.getElementById('msg_actions').innerHTML = count_new_msg + " " + document.getElementById('more_msg_unread').value;
+            } else {
+                document.getElementById('msg_actions').innerHTML = document.getElementById('msg_unread').value;
+            }
+        } else {
+            if(count_read_msg > 1) {
+                document.getElementById('msg_actions').innerHTML = count_read_msg + " " + document.getElementById('more_msg_read').value;
+            } else {
+                document.getElementById('msg_actions').innerHTML = document.getElementById('msg_read').value;
+            }
+        }
+    }
+    var total_new_msg = document.getElementById('new_msg_folder_'+ folder_id_state).value;
+    if (o.responseText == 't') {
+        var new_messages = (parseInt(total_new_msg)+parseInt(count_new_msg));
+    } else {
+        var new_messages = (parseInt(total_new_msg)-parseInt(count_read_msg));
+    }
+    document.getElementById('new_msg_folder_'+ folder_id_state).value = new_messages;
+    document.getElementById('more_actions_p').value = 't';
+    if (document.getElementById('checkbox_general') != null ) {
+        document.getElementById('checkbox_general').checked = false;
+    }
+    update_numbers();
+};
+
+var handleFailure_more_actions= function(o){
+};
+
+var callback_more_actions={success:handleSuccess_more_actions,failure:handleFailure_more_actions};
+
+function loadEditors(editorTag){
+    var editorContent = document.getElementById(editorTag);
+    if (editorContent != null && editorContent.innerHTML != "") {
+        var content = editorContent.innerHTML;
+        eval(editorContent.innerHTML);
+        editorContent.innerHTML = "";
+    }
+}
+
+var xinhaTimer;
+var initEditorsTry = 0;
+var iframe_id = "";
+
+function initEditors(){
+    var ids = document.getElementById('advancedMessagesIds');
+    var no_ids = document.getElementById('noTabsEditors');
+    if (ids != null) {
+        var id = ids.innerHTML;
+        var id = id.trim();
+        var id = id.replace(/\'/g,"");
+        loadEditors('advancedMessagesEditor');
+    } else {
+        if (no_ids == null && initEditorsTry < 20) {
+            initEditorsTry++;
+            xinhaTimer = window.setTimeout("initEditors()",500);
+            return false;
+        }
+    }
+    initEditorsTry = 0;
+    window.clearTimeout(xinhaTimer);
+}
+
+String.prototype.trim = function () {
+    return this.replace(/^\s*/, "").replace(/\s*$/, "");
+}
+
+
+function reply_dialog (option,msg_id,general_parent_id){
+    var msg_action_id = document.getElementById('msg_action_id').value;
+    document.getElementById('msg_action_p').value = 'f';
+    hidden_msg_actions();
+    if (document.getElementById('msg_action_' + msg_action_id) != null ) {
+        document.getElementById('msg_action_' + msg_action_id).style.display = 'none';
+    }
+    if (document.getElementById('reply_dialog_active').value == 't') {
+        var msg_id_reply = document.getElementById('msg_id_reply').value;
+        if (msg_id_reply != msg_id) {
+            var continue_reply = confirm(document.getElementById("other_msg_reply").value + "  " + document.getElementById("msg_cancel").value);
+        } else {
+            var continue_reply = 'false';
+        }
+    } else {
+        document.getElementById('reply_dialog_active').value = 't';
+        var continue_reply = 'false';
+    }
+    if (!continue_reply ) {
+            self.close
+    } else {
+        document.getElementById('autocomplete_loding').innerHTML = '';
+        document.getElementById('msg_action_p').value = 'f';
+        hidden_msg_actions();
+        document.getElementById('reply_'+msg_id).focus();
+        number = 0;
+        document.getElementById('parent_id_reply').value = general_parent_id;
+        document.getElementById('msg_action_' + msg_id).style.display = 'none';
+        var msg_id_reply = document.getElementById('msg_id_reply').value;
+        //pasar lo de los colores a variables y colocarlas hasta arriba
+        if (msg_id_reply != msg_id && msg_id_reply != "" && document.getElementById('response_reply_mail_'+msg_id_reply) != null) {
+            document.getElementById('response_reply_mail_'+msg_id_reply).innerHTML = "";
+            document.getElementById('reply_' + msg_id_reply).style.background = normal_color;
+            if ( document.getElementById('reply_all_' + msg_id_reply) != null) { 
+                document.getElementById('reply_all_'+ msg_id_reply).style.background = normal_color;
+            }
+            document.getElementById('forward_'+ msg_id).style.background = normal_color;
+        }
+        if (option == "reply") {
+            document.getElementById('reply_'+msg_id).style.background = select_color;
+        } else {
+            document.getElementById('reply_'+msg_id).style.background = normal_color;
+        }
+        if (option == "reply_all") {
+            document.getElementById('reply_all_'+msg_id).style.background = select_color;
+        } else {
+            if ( document.getElementById('reply_all_'+msg_id) != null) { 
+                document.getElementById('reply_all_'+msg_id).style.background = normal_color;
+            }
+        }
+        if (option == "forward") {
+            document.getElementById('forward_'+msg_id).style.background = select_color;
+        } else {
+            document.getElementById('forward_'+msg_id).style.background = normal_color;
+        }
+        document.getElementById('option_reply').value = option;
+        if (document.getElementById('response_reply_mail_'+msg_id).innerHTML.length < 14 ) { 
+            document.getElementById('top_menu').style.display = 'none';
+            document.getElementById('msg_id_reply').value = msg_id;
+            var url = "send?reply_p=1&reply_send=0&msg_id="+msg_id+ "&general_parent_id=" + general_parent_id + "&reply_option=" + option;
+            var request = YAHOO.util.Connect.asyncRequest('GET', url, callback_reply_dialog);
+        } else {
+            var option = document.getElementById('option_reply').value;
+            if (option == "forward") {
+                document.getElementById('attachment_forward').style.display = '';
+            }  else {
+                document.getElementById('attachment_forward').style.display = 'none';
+            }
+            var rel_types_checked = document.getElementById('rel_types_checked').value.split(",");
+            if (option == "reply_all" ) {
+                for (var i=0;i < rel_types_checked.length; i = i + 1) {
+                    if (document.getElementById(rel_types_checked[i]) != null) {
+                        document.getElementById(rel_types_checked[i]).checked = true;
+                    }
+                }
+            } else {
+                for (var i=0;i < rel_types_checked.length; i = i + 1) {
+                    if (document.getElementById(rel_types_checked[i]) != null) {
+                        document.getElementById(rel_types_checked[i]).checked = false;
+                    }
+                }
+            }
+            if (YAHOO.env.ua.ie) {
+                var offset = document.getElementById('scroll_message_'+msg_id).offsetTop;
+                scrollTo(0,offset + 200);
+            } else {
+                document.getElementById('scroll_message_'+msg_id).scrollTo();
+            }
+            reply_message(option,msg_id);
+        }
+    }
+}
+
+var handleSuccess_reply_dialog = function(o){
+    var msg_id = document.getElementById('msg_id_reply').value;
+    if (YAHOO.env.ua.ie) {
+        var offset = document.getElementById('scroll_message_'+msg_id).offsetTop;
+        scrollTo(0,offset + 200);
+    } else {
+
+        document.getElementById('scroll_message_'+msg_id).scrollTo();
+    }
+    var option = document.getElementById('option_reply').value;
+    document.getElementById('response_reply_mail_'+msg_id).innerHTML = o.responseText;
+    document.getElementById('top_menu').style.display = '';
+    //window.location.hash = "#scroll_message_"+msg_id;
+    if (option == "forward") {
+        document.getElementById('attachment_forward').style.display = '';
+    }  else {
+        document.getElementById('attachment_forward').style.display = 'none';
+    }
+    if (document.getElementById('rel_types_checked').value != "" ) {
+        var rel_types_checked = document.getElementById('rel_types_checked').value.split(",");
+        if (option == "reply_all" ) {
+            for (var i=0;i < rel_types_checked.length; i = i + 1) {
+                try { 
+                    document.getElementById(rel_types_checked[i]).checked = true;
+                } catch(err) {}
+            }
+        } else {
+            for (var i=0;i < rel_types_checked.length; i = i + 1) {
+                try {
+                    document.getElementById(rel_types_checked[i]).checked = false;
+                } catch(err) {}
+            }
+        }
+    }
+    reply_message(option,msg_id);
+    contact_users();
+    initEditors();
+    (new Image()).src='inbox/images/token.gif';
+    (new Image()).src='inbox/images/token_selected.gif';
+    (new Image()).src='inbox/images/token_hover.gif';
+    (new Image()).src='inbox/images/token_x.gif';
+};
+
+var handleFailure_reply_dialog = function(o){
+};
+
+var callback_reply_dialog = {success:handleSuccess_reply_dialog,failure:handleFailure_reply_dialog};
+
+function valid_search(value) {
+    if (value != "") {
+        document.getElementById('search_button').disabled = false;
+    } else {
+        document.getElementById('search_button').disabled = true;
+    }
+}
+
+function checkKey(key,value) {
+    var unicode
+    if (key.charCode) {
+        unicode=key.charCode;
+    } else {
+        unicode=key.keyCode;
+    }
+    if (unicode == 13){
+        valid_search(value);
+        search();
+    }
+}
+
+function search(page_number) {
+    document.getElementById('show_delete').style.display = '';
+    document.getElementById('move_to').style.display = '';
+    document.getElementById('show_delete').style.display = '';
+    document.getElementById('msg_system').style.display = 'none';
+    var data_search = document.getElementById('search_data').value;
+    var url_search = "search";
+    if (page_number == undefined ) {
+        var postData = "data_search=" + data_search;
+    } else {
+        var postData = "data_search=" + data_search + "&page_number=" + page_number;
+    }
+    document.getElementById('msg_action').style.display = '';
+    loading();
+    var request = YAHOO.util.Connect.asyncRequest('POST', url_search, callback_search,postData);
+}
+
+var handleSuccess_search = function(o){
+    document.getElementById('msg_action').style.display = 'none';
+    document.getElementById('msg_actions').innerHTML = '';
+    document.getElementById('folder_section_display').innerHTML = o.responseText;
+};
+
+var handleFailure_search = function(o){
+};
+
+var callback_search = {success:handleSuccess_search,failure:handleFailure_search};
+
+
+function delete_text_input (name) {
+    var text_length = document.getElementById('autocomplete_input').value.length;
+    if ( text_length > 0 && this.focus() != undefined) {
+        alert(document.getElementById('autocomplete_input').value + "  " + document.getElementById('msg_invalid_user').value);
+    }
+    document.getElementById('autocomplete_input').value = '';
+}
+
+function loading() {
+    document.getElementById('msg_action').style.display = '';
+    var text_action = document.getElementById('msg_loading').value;
+    document.getElementById('msg_actions').innerHTML = text_action + " <image src=/resources/ajaxhelper/images/indicator.gif>";
+}
+
+function loading_cancel() {
+    document.getElementById('msg_action').style.display = 'none';
+    document.getElementById('msg_actions').innerHTML = '';
+}
+
+function empty_trash() {
+    var continue_empty = confirm(document.getElementById("msg_continue_empty").value);
+    if (!continue_empty ) {
+        self.close
+    } else {
+        loading();
+        var url = "empty-trash";
+        var request = YAHOO.util.Connect.asyncRequest('GET', url, callback_empty_trash);
+    }
+}
+
+var handleSuccess_empty_trash = function(o){
+    document.getElementById('folder_id').value = 3;
+    get_messages_folder('more_actions');
+    document.getElementById('msg_action_p').value = 't';
+    document.getElementById('msg_action').style.display = '';
+    var text_action = document.getElementById('confirm_trash_empty').value;
+    document.getElementById('msg_actions').innerHTML = text_action;
+};
+
+var handleFailure_empty_trash = function(o){ };
+
+var callback_empty_trash = {success:handleSuccess_empty_trash,failure:handleFailure_empty_trash};
+
+function id_delete (contact_id) {
+    document.getElementById('span_' + contact_id).innerHTML = '';
+    var add = document.getElementById('contact_reply');
+    var olddiv = document.getElementById('span_' + contact_id);
+    add.removeChild(olddiv);
+}
+
+function select_all(form,option){
+    var formObject = document.getElementsByName(form.name);
+    var form = formObject[0];
+    var elements = form.elements;
+    for ( var i = 0; i < elements.length ; i++){
+        if (elements[i].name == 'subject_id' && elements[i].type == "checkbox") {
+            elements[i].checked = option;
+        }
+    }
+} 
+
+function select_all_rel_types(option){
+    var formObject = document.getElementsByName('compose_message');
+    var form = formObject[0];
+    var elements = form.elements;
+    var rel_types_checked_p = 0;
+    for ( var i = 0; i < elements.length ; i++) {
+        if (elements[i].name == 'rel_types') {
+            elements[i].checked = option;
+        }
+    }
+}
+
+function semuestra (name) {
+    document.getElementById(name).style.visible = '';
+}
+
+function html_xinha_editor (option) {
+    var editor = Xinha.getEditor("message");
+    if ( editor._doc != null) {
+        var message = editor.getEditorContent();
+        var position = message.search('id="history_conversation">');
+        if (option == 'forward') {
+            var history_conversation = document.getElementById('history_conversation').innerHTML;
+            var signature_text_position = message.search('id="signature"');
+            if ( signature_text_position > 0) {
+                var message = message.substring(0,signature_text_position-5);
+            }
+            var signature = document.getElementById('signature').innerHTML;
+            editor._doc.body.innerHTML = history_conversation + "<br><br>" + signature;
+        } else {
+            var signature = document.getElementById('signature').innerHTML;
+            if ( position > 0) {
+                var message = message.substring(0,position-15);
+            }
+            editor._doc.body.innerHTML = "<br><br><br>" +signature;
+        }
+    }
+}
Index: openacs-4/packages/messages/www/resources/next_arr.gif
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/resources/next_arr.gif,v
diff -u
Binary files differ
Index: openacs-4/packages/messages/www/resources/prototype.js
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/resources/prototype.js,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/resources/prototype.js	31 Aug 2010 21:30:48 -0000	1.1
@@ -0,0 +1,4223 @@
+/*  Prototype JavaScript framework, version 1.6.0.2
+ *  (c) 2005-2007 Sam Stephenson
+ *
+ *  Prototype is freely distributable under the terms of an MIT-style license.
+ *  For details, see the Prototype web site: http://www.prototypejs.org/
+ *
+ *--------------------------------------------------------------------------*/
+
+var Prototype = {
+  Version: '1.6.0.2',
+
+  Browser: {
+    IE:     !!(window.attachEvent && !window.opera),
+    Opera:  !!window.opera,
+    WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
+    Gecko:  navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1,
+    MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/)
+  },
+
+  BrowserFeatures: {
+    XPath: !!document.evaluate,
+    ElementExtensions: !!window.HTMLElement,
+    SpecificElementExtensions:
+      document.createElement('div').__proto__ &&
+      document.createElement('div').__proto__ !==
+        document.createElement('form').__proto__
+  },
+
+  ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
+  JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
+
+  emptyFunction: function() { },
+  K: function(x) { return x }
+};
+
+if (Prototype.Browser.MobileSafari)
+  Prototype.BrowserFeatures.SpecificElementExtensions = false;
+
+
+/* Based on Alex Arnell's inheritance implementation. */
+var Class = {
+  create: function() {
+    var parent = null, properties = $A(arguments);
+    if (Object.isFunction(properties[0]))
+      parent = properties.shift();
+
+    function klass() {
+      this.initialize.apply(this, arguments);
+    }
+
+    Object.extend(klass, Class.Methods);
+    klass.superclass = parent;
+    klass.subclasses = [];
+
+    if (parent) {
+      var subclass = function() { };
+      subclass.prototype = parent.prototype;
+      klass.prototype = new subclass;
+      parent.subclasses.push(klass);
+    }
+
+    for (var i = 0; i < properties.length; i++)
+      klass.addMethods(properties[i]);
+
+    if (!klass.prototype.initialize)
+      klass.prototype.initialize = Prototype.emptyFunction;
+
+    klass.prototype.constructor = klass;
+
+    return klass;
+  }
+};
+
+Class.Methods = {
+  addMethods: function(source) {
+    var ancestor   = this.superclass && this.superclass.prototype;
+    var properties = Object.keys(source);
+
+    if (!Object.keys({ toString: true }).length)
+      properties.push("toString", "valueOf");
+
+    for (var i = 0, length = properties.length; i < length; i++) {
+      var property = properties[i], value = source[property];
+      if (ancestor && Object.isFunction(value) &&
+          value.argumentNames().first() == "$super") {
+        var method = value, value = Object.extend((function(m) {
+          return function() { return ancestor[m].apply(this, arguments) };
+        })(property).wrap(method), {
+          valueOf:  function() { return method },
+          toString: function() { return method.toString() }
+        });
+      }
+      this.prototype[property] = value;
+    }
+
+    return this;
+  }
+};
+
+var Abstract = { };
+
+Object.extend = function(destination, source) {
+  for (var property in source)
+    destination[property] = source[property];
+  return destination;
+};
+
+Object.extend(Object, {
+  inspect: function(object) {
+    try {
+      if (Object.isUndefined(object)) return 'undefined';
+      if (object === null) return 'null';
+      return object.inspect ? object.inspect() : String(object);
+    } catch (e) {
+      if (e instanceof RangeError) return '...';
+      throw e;
+    }
+  },
+
+  toJSON: function(object) {
+    var type = typeof object;
+    switch (type) {
+      case 'undefined':
+      case 'function':
+      case 'unknown': return;
+      case 'boolean': return object.toString();
+    }
+
+    if (object === null) return 'null';
+    if (object.toJSON) return object.toJSON();
+    if (Object.isElement(object)) return;
+
+    var results = [];
+    for (var property in object) {
+      var value = Object.toJSON(object[property]);
+      if (!Object.isUndefined(value))
+        results.push(property.toJSON() + ': ' + value);
+    }
+
+    return '{' + results.join(', ') + '}';
+  },
+
+  toQueryString: function(object) {
+    return $H(object).toQueryString();
+  },
+
+  toHTML: function(object) {
+    return object && object.toHTML ? object.toHTML() : String.interpret(object);
+  },
+
+  keys: function(object) {
+    var keys = [];
+    for (var property in object)
+      keys.push(property);
+    return keys;
+  },
+
+  values: function(object) {
+    var values = [];
+    for (var property in object)
+      values.push(object[property]);
+    return values;
+  },
+
+  clone: function(object) {
+    return Object.extend({ }, object);
+  },
+
+  isElement: function(object) {
+    return object && object.nodeType == 1;
+  },
+
+  isArray: function(object) {
+    return object != null && typeof object == "object" &&
+      'splice' in object && 'join' in object;
+  },
+
+  isHash: function(object) {
+    return object instanceof Hash;
+  },
+
+  isFunction: function(object) {
+    return typeof object == "function";
+  },
+
+  isString: function(object) {
+    return typeof object == "string";
+  },
+
+  isNumber: function(object) {
+    return typeof object == "number";
+  },
+
+  isUndefined: function(object) {
+    return typeof object == "undefined";
+  }
+});
+
+Object.extend(Function.prototype, {
+  argumentNames: function() {
+    var names = this.toString().match(/^[\s\(]*function[^(]*\((.*?)\)/)[1].split(",").invoke("strip");
+    return names.length == 1 && !names[0] ? [] : names;
+  },
+
+  bind: function() {
+    if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
+    var __method = this, args = $A(arguments), object = args.shift();
+    return function() {
+      return __method.apply(object, args.concat($A(arguments)));
+    }
+  },
+
+  bindAsEventListener: function() {
+    var __method = this, args = $A(arguments), object = args.shift();
+    return function(event) {
+      return __method.apply(object, [event || window.event].concat(args));
+    }
+  },
+
+  curry: function() {
+    if (!arguments.length) return this;
+    var __method = this, args = $A(arguments);
+    return function() {
+      return __method.apply(this, args.concat($A(arguments)));
+    }
+  },
+
+  delay: function() {
+    var __method = this, args = $A(arguments), timeout = args.shift() * 1000;
+    return window.setTimeout(function() {
+      return __method.apply(__method, args);
+    }, timeout);
+  },
+
+  wrap: function(wrapper) {
+    var __method = this;
+    return function() {
+      return wrapper.apply(this, [__method.bind(this)].concat($A(arguments)));
+    }
+  },
+
+  methodize: function() {
+    if (this._methodized) return this._methodized;
+    var __method = this;
+    return this._methodized = function() {
+      return __method.apply(null, [this].concat($A(arguments)));
+    };
+  }
+});
+
+Function.prototype.defer = Function.prototype.delay.curry(0.01);
+
+Date.prototype.toJSON = function() {
+  return '"' + this.getUTCFullYear() + '-' +
+    (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
+    this.getUTCDate().toPaddedString(2) + 'T' +
+    this.getUTCHours().toPaddedString(2) + ':' +
+    this.getUTCMinutes().toPaddedString(2) + ':' +
+    this.getUTCSeconds().toPaddedString(2) + 'Z"';
+};
+
+var Try = {
+  these: function() {
+    var returnValue;
+
+    for (var i = 0, length = arguments.length; i < length; i++) {
+      var lambda = arguments[i];
+      try {
+        returnValue = lambda();
+        break;
+      } catch (e) { }
+    }
+
+    return returnValue;
+  }
+};
+
+RegExp.prototype.match = RegExp.prototype.test;
+
+RegExp.escape = function(str) {
+  return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
+};
+
+/*--------------------------------------------------------------------------*/
+
+var PeriodicalExecuter = Class.create({
+  initialize: function(callback, frequency) {
+    this.callback = callback;
+    this.frequency = frequency;
+    this.currentlyExecuting = false;
+
+    this.registerCallback();
+  },
+
+  registerCallback: function() {
+    this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+  },
+
+  execute: function() {
+    this.callback(this);
+  },
+
+  stop: function() {
+    if (!this.timer) return;
+    clearInterval(this.timer);
+    this.timer = null;
+  },
+
+  onTimerEvent: function() {
+    if (!this.currentlyExecuting) {
+      try {
+        this.currentlyExecuting = true;
+        this.execute();
+      } finally {
+        this.currentlyExecuting = false;
+      }
+    }
+  }
+});
+Object.extend(String, {
+  interpret: function(value) {
+    return value == null ? '' : String(value);
+  },
+  specialChar: {
+    '\b': '\\b',
+    '\t': '\\t',
+    '\n': '\\n',
+    '\f': '\\f',
+    '\r': '\\r',
+    '\\': '\\\\'
+  }
+});
+
+Object.extend(String.prototype, {
+  gsub: function(pattern, replacement) {
+    var result = '', source = this, match;
+    replacement = arguments.callee.prepareReplacement(replacement);
+
+    while (source.length > 0) {
+      if (match = source.match(pattern)) {
+        result += source.slice(0, match.index);
+        result += String.interpret(replacement(match));
+        source  = source.slice(match.index + match[0].length);
+      } else {
+        result += source, source = '';
+      }
+    }
+    return result;
+  },
+
+  sub: function(pattern, replacement, count) {
+    replacement = this.gsub.prepareReplacement(replacement);
+    count = Object.isUndefined(count) ? 1 : count;
+
+    return this.gsub(pattern, function(match) {
+      if (--count < 0) return match[0];
+      return replacement(match);
+    });
+  },
+
+  scan: function(pattern, iterator) {
+    this.gsub(pattern, iterator);
+    return String(this);
+  },
+
+  truncate: function(length, truncation) {
+    length = length || 30;
+    truncation = Object.isUndefined(truncation) ? '...' : truncation;
+    return this.length > length ?
+      this.slice(0, length - truncation.length) + truncation : String(this);
+  },
+
+  strip: function() {
+    return this.replace(/^\s+/, '').replace(/\s+$/, '');
+  },
+
+  stripTags: function() {
+    return this.replace(/<\/?[^>]+>/gi, '');
+  },
+
+  stripScripts: function() {
+    return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
+  },
+
+  extractScripts: function() {
+    var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
+    var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
+    return (this.match(matchAll) || []).map(function(scriptTag) {
+      return (scriptTag.match(matchOne) || ['', ''])[1];
+    });
+  },
+
+  evalScripts: function() {
+    return this.extractScripts().map(function(script) { return eval(script) });
+  },
+
+  escapeHTML: function() {
+    var self = arguments.callee;
+    self.text.data = this;
+    return self.div.innerHTML;
+  },
+
+  unescapeHTML: function() {
+    var div = new Element('div');
+    div.innerHTML = this.stripTags();
+    return div.childNodes[0] ? (div.childNodes.length > 1 ?
+      $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) :
+      div.childNodes[0].nodeValue) : '';
+  },
+
+  toQueryParams: function(separator) {
+    var match = this.strip().match(/([^?#]*)(#.*)?$/);
+    if (!match) return { };
+
+    return match[1].split(separator || '&').inject({ }, function(hash, pair) {
+      if ((pair = pair.split('='))[0]) {
+        var key = decodeURIComponent(pair.shift());
+        var value = pair.length > 1 ? pair.join('=') : pair[0];
+        if (value != undefined) value = decodeURIComponent(value);
+
+        if (key in hash) {
+          if (!Object.isArray(hash[key])) hash[key] = [hash[key]];
+          hash[key].push(value);
+        }
+        else hash[key] = value;
+      }
+      return hash;
+    });
+  },
+
+  toArray: function() {
+    return this.split('');
+  },
+
+  succ: function() {
+    return this.slice(0, this.length - 1) +
+      String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
+  },
+
+  times: function(count) {
+    return count < 1 ? '' : new Array(count + 1).join(this);
+  },
+
+  camelize: function() {
+    var parts = this.split('-'), len = parts.length;
+    if (len == 1) return parts[0];
+
+    var camelized = this.charAt(0) == '-'
+      ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
+      : parts[0];
+
+    for (var i = 1; i < len; i++)
+      camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
+
+    return camelized;
+  },
+
+  capitalize: function() {
+    return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
+  },
+
+  underscore: function() {
+    return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
+  },
+
+  dasherize: function() {
+    return this.gsub(/_/,'-');
+  },
+
+  inspect: function(useDoubleQuotes) {
+    var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) {
+      var character = String.specialChar[match[0]];
+      return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
+    });
+    if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
+    return "'" + escapedString.replace(/'/g, '\\\'') + "'";
+  },
+
+  toJSON: function() {
+    return this.inspect(true);
+  },
+
+  unfilterJSON: function(filter) {
+    return this.sub(filter || Prototype.JSONFilter, '#{1}');
+  },
+
+  isJSON: function() {
+    var str = this;
+    if (str.blank()) return false;
+    str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
+    return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);
+  },
+
+  evalJSON: function(sanitize) {
+    var json = this.unfilterJSON();
+    try {
+      if (!sanitize || json.isJSON()) return eval('(' + json + ')');
+    } catch (e) { }
+    throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
+  },
+
+  include: function(pattern) {
+    return this.indexOf(pattern) > -1;
+  },
+
+  startsWith: function(pattern) {
+    return this.indexOf(pattern) === 0;
+  },
+
+  endsWith: function(pattern) {
+    var d = this.length - pattern.length;
+    return d >= 0 && this.lastIndexOf(pattern) === d;
+  },
+
+  empty: function() {
+    return this == '';
+  },
+
+  blank: function() {
+    return /^\s*$/.test(this);
+  },
+
+  interpolate: function(object, pattern) {
+    return new Template(this, pattern).evaluate(object);
+  }
+});
+
+if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, {
+  escapeHTML: function() {
+    return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
+  },
+  unescapeHTML: function() {
+    return this.replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
+  }
+});
+
+String.prototype.gsub.prepareReplacement = function(replacement) {
+  if (Object.isFunction(replacement)) return replacement;
+  var template = new Template(replacement);
+  return function(match) { return template.evaluate(match) };
+};
+
+String.prototype.parseQuery = String.prototype.toQueryParams;
+
+Object.extend(String.prototype.escapeHTML, {
+  div:  document.createElement('div'),
+  text: document.createTextNode('')
+});
+
+with (String.prototype.escapeHTML) div.appendChild(text);
+
+var Template = Class.create({
+  initialize: function(template, pattern) {
+    this.template = template.toString();
+    this.pattern = pattern || Template.Pattern;
+  },
+
+  evaluate: function(object) {
+    if (Object.isFunction(object.toTemplateReplacements))
+      object = object.toTemplateReplacements();
+
+    return this.template.gsub(this.pattern, function(match) {
+      if (object == null) return '';
+
+      var before = match[1] || '';
+      if (before == '\\') return match[2];
+
+      var ctx = object, expr = match[3];
+      var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
+      match = pattern.exec(expr);
+      if (match == null) return before;
+
+      while (match != null) {
+        var comp = match[1].startsWith('[') ? match[2].gsub('\\\\]', ']') : match[1];
+        ctx = ctx[comp];
+        if (null == ctx || '' == match[3]) break;
+        expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
+        match = pattern.exec(expr);
+      }
+
+      return before + String.interpret(ctx);
+    });
+  }
+});
+Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
+
+var $break = { };
+
+var Enumerable = {
+  each: function(iterator, context) {
+    var index = 0;
+    iterator = iterator.bind(context);
+    try {
+      this._each(function(value) {
+        iterator(value, index++);
+      });
+    } catch (e) {
+      if (e != $break) throw e;
+    }
+    return this;
+  },
+
+  eachSlice: function(number, iterator, context) {
+    iterator = iterator ? iterator.bind(context) : Prototype.K;
+    var index = -number, slices = [], array = this.toArray();
+    while ((index += number) < array.length)
+      slices.push(array.slice(index, index+number));
+    return slices.collect(iterator, context);
+  },
+
+  all: function(iterator, context) {
+    iterator = iterator ? iterator.bind(context) : Prototype.K;
+    var result = true;
+    this.each(function(value, index) {
+      result = result && !!iterator(value, index);
+      if (!result) throw $break;
+    });
+    return result;
+  },
+
+  any: function(iterator, context) {
+    iterator = iterator ? iterator.bind(context) : Prototype.K;
+    var result = false;
+    this.each(function(value, index) {
+      if (result = !!iterator(value, index))
+        throw $break;
+    });
+    return result;
+  },
+
+  collect: function(iterator, context) {
+    iterator = iterator ? iterator.bind(context) : Prototype.K;
+    var results = [];
+    this.each(function(value, index) {
+      results.push(iterator(value, index));
+    });
+    return results;
+  },
+
+  detect: function(iterator, context) {
+    iterator = iterator.bind(context);
+    var result;
+    this.each(function(value, index) {
+      if (iterator(value, index)) {
+        result = value;
+        throw $break;
+      }
+    });
+    return result;
+  },
+
+  findAll: function(iterator, context) {
+    iterator = iterator.bind(context);
+    var results = [];
+    this.each(function(value, index) {
+      if (iterator(value, index))
+        results.push(value);
+    });
+    return results;
+  },
+
+  grep: function(filter, iterator, context) {
+    iterator = iterator ? iterator.bind(context) : Prototype.K;
+    var results = [];
+
+    if (Object.isString(filter))
+      filter = new RegExp(filter);
+
+    this.each(function(value, index) {
+      if (filter.match(value))
+        results.push(iterator(value, index));
+    });
+    return results;
+  },
+
+  include: function(object) {
+    if (Object.isFunction(this.indexOf))
+      if (this.indexOf(object) != -1) return true;
+
+    var found = false;
+    this.each(function(value) {
+      if (value == object) {
+        found = true;
+        throw $break;
+      }
+    });
+    return found;
+  },
+
+  inGroupsOf: function(number, fillWith) {
+    fillWith = Object.isUndefined(fillWith) ? null : fillWith;
+    return this.eachSlice(number, function(slice) {
+      while(slice.length < number) slice.push(fillWith);
+      return slice;
+    });
+  },
+
+  inject: function(memo, iterator, context) {
+    iterator = iterator.bind(context);
+    this.each(function(value, index) {
+      memo = iterator(memo, value, index);
+    });
+    return memo;
+  },
+
+  invoke: function(method) {
+    var args = $A(arguments).slice(1);
+    return this.map(function(value) {
+      return value[method].apply(value, args);
+    });
+  },
+
+  max: function(iterator, context) {
+    iterator = iterator ? iterator.bind(context) : Prototype.K;
+    var result;
+    this.each(function(value, index) {
+      value = iterator(value, index);
+      if (result == null || value >= result)
+        result = value;
+    });
+    return result;
+  },
+
+  min: function(iterator, context) {
+    iterator = iterator ? iterator.bind(context) : Prototype.K;
+    var result;
+    this.each(function(value, index) {
+      value = iterator(value, index);
+      if (result == null || value < result)
+        result = value;
+    });
+    return result;
+  },
+
+  partition: function(iterator, context) {
+    iterator = iterator ? iterator.bind(context) : Prototype.K;
+    var trues = [], falses = [];
+    this.each(function(value, index) {
+      (iterator(value, index) ?
+        trues : falses).push(value);
+    });
+    return [trues, falses];
+  },
+
+  pluck: function(property) {
+    var results = [];
+    this.each(function(value) {
+      results.push(value[property]);
+    });
+    return results;
+  },
+
+  reject: function(iterator, context) {
+    iterator = iterator.bind(context);
+    var results = [];
+    this.each(function(value, index) {
+      if (!iterator(value, index))
+        results.push(value);
+    });
+    return results;
+  },
+
+  sortBy: function(iterator, context) {
+    iterator = iterator.bind(context);
+    return this.map(function(value, index) {
+      return {value: value, criteria: iterator(value, index)};
+    }).sort(function(left, right) {
+      var a = left.criteria, b = right.criteria;
+      return a < b ? -1 : a > b ? 1 : 0;
+    }).pluck('value');
+  },
+
+  toArray: function() {
+    return this.map();
+  },
+
+  zip: function() {
+    var iterator = Prototype.K, args = $A(arguments);
+    if (Object.isFunction(args.last()))
+      iterator = args.pop();
+
+    var collections = [this].concat(args).map($A);
+    return this.map(function(value, index) {
+      return iterator(collections.pluck(index));
+    });
+  },
+
+  size: function() {
+    return this.toArray().length;
+  },
+
+  inspect: function() {
+    return '#<Enumerable:' + this.toArray().inspect() + '>';
+  }
+};
+
+Object.extend(Enumerable, {
+  map:     Enumerable.collect,
+  find:    Enumerable.detect,
+  select:  Enumerable.findAll,
+  filter:  Enumerable.findAll,
+  member:  Enumerable.include,
+  entries: Enumerable.toArray,
+  every:   Enumerable.all,
+  some:    Enumerable.any
+});
+function $A(iterable) {
+  if (!iterable) return [];
+  if (iterable.toArray) return iterable.toArray();
+  var length = iterable.length || 0, results = new Array(length);
+  while (length--) results[length] = iterable[length];
+  return results;
+}
+
+if (Prototype.Browser.WebKit) {
+  $A = function(iterable) {
+    if (!iterable) return [];
+    if (!(Object.isFunction(iterable) && iterable == '[object NodeList]') &&
+        iterable.toArray) return iterable.toArray();
+    var length = iterable.length || 0, results = new Array(length);
+    while (length--) results[length] = iterable[length];
+    return results;
+  };
+}
+
+Array.from = $A;
+
+Object.extend(Array.prototype, Enumerable);
+
+if (!Array.prototype._reverse) Array.prototype._reverse = Array.prototype.reverse;
+
+Object.extend(Array.prototype, {
+  _each: function(iterator) {
+    for (var i = 0, length = this.length; i < length; i++)
+      iterator(this[i]);
+  },
+
+  clear: function() {
+    this.length = 0;
+    return this;
+  },
+
+  first: function() {
+    return this[0];
+  },
+
+  last: function() {
+    return this[this.length - 1];
+  },
+
+  compact: function() {
+    return this.select(function(value) {
+      return value != null;
+    });
+  },
+
+  flatten: function() {
+    return this.inject([], function(array, value) {
+      return array.concat(Object.isArray(value) ?
+        value.flatten() : [value]);
+    });
+  },
+
+  without: function() {
+    var values = $A(arguments);
+    return this.select(function(value) {
+      return !values.include(value);
+    });
+  },
+
+  reverse: function(inline) {
+    return (inline !== false ? this : this.toArray())._reverse();
+  },
+
+  reduce: function() {
+    return this.length > 1 ? this : this[0];
+  },
+
+  uniq: function(sorted) {
+    return this.inject([], function(array, value, index) {
+      if (0 == index || (sorted ? array.last() != value : !array.include(value)))
+        array.push(value);
+      return array;
+    });
+  },
+
+  intersect: function(array) {
+    return this.uniq().findAll(function(item) {
+      return array.detect(function(value) { return item === value });
+    });
+  },
+
+  clone: function() {
+    return [].concat(this);
+  },
+
+  size: function() {
+    return this.length;
+  },
+
+  inspect: function() {
+    return '[' + this.map(Object.inspect).join(', ') + ']';
+  },
+
+  toJSON: function() {
+    var results = [];
+    this.each(function(object) {
+      var value = Object.toJSON(object);
+      if (!Object.isUndefined(value)) results.push(value);
+    });
+    return '[' + results.join(', ') + ']';
+  }
+});
+
+// use native browser JS 1.6 implementation if available
+if (Object.isFunction(Array.prototype.forEach))
+  Array.prototype._each = Array.prototype.forEach;
+
+if (!Array.prototype.indexOf) Array.prototype.indexOf = function(item, i) {
+  i || (i = 0);
+  var length = this.length;
+  if (i < 0) i = length + i;
+  for (; i < length; i++)
+    if (this[i] === item) return i;
+  return -1;
+};
+
+if (!Array.prototype.lastIndexOf) Array.prototype.lastIndexOf = function(item, i) {
+  i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
+  var n = this.slice(0, i).reverse().indexOf(item);
+  return (n < 0) ? n : i - n - 1;
+};
+
+Array.prototype.toArray = Array.prototype.clone;
+
+function $w(string) {
+  if (!Object.isString(string)) return [];
+  string = string.strip();
+  return string ? string.split(/\s+/) : [];
+}
+
+if (Prototype.Browser.Opera){
+  Array.prototype.concat = function() {
+    var array = [];
+    for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);
+    for (var i = 0, length = arguments.length; i < length; i++) {
+      if (Object.isArray(arguments[i])) {
+        for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
+          array.push(arguments[i][j]);
+      } else {
+        array.push(arguments[i]);
+      }
+    }
+    return array;
+  };
+}
+Object.extend(Number.prototype, {
+  toColorPart: function() {
+    return this.toPaddedString(2, 16);
+  },
+
+  succ: function() {
+    return this + 1;
+  },
+
+  times: function(iterator) {
+    $R(0, this, true).each(iterator);
+    return this;
+  },
+
+  toPaddedString: function(length, radix) {
+    var string = this.toString(radix || 10);
+    return '0'.times(length - string.length) + string;
+  },
+
+  toJSON: function() {
+    return isFinite(this) ? this.toString() : 'null';
+  }
+});
+
+$w('abs round ceil floor').each(function(method){
+  Number.prototype[method] = Math[method].methodize();
+});
+function $H(object) {
+  return new Hash(object);
+};
+
+var Hash = Class.create(Enumerable, (function() {
+
+  function toQueryPair(key, value) {
+    if (Object.isUndefined(value)) return key;
+    return key + '=' + encodeURIComponent(String.interpret(value));
+  }
+
+  return {
+    initialize: function(object) {
+      this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
+    },
+
+    _each: function(iterator) {
+      for (var key in this._object) {
+        var value = this._object[key], pair = [key, value];
+        pair.key = key;
+        pair.value = value;
+        iterator(pair);
+      }
+    },
+
+    set: function(key, value) {
+      return this._object[key] = value;
+    },
+
+    get: function(key) {
+      return this._object[key];
+    },
+
+    unset: function(key) {
+      var value = this._object[key];
+      delete this._object[key];
+      return value;
+    },
+
+    toObject: function() {
+      return Object.clone(this._object);
+    },
+
+    keys: function() {
+      return this.pluck('key');
+    },
+
+    values: function() {
+      return this.pluck('value');
+    },
+
+    index: function(value) {
+      var match = this.detect(function(pair) {
+        return pair.value === value;
+      });
+      return match && match.key;
+    },
+
+    merge: function(object) {
+      return this.clone().update(object);
+    },
+
+    update: function(object) {
+      return new Hash(object).inject(this, function(result, pair) {
+        result.set(pair.key, pair.value);
+        return result;
+      });
+    },
+
+    toQueryString: function() {
+      return this.map(function(pair) {
+        var key = encodeURIComponent(pair.key), values = pair.value;
+
+        if (values && typeof values == 'object') {
+          if (Object.isArray(values))
+            return values.map(toQueryPair.curry(key)).join('&');
+        }
+        return toQueryPair(key, values);
+      }).join('&');
+    },
+
+    inspect: function() {
+      return '#<Hash:{' + this.map(function(pair) {
+        return pair.map(Object.inspect).join(': ');
+      }).join(', ') + '}>';
+    },
+
+    toJSON: function() {
+      return Object.toJSON(this.toObject());
+    },
+
+    clone: function() {
+      return new Hash(this);
+    }
+  }
+})());
+
+Hash.prototype.toTemplateReplacements = Hash.prototype.toObject;
+Hash.from = $H;
+var ObjectRange = Class.create(Enumerable, {
+  initialize: function(start, end, exclusive) {
+    this.start = start;
+    this.end = end;
+    this.exclusive = exclusive;
+  },
+
+  _each: function(iterator) {
+    var value = this.start;
+    while (this.include(value)) {
+      iterator(value);
+      value = value.succ();
+    }
+  },
+
+  include: function(value) {
+    if (value < this.start)
+      return false;
+    if (this.exclusive)
+      return value < this.end;
+    return value <= this.end;
+  }
+});
+
+var $R = function(start, end, exclusive) {
+  return new ObjectRange(start, end, exclusive);
+};
+
+var Ajax = {
+  getTransport: function() {
+    return Try.these(
+      function() {return new XMLHttpRequest()},
+      function() {return new ActiveXObject('Msxml2.XMLHTTP')},
+      function() {return new ActiveXObject('Microsoft.XMLHTTP')}
+    ) || false;
+  },
+
+  activeRequestCount: 0
+};
+
+Ajax.Responders = {
+  responders: [],
+
+  _each: function(iterator) {
+    this.responders._each(iterator);
+  },
+
+  register: function(responder) {
+    if (!this.include(responder))
+      this.responders.push(responder);
+  },
+
+  unregister: function(responder) {
+    this.responders = this.responders.without(responder);
+  },
+
+  dispatch: function(callback, request, transport, json) {
+    this.each(function(responder) {
+      if (Object.isFunction(responder[callback])) {
+        try {
+          responder[callback].apply(responder, [request, transport, json]);
+        } catch (e) { }
+      }
+    });
+  }
+};
+
+Object.extend(Ajax.Responders, Enumerable);
+
+Ajax.Responders.register({
+  onCreate:   function() { Ajax.activeRequestCount++ },
+  onComplete: function() { Ajax.activeRequestCount-- }
+});
+
+Ajax.Base = Class.create({
+  initialize: function(options) {
+    this.options = {
+      method:       'post',
+      asynchronous: true,
+      contentType:  'application/x-www-form-urlencoded',
+      encoding:     'UTF-8',
+      parameters:   '',
+      evalJSON:     true,
+      evalJS:       true
+    };
+    Object.extend(this.options, options || { });
+
+    this.options.method = this.options.method.toLowerCase();
+
+    if (Object.isString(this.options.parameters))
+      this.options.parameters = this.options.parameters.toQueryParams();
+    else if (Object.isHash(this.options.parameters))
+      this.options.parameters = this.options.parameters.toObject();
+  }
+});
+
+Ajax.Request = Class.create(Ajax.Base, {
+  _complete: false,
+
+  initialize: function($super, url, options) {
+    $super(options);
+    this.transport = Ajax.getTransport();
+    this.request(url);
+  },
+
+  request: function(url) {
+    this.url = url;
+    this.method = this.options.method;
+    var params = Object.clone(this.options.parameters);
+
+    if (!['get', 'post'].include(this.method)) {
+      // simulate other verbs over post
+      params['_method'] = this.method;
+      this.method = 'post';
+    }
+
+    this.parameters = params;
+
+    if (params = Object.toQueryString(params)) {
+      // when GET, append parameters to URL
+      if (this.method == 'get')
+        this.url += (this.url.include('?') ? '&' : '?') + params;
+      else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
+        params += '&_=';
+    }
+
+    try {
+      var response = new Ajax.Response(this);
+      if (this.options.onCreate) this.options.onCreate(response);
+      Ajax.Responders.dispatch('onCreate', this, response);
+
+      this.transport.open(this.method.toUpperCase(), this.url,
+        this.options.asynchronous);
+
+      if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);
+
+      this.transport.onreadystatechange = this.onStateChange.bind(this);
+      this.setRequestHeaders();
+
+      this.body = this.method == 'post' ? (this.options.postBody || params) : null;
+      this.transport.send(this.body);
+
+      /* Force Firefox to handle ready state 4 for synchronous requests */
+      if (!this.options.asynchronous && this.transport.overrideMimeType)
+        this.onStateChange();
+
+    }
+    catch (e) {
+      this.dispatchException(e);
+    }
+  },
+
+  onStateChange: function() {
+    var readyState = this.transport.readyState;
+    if (readyState > 1 && !((readyState == 4) && this._complete))
+      this.respondToReadyState(this.transport.readyState);
+  },
+
+  setRequestHeaders: function() {
+    var headers = {
+      'X-Requested-With': 'XMLHttpRequest',
+      'X-Prototype-Version': Prototype.Version,
+      'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
+    };
+
+    if (this.method == 'post') {
+      headers['Content-type'] = this.options.contentType +
+        (this.options.encoding ? '; charset=' + this.options.encoding : '');
+
+      /* Force "Connection: close" for older Mozilla browsers to work
+       * around a bug where XMLHttpRequest sends an incorrect
+       * Content-length header. See Mozilla Bugzilla #246651.
+       */
+      if (this.transport.overrideMimeType &&
+          (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
+            headers['Connection'] = 'close';
+    }
+
+    // user-defined headers
+    if (typeof this.options.requestHeaders == 'object') {
+      var extras = this.options.requestHeaders;
+
+      if (Object.isFunction(extras.push))
+        for (var i = 0, length = extras.length; i < length; i += 2)
+          headers[extras[i]] = extras[i+1];
+      else
+        $H(extras).each(function(pair) { headers[pair.key] = pair.value });
+    }
+
+    for (var name in headers)
+      this.transport.setRequestHeader(name, headers[name]);
+  },
+
+  success: function() {
+    var status = this.getStatus();
+    return !status || (status >= 200 && status < 300);
+  },
+
+  getStatus: function() {
+    try {
+      return this.transport.status || 0;
+    } catch (e) { return 0 }
+  },
+
+  respondToReadyState: function(readyState) {
+    var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);
+
+    if (state == 'Complete') {
+      try {
+        this._complete = true;
+        (this.options['on' + response.status]
+         || this.options['on' + (this.success() ? 'Success' : 'Failure')]
+         || Prototype.emptyFunction)(response, response.headerJSON);
+      } catch (e) {
+        this.dispatchException(e);
+      }
+
+      var contentType = response.getHeader('Content-type');
+      if (this.options.evalJS == 'force'
+          || (this.options.evalJS && this.isSameOrigin() && contentType
+          && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
+        this.evalResponse();
+    }
+
+    try {
+      (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);
+      Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON);
+    } catch (e) {
+      this.dispatchException(e);
+    }
+
+    if (state == 'Complete') {
+      // avoid memory leak in MSIE: clean up
+      this.transport.onreadystatechange = Prototype.emptyFunction;
+    }
+  },
+
+  isSameOrigin: function() {
+    var m = this.url.match(/^\s*https?:\/\/[^\/]*/);
+    return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({
+      protocol: location.protocol,
+      domain: document.domain,
+      port: location.port ? ':' + location.port : ''
+    }));
+  },
+
+  getHeader: function(name) {
+    try {
+      return this.transport.getResponseHeader(name) || null;
+    } catch (e) { return null }
+  },
+
+  evalResponse: function() {
+    try {
+      return eval((this.transport.responseText || '').unfilterJSON());
+    } catch (e) {
+      this.dispatchException(e);
+    }
+  },
+
+  dispatchException: function(exception) {
+    (this.options.onException || Prototype.emptyFunction)(this, exception);
+    Ajax.Responders.dispatch('onException', this, exception);
+  }
+});
+
+Ajax.Request.Events =
+  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
+
+Ajax.Response = Class.create({
+  initialize: function(request){
+    this.request = request;
+    var transport  = this.transport  = request.transport,
+        readyState = this.readyState = transport.readyState;
+
+    if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
+      this.status       = this.getStatus();
+      this.statusText   = this.getStatusText();
+      this.responseText = String.interpret(transport.responseText);
+      this.headerJSON   = this._getHeaderJSON();
+    }
+
+    if(readyState == 4) {
+      var xml = transport.responseXML;
+      this.responseXML  = Object.isUndefined(xml) ? null : xml;
+      this.responseJSON = this._getResponseJSON();
+    }
+  },
+
+  status:      0,
+  statusText: '',
+
+  getStatus: Ajax.Request.prototype.getStatus,
+
+  getStatusText: function() {
+    try {
+      return this.transport.statusText || '';
+    } catch (e) { return '' }
+  },
+
+  getHeader: Ajax.Request.prototype.getHeader,
+
+  getAllHeaders: function() {
+    try {
+      return this.getAllResponseHeaders();
+    } catch (e) { return null }
+  },
+
+  getResponseHeader: function(name) {
+    return this.transport.getResponseHeader(name);
+  },
+
+  getAllResponseHeaders: function() {
+    return this.transport.getAllResponseHeaders();
+  },
+
+  _getHeaderJSON: function() {
+    var json = this.getHeader('X-JSON');
+    if (!json) return null;
+    json = decodeURIComponent(escape(json));
+    try {
+      return json.evalJSON(this.request.options.sanitizeJSON ||
+        !this.request.isSameOrigin());
+    } catch (e) {
+      this.request.dispatchException(e);
+    }
+  },
+
+  _getResponseJSON: function() {
+    var options = this.request.options;
+    if (!options.evalJSON || (options.evalJSON != 'force' &&
+      !(this.getHeader('Content-type') || '').include('application/json')) ||
+        this.responseText.blank())
+          return null;
+    try {
+      return this.responseText.evalJSON(options.sanitizeJSON ||
+        !this.request.isSameOrigin());
+    } catch (e) {
+      this.request.dispatchException(e);
+    }
+  }
+});
+
+Ajax.Updater = Class.create(Ajax.Request, {
+  initialize: function($super, container, url, options) {
+    this.container = {
+      success: (container.success || container),
+      failure: (container.failure || (container.success ? null : container))
+    };
+
+    options = Object.clone(options);
+    var onComplete = options.onComplete;
+    options.onComplete = (function(response, json) {
+      this.updateContent(response.responseText);
+      if (Object.isFunction(onComplete)) onComplete(response, json);
+    }).bind(this);
+
+    $super(url, options);
+  },
+
+  updateContent: function(responseText) {
+    var receiver = this.container[this.success() ? 'success' : 'failure'],
+        options = this.options;
+
+    if (!options.evalScripts) responseText = responseText.stripScripts();
+
+    if (receiver = $(receiver)) {
+      if (options.insertion) {
+        if (Object.isString(options.insertion)) {
+          var insertion = { }; insertion[options.insertion] = responseText;
+          receiver.insert(insertion);
+        }
+        else options.insertion(receiver, responseText);
+      }
+      else receiver.update(responseText);
+    }
+  }
+});
+
+Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
+  initialize: function($super, container, url, options) {
+    $super(options);
+    this.onComplete = this.options.onComplete;
+
+    this.frequency = (this.options.frequency || 2);
+    this.decay = (this.options.decay || 1);
+
+    this.updater = { };
+    this.container = container;
+    this.url = url;
+
+    this.start();
+  },
+
+  start: function() {
+    this.options.onComplete = this.updateComplete.bind(this);
+    this.onTimerEvent();
+  },
+
+  stop: function() {
+    this.updater.options.onComplete = undefined;
+    clearTimeout(this.timer);
+    (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
+  },
+
+  updateComplete: function(response) {
+    if (this.options.decay) {
+      this.decay = (response.responseText == this.lastText ?
+        this.decay * this.options.decay : 1);
+
+      this.lastText = response.responseText;
+    }
+    this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
+  },
+
+  onTimerEvent: function() {
+    this.updater = new Ajax.Updater(this.container, this.url, this.options);
+  }
+});
+function $(element) {
+  if (arguments.length > 1) {
+    for (var i = 0, elements = [], length = arguments.length; i < length; i++)
+      elements.push($(arguments[i]));
+    return elements;
+  }
+  if (Object.isString(element))
+    element = document.getElementById(element);
+  return Element.extend(element);
+}
+
+if (Prototype.BrowserFeatures.XPath) {
+  document._getElementsByXPath = function(expression, parentElement) {
+    var results = [];
+    var query = document.evaluate(expression, $(parentElement) || document,
+      null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
+    for (var i = 0, length = query.snapshotLength; i < length; i++)
+      results.push(Element.extend(query.snapshotItem(i)));
+    return results;
+  };
+}
+
+/*--------------------------------------------------------------------------*/
+
+if (!window.Node) var Node = { };
+
+if (!Node.ELEMENT_NODE) {
+  // DOM level 2 ECMAScript Language Binding
+  Object.extend(Node, {
+    ELEMENT_NODE: 1,
+    ATTRIBUTE_NODE: 2,
+    TEXT_NODE: 3,
+    CDATA_SECTION_NODE: 4,
+    ENTITY_REFERENCE_NODE: 5,
+    ENTITY_NODE: 6,
+    PROCESSING_INSTRUCTION_NODE: 7,
+    COMMENT_NODE: 8,
+    DOCUMENT_NODE: 9,
+    DOCUMENT_TYPE_NODE: 10,
+    DOCUMENT_FRAGMENT_NODE: 11,
+    NOTATION_NODE: 12
+  });
+}
+
+(function() {
+  var element = this.Element;
+  this.Element = function(tagName, attributes) {
+    attributes = attributes || { };
+    tagName = tagName.toLowerCase();
+    var cache = Element.cache;
+    if (Prototype.Browser.IE && attributes.name) {
+      tagName = '<' + tagName + ' name="' + attributes.name + '">';
+      delete attributes.name;
+      return Element.writeAttribute(document.createElement(tagName), attributes);
+    }
+    if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));
+    return Element.writeAttribute(cache[tagName].cloneNode(false), attributes);
+  };
+  Object.extend(this.Element, element || { });
+}).call(window);
+
+Element.cache = { };
+
+Element.Methods = {
+  visible: function(element) {
+    return $(element).style.display != 'none';
+  },
+
+  toggle: function(element) {
+    element = $(element);
+    Element[Element.visible(element) ? 'hide' : 'show'](element);
+    return element;
+  },
+
+  hide: function(element) {
+    $(element).style.display = 'none';
+    return element;
+  },
+
+  show: function(element) {
+    $(element).style.display = '';
+    return element;
+  },
+
+  remove: function(element) {
+    element = $(element);
+    element.parentNode.removeChild(element);
+    return element;
+  },
+
+  update: function(element, content) {
+    element = $(element);
+    if (content && content.toElement) content = content.toElement();
+    if (Object.isElement(content)) return element.update().insert(content);
+    content = Object.toHTML(content);
+    element.innerHTML = content.stripScripts();
+    content.evalScripts.bind(content).defer();
+    return element;
+  },
+
+  replace: function(element, content) {
+    element = $(element);
+    if (content && content.toElement) content = content.toElement();
+    else if (!Object.isElement(content)) {
+      content = Object.toHTML(content);
+      var range = element.ownerDocument.createRange();
+      range.selectNode(element);
+      content.evalScripts.bind(content).defer();
+      content = range.createContextualFragment(content.stripScripts());
+    }
+    element.parentNode.replaceChild(content, element);
+    return element;
+  },
+
+  insert: function(element, insertions) {
+    element = $(element);
+
+    if (Object.isString(insertions) || Object.isNumber(insertions) ||
+        Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
+          insertions = {bottom:insertions};
+
+    var content, insert, tagName, childNodes;
+
+    for (var position in insertions) {
+      content  = insertions[position];
+      position = position.toLowerCase();
+      insert = Element._insertionTranslations[position];
+
+      if (content && content.toElement) content = content.toElement();
+      if (Object.isElement(content)) {
+        insert(element, content);
+        continue;
+      }
+
+      content = Object.toHTML(content);
+
+      tagName = ((position == 'before' || position == 'after')
+        ? element.parentNode : element).tagName.toUpperCase();
+
+      childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
+
+      if (position == 'top' || position == 'after') childNodes.reverse();
+      childNodes.each(insert.curry(element));
+
+      content.evalScripts.bind(content).defer();
+    }
+
+    return element;
+  },
+
+  wrap: function(element, wrapper, attributes) {
+    element = $(element);
+    if (Object.isElement(wrapper))
+      $(wrapper).writeAttribute(attributes || { });
+    else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes);
+    else wrapper = new Element('div', wrapper);
+    if (element.parentNode)
+      element.parentNode.replaceChild(wrapper, element);
+    wrapper.appendChild(element);
+    return wrapper;
+  },
+
+  inspect: function(element) {
+    element = $(element);
+    var result = '<' + element.tagName.toLowerCase();
+    $H({'id': 'id', 'className': 'class'}).each(function(pair) {
+      var property = pair.first(), attribute = pair.last();
+      var value = (element[property] || '').toString();
+      if (value) result += ' ' + attribute + '=' + value.inspect(true);
+    });
+    return result + '>';
+  },
+
+  recursivelyCollect: function(element, property) {
+    element = $(element);
+    var elements = [];
+    while (element = element[property])
+      if (element.nodeType == 1)
+        elements.push(Element.extend(element));
+    return elements;
+  },
+
+  ancestors: function(element) {
+    return $(element).recursivelyCollect('parentNode');
+  },
+
+  descendants: function(element) {
+    return $(element).select("*");
+  },
+
+  firstDescendant: function(element) {
+    element = $(element).firstChild;
+    while (element && element.nodeType != 1) element = element.nextSibling;
+    return $(element);
+  },
+
+  immediateDescendants: function(element) {
+    if (!(element = $(element).firstChild)) return [];
+    while (element && element.nodeType != 1) element = element.nextSibling;
+    if (element) return [element].concat($(element).nextSiblings());
+    return [];
+  },
+
+  previousSiblings: function(element) {
+    return $(element).recursivelyCollect('previousSibling');
+  },
+
+  nextSiblings: function(element) {
+    return $(element).recursivelyCollect('nextSibling');
+  },
+
+  siblings: function(element) {
+    element = $(element);
+    return element.previousSiblings().reverse().concat(element.nextSiblings());
+  },
+
+  match: function(element, selector) {
+    if (Object.isString(selector))
+      selector = new Selector(selector);
+    return selector.match($(element));
+  },
+
+  up: function(element, expression, index) {
+    element = $(element);
+    if (arguments.length == 1) return $(element.parentNode);
+    var ancestors = element.ancestors();
+    return Object.isNumber(expression) ? ancestors[expression] :
+      Selector.findElement(ancestors, expression, index);
+  },
+
+  down: function(element, expression, index) {
+    element = $(element);
+    if (arguments.length == 1) return element.firstDescendant();
+    return Object.isNumber(expression) ? element.descendants()[expression] :
+      element.select(expression)[index || 0];
+  },
+
+  previous: function(element, expression, index) {
+    element = $(element);
+    if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
+    var previousSiblings = element.previousSiblings();
+    return Object.isNumber(expression) ? previousSiblings[expression] :
+      Selector.findElement(previousSiblings, expression, index);
+  },
+
+  next: function(element, expression, index) {
+    element = $(element);
+    if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
+    var nextSiblings = element.nextSiblings();
+    return Object.isNumber(expression) ? nextSiblings[expression] :
+      Selector.findElement(nextSiblings, expression, index);
+  },
+
+  select: function() {
+    var args = $A(arguments), element = $(args.shift());
+    return Selector.findChildElements(element, args);
+  },
+
+  adjacent: function() {
+    var args = $A(arguments), element = $(args.shift());
+    return Selector.findChildElements(element.parentNode, args).without(element);
+  },
+
+  identify: function(element) {
+    element = $(element);
+    var id = element.readAttribute('id'), self = arguments.callee;
+    if (id) return id;
+    do { id = 'anonymous_element_' + self.counter++ } while ($(id));
+    element.writeAttribute('id', id);
+    return id;
+  },
+
+  readAttribute: function(element, name) {
+    element = $(element);
+    if (Prototype.Browser.IE) {
+      var t = Element._attributeTranslations.read;
+      if (t.values[name]) return t.values[name](element, name);
+      if (t.names[name]) name = t.names[name];
+      if (name.include(':')) {
+        return (!element.attributes || !element.attributes[name]) ? null :
+         element.attributes[name].value;
+      }
+    }
+    return element.getAttribute(name);
+  },
+
+  writeAttribute: function(element, name, value) {
+    element = $(element);
+    var attributes = { }, t = Element._attributeTranslations.write;
+
+    if (typeof name == 'object') attributes = name;
+    else attributes[name] = Object.isUndefined(value) ? true : value;
+
+    for (var attr in attributes) {
+      name = t.names[attr] || attr;
+      value = attributes[attr];
+      if (t.values[attr]) name = t.values[attr](element, value);
+      if (value === false || value === null)
+        element.removeAttribute(name);
+      else if (value === true)
+        element.setAttribute(name, name);
+      else element.setAttribute(name, value);
+    }
+    return element;
+  },
+
+  getHeight: function(element) {
+    return $(element).getDimensions().height;
+  },
+
+  getWidth: function(element) {
+    return $(element).getDimensions().width;
+  },
+
+  classNames: function(element) {
+    return new Element.ClassNames(element);
+  },
+
+  hasClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    var elementClassName = element.className;
+    return (elementClassName.length > 0 && (elementClassName == className ||
+      new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
+  },
+
+  addClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    if (!element.hasClassName(className))
+      element.className += (element.className ? ' ' : '') + className;
+    return element;
+  },
+
+  removeClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    element.className = element.className.replace(
+      new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip();
+    return element;
+  },
+
+  toggleClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    return element[element.hasClassName(className) ?
+      'removeClassName' : 'addClassName'](className);
+  },
+
+  // removes whitespace-only text node children
+  cleanWhitespace: function(element) {
+    element = $(element);
+    var node = element.firstChild;
+    while (node) {
+      var nextNode = node.nextSibling;
+      if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
+        element.removeChild(node);
+      node = nextNode;
+    }
+    return element;
+  },
+
+  empty: function(element) {
+    return $(element).innerHTML.blank();
+  },
+
+  descendantOf: function(element, ancestor) {
+    element = $(element), ancestor = $(ancestor);
+    var originalAncestor = ancestor;
+
+    if (element.compareDocumentPosition)
+      return (element.compareDocumentPosition(ancestor) & 8) === 8;
+
+    if (element.sourceIndex && !Prototype.Browser.Opera) {
+      var e = element.sourceIndex, a = ancestor.sourceIndex,
+       nextAncestor = ancestor.nextSibling;
+      if (!nextAncestor) {
+        do { ancestor = ancestor.parentNode; }
+        while (!(nextAncestor = ancestor.nextSibling) && ancestor.parentNode);
+      }
+      if (nextAncestor && nextAncestor.sourceIndex)
+       return (e > a && e < nextAncestor.sourceIndex);
+    }
+
+    while (element = element.parentNode)
+      if (element == originalAncestor) return true;
+    return false;
+  },
+
+  scrollTo: function(element) {
+    element = $(element);
+    var pos = element.cumulativeOffset();
+    window.scrollTo(pos[0], pos[1]);
+    return element;
+  },
+
+  getStyle: function(element, style) {
+    element = $(element);
+    style = style == 'float' ? 'cssFloat' : style.camelize();
+    var value = element.style[style];
+    if (!value) {
+      var css = document.defaultView.getComputedStyle(element, null);
+      value = css ? css[style] : null;
+    }
+    if (style == 'opacity') return value ? parseFloat(value) : 1.0;
+    return value == 'auto' ? null : value;
+  },
+
+  getOpacity: function(element) {
+    return $(element).getStyle('opacity');
+  },
+
+  setStyle: function(element, styles) {
+    element = $(element);
+    var elementStyle = element.style, match;
+    if (Object.isString(styles)) {
+      element.style.cssText += ';' + styles;
+      return styles.include('opacity') ?
+        element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element;
+    }
+    for (var property in styles)
+      if (property == 'opacity') element.setOpacity(styles[property]);
+      else
+        elementStyle[(property == 'float' || property == 'cssFloat') ?
+          (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') :
+            property] = styles[property];
+
+    return element;
+  },
+
+  setOpacity: function(element, value) {
+    element = $(element);
+    element.style.opacity = (value == 1 || value === '') ? '' :
+      (value < 0.00001) ? 0 : value;
+    return element;
+  },
+
+  getDimensions: function(element) {
+    element = $(element);
+    var display = $(element).getStyle('display');
+    if (display != 'none' && display != null) // Safari bug
+      return {width: element.offsetWidth, height: element.offsetHeight};
+
+    // All *Width and *Height properties give 0 on elements with display none,
+    // so enable the element temporarily
+    var els = element.style;
+    var originalVisibility = els.visibility;
+    var originalPosition = els.position;
+    var originalDisplay = els.display;
+    els.visibility = 'hidden';
+    els.position = 'absolute';
+    els.display = 'block';
+    var originalWidth = element.clientWidth;
+    var originalHeight = element.clientHeight;
+    els.display = originalDisplay;
+    els.position = originalPosition;
+    els.visibility = originalVisibility;
+    return {width: originalWidth, height: originalHeight};
+  },
+
+  makePositioned: function(element) {
+    element = $(element);
+    var pos = Element.getStyle(element, 'position');
+    if (pos == 'static' || !pos) {
+      element._madePositioned = true;
+      element.style.position = 'relative';
+      // Opera returns the offset relative to the positioning context, when an
+      // element is position relative but top and left have not been defined
+      if (window.opera) {
+        element.style.top = 0;
+        element.style.left = 0;
+      }
+    }
+    return element;
+  },
+
+  undoPositioned: function(element) {
+    element = $(element);
+    if (element._madePositioned) {
+      element._madePositioned = undefined;
+      element.style.position =
+        element.style.top =
+        element.style.left =
+        element.style.bottom =
+        element.style.right = '';
+    }
+    return element;
+  },
+
+  makeClipping: function(element) {
+    element = $(element);
+    if (element._overflow) return element;
+    element._overflow = Element.getStyle(element, 'overflow') || 'auto';
+    if (element._overflow !== 'hidden')
+      element.style.overflow = 'hidden';
+    return element;
+  },
+
+  undoClipping: function(element) {
+    element = $(element);
+    if (!element._overflow) return element;
+    element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
+    element._overflow = null;
+    return element;
+  },
+
+  cumulativeOffset: function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      element = element.offsetParent;
+    } while (element);
+    return Element._returnOffset(valueL, valueT);
+  },
+
+  positionedOffset: function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      element = element.offsetParent;
+      if (element) {
+        if (element.tagName == 'BODY') break;
+        var p = Element.getStyle(element, 'position');
+        if (p !== 'static') break;
+      }
+    } while (element);
+    return Element._returnOffset(valueL, valueT);
+  },
+
+  absolutize: function(element) {
+    element = $(element);
+    if (element.getStyle('position') == 'absolute') return;
+    // Position.prepare(); // To be done manually by Scripty when it needs it.
+
+    var offsets = element.positionedOffset();
+    var top     = offsets[1];
+    var left    = offsets[0];
+    var width   = element.clientWidth;
+    var height  = element.clientHeight;
+
+    element._originalLeft   = left - parseFloat(element.style.left  || 0);
+    element._originalTop    = top  - parseFloat(element.style.top || 0);
+    element._originalWidth  = element.style.width;
+    element._originalHeight = element.style.height;
+
+    element.style.position = 'absolute';
+    element.style.top    = top + 'px';
+    element.style.left   = left + 'px';
+    element.style.width  = width + 'px';
+    element.style.height = height + 'px';
+    return element;
+  },
+
+  relativize: function(element) {
+    element = $(element);
+    if (element.getStyle('position') == 'relative') return;
+    // Position.prepare(); // To be done manually by Scripty when it needs it.
+
+    element.style.position = 'relative';
+    var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
+    var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
+
+    element.style.top    = top + 'px';
+    element.style.left   = left + 'px';
+    element.style.height = element._originalHeight;
+    element.style.width  = element._originalWidth;
+    return element;
+  },
+
+  cumulativeScrollOffset: function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.scrollTop  || 0;
+      valueL += element.scrollLeft || 0;
+      element = element.parentNode;
+    } while (element);
+    return Element._returnOffset(valueL, valueT);
+  },
+
+  getOffsetParent: function(element) {
+    if (element.offsetParent) return $(element.offsetParent);
+    if (element == document.body) return $(element);
+
+    while ((element = element.parentNode) && element != document.body)
+      if (Element.getStyle(element, 'position') != 'static')
+        return $(element);
+
+    return $(document.body);
+  },
+
+  viewportOffset: function(forElement) {
+    var valueT = 0, valueL = 0;
+
+    var element = forElement;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+
+      // Safari fix
+      if (element.offsetParent == document.body &&
+        Element.getStyle(element, 'position') == 'absolute') break;
+
+    } while (element = element.offsetParent);
+
+    element = forElement;
+    do {
+      if (!Prototype.Browser.Opera || element.tagName == 'BODY') {
+        valueT -= element.scrollTop  || 0;
+        valueL -= element.scrollLeft || 0;
+      }
+    } while (element = element.parentNode);
+
+    return Element._returnOffset(valueL, valueT);
+  },
+
+  clonePosition: function(element, source) {
+    var options = Object.extend({
+      setLeft:    true,
+      setTop:     true,
+      setWidth:   true,
+      setHeight:  true,
+      offsetTop:  0,
+      offsetLeft: 0
+    }, arguments[2] || { });
+
+    // find page position of source
+    source = $(source);
+    var p = source.viewportOffset();
+
+    // find coordinate system to use
+    element = $(element);
+    var delta = [0, 0];
+    var parent = null;
+    // delta [0,0] will do fine with position: fixed elements,
+    // position:absolute needs offsetParent deltas
+    if (Element.getStyle(element, 'position') == 'absolute') {
+      parent = element.getOffsetParent();
+      delta = parent.viewportOffset();
+    }
+
+    // correct by body offsets (fixes Safari)
+    if (parent == document.body) {
+      delta[0] -= document.body.offsetLeft;
+      delta[1] -= document.body.offsetTop;
+    }
+
+    // set position
+    if (options.setLeft)   element.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
+    if (options.setTop)    element.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
+    if (options.setWidth)  element.style.width = source.offsetWidth + 'px';
+    if (options.setHeight) element.style.height = source.offsetHeight + 'px';
+    return element;
+  }
+};
+
+Element.Methods.identify.counter = 1;
+
+Object.extend(Element.Methods, {
+  getElementsBySelector: Element.Methods.select,
+  childElements: Element.Methods.immediateDescendants
+});
+
+Element._attributeTranslations = {
+  write: {
+    names: {
+      className: 'class',
+      htmlFor:   'for'
+    },
+    values: { }
+  }
+};
+
+if (Prototype.Browser.Opera) {
+  Element.Methods.getStyle = Element.Methods.getStyle.wrap(
+    function(proceed, element, style) {
+      switch (style) {
+        case 'left': case 'top': case 'right': case 'bottom':
+          if (proceed(element, 'position') === 'static') return null;
+        case 'height': case 'width':
+          // returns '0px' for hidden elements; we want it to return null
+          if (!Element.visible(element)) return null;
+
+          // returns the border-box dimensions rather than the content-box
+          // dimensions, so we subtract padding and borders from the value
+          var dim = parseInt(proceed(element, style), 10);
+
+          if (dim !== element['offset' + style.capitalize()])
+            return dim + 'px';
+
+          var properties;
+          if (style === 'height') {
+            properties = ['border-top-width', 'padding-top',
+             'padding-bottom', 'border-bottom-width'];
+          }
+          else {
+            properties = ['border-left-width', 'padding-left',
+             'padding-right', 'border-right-width'];
+          }
+          return properties.inject(dim, function(memo, property) {
+            var val = proceed(element, property);
+            return val === null ? memo : memo - parseInt(val, 10);
+          }) + 'px';
+        default: return proceed(element, style);
+      }
+    }
+  );
+
+  Element.Methods.readAttribute = Element.Methods.readAttribute.wrap(
+    function(proceed, element, attribute) {
+      if (attribute === 'title') return element.title;
+      return proceed(element, attribute);
+    }
+  );
+}
+
+else if (Prototype.Browser.IE) {
+  // IE doesn't report offsets correctly for static elements, so we change them
+  // to "relative" to get the values, then change them back.
+  Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap(
+    function(proceed, element) {
+      element = $(element);
+      var position = element.getStyle('position');
+      if (position !== 'static') return proceed(element);
+      element.setStyle({ position: 'relative' });
+      var value = proceed(element);
+      element.setStyle({ position: position });
+      return value;
+    }
+  );
+
+  $w('positionedOffset viewportOffset').each(function(method) {
+    Element.Methods[method] = Element.Methods[method].wrap(
+      function(proceed, element) {
+        element = $(element);
+        var position = element.getStyle('position');
+        if (position !== 'static') return proceed(element);
+        // Trigger hasLayout on the offset parent so that IE6 reports
+        // accurate offsetTop and offsetLeft values for position: fixed.
+        var offsetParent = element.getOffsetParent();
+        element.setStyle({ position: 'relative' });
+        var value = proceed(element);
+        element.setStyle({ position: position });
+        return value;
+      }
+    );
+  });
+
+  Element.Methods.getStyle = function(element, style) {
+    element = $(element);
+    style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
+    //console.log(element.style[style],element.style);
+    try {
+        var value = element.style[style];
+    } catch (err) {
+        var value = '';
+    }
+    if (!value && element.currentStyle) value = element.currentStyle[style];
+    if (style == 'opacity') {
+      if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
+        if (value[1]) return parseFloat(value[1]) / 100;
+      return 1.0;
+    }
+
+    if (value == 'auto') {
+      if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
+        return element['offset' + style.capitalize()] + 'px';
+      return null;
+    }
+    return value;
+  };
+
+  Element.Methods.setOpacity = function(element, value) {
+    function stripAlpha(filter){
+      return filter.replace(/alpha\([^\)]*\)/gi,'');
+    }
+    element = $(element);
+    var currentStyle = element.currentStyle;
+    if ((currentStyle && !currentStyle.hasLayout) ||
+      (!currentStyle && element.style.zoom == 'normal'))
+        element.style.zoom = 1;
+
+    var filter = element.getStyle('filter'), style = element.style;
+    if (value == 1 || value === '') {
+      (filter = stripAlpha(filter)) ?
+        style.filter = filter : style.removeAttribute('filter');
+      return element;
+    } else if (value < 0.00001) value = 0;
+    style.filter = stripAlpha(filter) +
+      'alpha(opacity=' + (value * 100) + ')';
+    return element;
+  };
+
+  Element._attributeTranslations = {
+    read: {
+      names: {
+        'class': 'className',
+        'for':   'htmlFor'
+      },
+      values: {
+        _getAttr: function(element, attribute) {
+          return element.getAttribute(attribute, 2);
+        },
+        _getAttrNode: function(element, attribute) {
+          var node = element.getAttributeNode(attribute);
+          return node ? node.value : "";
+        },
+        _getEv: function(element, attribute) {
+          attribute = element.getAttribute(attribute);
+          return attribute ? attribute.toString().slice(23, -2) : null;
+        },
+        _flag: function(element, attribute) {
+          return $(element).hasAttribute(attribute) ? attribute : null;
+        },
+        style: function(element) {
+          return element.style.cssText.toLowerCase();
+        },
+        title: function(element) {
+          return element.title;
+        }
+      }
+    }
+  };
+
+  Element._attributeTranslations.write = {
+    names: Object.extend({
+      cellpadding: 'cellPadding',
+      cellspacing: 'cellSpacing'
+    }, Element._attributeTranslations.read.names),
+    values: {
+      checked: function(element, value) {
+        element.checked = !!value;
+      },
+
+      style: function(element, value) {
+        element.style.cssText = value ? value : '';
+      }
+    }
+  };
+
+  Element._attributeTranslations.has = {};
+
+  $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +
+      'encType maxLength readOnly longDesc').each(function(attr) {
+    Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;
+    Element._attributeTranslations.has[attr.toLowerCase()] = attr;
+  });
+
+  (function(v) {
+    Object.extend(v, {
+      href:        v._getAttr,
+      src:         v._getAttr,
+      type:        v._getAttr,
+      action:      v._getAttrNode,
+      disabled:    v._flag,
+      checked:     v._flag,
+      readonly:    v._flag,
+      multiple:    v._flag,
+      onload:      v._getEv,
+      onunload:    v._getEv,
+      onclick:     v._getEv,
+      ondblclick:  v._getEv,
+      onmousedown: v._getEv,
+      onmouseup:   v._getEv,
+      onmouseover: v._getEv,
+      onmousemove: v._getEv,
+      onmouseout:  v._getEv,
+      onfocus:     v._getEv,
+      onblur:      v._getEv,
+      onkeypress:  v._getEv,
+      onkeydown:   v._getEv,
+      onkeyup:     v._getEv,
+      onsubmit:    v._getEv,
+      onreset:     v._getEv,
+      onselect:    v._getEv,
+      onchange:    v._getEv
+    });
+  })(Element._attributeTranslations.read.values);
+}
+
+else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) {
+  Element.Methods.setOpacity = function(element, value) {
+    element = $(element);
+    element.style.opacity = (value == 1) ? 0.999999 :
+      (value === '') ? '' : (value < 0.00001) ? 0 : value;
+    return element;
+  };
+}
+
+else if (Prototype.Browser.WebKit) {
+  Element.Methods.setOpacity = function(element, value) {
+    element = $(element);
+    element.style.opacity = (value == 1 || value === '') ? '' :
+      (value < 0.00001) ? 0 : value;
+
+    if (value == 1)
+      if(element.tagName == 'IMG' && element.width) {
+        element.width++; element.width--;
+      } else try {
+        var n = document.createTextNode(' ');
+        element.appendChild(n);
+        element.removeChild(n);
+      } catch (e) { }
+
+    return element;
+  };
+
+  // Safari returns margins on body which is incorrect if the child is absolutely
+  // positioned.  For performance reasons, redefine Element#cumulativeOffset for
+  // KHTML/WebKit only.
+  Element.Methods.cumulativeOffset = function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      if (element.offsetParent == document.body)
+        if (Element.getStyle(element, 'position') == 'absolute') break;
+
+      element = element.offsetParent;
+    } while (element);
+
+    return Element._returnOffset(valueL, valueT);
+  };
+}
+
+if (Prototype.Browser.IE || Prototype.Browser.Opera) {
+  // IE and Opera are missing .innerHTML support for TABLE-related and SELECT elements
+  Element.Methods.update = function(element, content) {
+    element = $(element);
+
+    if (content && content.toElement) content = content.toElement();
+    if (Object.isElement(content)) return element.update().insert(content);
+
+    content = Object.toHTML(content);
+    var tagName = element.tagName.toUpperCase();
+
+    if (tagName in Element._insertionTranslations.tags) {
+      $A(element.childNodes).each(function(node) { element.removeChild(node) });
+      Element._getContentFromAnonymousElement(tagName, content.stripScripts())
+        .each(function(node) { element.appendChild(node) });
+    }
+    else element.innerHTML = content.stripScripts();
+
+    content.evalScripts.bind(content).defer();
+    return element;
+  };
+}
+
+if ('outerHTML' in document.createElement('div')) {
+  Element.Methods.replace = function(element, content) {
+    element = $(element);
+
+    if (content && content.toElement) content = content.toElement();
+    if (Object.isElement(content)) {
+      element.parentNode.replaceChild(content, element);
+      return element;
+    }
+
+    content = Object.toHTML(content);
+    var parent = element.parentNode, tagName = parent.tagName.toUpperCase();
+
+    if (Element._insertionTranslations.tags[tagName]) {
+      var nextSibling = element.next();
+      var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
+      parent.removeChild(element);
+      if (nextSibling)
+        fragments.each(function(node) { parent.insertBefore(node, nextSibling) });
+      else
+        fragments.each(function(node) { parent.appendChild(node) });
+    }
+    else element.outerHTML = content.stripScripts();
+
+    content.evalScripts.bind(content).defer();
+    return element;
+  };
+}
+
+Element._returnOffset = function(l, t) {
+  var result = [l, t];
+  result.left = l;
+  result.top = t;
+  return result;
+};
+
+Element._getContentFromAnonymousElement = function(tagName, html) {
+  var div = new Element('div'), t = Element._insertionTranslations.tags[tagName];
+  if (t) {
+    div.innerHTML = t[0] + html + t[1];
+    t[2].times(function() { div = div.firstChild });
+  } else div.innerHTML = html;
+  return $A(div.childNodes);
+};
+
+Element._insertionTranslations = {
+  before: function(element, node) {
+    element.parentNode.insertBefore(node, element);
+  },
+  top: function(element, node) {
+    element.insertBefore(node, element.firstChild);
+  },
+  bottom: function(element, node) {
+    element.appendChild(node);
+  },
+  after: function(element, node) {
+    element.parentNode.insertBefore(node, element.nextSibling);
+  },
+  tags: {
+    TABLE:  ['<table>',                '</table>',                   1],
+    TBODY:  ['<table><tbody>',         '</tbody></table>',           2],
+    TR:     ['<table><tbody><tr>',     '</tr></tbody></table>',      3],
+    TD:     ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],
+    SELECT: ['<select>',               '</select>',                  1]
+  }
+};
+
+(function() {
+  Object.extend(this.tags, {
+    THEAD: this.tags.TBODY,
+    TFOOT: this.tags.TBODY,
+    TH:    this.tags.TD
+  });
+}).call(Element._insertionTranslations);
+
+Element.Methods.Simulated = {
+  hasAttribute: function(element, attribute) {
+    attribute = Element._attributeTranslations.has[attribute] || attribute;
+    var node = $(element).getAttributeNode(attribute);
+    return node && node.specified;
+  }
+};
+
+Element.Methods.ByTag = { };
+
+Object.extend(Element, Element.Methods);
+
+if (!Prototype.BrowserFeatures.ElementExtensions &&
+    document.createElement('div').__proto__) {
+  window.HTMLElement = { };
+  window.HTMLElement.prototype = document.createElement('div').__proto__;
+  Prototype.BrowserFeatures.ElementExtensions = true;
+}
+
+Element.extend = (function() {
+  if (Prototype.BrowserFeatures.SpecificElementExtensions)
+    return Prototype.K;
+
+  var Methods = { }, ByTag = Element.Methods.ByTag;
+
+  var extend = Object.extend(function(element) {
+    if (!element || element._extendedByPrototype ||
+        element.nodeType != 1 || element == window) return element;
+
+    var methods = Object.clone(Methods),
+      tagName = element.tagName, property, value;
+
+    // extend methods for specific tags
+    if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);
+
+    for (property in methods) {
+      value = methods[property];
+      if (Object.isFunction(value) && !(property in element))
+        element[property] = value.methodize();
+    }
+
+    element._extendedByPrototype = Prototype.emptyFunction;
+    return element;
+
+  }, {
+    refresh: function() {
+      // extend methods for all tags (Safari doesn't need this)
+      if (!Prototype.BrowserFeatures.ElementExtensions) {
+        Object.extend(Methods, Element.Methods);
+        Object.extend(Methods, Element.Methods.Simulated);
+      }
+    }
+  });
+
+  extend.refresh();
+  return extend;
+})();
+
+Element.hasAttribute = function(element, attribute) {
+  if (element.hasAttribute) return element.hasAttribute(attribute);
+  return Element.Methods.Simulated.hasAttribute(element, attribute);
+};
+
+Element.addMethods = function(methods) {
+  var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
+
+  if (!methods) {
+    Object.extend(Form, Form.Methods);
+    Object.extend(Form.Element, Form.Element.Methods);
+    Object.extend(Element.Methods.ByTag, {
+      "FORM":     Object.clone(Form.Methods),
+      "INPUT":    Object.clone(Form.Element.Methods),
+      "SELECT":   Object.clone(Form.Element.Methods),
+      "TEXTAREA": Object.clone(Form.Element.Methods)
+    });
+  }
+
+  if (arguments.length == 2) {
+    var tagName = methods;
+    methods = arguments[1];
+  }
+
+  if (!tagName) Object.extend(Element.Methods, methods || { });
+  else {
+    if (Object.isArray(tagName)) tagName.each(extend);
+    else extend(tagName);
+  }
+
+  function extend(tagName) {
+    tagName = tagName.toUpperCase();
+    if (!Element.Methods.ByTag[tagName])
+      Element.Methods.ByTag[tagName] = { };
+    Object.extend(Element.Methods.ByTag[tagName], methods);
+  }
+
+  function copy(methods, destination, onlyIfAbsent) {
+    onlyIfAbsent = onlyIfAbsent || false;
+    for (var property in methods) {
+      var value = methods[property];
+      if (!Object.isFunction(value)) continue;
+      if (!onlyIfAbsent || !(property in destination))
+        destination[property] = value.methodize();
+    }
+  }
+
+  function findDOMClass(tagName) {
+    var klass;
+    var trans = {
+      "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
+      "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
+      "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
+      "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
+      "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
+      "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
+      "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
+      "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
+      "FrameSet", "IFRAME": "IFrame"
+    };
+    if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
+    if (window[klass]) return window[klass];
+    klass = 'HTML' + tagName + 'Element';
+    if (window[klass]) return window[klass];
+    klass = 'HTML' + tagName.capitalize() + 'Element';
+    if (window[klass]) return window[klass];
+
+    window[klass] = { };
+    window[klass].prototype = document.createElement(tagName).__proto__;
+    return window[klass];
+  }
+
+  if (F.ElementExtensions) {
+    copy(Element.Methods, HTMLElement.prototype);
+    copy(Element.Methods.Simulated, HTMLElement.prototype, true);
+  }
+
+  if (F.SpecificElementExtensions) {
+    for (var tag in Element.Methods.ByTag) {
+      var klass = findDOMClass(tag);
+      if (Object.isUndefined(klass)) continue;
+      copy(T[tag], klass.prototype);
+    }
+  }
+
+  Object.extend(Element, Element.Methods);
+  delete Element.ByTag;
+
+  if (Element.extend.refresh) Element.extend.refresh();
+  Element.cache = { };
+};
+
+document.viewport = {
+  getDimensions: function() {
+    var dimensions = { };
+    var B = Prototype.Browser;
+    $w('width height').each(function(d) {
+      var D = d.capitalize();
+      dimensions[d] = (B.WebKit && !document.evaluate) ? self['inner' + D] :
+        (B.Opera) ? document.body['client' + D] : document.documentElement['client' + D];
+    });
+    return dimensions;
+  },
+
+  getWidth: function() {
+    return this.getDimensions().width;
+  },
+
+  getHeight: function() {
+    return this.getDimensions().height;
+  },
+
+  getScrollOffsets: function() {
+    return Element._returnOffset(
+      window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
+      window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop);
+  }
+};
+/* Portions of the Selector class are derived from Jack Slocum’s DomQuery,
+ * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
+ * license.  Please see http://www.yui-ext.com/ for more information. */
+
+var Selector = Class.create({
+  initialize: function(expression) {
+    this.expression = expression.strip();
+    this.compileMatcher();
+  },
+
+  shouldUseXPath: function() {
+    if (!Prototype.BrowserFeatures.XPath) return false;
+
+    var e = this.expression;
+
+    // Safari 3 chokes on :*-of-type and :empty
+    if (Prototype.Browser.WebKit &&
+     (e.include("-of-type") || e.include(":empty")))
+      return false;
+
+    // XPath can't do namespaced attributes, nor can it read
+    // the "checked" property from DOM nodes
+    if ((/(\[[\w-]*?:|:checked)/).test(this.expression))
+      return false;
+
+    return true;
+  },
+
+  compileMatcher: function() {
+    if (this.shouldUseXPath())
+      return this.compileXPathMatcher();
+
+    var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
+        c = Selector.criteria, le, p, m;
+
+    if (Selector._cache[e]) {
+      this.matcher = Selector._cache[e];
+      return;
+    }
+
+    this.matcher = ["this.matcher = function(root) {",
+                    "var r = root, h = Selector.handlers, c = false, n;"];
+
+    while (e && le != e && (/\S/).test(e)) {
+      le = e;
+      for (var i in ps) {
+        p = ps[i];
+        if (m = e.match(p)) {
+          this.matcher.push(Object.isFunction(c[i]) ? c[i](m) :
+    	      new Template(c[i]).evaluate(m));
+          e = e.replace(m[0], '');
+          break;
+        }
+      }
+    }
+
+    this.matcher.push("return h.unique(n);\n}");
+    eval(this.matcher.join('\n'));
+    Selector._cache[this.expression] = this.matcher;
+  },
+
+  compileXPathMatcher: function() {
+    var e = this.expression, ps = Selector.patterns,
+        x = Selector.xpath, le, m;
+
+    if (Selector._cache[e]) {
+      this.xpath = Selector._cache[e]; return;
+    }
+
+    this.matcher = ['.//*'];
+    while (e && le != e && (/\S/).test(e)) {
+      le = e;
+      for (var i in ps) {
+        if (m = e.match(ps[i])) {
+          this.matcher.push(Object.isFunction(x[i]) ? x[i](m) :
+            new Template(x[i]).evaluate(m));
+          e = e.replace(m[0], '');
+          break;
+        }
+      }
+    }
+
+    this.xpath = this.matcher.join('');
+    Selector._cache[this.expression] = this.xpath;
+  },
+
+  findElements: function(root) {
+    root = root || document;
+    if (this.xpath) return document._getElementsByXPath(this.xpath, root);
+    return this.matcher(root);
+  },
+
+  match: function(element) {
+    this.tokens = [];
+
+    var e = this.expression, ps = Selector.patterns, as = Selector.assertions;
+    var le, p, m;
+
+    while (e && le !== e && (/\S/).test(e)) {
+      le = e;
+      for (var i in ps) {
+        p = ps[i];
+        if (m = e.match(p)) {
+          // use the Selector.assertions methods unless the selector
+          // is too complex.
+          if (as[i]) {
+            this.tokens.push([i, Object.clone(m)]);
+            e = e.replace(m[0], '');
+          } else {
+            // reluctantly do a document-wide search
+            // and look for a match in the array
+            return this.findElements(document).include(element);
+          }
+        }
+      }
+    }
+
+    var match = true, name, matches;
+    for (var i = 0, token; token = this.tokens[i]; i++) {
+      name = token[0], matches = token[1];
+      if (!Selector.assertions[name](element, matches)) {
+        match = false; break;
+      }
+    }
+
+    return match;
+  },
+
+  toString: function() {
+    return this.expression;
+  },
+
+  inspect: function() {
+    return "#<Selector:" + this.expression.inspect() + ">";
+  }
+});
+
+Object.extend(Selector, {
+  _cache: { },
+
+  xpath: {
+    descendant:   "//*",
+    child:        "/*",
+    adjacent:     "/following-sibling::*[1]",
+    laterSibling: '/following-sibling::*',
+    tagName:      function(m) {
+      if (m[1] == '*') return '';
+      return "[local-name()='" + m[1].toLowerCase() +
+             "' or local-name()='" + m[1].toUpperCase() + "']";
+    },
+    className:    "[contains(concat(' ', @class, ' '), ' #{1} ')]",
+    id:           "[@id='#{1}']",
+    attrPresence: function(m) {
+      m[1] = m[1].toLowerCase();
+      return new Template("[@#{1}]").evaluate(m);
+    },
+    attr: function(m) {
+      m[1] = m[1].toLowerCase();
+      m[3] = m[5] || m[6];
+      return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
+    },
+    pseudo: function(m) {
+      var h = Selector.xpath.pseudos[m[1]];
+      if (!h) return '';
+      if (Object.isFunction(h)) return h(m);
+      return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
+    },
+    operators: {
+      '=':  "[@#{1}='#{3}']",
+      '!=': "[@#{1}!='#{3}']",
+      '^=': "[starts-with(@#{1}, '#{3}')]",
+      '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
+      '*=': "[contains(@#{1}, '#{3}')]",
+      '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
+      '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
+    },
+    pseudos: {
+      'first-child': '[not(preceding-sibling::*)]',
+      'last-child':  '[not(following-sibling::*)]',
+      'only-child':  '[not(preceding-sibling::* or following-sibling::*)]',
+      'empty':       "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]",
+      'checked':     "[@checked]",
+      'disabled':    "[@disabled]",
+      'enabled':     "[not(@disabled)]",
+      'not': function(m) {
+        var e = m[6], p = Selector.patterns,
+            x = Selector.xpath, le, v;
+
+        var exclusion = [];
+        while (e && le != e && (/\S/).test(e)) {
+          le = e;
+          for (var i in p) {
+            if (m = e.match(p[i])) {
+              v = Object.isFunction(x[i]) ? x[i](m) : new Template(x[i]).evaluate(m);
+              exclusion.push("(" + v.substring(1, v.length - 1) + ")");
+              e = e.replace(m[0], '');
+              break;
+            }
+          }
+        }
+        return "[not(" + exclusion.join(" and ") + ")]";
+      },
+      'nth-child':      function(m) {
+        return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
+      },
+      'nth-last-child': function(m) {
+        return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
+      },
+      'nth-of-type':    function(m) {
+        return Selector.xpath.pseudos.nth("position() ", m);
+      },
+      'nth-last-of-type': function(m) {
+        return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
+      },
+      'first-of-type':  function(m) {
+        m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
+      },
+      'last-of-type':   function(m) {
+        m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m);
+      },
+      'only-of-type':   function(m) {
+        var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
+      },
+      nth: function(fragment, m) {
+        var mm, formula = m[6], predicate;
+        if (formula == 'even') formula = '2n+0';
+        if (formula == 'odd')  formula = '2n+1';
+        if (mm = formula.match(/^(\d+)$/)) // digit only
+          return '[' + fragment + "= " + mm[1] + ']';
+        if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
+          if (mm[1] == "-") mm[1] = -1;
+          var a = mm[1] ? Number(mm[1]) : 1;
+          var b = mm[2] ? Number(mm[2]) : 0;
+          predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " +
+          "((#{fragment} - #{b}) div #{a} >= 0)]";
+          return new Template(predicate).evaluate({
+            fragment: fragment, a: a, b: b });
+        }
+      }
+    }
+  },
+
+  criteria: {
+    tagName:      'n = h.tagName(n, r, "#{1}", c);      c = false;',
+    className:    'n = h.className(n, r, "#{1}", c);    c = false;',
+    id:           'n = h.id(n, r, "#{1}", c);           c = false;',
+    attrPresence: 'n = h.attrPresence(n, r, "#{1}", c); c = false;',
+    attr: function(m) {
+      m[3] = (m[5] || m[6]);
+      return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); c = false;').evaluate(m);
+    },
+    pseudo: function(m) {
+      if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
+      return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
+    },
+    descendant:   'c = "descendant";',
+    child:        'c = "child";',
+    adjacent:     'c = "adjacent";',
+    laterSibling: 'c = "laterSibling";'
+  },
+
+  patterns: {
+    // combinators must be listed first
+    // (and descendant needs to be last combinator)
+    laterSibling: /^\s*~\s*/,
+    child:        /^\s*>\s*/,
+    adjacent:     /^\s*\+\s*/,
+    descendant:   /^\s/,
+
+    // selectors follow
+    tagName:      /^\s*(\*|[\w\-]+)(\b|$)?/,
+    id:           /^#([\w\-\*]+)(\b|$)/,
+    className:    /^\.([\w\-\*]+)(\b|$)/,
+    pseudo:
+/^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/,
+    attrPresence: /^\[([\w]+)\]/,
+    attr:         /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/
+  },
+
+  // for Selector.match and Element#match
+  assertions: {
+    tagName: function(element, matches) {
+      return matches[1].toUpperCase() == element.tagName.toUpperCase();
+    },
+
+    className: function(element, matches) {
+      return Element.hasClassName(element, matches[1]);
+    },
+
+    id: function(element, matches) {
+      return element.id === matches[1];
+    },
+
+    attrPresence: function(element, matches) {
+      return Element.hasAttribute(element, matches[1]);
+    },
+
+    attr: function(element, matches) {
+      var nodeValue = Element.readAttribute(element, matches[1]);
+      return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]);
+    }
+  },
+
+  handlers: {
+    // UTILITY FUNCTIONS
+    // joins two collections
+    concat: function(a, b) {
+      for (var i = 0, node; node = b[i]; i++)
+        a.push(node);
+      return a;
+    },
+
+    // marks an array of nodes for counting
+    mark: function(nodes) {
+      var _true = Prototype.emptyFunction;
+      for (var i = 0, node; node = nodes[i]; i++)
+        node._countedByPrototype = _true;
+      return nodes;
+    },
+
+    unmark: function(nodes) {
+      for (var i = 0, node; node = nodes[i]; i++)
+        node._countedByPrototype = undefined;
+      return nodes;
+    },
+
+    // mark each child node with its position (for nth calls)
+    // "ofType" flag indicates whether we're indexing for nth-of-type
+    // rather than nth-child
+    index: function(parentNode, reverse, ofType) {
+      parentNode._countedByPrototype = Prototype.emptyFunction;
+      if (reverse) {
+        for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
+          var node = nodes[i];
+          if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
+        }
+      } else {
+        for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
+          if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
+      }
+    },
+
+    // filters out duplicates and extends all nodes
+    unique: function(nodes) {
+      if (nodes.length == 0) return nodes;
+      var results = [], n;
+      for (var i = 0, l = nodes.length; i < l; i++)
+        if (!(n = nodes[i])._countedByPrototype) {
+          n._countedByPrototype = Prototype.emptyFunction;
+          results.push(Element.extend(n));
+        }
+      return Selector.handlers.unmark(results);
+    },
+
+    // COMBINATOR FUNCTIONS
+    descendant: function(nodes) {
+      var h = Selector.handlers;
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        h.concat(results, node.getElementsByTagName('*'));
+      return results;
+    },
+
+    child: function(nodes) {
+      var h = Selector.handlers;
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        for (var j = 0, child; child = node.childNodes[j]; j++)
+          if (child.nodeType == 1 && child.tagName != '!') results.push(child);
+      }
+      return results;
+    },
+
+    adjacent: function(nodes) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        var next = this.nextElementSibling(node);
+        if (next) results.push(next);
+      }
+      return results;
+    },
+
+    laterSibling: function(nodes) {
+      var h = Selector.handlers;
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        h.concat(results, Element.nextSiblings(node));
+      return results;
+    },
+
+    nextElementSibling: function(node) {
+      while (node = node.nextSibling)
+	      if (node.nodeType == 1) return node;
+      return null;
+    },
+
+    previousElementSibling: function(node) {
+      while (node = node.previousSibling)
+        if (node.nodeType == 1) return node;
+      return null;
+    },
+
+    // TOKEN FUNCTIONS
+    tagName: function(nodes, root, tagName, combinator) {
+      var uTagName = tagName.toUpperCase();
+      var results = [], h = Selector.handlers;
+      if (nodes) {
+        if (combinator) {
+          // fastlane for ordinary descendant combinators
+          if (combinator == "descendant") {
+            for (var i = 0, node; node = nodes[i]; i++)
+              h.concat(results, node.getElementsByTagName(tagName));
+            return results;
+          } else nodes = this[combinator](nodes);
+          if (tagName == "*") return nodes;
+        }
+        for (var i = 0, node; node = nodes[i]; i++)
+          if (node.tagName.toUpperCase() === uTagName) results.push(node);
+        return results;
+      } else return root.getElementsByTagName(tagName);
+    },
+
+    id: function(nodes, root, id, combinator) {
+      var targetNode = $(id), h = Selector.handlers;
+      if (!targetNode) return [];
+      if (!nodes && root == document) return [targetNode];
+      if (nodes) {
+        if (combinator) {
+          if (combinator == 'child') {
+            for (var i = 0, node; node = nodes[i]; i++)
+              if (targetNode.parentNode == node) return [targetNode];
+          } else if (combinator == 'descendant') {
+            for (var i = 0, node; node = nodes[i]; i++)
+              if (Element.descendantOf(targetNode, node)) return [targetNode];
+          } else if (combinator == 'adjacent') {
+            for (var i = 0, node; node = nodes[i]; i++)
+              if (Selector.handlers.previousElementSibling(targetNode) == node)
+                return [targetNode];
+          } else nodes = h[combinator](nodes);
+        }
+        for (var i = 0, node; node = nodes[i]; i++)
+          if (node == targetNode) return [targetNode];
+        return [];
+      }
+      return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];
+    },
+
+    className: function(nodes, root, className, combinator) {
+      if (nodes && combinator) nodes = this[combinator](nodes);
+      return Selector.handlers.byClassName(nodes, root, className);
+    },
+
+    byClassName: function(nodes, root, className) {
+      if (!nodes) nodes = Selector.handlers.descendant([root]);
+      var needle = ' ' + className + ' ';
+      for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {
+        nodeClassName = node.className;
+        if (nodeClassName.length == 0) continue;
+        if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))
+          results.push(node);
+      }
+      return results;
+    },
+
+    attrPresence: function(nodes, root, attr, combinator) {
+      if (!nodes) nodes = root.getElementsByTagName("*");
+      if (nodes && combinator) nodes = this[combinator](nodes);
+      var results = [];
+      for (var i = 0, node; node = nodes[i]; i++)
+        if (Element.hasAttribute(node, attr)) results.push(node);
+      return results;
+    },
+
+    attr: function(nodes, root, attr, value, operator, combinator) {
+      if (!nodes) nodes = root.getElementsByTagName("*");
+      if (nodes && combinator) nodes = this[combinator](nodes);
+      var handler = Selector.operators[operator], results = [];
+      for (var i = 0, node; node = nodes[i]; i++) {
+        var nodeValue = Element.readAttribute(node, attr);
+        if (nodeValue === null) continue;
+        if (handler(nodeValue, value)) results.push(node);
+      }
+      return results;
+    },
+
+    pseudo: function(nodes, name, value, root, combinator) {
+      if (nodes && combinator) nodes = this[combinator](nodes);
+      if (!nodes) nodes = root.getElementsByTagName("*");
+      return Selector.pseudos[name](nodes, value, root);
+    }
+  },
+
+  pseudos: {
+    'first-child': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        if (Selector.handlers.previousElementSibling(node)) continue;
+          results.push(node);
+      }
+      return results;
+    },
+    'last-child': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        if (Selector.handlers.nextElementSibling(node)) continue;
+          results.push(node);
+      }
+      return results;
+    },
+    'only-child': function(nodes, value, root) {
+      var h = Selector.handlers;
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
+          results.push(node);
+      return results;
+    },
+    'nth-child':        function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, formula, root);
+    },
+    'nth-last-child':   function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, formula, root, true);
+    },
+    'nth-of-type':      function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, formula, root, false, true);
+    },
+    'nth-last-of-type': function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, formula, root, true, true);
+    },
+    'first-of-type':    function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, "1", root, false, true);
+    },
+    'last-of-type':     function(nodes, formula, root) {
+      return Selector.pseudos.nth(nodes, "1", root, true, true);
+    },
+    'only-of-type':     function(nodes, formula, root) {
+      var p = Selector.pseudos;
+      return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
+    },
+
+    // handles the an+b logic
+    getIndices: function(a, b, total) {
+      if (a == 0) return b > 0 ? [b] : [];
+      return $R(1, total).inject([], function(memo, i) {
+        if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);
+        return memo;
+      });
+    },
+
+    // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type
+    nth: function(nodes, formula, root, reverse, ofType) {
+      if (nodes.length == 0) return [];
+      if (formula == 'even') formula = '2n+0';
+      if (formula == 'odd')  formula = '2n+1';
+      var h = Selector.handlers, results = [], indexed = [], m;
+      h.mark(nodes);
+      for (var i = 0, node; node = nodes[i]; i++) {
+        if (!node.parentNode._countedByPrototype) {
+          h.index(node.parentNode, reverse, ofType);
+          indexed.push(node.parentNode);
+        }
+      }
+      if (formula.match(/^\d+$/)) { // just a number
+        formula = Number(formula);
+        for (var i = 0, node; node = nodes[i]; i++)
+          if (node.nodeIndex == formula) results.push(node);
+      } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
+        if (m[1] == "-") m[1] = -1;
+        var a = m[1] ? Number(m[1]) : 1;
+        var b = m[2] ? Number(m[2]) : 0;
+        var indices = Selector.pseudos.getIndices(a, b, nodes.length);
+        for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {
+          for (var j = 0; j < l; j++)
+            if (node.nodeIndex == indices[j]) results.push(node);
+        }
+      }
+      h.unmark(nodes);
+      h.unmark(indexed);
+      return results;
+    },
+
+    'empty': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++) {
+        // IE treats comments as element nodes
+        if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue;
+        results.push(node);
+      }
+      return results;
+    },
+
+    'not': function(nodes, selector, root) {
+      var h = Selector.handlers, selectorType, m;
+      var exclusions = new Selector(selector).findElements(root);
+      h.mark(exclusions);
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (!node._countedByPrototype) results.push(node);
+      h.unmark(exclusions);
+      return results;
+    },
+
+    'enabled': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (!node.disabled) results.push(node);
+      return results;
+    },
+
+    'disabled': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (node.disabled) results.push(node);
+      return results;
+    },
+
+    'checked': function(nodes, value, root) {
+      for (var i = 0, results = [], node; node = nodes[i]; i++)
+        if (node.checked) results.push(node);
+      return results;
+    }
+  },
+
+  operators: {
+    '=':  function(nv, v) { return nv == v; },
+    '!=': function(nv, v) { return nv != v; },
+    '^=': function(nv, v) { return nv.startsWith(v); },
+    '$=': function(nv, v) { return nv.endsWith(v); },
+    '*=': function(nv, v) { return nv.include(v); },
+    '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
+    '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); }
+  },
+
+  split: function(expression) {
+    var expressions = [];
+    expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
+      expressions.push(m[1].strip());
+    });
+    return expressions;
+  },
+
+  matchElements: function(elements, expression) {
+    var matches = $$(expression), h = Selector.handlers;
+    h.mark(matches);
+    for (var i = 0, results = [], element; element = elements[i]; i++)
+      if (element._countedByPrototype) results.push(element);
+    h.unmark(matches);
+    return results;
+  },
+
+  findElement: function(elements, expression, index) {
+    if (Object.isNumber(expression)) {
+      index = expression; expression = false;
+    }
+    return Selector.matchElements(elements, expression || '*')[index || 0];
+  },
+
+  findChildElements: function(element, expressions) {
+    expressions = Selector.split(expressions.join(','));
+    var results = [], h = Selector.handlers;
+    for (var i = 0, l = expressions.length, selector; i < l; i++) {
+      selector = new Selector(expressions[i].strip());
+      h.concat(results, selector.findElements(element));
+    }
+    return (l > 1) ? h.unique(results) : results;
+  }
+});
+
+if (Prototype.Browser.IE) {
+  Object.extend(Selector.handlers, {
+    // IE returns comment nodes on getElementsByTagName("*").
+    // Filter them out.
+    concat: function(a, b) {
+      for (var i = 0, node; node = b[i]; i++)
+        if (node.tagName !== "!") a.push(node);
+      return a;
+    },
+
+    // IE improperly serializes _countedByPrototype in (inner|outer)HTML.
+    unmark: function(nodes) {
+      for (var i = 0, node; node = nodes[i]; i++)
+        node.removeAttribute('_countedByPrototype');
+      return nodes;
+    }
+  });
+}
+
+function $$() {
+  return Selector.findChildElements(document, $A(arguments));
+}
+var Form = {
+  reset: function(form) {
+    $(form).reset();
+    return form;
+  },
+
+  serializeElements: function(elements, options) {
+    if (typeof options != 'object') options = { hash: !!options };
+    else if (Object.isUndefined(options.hash)) options.hash = true;
+    var key, value, submitted = false, submit = options.submit;
+
+    var data = elements.inject({ }, function(result, element) {
+      if (!element.disabled && element.name) {
+        key = element.name; value = $(element).getValue();
+        if (value != null && (element.type != 'submit' || (!submitted &&
+            submit !== false && (!submit || key == submit) && (submitted = true)))) {
+          if (key in result) {
+            // a key is already present; construct an array of values
+            if (!Object.isArray(result[key])) result[key] = [result[key]];
+            result[key].push(value);
+          }
+          else result[key] = value;
+        }
+      }
+      return result;
+    });
+
+    return options.hash ? data : Object.toQueryString(data);
+  }
+};
+
+Form.Methods = {
+  serialize: function(form, options) {
+    return Form.serializeElements(Form.getElements(form), options);
+  },
+
+  getElements: function(form) {
+    return $A($(form).getElementsByTagName('*')).inject([],
+      function(elements, child) {
+        if (Form.Element.Serializers[child.tagName.toLowerCase()])
+          elements.push(Element.extend(child));
+        return elements;
+      }
+    );
+  },
+
+  getInputs: function(form, typeName, name) {
+    form = $(form);
+    var inputs = form.getElementsByTagName('input');
+
+    if (!typeName && !name) return $A(inputs).map(Element.extend);
+
+    for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
+      var input = inputs[i];
+      if ((typeName && input.type != typeName) || (name && input.name != name))
+        continue;
+      matchingInputs.push(Element.extend(input));
+    }
+
+    return matchingInputs;
+  },
+
+  disable: function(form) {
+    form = $(form);
+    Form.getElements(form).invoke('disable');
+    return form;
+  },
+
+  enable: function(form) {
+    form = $(form);
+    Form.getElements(form).invoke('enable');
+    return form;
+  },
+
+  findFirstElement: function(form) {
+    var elements = $(form).getElements().findAll(function(element) {
+      return 'hidden' != element.type && !element.disabled;
+    });
+    var firstByIndex = elements.findAll(function(element) {
+      return element.hasAttribute('tabIndex') && element.tabIndex >= 0;
+    }).sortBy(function(element) { return element.tabIndex }).first();
+
+    return firstByIndex ? firstByIndex : elements.find(function(element) {
+      return ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
+    });
+  },
+
+  focusFirstElement: function(form) {
+    form = $(form);
+    form.findFirstElement().activate();
+    return form;
+  },
+
+  request: function(form, options) {
+    form = $(form), options = Object.clone(options || { });
+
+    var params = options.parameters, action = form.readAttribute('action') || '';
+    if (action.blank()) action = window.location.href;
+    options.parameters = form.serialize(true);
+
+    if (params) {
+      if (Object.isString(params)) params = params.toQueryParams();
+      Object.extend(options.parameters, params);
+    }
+
+    if (form.hasAttribute('method') && !options.method)
+      options.method = form.method;
+
+    return new Ajax.Request(action, options);
+  }
+};
+
+/*--------------------------------------------------------------------------*/
+
+Form.Element = {
+  focus: function(element) {
+    $(element).focus();
+    return element;
+  },
+
+  select: function(element) {
+    $(element).select();
+    return element;
+  }
+};
+
+Form.Element.Methods = {
+  serialize: function(element) {
+    element = $(element);
+    if (!element.disabled && element.name) {
+      var value = element.getValue();
+      if (value != undefined) {
+        var pair = { };
+        pair[element.name] = value;
+        return Object.toQueryString(pair);
+      }
+    }
+    return '';
+  },
+
+  getValue: function(element) {
+    element = $(element);
+    var method = element.tagName.toLowerCase();
+    return Form.Element.Serializers[method](element);
+  },
+
+  setValue: function(element, value) {
+    element = $(element);
+    var method = element.tagName.toLowerCase();
+    Form.Element.Serializers[method](element, value);
+    return element;
+  },
+
+  clear: function(element) {
+    $(element).value = '';
+    return element;
+  },
+
+  present: function(element) {
+    return $(element).value != '';
+  },
+
+  activate: function(element) {
+    element = $(element);
+    try {
+      element.focus();
+      if (element.select && (element.tagName.toLowerCase() != 'input' ||
+          !['button', 'reset', 'submit'].include(element.type)))
+        element.select();
+    } catch (e) { }
+    return element;
+  },
+
+  disable: function(element) {
+    element = $(element);
+    element.blur();
+    element.disabled = true;
+    return element;
+  },
+
+  enable: function(element) {
+    element = $(element);
+    element.disabled = false;
+    return element;
+  }
+};
+
+/*--------------------------------------------------------------------------*/
+
+var Field = Form.Element;
+var $F = Form.Element.Methods.getValue;
+
+/*--------------------------------------------------------------------------*/
+
+Form.Element.Serializers = {
+  input: function(element, value) {
+    switch (element.type.toLowerCase()) {
+      case 'checkbox':
+      case 'radio':
+        return Form.Element.Serializers.inputSelector(element, value);
+      default:
+        return Form.Element.Serializers.textarea(element, value);
+    }
+  },
+
+  inputSelector: function(element, value) {
+    if (Object.isUndefined(value)) return element.checked ? element.value : null;
+    else element.checked = !!value;
+  },
+
+  textarea: function(element, value) {
+    if (Object.isUndefined(value)) return element.value;
+    else element.value = value;
+  },
+
+  select: function(element, index) {
+    if (Object.isUndefined(index))
+      return this[element.type == 'select-one' ?
+        'selectOne' : 'selectMany'](element);
+    else {
+      var opt, value, single = !Object.isArray(index);
+      for (var i = 0, length = element.length; i < length; i++) {
+        opt = element.options[i];
+        value = this.optionValue(opt);
+        if (single) {
+          if (value == index) {
+            opt.selected = true;
+            return;
+          }
+        }
+        else opt.selected = index.include(value);
+      }
+    }
+  },
+
+  selectOne: function(element) {
+    var index = element.selectedIndex;
+    return index >= 0 ? this.optionValue(element.options[index]) : null;
+  },
+
+  selectMany: function(element) {
+    var values, length = element.length;
+    if (!length) return null;
+
+    for (var i = 0, values = []; i < length; i++) {
+      var opt = element.options[i];
+      if (opt.selected) values.push(this.optionValue(opt));
+    }
+    return values;
+  },
+
+  optionValue: function(opt) {
+    // extend element because hasAttribute may not be native
+    return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
+  }
+};
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
+  initialize: function($super, element, frequency, callback) {
+    $super(callback, frequency);
+    this.element   = $(element);
+    this.lastValue = this.getValue();
+  },
+
+  execute: function() {
+    var value = this.getValue();
+    if (Object.isString(this.lastValue) && Object.isString(value) ?
+        this.lastValue != value : String(this.lastValue) != String(value)) {
+      this.callback(this.element, value);
+      this.lastValue = value;
+    }
+  }
+});
+
+Form.Element.Observer = Class.create(Abstract.TimedObserver, {
+  getValue: function() {
+    return Form.Element.getValue(this.element);
+  }
+});
+
+Form.Observer = Class.create(Abstract.TimedObserver, {
+  getValue: function() {
+    return Form.serialize(this.element);
+  }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.EventObserver = Class.create({
+  initialize: function(element, callback) {
+    this.element  = $(element);
+    this.callback = callback;
+
+    this.lastValue = this.getValue();
+    if (this.element.tagName.toLowerCase() == 'form')
+      this.registerFormCallbacks();
+    else
+      this.registerCallback(this.element);
+  },
+
+  onElementEvent: function() {
+    var value = this.getValue();
+    if (this.lastValue != value) {
+      this.callback(this.element, value);
+      this.lastValue = value;
+    }
+  },
+
+  registerFormCallbacks: function() {
+    Form.getElements(this.element).each(this.registerCallback, this);
+  },
+
+  registerCallback: function(element) {
+    if (element.type) {
+      switch (element.type.toLowerCase()) {
+        case 'checkbox':
+        case 'radio':
+          Event.observe(element, 'click', this.onElementEvent.bind(this));
+          break;
+        default:
+          Event.observe(element, 'change', this.onElementEvent.bind(this));
+          break;
+      }
+    }
+  }
+});
+
+Form.Element.EventObserver = Class.create(Abstract.EventObserver, {
+  getValue: function() {
+    return Form.Element.getValue(this.element);
+  }
+});
+
+Form.EventObserver = Class.create(Abstract.EventObserver, {
+  getValue: function() {
+    return Form.serialize(this.element);
+  }
+});
+if (!window.Event) var Event = { };
+
+Object.extend(Event, {
+  KEY_BACKSPACE: 8,
+  KEY_TAB:       9,
+  KEY_RETURN:   13,
+  KEY_ESC:      27,
+  KEY_LEFT:     37,
+  KEY_UP:       38,
+  KEY_RIGHT:    39,
+  KEY_DOWN:     40,
+  KEY_DELETE:   46,
+  KEY_HOME:     36,
+  KEY_END:      35,
+  KEY_PAGEUP:   33,
+  KEY_PAGEDOWN: 34,
+  KEY_INSERT:   45,
+
+  cache: { },
+
+  relatedTarget: function(event) {
+    var element;
+    switch(event.type) {
+      case 'mouseover': element = event.fromElement; break;
+      case 'mouseout':  element = event.toElement;   break;
+      default: return null;
+    }
+    return Element.extend(element);
+  }
+});
+
+Event.Methods = (function() {
+  var isButton;
+
+  if (Prototype.Browser.IE) {
+    var buttonMap = { 0: 1, 1: 4, 2: 2 };
+    isButton = function(event, code) {
+      return event.button == buttonMap[code];
+    };
+
+  } else if (Prototype.Browser.WebKit) {
+    isButton = function(event, code) {
+      switch (code) {
+        case 0: return event.which == 1 && !event.metaKey;
+        case 1: return event.which == 1 && event.metaKey;
+        default: return false;
+      }
+    };
+
+  } else {
+    isButton = function(event, code) {
+      return event.which ? (event.which === code + 1) : (event.button === code);
+    };
+  }
+
+  return {
+    isLeftClick:   function(event) { return isButton(event, 0) },
+    isMiddleClick: function(event) { return isButton(event, 1) },
+    isRightClick:  function(event) { return isButton(event, 2) },
+
+    element: function(event) {
+      var node = Event.extend(event).target;
+      return Element.extend(node.nodeType == Node.TEXT_NODE ? node.parentNode : node);
+    },
+
+    findElement: function(event, expression) {
+      var element = Event.element(event);
+      if (!expression) return element;
+      var elements = [element].concat(element.ancestors());
+      return Selector.findElement(elements, expression, 0);
+    },
+
+    pointer: function(event) {
+      return {
+        x: event.pageX || (event.clientX +
+          (document.documentElement.scrollLeft || document.body.scrollLeft)),
+        y: event.pageY || (event.clientY +
+          (document.documentElement.scrollTop || document.body.scrollTop))
+      };
+    },
+
+    pointerX: function(event) { return Event.pointer(event).x },
+    pointerY: function(event) { return Event.pointer(event).y },
+
+    stop: function(event) {
+      Event.extend(event);
+      event.preventDefault();
+      event.stopPropagation();
+      event.stopped = true;
+    }
+  };
+})();
+
+Event.extend = (function() {
+  var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
+    m[name] = Event.Methods[name].methodize();
+    return m;
+  });
+
+  if (Prototype.Browser.IE) {
+    Object.extend(methods, {
+      stopPropagation: function() { this.cancelBubble = true },
+      preventDefault:  function() { this.returnValue = false },
+      inspect: function() { return "[object Event]" }
+    });
+
+    return function(event) {
+      if (!event) return false;
+      if (event._extendedByPrototype) return event;
+
+      event._extendedByPrototype = Prototype.emptyFunction;
+      var pointer = Event.pointer(event);
+      Object.extend(event, {
+        target: event.srcElement,
+        relatedTarget: Event.relatedTarget(event),
+        pageX:  pointer.x,
+        pageY:  pointer.y
+      });
+      return Object.extend(event, methods);
+    };
+
+  } else {
+    Event.prototype = Event.prototype || document.createEvent("HTMLEvents").__proto__;
+    Object.extend(Event.prototype, methods);
+    return Prototype.K;
+  }
+})();
+
+Object.extend(Event, (function() {
+  var cache = Event.cache;
+
+  function getEventID(element) {
+    if (element._prototypeEventID) return element._prototypeEventID[0];
+    arguments.callee.id = arguments.callee.id || 1;
+    return element._prototypeEventID = [++arguments.callee.id];
+  }
+
+  function getDOMEventName(eventName) {
+    if (eventName && eventName.include(':')) return "dataavailable";
+    return eventName;
+  }
+
+  function getCacheForID(id) {
+    return cache[id] = cache[id] || { };
+  }
+
+  function getWrappersForEventName(id, eventName) {
+    var c = getCacheForID(id);
+    return c[eventName] = c[eventName] || [];
+  }
+
+  function createWrapper(element, eventName, handler) {
+    var id = getEventID(element);
+    var c = getWrappersForEventName(id, eventName);
+    if (c.pluck("handler").include(handler)) return false;
+
+    var wrapper = function(event) {
+      if (!Event || !Event.extend ||
+        (event.eventName && event.eventName != eventName))
+          return false;
+
+      Event.extend(event);
+      handler.call(element, event);
+    };
+
+    wrapper.handler = handler;
+    c.push(wrapper);
+    return wrapper;
+  }
+
+  function findWrapper(id, eventName, handler) {
+    var c = getWrappersForEventName(id, eventName);
+    return c.find(function(wrapper) { return wrapper.handler == handler });
+  }
+
+  function destroyWrapper(id, eventName, handler) {
+    var c = getCacheForID(id);
+    if (!c[eventName]) return false;
+    c[eventName] = c[eventName].without(findWrapper(id, eventName, handler));
+  }
+
+  function destroyCache() {
+    for (var id in cache)
+      for (var eventName in cache[id])
+        cache[id][eventName] = null;
+  }
+
+  if (window.attachEvent) {
+    window.attachEvent("onunload", destroyCache);
+  }
+
+  return {
+    observe: function(element, eventName, handler) {
+      element = $(element);
+      var name = getDOMEventName(eventName);
+
+      var wrapper = createWrapper(element, eventName, handler);
+      if (!wrapper) return element;
+
+      if (element.addEventListener) {
+        element.addEventListener(name, wrapper, false);
+      } else {
+        element.attachEvent("on" + name, wrapper);
+      }
+
+      return element;
+    },
+
+    stopObserving: function(element, eventName, handler) {
+      element = $(element);
+      var id = getEventID(element), name = getDOMEventName(eventName);
+
+      if (!handler && eventName) {
+        getWrappersForEventName(id, eventName).each(function(wrapper) {
+          element.stopObserving(eventName, wrapper.handler);
+        });
+        return element;
+
+      } else if (!eventName) {
+        Object.keys(getCacheForID(id)).each(function(eventName) {
+          element.stopObserving(eventName);
+        });
+        return element;
+      }
+
+      var wrapper = findWrapper(id, eventName, handler);
+      if (!wrapper) return element;
+
+      if (element.removeEventListener) {
+        element.removeEventListener(name, wrapper, false);
+      } else {
+        element.detachEvent("on" + name, wrapper);
+      }
+
+      destroyWrapper(id, eventName, handler);
+
+      return element;
+    },
+
+    fire: function(element, eventName, memo) {
+      element = $(element);
+      if (element == document && document.createEvent && !element.dispatchEvent)
+        element = document.documentElement;
+
+      var event;
+      if (document.createEvent) {
+        event = document.createEvent("HTMLEvents");
+        event.initEvent("dataavailable", true, true);
+      } else {
+        event = document.createEventObject();
+        event.eventType = "ondataavailable";
+      }
+
+      event.eventName = eventName;
+      event.memo = memo || { };
+
+      if (document.createEvent) {
+        element.dispatchEvent(event);
+      } else {
+        element.fireEvent(event.eventType, event);
+      }
+
+      return Event.extend(event);
+    }
+  };
+})());
+
+Object.extend(Event, Event.Methods);
+
+Element.addMethods({
+  fire:          Event.fire,
+  observe:       Event.observe,
+  stopObserving: Event.stopObserving
+});
+
+Object.extend(document, {
+  fire:          Element.Methods.fire.methodize(),
+  observe:       Element.Methods.observe.methodize(),
+  stopObserving: Element.Methods.stopObserving.methodize(),
+  loaded:        false
+});
+
+(function() {
+  /* Support for the DOMContentLoaded event is based on work by Dan Webb,
+     Matthias Miller, Dean Edwards and John Resig. */
+
+  var timer;
+
+  function fireContentLoadedEvent() {
+    if (document.loaded) return;
+    if (timer) window.clearInterval(timer);
+    document.fire("dom:loaded");
+    document.loaded = true;
+  }
+
+  if (document.addEventListener) {
+    if (Prototype.Browser.WebKit) {
+      timer = window.setInterval(function() {
+        if (/loaded|complete/.test(document.readyState))
+          fireContentLoadedEvent();
+      }, 0);
+
+      Event.observe(window, "load", fireContentLoadedEvent);
+
+    } else {
+      document.addEventListener("DOMContentLoaded",
+        fireContentLoadedEvent, false);
+    }
+
+  } else {
+    document.write("<script id=__onDOMContentLoaded defer src=//:><\/script>");
+    $("__onDOMContentLoaded").onreadystatechange = function() {
+      if (this.readyState == "complete") {
+        this.onreadystatechange = null;
+        fireContentLoadedEvent();
+      }
+    };
+  }
+})();
+/*------------------------------- DEPRECATED -------------------------------*/
+
+Hash.toQueryString = Object.toQueryString;
+
+var Toggle = { display: Element.toggle };
+
+Element.Methods.childOf = Element.Methods.descendantOf;
+
+var Insertion = {
+  Before: function(element, content) {
+    return Element.insert(element, {before:content});
+  },
+
+  Top: function(element, content) {
+    return Element.insert(element, {top:content});
+  },
+
+  Bottom: function(element, content) {
+    return Element.insert(element, {bottom:content});
+  },
+
+  After: function(element, content) {
+    return Element.insert(element, {after:content});
+  }
+};
+
+var $continue = new Error('"throw $continue" is deprecated, use "return" instead');
+
+// This should be moved to script.aculo.us; notice the deprecated methods
+// further below, that map to the newer Element methods.
+var Position = {
+  // set to true if needed, warning: firefox performance problems
+  // NOT neeeded for page scrolling, only if draggable contained in
+  // scrollable elements
+  includeScrollOffsets: false,
+
+  // must be called before calling withinIncludingScrolloffset, every time the
+  // page is scrolled
+  prepare: function() {
+    this.deltaX =  window.pageXOffset
+                || document.documentElement.scrollLeft
+                || document.body.scrollLeft
+                || 0;
+    this.deltaY =  window.pageYOffset
+                || document.documentElement.scrollTop
+                || document.body.scrollTop
+                || 0;
+  },
+
+  // caches x/y coordinate pair to use with overlap
+  within: function(element, x, y) {
+    if (this.includeScrollOffsets)
+      return this.withinIncludingScrolloffsets(element, x, y);
+    this.xcomp = x;
+    this.ycomp = y;
+    this.offset = Element.cumulativeOffset(element);
+
+    return (y >= this.offset[1] &&
+            y <  this.offset[1] + element.offsetHeight &&
+            x >= this.offset[0] &&
+            x <  this.offset[0] + element.offsetWidth);
+  },
+
+  withinIncludingScrolloffsets: function(element, x, y) {
+    var offsetcache = Element.cumulativeScrollOffset(element);
+
+    this.xcomp = x + offsetcache[0] - this.deltaX;
+    this.ycomp = y + offsetcache[1] - this.deltaY;
+    this.offset = Element.cumulativeOffset(element);
+
+    return (this.ycomp >= this.offset[1] &&
+            this.ycomp <  this.offset[1] + element.offsetHeight &&
+            this.xcomp >= this.offset[0] &&
+            this.xcomp <  this.offset[0] + element.offsetWidth);
+  },
+
+  // within must be called directly before
+  overlap: function(mode, element) {
+    if (!mode) return 0;
+    if (mode == 'vertical')
+      return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
+        element.offsetHeight;
+    if (mode == 'horizontal')
+      return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
+        element.offsetWidth;
+  },
+
+  // Deprecation layer -- use newer Element methods now (1.5.2).
+
+  cumulativeOffset: Element.Methods.cumulativeOffset,
+
+  positionedOffset: Element.Methods.positionedOffset,
+
+  absolutize: function(element) {
+    Position.prepare();
+    return Element.absolutize(element);
+  },
+
+  relativize: function(element) {
+    Position.prepare();
+    return Element.relativize(element);
+  },
+
+  realOffset: Element.Methods.cumulativeScrollOffset,
+
+  offsetParent: Element.Methods.getOffsetParent,
+
+  page: Element.Methods.viewportOffset,
+
+  clone: function(source, target, options) {
+    options = options || { };
+    return Element.clonePosition(target, source, options);
+  }
+};
+
+/*--------------------------------------------------------------------------*/
+
+if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){
+  function iter(name) {
+    return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]";
+  }
+
+  instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ?
+  function(element, className) {
+    className = className.toString().strip();
+    var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className);
+    return cond ? document._getElementsByXPath('.//*' + cond, element) : [];
+  } : function(element, className) {
+    className = className.toString().strip();
+    var elements = [], classNames = (/\s/.test(className) ? $w(className) : null);
+    if (!classNames && !className) return elements;
+
+    var nodes = $(element).getElementsByTagName('*');
+    className = ' ' + className + ' ';
+
+    for (var i = 0, child, cn; child = nodes[i]; i++) {
+      if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) ||
+          (classNames && classNames.all(function(name) {
+            return !name.toString().blank() && cn.include(' ' + name + ' ');
+          }))))
+        elements.push(Element.extend(child));
+    }
+    return elements;
+  };
+
+  return function(className, parentElement) {
+    return $(parentElement || document.body).getElementsByClassName(className);
+  };
+}(Element.Methods);
+
+/*--------------------------------------------------------------------------*/
+
+Element.ClassNames = Class.create();
+Element.ClassNames.prototype = {
+  initialize: function(element) {
+    this.element = $(element);
+  },
+
+  _each: function(iterator) {
+    this.element.className.split(/\s+/).select(function(name) {
+      return name.length > 0;
+    })._each(iterator);
+  },
+
+  set: function(className) {
+    this.element.className = className;
+  },
+
+  add: function(classNameToAdd) {
+    if (this.include(classNameToAdd)) return;
+    this.set($A(this).concat(classNameToAdd).join(' '));
+  },
+
+  remove: function(classNameToRemove) {
+    if (!this.include(classNameToRemove)) return;
+    this.set($A(this).without(classNameToRemove).join(' '));
+  },
+
+  toString: function() {
+    return $A(this).join(' ');
+  }
+};
+
+Object.extend(Element.ClassNames.prototype, Enumerable);
+
+/*--------------------------------------------------------------------------*/
+
+Element.addMethods();
\ No newline at end of file
Index: openacs-4/packages/messages/www/resources/reply-all.png
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/resources/reply-all.png,v
diff -u
Binary files differ
Index: openacs-4/packages/messages/www/resources/reply.png
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/resources/reply.png,v
diff -u
Binary files differ
Index: openacs-4/packages/messages/www/resources/rol-dialog.js
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/resources/rol-dialog.js,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/resources/rol-dialog.js	31 Aug 2010 21:30:50 -0000	1.1
@@ -0,0 +1,113 @@
+//Se verifica si se debe de agregar el classname, si es mensaje de respuesta no se agrega
+if ( document.getElementById('dialog_rol_set').value == 1) {
+    document.body.className += 'yui-skin-sam';
+    document.documentElement.className = "yui-pe";
+}
+            
+YAHOO.namespace("roles.container_roles");
+
+YAHOO.util.Event.onDOMReady(function () {
+    
+    // Define various event handlers for Dialog
+    var handleSubmit = function() {
+        var formObject = document.getElementsByName('members_list');
+        var form = formObject[0];
+        var elements = form.elements;
+        var member_ids = "";
+        var count = 0;
+        for ( var i = 0; i < elements.length ; i++) {
+            if (elements[i].checked && elements[i].name != '_dummy')  {
+                    count = count + 2;
+                    member_ids = member_ids + elements[i].value + "," + elements[i+1].value + ",";
+            }
+        }
+        var total_members = document.getElementById('total_rel_members').value;
+        member_ids = member_ids.substring(0,member_ids.length-1);
+        member_ids = member_ids.split(',');
+        var recipients = "";
+        var recipients_exists = document.getElementsByName('ids[]');
+        var recipients_exists_ids = "";
+        for ( var i = 0; i < document.getElementsByName('ids[]').length; i++) {
+            recipients_exists_ids = recipients_exists_ids + document.getElementsByName('ids[]')[i].value + ",";
+        }
+        for ( var i = 0; i < count; i = i + 2 ) {
+            var party_id = member_ids[i];
+            var party_name = member_ids[i+1];
+            if (recipients_exists_ids.match(party_id) == null) {
+                recipients = recipients + "<span id=span_" + party_id + " class=\"token\"><span><span><span><span><input type=\"hidden\" value=" + party_id + " name=\"ids[]\"/>" + party_name + "<span class=\"x\" onclick=id_delete(" + party_id + ")>&nbsp;</span></span></span></span></span></span>";
+            }
+        }
+        var recipients_old = document.getElementById('contact_reply').innerHTML;
+        document.getElementById('contact_reply').innerHTML = recipients_old + recipients;
+        this.cancel();
+    };
+    
+    var handleCancel = function() {
+        this.cancel();
+    };
+    
+    var handleSuccess = function(o) {
+        alert("error");
+    };
+    var handleFailure = function(o) {
+        alert("Submission failed: " + o.status);
+    };
+
+    // Remove progressively enhanced content class, just before creating the module
+    YAHOO.util.Dom.removeClass("dialog2", "yui-pe-content");
+
+    // Instantiate the Dialog
+    YAHOO.roles.container_roles.dialog2 = new YAHOO.widget.Dialog("dialog2", 
+                            { width : "30em",
+                              fixedcenter : true,
+                              visible : false,
+                              modal: true,
+                              constraintoviewport : true,
+                              buttons : [ { text:document.getElementById('add').value, handler:handleSubmit, isDefault:true },
+                                      { text:"Cancel", handler:handleCancel } ]
+                            });
+
+    // Validate the entries in the form to require that both first and last name are entered
+    YAHOO.roles.container_roles.dialog2.validate = function() {
+        var data = this.getData();
+        if (data.firstname == "" || data.lastname == "") {
+            alert("Please enter your first and last names.");
+            return false;
+        } else {
+            return true;
+        }
+    };
+
+    // Wire up the success and failure handlers
+    YAHOO.roles.container_roles.dialog2.callback = { success: handleSuccess,
+                             failure: handleFailure };
+    
+    // Render the Dialog
+    YAHOO.roles.container_roles.dialog2.render();
+
+});
+
+function loading_dialog() {
+    document.getElementById('members_list_div').innerHTML = "<image src=/resources/ajaxhelper/images/indicator.gif>";
+}
+
+function search_members_rel_type () {
+    var data_search = document.getElementById('search_name').value;
+    var rel_type = document.getElementById('rel_type').value;
+    if (data_search == "") {
+        data_search = "none";
+    }
+    var url_search = "members-rol";
+    var postData = "data_search=" + data_search + "&rel_type=" + rel_type;
+    loading_dialog();
+    var request = YAHOO.util.Connect.asyncRequest('POST', url_search, callback_rel_type,postData);
+}
+
+var handleSuccess_rel_type = function(o){
+    document.getElementById('members_list_div').innerHTML = o.responseText;
+};
+
+var handleFailure_rel_type = function(o){
+};
+
+var callback_rel_type = {success:handleSuccess_rel_type,failure:handleFailure_rel_type};
\ No newline at end of file
Index: openacs-4/packages/messages/www/resources/scriptaculous.js
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/resources/scriptaculous.js,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/resources/scriptaculous.js	31 Aug 2010 21:30:48 -0000	1.1
@@ -0,0 +1,35 @@
+var Scriptaculous = {
+  Version: '1.8.1',
+  require: function(libraryName) {
+    // inserting via DOM fails in Safari 2.0, so brute force approach
+    document.write('<script type="text/javascript" src="'+libraryName+'"><\/script>');
+  },
+  REQUIRED_PROTOTYPE: '1.6.0.2',
+  load: function() {
+    function convertVersionString(versionString) {
+      var v = versionString.replace(/_.*|\./g, '');
+      v = parseInt(v + '0'.times(4-v.length));
+      return versionString.indexOf('_') > -1 ? v-1 : v;
+    }
+
+    if((typeof Prototype=='undefined') ||
+       (typeof Element == 'undefined') ||
+       (typeof Element.Methods=='undefined') ||
+       (convertVersionString(Prototype.Version) <
+        convertVersionString(Scriptaculous.REQUIRED_PROTOTYPE)))
+       throw("script.aculo.us requires the Prototype JavaScript framework >= " +
+        Scriptaculous.REQUIRED_PROTOTYPE);
+
+    var js = /scriptaculous\.js(\?.*)?$/;
+    $$('head script[src]').findAll(function(s) {
+      return s.src.match(js);
+    }).each(function(s) {
+      var path = s.src.replace(js, ''),
+      includes = s.src.match(/\?.*load=([a-z,]*)/);
+      (includes ? includes[1] : 'builder,effects,dragdrop,controls,slider,sound').split(',').each(
+       function(include) { Scriptaculous.require(path+include+'.js') });
+    });
+  }
+};
+
+Scriptaculous.load();
\ No newline at end of file
Index: openacs-4/packages/messages/www/resources/send.css
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/resources/send.css,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/resources/send.css	31 Aug 2010 21:30:51 -0000	1.1
@@ -0,0 +1,82 @@
+#D8D8D8 
+/* common */
+body{background:#fff;font-family:"lucida grande", tahoma, verdana, arial, sans-serif;font-size:11px;margin:0px;padding:0px;text-align:left;}
+a{cursor:pointer;color:black;text-decoration:none;}
+a:hover{text-decoration:underline;}
+.clearfix:after{content:".";display:block;clear:both;visibility:hidden;line-height:0;height:0;}
+.clearfix{display:inline-block;}
+html[xmlns] .clearfix{display:block;}
+* html .clearfix{height:1%;}
+
+
+.composer{padding-top:10px;padding-left:20px;padding:10px 0 10px 10px;background:#F0F0F0;}
+.composer_fields {
+    margin:0;
+    width: 100%;
+}
+.composer_fields dt{float:left;width:65px;margin:0;padding:0 10px 0 0;text-align:right;}
+.composer_fields label{display:block;padding-top:6px;}
+.composer_fields .field label{padding-bottom:5px;}
+.composer_fields dd{float:left;width:382px;margin:0 0 10px 0;}
+.composer_fields .field input.inputtext,
+.composer_fields .field textarea,
+#compose_message div.tokenizer{border:1px solid #8496ba;}
+#compose_message div.tokenizer{
+    /*width:450px; */
+}
+
+.composer_fields .field #subject_field,
+.composer_fields textarea{width:450px;margin:0;padding:5px 0 5px 3px;font-size:11px;}
+.composer_fields textarea{height:100px;}
+
+
+/* autcompleter.advancedlocal css */
+.tokenizer{min-height:5px;padding:0px 0px 3px 3px;width:99%;background:#fff;font-size:11px;}
+.subject{min-height:5px;padding:0px 0px 3px 3px;width:99%;}
+.tokenizer_locked{background:#f4f4f4;}
+.tokenizer,
+.tokenizer *{cursor:text}
+.tokenizer input{width:100%;}
+.tokenizer .tokenizer_input,
+.tokenizer .token{float:left;margin-right:3px;margin-top:3px;}
+.tokenizer .tab_stop,
+.tokenizer .tokenizer_stretcher{display:block;float:left;overflow:hidden;width:0px;}
+.tokenizer .tab_stop{height:0px;}
+.tokenizer .tokenizer_stretcher{padding-top:7px;}
+#autocomplete_input{width:20px;}
+#autocomplete .tokenizer .tab_stop input{border:0px solid black;display:inline;position:relative;left:-500px;}
+#autocomplete .tokenizer .tokenizer_input_borderless {left:4px;margin-left:-1px;overflow:hidden;position:relative;}
+#autocomplete .tokenizer_input_borderless  #autocomplete_input{border:3px solid white!important;border-left:none;display:block;margin:-3px 3px -4px -2px;padding:0px!important;}
+/*IE6-/Win only*/
+/*\*/ * html#autocomplete .tokenizer_input_borderless #autocomplete_input { border:3px solid black;margin: -3px 3px -4px 14px:padding-left:10px; } /**/
+.tokenizer div:-moz-first-node{padding-top:1px!important;}
+.tokenizer_input{max-width:450px;overflow:hidden;padding:1px 0px;}
+#autocomplete .tokenizer_input input,
+.tokenizer_input_shadow{border:0px solid black;outline:0;font-family:'lucida grande', tahoma, verdana, arial, sans-serif;font-size:11px;padding:0px 5px;margin:0 0 -1px 0;white-space:pre;}
+.tokenizer_input_shadow{display:inline;left:-10000px;position:absolute;top:-10000px;}
+.tokenizer .tokenizer_input_shadow{height:0px;display:block;left:0px;overflow:hidden;position:relative;top:0px;}
+div.tokenizer .token{background-image:url('/resources/messages/inbox/images/token.gif');background-repeat:no-repeat;color:black;white-space:nowrap;}
+div.tokenizer .token span{background-image:url('/resources/messages/inbox/images/token.gif');background-position:top right;background-repeat:no-repeat;display:block;}
+div.tokenizer .token span span{background-position:bottom right;}
+div.tokenizer .token span span span{background-position:bottom left;}
+div.tokenizer .token span span span span{background-image:none;padding:2px 3px 2px 5px;}
+div.tokenizer.tokenizer_locked .token span span span span{padding-right:5px;}
+html div.tokenizer_locked .token:hover,
+html div.tokenizer_locked .token:hover span{background-image:url('/resources/messages/inbox/images/token.gif');}
+div.tokenizer .token:hover,
+div.tokenizer .token:hover span{background-image:url('/resources/messages/inbox/images/token_hover.gif');text-decoration:none;}
+div.tokenizer .token_selected,
+div.tokenizer .token_selected span,
+div.tokenizer .token_selected:hover,
+div.tokenizer .token_selected:hover span{background-image:url('/resources/messages/inbox/images/token_selected.gif');color:white;text-decoration:none;}
+div.tokenizer .token span.x,
+div.tokenizer .token span.x_hover,
+div.tokenizer .token:hover span.x,
+div.tokenizer .token:hover span.x_hover{background-image:url('/resources/messages/inbox/images/token_x.gif');background-position:4px 2px;cursor:pointer;display:inline;padding:0px 6px 0px 5px;}
+div.tokenizer.tokenizer_locked .token span.x,
+div.tokenizer.tokenizer_locked .token span.x_hover{display:none;}
+div.autocomplete {  position:absolute;  width:355px;  background-color:white;  border:1px solid #888;  margin-top:-2px;  padding:0px;}
+div.autocomplete ul {  list-style-type:none;margin:0px; padding:0px;}
+div.autocomplete ul li.selected { background-color: #E0ECF8;}
+div.autocomplete ul li {  list-style-type:none;  display:block;  margin:0;  padding:2px;  height:32px;cursor:pointer;}
+/* end of autcompleter.advancedlocal css */
\ No newline at end of file
Index: openacs-4/packages/messages/www/resources/inbox/.svn/entries
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/resources/inbox/.svn/entries,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/resources/inbox/.svn/entries	31 Aug 2010 21:30:54 -0000	1.1
@@ -0,0 +1,23 @@
+8
+
+dir
+0
+https://rorcraft.unfuddle.com/svn/rorcraft_rorcraft/website/output/autocomplete_advanced_local/inbox
+https://rorcraft.unfuddle.com/svn/rorcraft_rorcraft
+add
+
+
+
+
+
+
+
+svn:special svn:externals svn:needs-lock
+
+images
+dir
+
+
+
+add
+
Index: openacs-4/packages/messages/www/resources/inbox/.svn/format
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/resources/inbox/.svn/format,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/resources/inbox/.svn/format	31 Aug 2010 21:30:54 -0000	1.1
@@ -0,0 +1 @@
+8
Index: openacs-4/packages/messages/www/resources/inbox/images/token.gif
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/resources/inbox/images/token.gif,v
diff -u
Binary files differ
Index: openacs-4/packages/messages/www/resources/inbox/images/token_hover.gif
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/resources/inbox/images/token_hover.gif,v
diff -u
Binary files differ
Index: openacs-4/packages/messages/www/resources/inbox/images/token_selected.gif
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/resources/inbox/images/token_selected.gif,v
diff -u
Binary files differ
Index: openacs-4/packages/messages/www/resources/inbox/images/token_x.gif
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/resources/inbox/images/token_x.gif,v
diff -u
Binary files differ
Index: openacs-4/packages/messages/www/resources/inbox/images/.svn/entries
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/resources/inbox/images/.svn/entries,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/resources/inbox/images/.svn/entries	31 Aug 2010 21:30:54 -0000	1.1
@@ -0,0 +1,72 @@
+8
+
+dir
+0
+https://rorcraft.unfuddle.com/svn/rorcraft_rorcraft/website/output/autocomplete_advanced_local/inbox/images
+https://rorcraft.unfuddle.com/svn/rorcraft_rorcraft
+add
+
+
+
+
+
+
+
+svn:special svn:externals svn:needs-lock
+
+token.gif
+file
+
+
+
+add
+
+
+
+
+
+has-props
+has-prop-mods
+
+token_hover.gif
+file
+
+
+
+add
+
+
+
+
+
+has-props
+has-prop-mods
+
+token_x.gif
+file
+
+
+
+add
+
+
+
+
+
+has-props
+has-prop-mods
+
+token_selected.gif
+file
+
+
+
+add
+
+
+
+
+
+has-props
+has-prop-mods
+
Index: openacs-4/packages/messages/www/resources/inbox/images/.svn/format
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/resources/inbox/images/.svn/format,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/resources/inbox/images/.svn/format	31 Aug 2010 21:30:54 -0000	1.1
@@ -0,0 +1 @@
+8
Index: openacs-4/packages/messages/www/resources/inbox/images/.svn/props/token.gif.svn-work
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/resources/inbox/images/.svn/props/token.gif.svn-work,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/resources/inbox/images/.svn/props/token.gif.svn-work	31 Aug 2010 21:30:54 -0000	1.1
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
Index: openacs-4/packages/messages/www/resources/inbox/images/.svn/props/token_hover.gif.svn-work
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/resources/inbox/images/.svn/props/token_hover.gif.svn-work,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/resources/inbox/images/.svn/props/token_hover.gif.svn-work	31 Aug 2010 21:30:54 -0000	1.1
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
Index: openacs-4/packages/messages/www/resources/inbox/images/.svn/props/token_selected.gif.svn-work
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/resources/inbox/images/.svn/props/token_selected.gif.svn-work,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/resources/inbox/images/.svn/props/token_selected.gif.svn-work	31 Aug 2010 21:30:54 -0000	1.1
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
Index: openacs-4/packages/messages/www/resources/inbox/images/.svn/props/token_x.gif.svn-work
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/resources/inbox/images/.svn/props/token_x.gif.svn-work,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/resources/inbox/images/.svn/props/token_x.gif.svn-work	31 Aug 2010 21:30:54 -0000	1.1
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
Index: openacs-4/packages/messages/www/view/index.vuh
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages/www/view/index.vuh,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages/www/view/index.vuh	31 Aug 2010 21:30:45 -0000	1.1
@@ -0,0 +1,36 @@
+# packages/file-storage/www/download/index.vuh
+
+ad_page_contract {
+
+    Virtual URL handler for file downloads
+
+    @author Kevin Scaldeferri (kevin@arsdigita.com)
+    @author Don Baccus (simplified it by using cr utility)
+    @creation-date 18 December 2000
+    @cvs-id $Id: index.vuh,v 1.1 2010/08/31 21:30:45 alvaror Exp $
+} {
+    file_id:integer,optional
+    version_id:integer,optional
+} -validate {
+    either_one {
+    if { ! [info exists file_id] && ! [info exists version_id] } {
+        ad_complain "Either file_id or version_id must exist"
+    }
+    }
+}
+
+if {![exists_and_not_null version_id] && [info exists file_id]} {
+    set version_id [item::get_live_revision $file_id]
+}
+if { ![exists_and_not_null file_id] && [info exists version_id] } {
+    set file_id [content::revision::item_id -revision_id $version_id]
+}
+
+set user_id [ad_conn user_id]
+permission::require_permission -party_id $user_id -object_id $version_id -privilege read
+
+if {[apm_package_installed_p views]} {
+    views::record_view -object_id $file_id -viewer_id $user_id
+}
+
+cr_write_content -revision_id $version_id
Index: openacs-4/packages/messages-portlet/tcl/apm-callback-procs.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages-portlet/tcl/apm-callback-procs.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages-portlet/tcl/apm-callback-procs.tcl	31 Aug 2010 21:31:49 -0000	1.1
@@ -0,0 +1,29 @@
+ad_library {
+    Procedures for initializing service contracts etc. for the
+    messages portlet package. Should only be executed 
+    once upon installation.
+    
+    @creation-date 2010-01-20
+    @author Pedro Castellanos (pedro@viaro.net)
+}
+
+namespace eval apm::messages_portlet {}
+namespace eval apm::messages_admin_portlet {}
+
+ad_proc -public apm::messages_portlet::after_install {} {
+    Create the datasources needed by the messages portlets.
+} {
+    db_transaction {
+        messages_portlet::after_install
+    }
+}
+
+ad_proc -public apm::messages_portlet::before_uninstall {} {
+    messages Portlet package uninstall proc
+} {
+    db_transaction {
+        messages_portlet::uninstall
+    }
+}
+
+
Index: openacs-4/packages/messages-portlet/tcl/messages-portlet-procs.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages-portlet/tcl/messages-portlet-procs.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages-portlet/tcl/messages-portlet-procs.tcl	31 Aug 2010 21:31:49 -0000	1.1
@@ -0,0 +1,355 @@
+ad_library {
+
+    Procedures to support the messages portlets
+
+    @creation-date 2010-06-01
+    @author Pedro Castellanos (pedro@viaro.net)
+    
+}
+
+namespace eval messages_portlet {}
+namespace eval messages_admin_portlet {}
+
+
+#
+# messages namespace
+#
+
+ad_proc -private messages_portlet::get_my_name {
+} {
+    return "messages_portlet"
+}
+
+
+
+ad_proc -private messages_portlet::my_package_key {
+} {
+    return "messages-portlet"
+}
+
+
+
+ad_proc -public messages_portlet::get_pretty_name {
+} {
+    return "#messages.messages#"
+}
+
+
+
+ad_proc -public messages_portlet::link {
+} {
+    return ""
+}
+
+
+
+ad_proc -public messages_portlet::add_self_to_page {
+    {-portal_id:required}
+    {-package_id:required}
+    {-param_action:required}
+    {-force_region ""}
+    {-page_name "" }
+} {
+    Adds a messages PE to the given portal.
+    
+    @param portal_id The page to add self to
+    @param package_id The community with the folder
+    
+    @return element_id The new element's id
+} {
+
+    ns_log Notice "portal_id:: $portal_id;; $page_name"
+    return [portal::add_element_parameters \
+        -portal_id $portal_id \
+        -portlet_name [get_my_name] \
+        -value $package_id \
+        -force_region $force_region \
+        -page_name $page_name \
+        -pretty_name [get_pretty_name] \
+        -param_action $param_action
+       ]
+}
+
+
+
+ad_proc -public messages_portlet::remove_self_from_page {
+    {-portal_id:required}
+    {-package_id:required}
+} {
+    Removes a messages PE from the given page or the package_id of the
+    messages package from the portlet if there are others remaining
+    
+    @param portal_id The page to remove self from
+    @param package_id
+} {
+    portal::remove_element_parameters \
+        -portal_id $portal_id \
+        -portlet_name [get_my_name] \
+        -value $package_id
+}
+
+
+
+ad_proc -public messages_portlet::show {
+    cf
+} {
+    portal::show_proc_helper \
+        -package_key [my_package_key] \
+        -config_list $cf \
+        -template_src "messages-portlet"
+}
+
+#
+# messages admin namespace
+#
+
+ad_proc -private messages_admin_portlet::get_my_name {} {
+    return "messages_admin_portlet"
+}
+
+
+ad_proc -public messages_admin_portlet::get_pretty_name {} {
+    return "#messages.messages_Administration#"
+}
+
+
+
+ad_proc -private messages_admin_portlet::my_package_key {} {
+    return "messages-portlet"
+}
+
+
+
+ad_proc -public messages_admin_portlet::link {} {
+    return ""
+}
+
+
+
+ad_proc -public messages_admin_portlet::add_self_to_page {
+    {-portal_id:required}
+    {-page_name ""}
+    {-package_id:required}
+} {
+    Adds a messages admin PE to the given portal
+
+    @param portal_id The page to add self to
+    @param package_id The package_id of the messages package
+
+    @return element_id The new element's id
+} {
+    return [portal::add_element_parameters \
+                -portal_id $portal_id \
+                -portlet_name [get_my_name] \
+                -key package_id \
+                -value $package_id
+           ]
+}
+
+ad_proc -public messages_admin_portlet::remove_self_from_page {
+    {-portal_id:required}
+} {
+    Removes a messages admin PE from the given page
+} {
+    portal::remove_element \
+        -portal_id $portal_id \
+        -portlet_name [get_my_name]
+}
+
+
+ad_proc -public messages_admin_portlet::show {
+    cf
+} {
+    portal::show_proc_helper \
+        -package_key [my_package_key] \
+        -config_list $cf \
+        -template_src "messages-admin-portlet"
+}
+
+ad_proc -private messages_portlet::after_install {} {
+    Create the datasources needed by the messages portlet.
+} {
+    
+    db_transaction {
+    set ds_id [portal::datasource::new \
+               -name "messages_portlet" \
+               -description "Messages Portlet"]
+
+    portal::datasource::set_def_param \
+            -datasource_id $ds_id \
+            -config_required_p t \
+            -configured_p t \
+            -key shadeable_p \
+            -value t
+
+    portal::datasource::set_def_param \
+            -datasource_id $ds_id \
+            -config_required_p t \
+            -configured_p t \
+            -key hideable_p \
+            -value t
+
+        portal::datasource::set_def_param \
+            -datasource_id $ds_id \
+            -config_required_p t \
+            -configured_p t \
+            -key user_editable_p \
+            -value f
+
+        portal::datasource::set_def_param \
+            -datasource_id $ds_id \
+            -config_required_p t \
+            -configured_p t \
+            -key shaded_p \
+            -value f
+
+        portal::datasource::set_def_param \
+            -datasource_id $ds_id \
+            -config_required_p t \
+            -configured_p t \
+            -key link_hideable_p \
+            -value f
+
+        portal::datasource::set_def_param \
+            -datasource_id $ds_id \
+            -config_required_p t \
+            -configured_p f \
+            -key package_id \
+            -value ""
+
+    register_portal_datasource_impl
+
+    }
+}
+
+ad_proc -private messages_portlet::register_portal_datasource_impl {} {
+    Create the service contracts needed by the messages portlet.
+} {
+    set spec {
+        name "messages_portlet"
+    contract_name "portal_datasource"
+    owner "messages-portlet"
+        aliases {
+        GetMyName messages_portlet::get_my_name
+        GetPrettyName  messages_portlet::get_pretty_name
+        Link messages_portlet::link
+        AddSelfToPage messages_portlet::add_self_to_page
+        Show messages_portlet::show
+        Edit messages_portlet::edit
+        RemoveSelfFromPage messages_portlet::remove_self_from_page
+        }
+    }
+    
+    acs_sc::impl::new_from_spec -spec $spec
+}
+
+ad_proc -private messages_admin_portlet::after_install {} {
+    Create the datasources needed by the messages portlet.
+} {
+
+    db_transaction {
+    set ds_id [portal::datasource::new \
+               -name "messages_admin_portlet" \
+               -description "Messages Admin Portlet"]
+
+    portal::datasource::set_def_param \
+            -datasource_id $ds_id \
+            -config_required_p t \
+            -configured_p t \
+            -key shadeable_p \
+            -value f
+
+    portal::datasource::set_def_param \
+            -datasource_id $ds_id \
+            -config_required_p t \
+            -configured_p t \
+            -key hideable_p \
+            -value f
+
+        portal::datasource::set_def_param \
+            -datasource_id $ds_id \
+            -config_required_p t \
+            -configured_p t \
+            -key user_editable_p \
+            -value f
+
+        portal::datasource::set_def_param \
+            -datasource_id $ds_id \
+            -config_required_p t \
+            -configured_p t \
+            -key shaded_p \
+            -value f
+
+        portal::datasource::set_def_param \
+            -datasource_id $ds_id \
+            -config_required_p t \
+            -configured_p t \
+            -key link_hideable_p \
+            -value t
+
+        portal::datasource::set_def_param \
+            -datasource_id $ds_id \
+            -config_required_p t \
+            -configured_p f \
+            -key package_id \
+            -value ""
+
+    register_portal_datasource_impl
+    }
+
+}
+
+
+
+ad_proc -private messages_admin_portlet::register_portal_datasource_impl {} {
+    Create the service contracts needed by the messages admin portlet.
+} {
+    set spec {
+        name "messages_admin_portlet"
+    contract_name "portal_datasource"
+    owner "messages-portlet"
+        aliases {
+        GetMyName messages_admin_portlet::get_my_name
+        GetPrettyName  messages_admin_portlet::get_pretty_name
+        Link messages_admin_portlet::link
+        AddSelfToPage messages_admin_portlet::add_self_to_page
+        Show messages_admin_portlet::show
+        Edit messages_admin_portlet::edit
+        RemoveSelfFromPage messages_admin_portlet::remove_self_from_page
+        }
+    }
+    
+    acs_sc::impl::new_from_spec -spec $spec
+}
+
+ad_proc -private messages_portlet::uninstall {} {
+    messages Portlet package uninstall proc
+} {
+    unregister_implementations
+    set ds_id [portal::get_datasource_id messages_portlet]
+    db_exec_plsql delete_messages_ds { *SQL* }
+}
+
+ad_proc -private messages_admin_portlet::uninstall {} {
+    messages Portlet package uninstall proc
+} {
+    unregister_implementations
+    set ds_id [portal::get_datasource_id messages_admin_portlet]
+    db_exec_plsql delete_admin_ds { *SQL* }
+}
+
+ad_proc -private messages_portlet::unregister_implementations {} {
+    Unregister service contract implementations
+} {
+    acs_sc::impl::delete \
+        -contract_name "portal_datasource" \
+        -impl_name "messages_portlet"
+}
+
+ad_proc -private messages_admin_portlet::unregister_implementations {} {
+    Unregister service contract implementations
+} {
+    acs_sc::impl::delete \
+        -contract_name "portal_datasource" \
+        -impl_name "messages_admin_portlet"
+}
Index: openacs-4/packages/messages-portlet/www/messages-admin-portlet.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages-portlet/www/messages-admin-portlet.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages-portlet/www/messages-admin-portlet.adp	31 Aug 2010 21:31:49 -0000	1.1
@@ -0,0 +1,5 @@
+<ul>
+
+</ul>
+
+
Index: openacs-4/packages/messages-portlet/www/messages-admin-portlet.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages-portlet/www/messages-admin-portlet.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages-portlet/www/messages-admin-portlet.tcl	31 Aug 2010 21:31:49 -0000	1.1
@@ -0,0 +1,11 @@
+
+ad_page_contract {
+    The display logic for the messages admin portlet
+
+    @creation-date 2010-06-1
+    @author Pedro Castellanos (pedro@viaro.net)
+} {
+
+} -properties {
+    
+}
Index: openacs-4/packages/messages-portlet/www/messages-portlet.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages-portlet/www/messages-portlet.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages-portlet/www/messages-portlet.adp	31 Aug 2010 21:31:49 -0000	1.1
@@ -0,0 +1,8 @@
+<span style="margin-left:25px;font-weight: bold;">#messages.inbox# @data_info@</span>
+<br>
+<br>
+<ul>
+    <li><a href="messages/messages">#messages.view_messages#</a></li>
+    <li><a href="messages/send">#messages.write_message#</a></li>
+    <li><a href="messages/message-option">#messages.configuration#</a></li>
+</ul>
\ No newline at end of file
Index: openacs-4/packages/messages-portlet/www/messages-portlet.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages-portlet/www/messages-portlet.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages-portlet/www/messages-portlet.tcl	31 Aug 2010 21:31:49 -0000	1.1
@@ -0,0 +1,20 @@
+ad_page_contract {
+    The display logic for the messages portlet
+
+    @author Pedro Castellanos (pedro@viaro.net)
+    @creation-date 2010-06-01
+
+} {
+
+}
+
+append folder_ids [messages::get_folder_id -folder_name "inbox"]
+set user_id [ad_conn user_id]
+set community_id [dotlrn_community::get_community_id]
+set folders_info [lindex [db_list_of_lists count_new_messages {}] 0]
+
+if { [llength $folders_info] } {
+    set data_info "([lindex $folders_info 0]/[lindex $folders_info 1])"
+} else {
+    set data_info ""
+}
Index: openacs-4/packages/messages-portlet/www/messages-portlet.xql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/messages-portlet/www/messages-portlet.xql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/messages-portlet/www/messages-portlet.xql	31 Aug 2010 21:31:49 -0000	1.1
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+<queryset>
+
+    <fullquery name="count_new_messages">
+        <querytext>
+                select count (case when um.new_p = 't' then 1 end ) as new_t,count (um.new_p) as total
+                from (select distinct(parent_id) as msg_id,folder_id,new_p
+                        from messages_user_messages 
+                        where user_id = :user_id
+                        and folder_id not in ($folder_ids)
+                        ) um,
+                      (select msg_id 
+                        from messages_messages 
+                        where community_id = :community_id) m
+                where um.msg_id = m.msg_id
+                group by um.folder_id
+                order by folder_id
+        </querytext>
+    </fullquery>
+
+</queryset>