[pkg-horde] [SCM] Debian Horde Packages repository: turba2 package branch, debian-sid, updated. cf7c7f7d40b5a0dfe7b9c7ae50cb1cf0484683e0

Gregory Colpart reg at foulademer.gcolpart.com
Sun Apr 26 18:22:13 UTC 2009


The following commit has been merged in the debian-sid branch:
commit 07c353451974be25e9127d5e7211c2eef902db3f
Author: Gregory Colpart <reg at foulademer.gcolpart.com>
Date:   Sun Apr 26 19:32:17 2009 +0200

    Merge from upstream

diff --git a/bug5476.phpt b/bug5476.phpt
deleted file mode 100644
index 9a6177a..0000000
--- a/bug5476.phpt
+++ /dev/null
@@ -1,264 +0,0 @@
---TEST--
-[Bug #5476] Possibly invalid VBook search criteria
---FILE--
-<?php
-// Pretend that we are turba
-class Registry {
-    function getApp()
-    {
-        return 'turba';
-    }
-    function get()
-    {
-        return 'turba';
-    }
-}
-$registry = new Registry;
-
-// Set a dummy server name
-$_SERVER['SERVER_NAME'] = 'localhost';
-
-// Indicate that we want the dummy IMAP driver
-$GLOBALS['KOLAB_TESTING'] = array(
-    "INBOX/Contacts" => array(
-        "status" => array(
-            "uidvalidity" => 1205144313,
-            "uidnext" => 1
-        ),
-        "mails" => array(),
-        "permissions" => array(
-            "wrobel at dev.pardus.de" => "alriswcd"
-        ),
-        "annotations" => array(
-            "/vendor/kolab/folder-type" => array(
-                "value.shared" => "contact.default"
-            ),
-        ),
-    ),
-    "INBOX/test2" => array(
-        "status" => array(
-            "uidvalidity" => 1205144313,
-            "uidnext" => 1
-        ),
-        "mails" => array(),
-        "permissions" => array(
-            "wrobel at dev.pardus.de" => "alriswcd"
-        ),
-        "annotations" => array(
-            "/vendor/kolab/folder-type" => array(
-                "value.shared" => "contact"
-            ),
-            "/vendor/horde/share-params" => array(
-                "value.shared" => "YTozOntzOjQ6InR5cGUiO3M6NToidmJvb2siO3M6Njoic291cmNlIjtzOjIwOiJ3cm9iZWxAZGV2LnBhcmR1cy5kZSI7czo4OiJjcml0ZXJpYSI7YToxOntzOjk6ImZpcnN0bmFtZSI7czo1OiJ0ZXN0MiI7fX0="
-            ),
-        ),
-    ),
-);
-
-// We need the basic PEAR library
-require_once 'PEAR.php';
-require_once 'Horde/Auth.php';
-require_once 'Horde/Perms.php';
-require_once 'Horde/Share.php';
-require_once 'Horde/Kolab.php';
-
-session_start();
-$_SESSION['__auth']['userId'] = 'wrobel at dev.pardus.de';
-$_SESSION['__auth']['authenticated'] = true;
-
-// Find the base file path of Turba.
-if (!defined('TURBA_BASE')) {
-    define('TURBA_BASE', dirname(__FILE__) . '/../..');
-}
-
-// Turba base libraries.
-require_once TURBA_BASE . '/lib/Turba.php';
-require_once TURBA_BASE . '/lib/Driver.php';
-require_once TURBA_BASE . '/lib/Object.php';
-
-$cfgSources = array();
-
-    $cfgSources['kolab'] = array(
-        'title' => _("Contacts"),
-        'type' => 'kolab',
-        'params' => array(
-            'charset' => 'utf-8',
-        ),
-        'map' => array(
-            '__key' => 'uid',
-            '__uid' => 'uid',
-            /* Personal */
-            'name' => array('fields' => array('firstname', 'middlenames', 'lastname'),
-                            'format' => '%s %s %s',
-                            'attribute' => 'full-name'),
-            'firstname'         => 'given-name',
-            'lastname'          => 'last-name',
-            'middlenames'       => 'middle-names',
-            'namePrefix'        => 'prefix',
-            'nameSuffix'        => 'suffix',
-            'initials'          => 'initials',
-            'nickname'          => 'nick-name',
-            'gender'            => 'gender',
-            'birthday'          => 'birthday',
-            'spouse'            => 'spouse-name',
-            'anniversary'       => 'anniversary',
-            'children'          => 'children',
-            /* Location */
-            'workStreet'        => 'addr-business-street',
-            'workCity'          => 'addr-business-locality',
-            'workProvince'      => 'addr-business-region',
-            'workPostalCode'    => 'addr-business-postal-code',
-            'workCountry'       => 'addr-business-country',
-            'homeStreet'        => 'addr-home-street',
-            'homeCity'          => 'addr-home-locality',
-            'homeProvince'      => 'addr-home-region',
-            'homePostalCode'    => 'addr-home-postal-code',
-            'homeCountry'       => 'addr-home-country',
-            /* Communications */
-            'emails'            => 'emails',
-            'homePhone'         => 'phone-home1',
-            'workPhone'         => 'phone-business1',
-            'cellPhone'         => 'phone-mobile',
-            'fax'               => 'phone-businessfax',
-            'instantMessenger'  => 'im-address',
-            /* Organization */
-            'title'             => 'job-title',
-            'role'              => 'profession',
-            'company'           => 'organization',
-            'department'        => 'department',
-            'office'            => 'office-location',
-            'manager'           => 'manager-name',
-            'assistant'         => 'assistant',
-            /* Other */
-            'category'          => 'categories',
-            'notes'             => 'body',
-            'website'           => 'web-page',
-            'freebusyUrl'       => 'free-busy-url',
-            'language'          => 'language',
-            'latitude'          => 'latitude',
-            'longitude'         => 'longitude',
-        ),
-        'tabs' => array(
-            _("Personal") => array('name', 'firstname', 'lastname', 'middlenames',
-                                   'namePrefix', 'nameSuffix', 'initials', 'nickname',
-                                   'gender', 'birthday', 'spouse', 'anniversary',
-                                   'children'),
-            _("Location") => array('homeStreet', 'homeCity', 'homeProvince',
-                                   'homePostalCode', 'homeCountry', 'workStreet',
-                                   'workCity', 'workProvince', 'workPostalCode',
-                                   'workCountry'),
-            _("Communications") => array('emails', 'homePhone', 'workPhone',
-                                         'cellPhone', 'fax', 'instantMessenger'),
-            _("Organization") => array('title', 'role', 'company', 'department',
-                                       'office', 'manager', 'assistant'),
-            _("Other") => array('category', 'notes', 'website', 'freebusyUrl',
-                                'language', 'latitude', 'longitude'),
-        ),
-        'search' => array(
-            /* Personal */
-            'firstname',
-            'lastname',
-            'middlenames',
-            'namePrefix',
-            'nameSuffix',
-            'initials',
-            'nickname',
-            'gender',
-            'birthday',
-            'spouse',
-            'anniversary',
-            'children',
-            /* Location */
-            'workStreet',
-            'workCity',
-            'workProvince',
-            'workPostalCode',
-            'workCountry',
-            'homeStreet',
-            'homeCity',
-            'homeProvince',
-            'homePostalCode',
-            'homeCountry',
-            /* Communications */
-            'emails',
-            'homePhone',
-            'workPhone',
-            'cellPhone',
-            'fax',
-            'instantMessenger',
-            /* Organization */
-            'title',
-            'role',
-            'company',
-            'department',
-            'office',
-            'manager',
-            'assistant',
-            /* Other */
-            'category',
-            'notes',
-            'website',
-            'language',
-        ),
-        'strict' => array(
-            'uid',
-        ),
-        'export' => true,
-        'browse' => true,
-        'use_shares' => true,
-        'shares_only' => true,
-    );
-
-
-/* Pretend that we are kronolith */
-$kolab = &new Kolab('turba');
-
-/* Open our calendar */
-$kolab->open('INBOX/Contacts', 1);
-
-$object = array(
-    'uid' => 1,
-    'given-name' => 'test',
-    'last-name' => 'test',
-    'full-name' => 'test  test',
-);
-
-// Save the contact
-$kolab->_storage->save($object);
-
-$object = array(
-    'uid' => 2,
-    'given-name' => 'test2',
-    'last-name' => 'test2',
-    'full-name' => 'test2  test2',
-);
-
-// Save the contact
-$kolab->_storage->save($object);
-
-$GLOBALS['conf']['log']['enabled'] = false;
-$GLOBALS['conf']['prefs']['driver'] = 'session';
-$GLOBALS['conf']['share']['driver'] = 'kolab';
-$GLOBALS['conf']['kolab']['enabled'] = true;
-$GLOBALS['conf']['kolab']['imap']['server'] = 'localhost';
-$GLOBALS['conf']['kolab']['imap']['port'] = 0;
-$GLOBALS['perms'] = &Perms::singleton();
-$_SESSION['turba']['has_share'] =  true;
-$GLOBALS['turba_shares'] = &Horde_Share::singleton($registry->getApp());
-
-$GLOBALS['cfgSources'] = Turba::getConfigFromShares($cfgSources);
-
-// Check that the driver can be created
-$turba = Turba_Driver::singleton('wrobel at dev.pardus.de');
-$result = $turba->search(array(), array('last-name'));
-
-var_dump($result->count());
-
-$turba = Turba_Driver::singleton('INBOX%2Ftest2');
-$result = $turba->search(array(), array('last-name'));
-
-var_dump($result->count());
-
---EXPECT--
-int(2)
-int(0)
diff --git a/config/attributes.php.dist b/config/attributes.php.dist
index 9532ed1..e2f58a8 100644
--- a/config/attributes.php.dist
+++ b/config/attributes.php.dist
@@ -39,7 +39,7 @@
  *                           http://wiki.horde.org/Doc/Dev/FormTypes.
  * </pre>
  *
- * $Horde: turba/config/attributes.php.dist,v 1.36.6.10 2008/05/09 22:34:47 jan Exp $
+ * $Horde: turba/config/attributes.php.dist,v 1.36.6.18 2008/11/12 06:29:26 wrobel Exp $
  */
 
 /* Personal stuff. */
@@ -95,13 +95,13 @@ $attributes['birthday'] = array(
     'label' => _("Birthday"),
     'type' => 'monthdayyear',
     'required' => false,
-    'params' => array('start_year' => 1900, 'end_year' => null, 'picker' => true, 'format_in' => '%Y-%m-%d', 'format_out' => '%x'),
+    'params' => array('start_year' => 1900, 'end_year' => null, 'picker' => true, 'format_in' => '%Y-%m-%d', 'format_out' => $GLOBALS['prefs']->getValue('date_format')),
     'time_object_label' => _("Birthdays"),
 );
 $attributes['anniversary'] = array(
     'label' => _("Anniversary"),
     'type' => 'monthdayyear',
-    'params' => array('start_year' => 1900, 'end_year' => null, 'picker' => true, 'format_in' => '%Y-%m-%d', 'format_out' => '%x'),
+    'params' => array('start_year' => 1900, 'end_year' => null, 'picker' => true, 'format_in' => '%Y-%m-%d', 'format_out' => $GLOBALS['prefs']->getValue('date_format')),
     'required' => false,
     'time_object_label' => _("Anniversaries"),
 );
@@ -117,6 +117,18 @@ $attributes['children'] = array(
     'required' => false,
     'params' => array('regex' => '', 'size' => 40, 'maxlength' => 255)
 );
+$attributes['photo'] = array(
+    'label' => _("Photo"),
+    'type' => 'image',
+    'required' => false,
+    'params' => array('show_upload' => true, 'show_keeporig' => true, 'max_filesize'  => null),
+);
+$attributes['phototype'] = array(
+    'label' => _("Photo MIME Type"),
+    'type' => 'text',
+    'required' => false,
+    'params' => array('regex' => '', 'size' => 40, 'maxlength' => 255)
+);
 
 /* Locations, addresses. */
 $attributes['homeAddress'] = array(
@@ -308,6 +320,18 @@ $attributes['office'] = array(
     'required' => false,
     'params' => array('regex' => '', 'size' => 40, 'maxlength' => 255)
 );
+$attributes['logo'] = array(
+    'label' => _("Logo"),
+    'type' => 'image',
+    'required' => false,
+    'params' => array('show_upload' => true, 'show_keeporig' => true, 'max_filesize'  => null),
+);
+$attributes['logotype'] = array(
+    'label' => _("Logo MIME Type"),
+    'type' => 'text',
+    'required' => false,
+    'params' => array('regex' => '', 'size' => 40, 'maxlength' => 255)
+);
 
 /* Other */
 $attributes['notes'] = array(
@@ -340,6 +364,20 @@ $attributes['smimePublicKey'] = array(
     'required' => false,
     'params' => array('rows' => 3, 'cols' => 40)
 );
+/* If using Horde 3.3 or later, you can enable the following attributes to
+ * enable pretty rendering of PGP and S/MIME keys. */
+// $attributes['pgpPublicKey'] = array(
+//     'label' => _("PGP Public Key"),
+//     'type' => 'pgp',
+//     'required' => false,
+//     'params' => array('gpg' => '/usr/bin/gpg', 'temp_dir' => Horde::getTempDir(), 'rows' => 3, 'cols' => 40)
+// );
+// $attributes['smimePublicKey'] = array(
+//     'label' => _("S/MIME Public Certificate"),
+//     'type' => 'smime',
+//     'required' => false,
+//     'params' => array('temp_dir' => Horde::getTempDir(), 'rows' => 3, 'cols' => 40)
+// );
 /* This attribute uses Horde's categories and is an example how to use an enum
  * field.  Don't forget to add a 'map' entry to config/sources.php if you want
  * to use this attribute. */
@@ -354,8 +392,22 @@ $attributes['category'] = array(
         'prompt' => false),
     'required' => false
 );
+/* If using Horde 3.2 or later, you can use the following category attribute
+ * instead which shows category colors and allows to add new categories. */
+// $attributes['category'] = array(
+//     'label' => _("Category"),
+//     'type' => 'category',
+//     'params' => array(),
+//     'required' => false
+// );
 
 /* Additional attributes supported by Kolab */
+$attributes['kolabHomeServer'] = array(
+    'label' => _("Kolab Home Server"),
+    'type' => 'text',
+    'required' => false,
+    'params' => array('regex' => '', 'size' => 40, 'maxlength' => 255)
+);
 $attributes['initials'] = array(
     'label' => _("Initials"),
     'type' => 'text',
diff --git a/config/conf.xml b/config/conf.xml
index 8eaaf4a..b7a80b8 100644
--- a/config/conf.xml
+++ b/config/conf.xml
@@ -1,10 +1,12 @@
 <?xml version="1.0"?>
-<!-- $Horde: turba/config/conf.xml,v 1.6.2.5 2008/05/06 21:26:59 bklang Exp $ -->
+<!-- $Horde: turba/config/conf.xml,v 1.6.2.6 2008/06/25 15:52:54 jan Exp $ -->
 <configuration>
  <configsection name="menu">
   <configheader>Menu Settings</configheader>
-  <configboolean name="import_export" desc="Should we display an Import/Export link in Turba's menu?">true</configboolean>
-  <configmultienum name="apps" desc="Select any applications that should be linked in Turba's menu">
+  <configboolean name="import_export" desc="Should we display an Import/Export
+  link in Turba's menu?">true</configboolean>
+  <configmultienum name="apps" desc="Select any applications that should be
+  linked in Turba's menu">
    <values>
     <configspecial name="list-horde-apps"/>
    </values>
@@ -13,20 +15,23 @@
 
  <configsection name="client">
    <configheader>Clients</configheader>
-   <configstring name="addressbook" desc="Name of client addressbook. Must not be configured for shares.">localsql</configstring>
+   <configstring name="addressbook" desc="Name of client addressbook. If this
+   is a shared address book, use the share id, not the source
+   name.">localsql</configstring>
  </configsection>
 
  <configsection name="shares">
-   <configheader>Shares</configheader>
-   <configstring name="source" desc="Name of source for creating new shares.&lt;br /&gt;
-    Note that leaving this blank will prevent users from being able to create
-    new address books."
-   required="false">localsql</configstring>
+  <configheader>Shares</configheader>
+  <configstring name="source" desc="Name of source for creating new
+  shares.&lt;br /&gt; Note that leaving this blank will prevent users from
+  being able to create new address books."
+  required="false">localsql</configstring>
  </configsection>
 
  <configsection name="comments">
   <configheader>Comments</configheader>
-  <configboolean name="allow" desc="Can users comment on contacts?">true</configboolean>
+  <configboolean name="allow" desc="Can users comment on
+  contacts?">true</configboolean>
  </configsection>
 
  <configsection name="documents">
diff --git a/config/prefs.php.dist b/config/prefs.php.dist
index 282d4f1..2f7c7c3 100644
--- a/config/prefs.php.dist
+++ b/config/prefs.php.dist
@@ -1,6 +1,6 @@
 <?php
 /**
- * $Horde: turba/config/prefs.php.dist,v 1.28.10.9 2008/06/13 14:44:04 jan Exp $
+ * $Horde: turba/config/prefs.php.dist,v 1.28.10.10 2008/07/10 22:52:41 jan Exp $
  *
  * See horde/config/prefs.php for documentation on the structure of this file.
  */
@@ -153,3 +153,12 @@ $_prefs['turba_maintenance_tasks'] = array(
     'shared' => false,
     'type' => 'implicit'
 );
+
+// Personal contact.
+$_prefs['own_contact'] = array(
+    // The format is 'source_name;contact_id'.
+    'value' => '',
+    'locked' => false,
+    'shared' => false,
+    'type' => 'implicit'
+);
diff --git a/config/sources.php.dist b/config/sources.php.dist
index 88ac1c5..bc0bf1d 100644
--- a/config/sources.php.dist
+++ b/config/sources.php.dist
@@ -1,6 +1,6 @@
 <?php
 /**
- * $Horde: turba/config/sources.php.dist,v 1.97.6.29 2008/04/24 12:14:29 wrobel Exp $
+ * $Horde: turba/config/sources.php.dist,v 1.97.6.38 2008/11/12 06:29:26 wrobel Exp $
  *
  * This file is where you specify the sources of contacts available to users
  * at your installation. It contains a large number of EXAMPLES. Please
@@ -142,10 +142,15 @@
  *              will still be enforced. Also note that the backend driver
  *              must have support for using this. Currently SQL and IMSP.
  *
- * list_name_field: If this is present and non-empty, it will be taken as the
- *                  name of the turba field to store contact list names in.
- *                  This is required when using a composite field as the 'name'
- *                  field.
+ * list_name_field:  If this is present and non-empty, it will be taken as the
+ *                   field to store contact list names in.
+ *                   This is required when using a composite field as the 'name'
+ *                   field.
+ *
+ * alternative_name: If this is present and non-empty, it will be taken as the
+ *                   field to use an alternative in case that the name field is
+ *                   empty.
+ *
  * Here are some example configurations:
  */
 
@@ -195,6 +200,10 @@ $cfgSources['localsql'] = array(
         //                 'format' => '%s %s'),
         'alias' => 'object_alias',
         'birthday' => 'object_bday',
+        // The photo field requires at least Horde 3.3 and a matching type
+        // field.
+        // 'photo' => 'object_photo',
+        // 'phototype' => 'object_phototype',
         'homeStreet' => 'object_homestreet',
         'homePOBox' => 'object_homepob',
         'homeCity' => 'object_homecity',
@@ -204,10 +213,10 @@ $cfgSources['localsql'] = array(
         // This is an example composite field for addresses, so you can display
         // the various map links. If you use this, be sure to add 'homeAddress'
         // to the 'tabs' parameter below.
-        //'homeAddress' => array('fields' => array('homeStreet', 'homeCity',
-        //                                         'homeProvince',
-        //                                         'homePostalCode'),
-        //                       'format' => "%s \n %s, %s  %s"),
+        // 'homeAddress' => array('fields' => array('homeStreet', 'homeCity',
+        //                                          'homeProvince',
+        //                                          'homePostalCode'),
+        //                        'format' => "%s \n %s, %s  %s"),
         'workStreet' => 'object_workstreet',
         'workPOBox' => 'object_workpob',
         'workCity' => 'object_workcity',
@@ -224,6 +233,10 @@ $cfgSources['localsql'] = array(
         'title' => 'object_title',
         'role' => 'object_role',
         'company' => 'object_company',
+        // The logo field requires at least Horde 3.3 and a matching type
+        // field.
+        // 'logo' => 'object_logo',
+        // 'logotype' => 'object_logotype',
         'category' => 'object_category',
         'notes' => 'object_notes',
         'website' => 'object_url',
@@ -234,7 +247,7 @@ $cfgSources['localsql'] = array(
     'tabs' => array(
         _("Personal") => array('firstname', 'lastname', 'middlenames',
                                'namePrefix', 'nameSuffix', 'name', 'alias',
-                               'birthday'),
+                               'birthday', 'photo'),
         _("Location") => array('homeStreet', 'homePOBox', 'homeCity',
                                'homeProvince', 'homePostalCode', 'homeCountry',
                                'workStreet', 'workPOBox', 'workCity',
@@ -242,7 +255,7 @@ $cfgSources['localsql'] = array(
                                'timezone'),
         _("Communications") => array('email', 'homePhone', 'workPhone',
                                      'cellPhone', 'fax', 'pager'),
-        _("Organization") => array('title', 'role', 'company'),
+        _("Organization") => array('title', 'role', 'company', 'logo'),
         _("Other") => array('category', 'notes', 'website', 'freebusyUrl',
                             'pgpPublicKey', 'smimePublicKey'),
     ),
@@ -259,6 +272,7 @@ $cfgSources['localsql'] = array(
     'browse' => true,
     'use_shares' => true,
     'list_name_field' => 'lastname',
+    'alternative_name' => 'company',
 );
 
 /**
@@ -288,9 +302,10 @@ $cfgSources['localsql'] = array(
 //                                'organizationalPerson',
 //                                'inetOrgPerson'),
 //                                // Add 'turbaContact' to this array if using
-//                                // 'turbaType' attribute below
+//                                // 'turbaType' attribute below, and 'calEntry'
+//                                // if using 'freebusyUrl'.
 //         'scope' => 'one',
-//         'charset' => 'iso-8859-1',
+//         'charset' => 'utf-8',
 //         // Consult the LDAP schema to verify that all required attributes for
 //         // an entry are set and add them if needed.
 //         'checkrequired' => false,
@@ -309,7 +324,7 @@ $cfgSources['localsql'] = array(
 //         // From horde.schema.  Make sure you have 'turbaContact' objectClass
 //         // included above:
 //         // '__type' => 'turbaType',
-//         // '__members' => 'turbaMembers'
+//         // '__members' => 'turbaMembers',
 //
 //         'name' => 'cn',
 //         'email' => 'mail',
@@ -364,7 +379,7 @@ $cfgSources['localsql'] = array(
 //         'root' => 'ou=' . $_ldap_uid . ',ou=personal_addressbook,' . $_ldap_basedn,
 //         'bind_dn' => 'uid=' . $_ldap_uid . ',ou=People,' . $_ldap_basedn,
 //         'bind_password' => Auth::getCredential('password'),
-//         'dn' => array('cn', 'uid'),
+//         'dn' => array('uid'),
 //         'objectclass' => array('top',
 //                                'person',
 //                                // 'turbaContact',
@@ -381,7 +396,7 @@ $cfgSources['localsql'] = array(
 //
 //         // From horde.schema:
 //         // '__type' => 'turbaType',
-//         // '__members' => 'turbaMembers'
+//         // '__members' => 'turbaMembers',
 //
 //         'name' => 'cn',
 //         'email' => 'mail',
@@ -810,7 +825,9 @@ if (!empty($GLOBALS['conf']['kolab']['enabled'])) {
                 'cellPhone'         => 'mobile',
                 'fax'               => 'fax',
                 'notes'             => 'description',
-                'freebusyUrl'       => 'kolabHomeServer',
+                'kolabHomeServer'   => 'kolabHomeServer',
+                'freebusyUrl'       => array('fields' => array('kolabHomeServer', 'email'),
+                                             'format' => 'https://%s/freebusy/%s.ifb'),
             ),
             'search' => array(
                 'name',
@@ -862,7 +879,15 @@ if (!empty($GLOBALS['conf']['kolab']['enabled'])) {
             /* Personal */
             'name' => array('fields' => array('firstname', 'middlenames', 'lastname'),
                             'format' => '%s %s %s',
-                            'attribute' => 'full-name'),
+                            'parse' => array(
+                                array('fields' => array('firstname', 'middlenames',
+                                                        'lastname'),
+                                      'format' => '%s %s %s'),
+                                array('fields' => array( 'lastname', 'firstname'),
+                                      'format' => '%s, %s'),
+                                array('fields' => array('firstname', 'lastname'),
+                                      'format' => '%s %s'),
+                            )),
             'firstname'         => 'given-name',
             'lastname'          => 'last-name',
             'middlenames'       => 'middle-names',
@@ -870,6 +895,8 @@ if (!empty($GLOBALS['conf']['kolab']['enabled'])) {
             'nameSuffix'        => 'suffix',
             'initials'          => 'initials',
             'nickname'          => 'nick-name',
+            'photo'             => 'photo',
+            'phototype'         => 'phototype',
             'gender'            => 'gender',
             'birthday'          => 'birthday',
             'spouse'            => 'spouse-name',
@@ -909,11 +936,14 @@ if (!empty($GLOBALS['conf']['kolab']['enabled'])) {
             'language'          => 'language',
             'latitude'          => 'latitude',
             'longitude'         => 'longitude',
+            /* Invisible */
+            'email'             => 'email',
+            'pgpPublicKey'      => 'pgp-publickey',
         ),
         'tabs' => array(
             _("Personal") => array('name', 'firstname', 'lastname', 'middlenames',
                                    'namePrefix', 'nameSuffix', 'initials', 'nickname',
-                                   'gender', 'birthday', 'spouse', 'anniversary',
+                                   'photo', 'gender', 'birthday', 'spouse', 'anniversary',
                                    'children'),
             _("Location") => array('homeStreet', 'homeCity', 'homeProvince',
                                    'homePostalCode', 'homeCountry', 'workStreet',
@@ -928,6 +958,7 @@ if (!empty($GLOBALS['conf']['kolab']['enabled'])) {
         ),
         'search' => array(
             /* Personal */
+            'name',
             'firstname',
             'lastname',
             'middlenames',
diff --git a/contact.php b/contact.php
index 7f6738c..1b43fd7 100644
--- a/contact.php
+++ b/contact.php
@@ -1,6 +1,6 @@
 <?php
 /**
- * $Horde: turba/contact.php,v 1.8.2.5 2008/06/13 21:48:56 chuck Exp $
+ * $Horde: turba/contact.php,v 1.8.2.6 2008/07/10 22:52:41 jan Exp $
  *
  * Copyright 2000-2008 The Horde Project (http://www.horde.org/)
  *
@@ -51,6 +51,12 @@ if (!$contact || is_a($contact, 'PEAR_Error')) {
     }
 }
 
+// Mark this contact as the user's own?
+if ($vars->get('action') == 'mark_own') {
+    $prefs->setValue('own_contact', $source . ';' . $contact->getValue('__key'));
+    $notification->push(_("This contact has been marked as your own."), 'horde.success');
+}
+
 // Are we printing?
 $print_view = (bool)Util::getFormData('print');
 
@@ -90,6 +96,19 @@ if ($contact->hasPermission(PERMS_DELETE)) {
                   array('tabname' => 'DeleteContact', 'id' => 'tabDeleteContact', 'onclick' => 'return ShowTab(\'DeleteContact\');'));
 }
 
+ at list($own_source, $own_id) = explode(';', $prefs->getValue('own_contact'));
+if ($own_source == $source && $own_id == $contact->getValue('__key')) {
+    $own_icon = ' ' . Horde::img('user.png', _("Your own contact"),
+                                 array('title' => _("Your own contact")),
+                                 $registry->getImageDir('horde'));
+    $own_link = '';
+} else {
+    $own_icon = '';
+    $own_link = '<span class="smallheader rightFloat">'
+        . Horde::link(Util::addParameter($url, array('action' => 'mark_own')))
+        . _("Mark this as your own contact") . '</a></span>';
+}
+
 $title = $view->getTitle();
 Horde::addScriptFile('prototype.js', 'turba', true);
 Horde::addScriptFile('contact_tabs.js', 'turba', true);
@@ -103,7 +122,11 @@ echo '<div id="page">';
 if (!$print_view) {
     echo $tabs->render($viewName);
 }
-echo '<h1 class="header">' . ($contact->getValue('name') ? htmlspecialchars($contact->getValue('name')) : '<em>' . _("Blank name") . '</em>') . '</h1>';
+echo '<h1 class="header">' . $own_link
+    . ($contact->getValue('name')
+       ? htmlspecialchars($contact->getValue('name'))
+       : '<em>' . _("Blank name") . '</em>')
+    . $own_icon . '</h1>';
 $view->html();
 echo '</div>';
 require $registry->get('templates', 'horde') . '/common-footer.inc';
diff --git a/data.php b/data.php
index 0e62e97..d6c867d 100644
--- a/data.php
+++ b/data.php
@@ -1,6 +1,6 @@
 <?php
 /**
- * $Horde: turba/data.php,v 1.70.4.17 2008/06/13 08:40:03 jan Exp $
+ * $Horde: turba/data.php,v 1.70.4.20 2008/08/11 12:50:18 jan Exp $
  *
  * Copyright 2001-2008 The Horde Project (http://www.horde.org/)
  *
@@ -42,10 +42,11 @@ function _emptyAttributeFilter($var)
  * Static function to make a given email address rfc822 compliant.
  *
  * @param string $address  An email address.
+ * @param boolean $allow_multi  Allow multiple email addresses.
  *
  * @return string  The RFC822-formatted email address.
  */
-function _getBareEmail($address)
+function _getBareEmail($address, $allow_multi = false)
 {
     // Empty values are still empty.
     if (!$address) {
@@ -60,12 +61,26 @@ function _getBareEmail($address)
         $rfc822 = new Mail_RFC822();
     }
 
-    $rfc822->validateMailbox($address);
-    return MIME::rfc822WriteAddress($address->mailbox, $address->host);
+    // Split multiple email addresses
+    if ($allow_multi) {
+        $addrs = MIME::rfc822Explode($address);
+    } else {
+        $addrs = array($address);
+    }
+
+    $result = array();
+    foreach ($addrs as $addr) {
+        $addr = trim($addr);
+
+        if ($rfc822->validateMailbox($addr)) {
+            $result[] = MIME::rfc822WriteAddress($addr->mailbox, $addr->host);
+        }
+    }
+
+    return implode(', ', $result);
 }
 
- at define('TURBA_BASE', dirname(__FILE__));
-require_once TURBA_BASE . '/lib/base.php';
+require_once dirname(__FILE__) . '/lib/base.php';
 require_once 'Horde/Data.php';
 
 if (!$conf['menu']['import_export']) {
@@ -107,6 +122,48 @@ $app_fields = array();
 $time_fields = array();
 $error = false;
 $outlook_mapping = array(
+    'Title' => 'namePrefix',
+    'First Name' => 'firstname',
+    'Middle Name' => 'middlenames',
+    'Last Name' => 'lastname',
+    'Nickname' => 'nickname',
+    'Suffix' => 'nameSuffix',
+    'Company' => 'company',
+    'Department' => 'department',
+    'Job Title' => 'title',
+    'Business Street' => 'workStreet',
+    'Business City' => 'workCity',
+    'Business State' => 'workProvince',
+    'Business Postal Code' => 'workPostalCode',
+    'Business Country' => 'workCountry',
+    'Home Street' => 'homeStreet',
+    'Home City' => 'homeCity',
+    'Home State' => 'homeProvince',
+    'Home Postal Code' => 'homePostalCode',
+    'Home Country' => 'homeCountry',
+    'Business Fax' => 'workFax',
+    'Business Phone' => 'workPhone',
+    'Home Phone' => 'homePhone',
+    'Mobile Phone' => 'cellPhone',
+    'Pager' => 'pager',
+    'Anniversary' => 'anniversary',
+    'Assistant\'s Name' => 'assistant',
+    'Birthday' => 'birthday',
+    'Business Address PO Box' => 'workPOBox',
+    'Categories' => 'category',
+    'Children' => 'children',
+    'E-mail Address' => 'email',
+    'Home Address PO Box' => 'homePOBox',
+    'Initials' => 'initials',
+    'Internet Free Busy' => 'freebusyUrl',
+    'Language' => 'language',
+    'Notes' => 'notes',
+    'Profession' => 'role',
+    'Office Location' => 'office',
+    'Spouse' => 'spouse',
+    'Web Page' => 'website',
+);
+$import_mapping = array(
     'e-mail' => 'email',
     'homeaddress' => 'homeAddress',
     'businessaddress' => 'workAddress',
@@ -116,10 +173,41 @@ $outlook_mapping = array(
     'businessfax' => 'fax',
     'jobtitle' => 'title',
     'internetfreebusy' => 'freebusyUrl',
-    );
+
+    // Entourage on MacOS
+    'Dept' => 'department',
+    'Work Street Address' => 'workStreet',
+    'Work City' => 'workCity',
+    'Work State' => 'workProvince',
+    'Work Zip' => 'workPostalCode',
+    'Work Country/Region' => 'workCountry',
+    'Home Street Address' => 'homeStreet',
+    'Home City' => 'homeCity',
+    'Home State' => 'homeProvince',
+    'Home Zip' => 'homePostalCode',
+    'Home Country/Region' => 'homeCountry',
+    'Work Fax' => 'workFax',
+    'Work Phone 1' => 'workPhone',
+    'Home Phone 1' => 'homePhone',
+    'Instant Messaging 1' => 'instantMessenger',
+
+    // Thunderbird
+    'Primary Email' => 'email',
+    'Fax Number' => 'fax',
+    'Pager Number' => 'pager',
+    'Mobile Number' => 'Mobile Phone',
+    'Home Address' => 'homeStreet',
+    'Home ZipCode' => 'homePostalCode',
+    'Work Address' => 'workStreet',
+    'Work ZipCode' => 'workPostalCode',
+    'Work Country' => 'workCountry',
+    'Work Phone' => 'workPhone',
+    'Organization' => 'company',
+    'Web Page 1' => 'website',
+);
 $param = array('time_fields' => $time_fields,
                'file_types'  => $file_types,
-               'import_mapping' => $outlook_mapping);
+               'import_mapping' => array_merge($outlook_mapping, $import_mapping));
 $import_format = Util::getFormData('import_format', '');
 if ($import_format == 'mulberry' || $import_format == 'pine') {
     $import_format = 'tsv';
@@ -364,12 +452,15 @@ if (is_array($next_step)) {
             } elseif ($result->count()) {
                 $result->reset();
                 $object = $result->next();
-                $notification->push(sprintf(_("\"%s\" already exists and was not imported."), $object->getValue('name')), 'horde.message');
+                $notification->push(sprintf(_("\"%s\" already exists and was not imported."),
+                                            $object->getValue('name')), 'horde.message');
             } else {
                 /* Check for, and validate, any email fields */
                 foreach (array_keys($row) as $field) {
                     if ($attributes[$field]['type'] == 'email') {
-                        $row[$field] = _getBareEmail($row[$field]);
+                        $allow_multi = is_array($attributes[$field]['params']) &&
+                            !empty($attributes[$field]['params']['allow_multi']);
+                        $row[$field] = _getBareEmail($row[$field], $allow_multi);
                     }
                 }
                 $row['__owner'] = $driver->getContactOwner();
diff --git a/docs/CHANGES b/docs/CHANGES
index 8cf6776..a8a3a27 100644
--- a/docs/CHANGES
+++ b/docs/CHANGES
@@ -1,4 +1,76 @@
 ------
+v2.3.1
+------
+
+[jan] SECURITY: Escape output in test.php.
+[jan] Add script to import contacts from SquirrelMail database.
+[gwr] Correct support for the freebusy URL in Kolab.
+[gwr] Add photo support for the Kolab driver.
+[jan] Import broken vCards from Synthesis clients (Bug #7407).
+[cjh] Add two retries to find a contact after adding it (Bug #7478).
+[cjh] Add a PostgreSQL-specific upgrade script for 2.2.1 to 2.3
+      (michael.menge at zdv.uni-tuebingen.de, Bug #7462).
+[jan] Don't overwrite empty address books preference when creating new shares
+      (Bug #7399).
+[mms] Upgrade prototype.js to v1.6.0.3.
+
+
+----
+v2.3
+----
+
+[jan] Change group field in shares table to work with LDAP groups (Bug #6883).
+[jan] Fix exporting address books with country fields as vCards (Bug #7372).
+[mjr] Make sure we use all address books when searching via the api and the
+      user's addressbooks pref is empty (Bug #7357).
+[mjr] Correctly calculate the age of time objects (Bug #7330).
+
+
+--------
+v2.3-RC1
+--------
+
+[jan] Improve vCard EMAIL field parsing (Bug #7281).
+[jan] Correctly import and export address extensions (Bug #7279).
+[jan] Export full country names to vCard addresses.
+[jan] Respect precedence of multiple vCard properties (Request #7122).
+[jan] Remove deleted address books from address book preference (Request #6938).
+[jan] Export LABEL properties to vCard if address fields are available.
+[gwr] Add support for storing public PGP keys when using Kolab.
+[gwr] Add support for adding addresses from IMP when using Kolab.
+[jan] Add configuration option for an alternative name field.
+[jan] Add support for image fields to SQL driver (requires Horde 3.3).
+[jan] Add more default field mappings for CSV import (Request #6901).
+[jan] Update field names for Outlook 2003 export (Request #6901).
+[jan] Improve logic which entries to show in the sidebar menu.
+[jan] Add support for the category form field (Request #6884).
+[jan] Fix sorting so that sort order is always reflected correctly,
+      independent of name format.
+[jan] Add Basque translation (Euskal Herriko Unibertsitatea EHU/UPV
+      <xabier.arrieta at ehu.es>).
+[jan] Add ability to mark a contact as your own and API method to export it.
+[jan] Fix importing phone numbers from Synthesis clients (Bug #7011).
+[mjr] Fix issue with searching composite fields when the number of search terms
+      is less than the number of composed fields (Bug #6796)
+[cjh] Allow importing multiple address into an email field if allow_multi
+      is set to true on the field in attributes.php (Bug #6897).
+[mjr] Remove user permissions on all shares when deleting a user.
+[jan] Fix searching of client address books that have been disabled in the
+      preferences (Bug #6961).
+[mjr] Fix issue with removeUserData api that caused the deleted user's
+      address books to not be deleted as well as remove an erroneous error
+      notification (Bug #6969).
+[cjh] Fix the column selector/sorter in IE (Bug #6912).
+
+
+------
+v2.2.2
+------
+
+[jan] SECURITY: Escape output in test.php.
+
+
+------
 v2.2.1
 ------
 
diff --git a/docs/CREDITS b/docs/CREDITS
index d8ef2d1..c56d36b 100644
--- a/docs/CREDITS
+++ b/docs/CREDITS
@@ -8,6 +8,7 @@ Core Developers
 
 - Chuck Hagenbuch <chuck at horde.org>
 - Jan Schneider <jan at horde.org>
+- Michael J. Rubinsky <mrubinsk at horde.org>
 
 
 Localization
@@ -15,8 +16,11 @@ Localization
 
 =====================   ======================================================
 Arabic (Syria)          Platinum Development Team <devteam at platinum-sy.net>
+Basque                  Euskal Herriko Unibertsitatea <xabier.arrieta at ehu.es>
 Brazilian Portuguese    Carlos Daniel Kibrit <kibrit at terra.com.br>
                         Fabio Gomes <flgoms at uol.com.br>
+                        Luis Felipe Marzagao <duli at fedoraproject.org>
+                        Eduardo de Carli <carliedu at ig.com.br>
 Bulgarian               Miroslav Pendev <pendev at hotmail.com>
 Catalan                 Angels Guimerà <angels.Guimera at uab.es>
                         Jordi Giralt <projecte.k2 at upcnet.es>
@@ -30,6 +34,7 @@ Danish                  Martin List-Petersen <martin at list-petersen.dk>
 Dutch                   Jan Kuipers <jrkuipers at lauwerscollege.nl>
                         Han Spruyt <han.spruyt at ijsselgroep.nl>
 Estonian                Toomas Aas <toomas.aas at raad.tartu.ee>
+                        Alar Sing <alar.sing at err.ee>
 Finnish                 Leena Heino <liinu at uta.fi>
 French                  Sam Przyswa <samp at arial-concept.com>
                         Ronnie Garcia <ronnie at mk2.net>
diff --git a/docs/INSTALL b/docs/INSTALL
index e9596f7..7c9bd35 100644
--- a/docs/INSTALL
+++ b/docs/INSTALL
@@ -1,9 +1,9 @@
 ======================
- Installing Turba 2.2
+ Installing Turba 2.3
 ======================
 
-:Last update:   $Date: 2007/12/20 14:34:25 $
-:Revision:      $Revision: 1.10.10.6 $
+:Last update:   $Date: 2008/09/05 14:09:08 $
+:Revision:      $Revision: 1.10.10.7 $
 :Contact:       turba at lists.horde.org
 
 .. contents:: Contents
diff --git a/docs/RELEASE_NOTES b/docs/RELEASE_NOTES
index a0fe244..6c08eb8 100644
--- a/docs/RELEASE_NOTES
+++ b/docs/RELEASE_NOTES
@@ -17,27 +17,32 @@ $this->notes['fm']['focus'] = 8;
 /* Mailing list release notes. */
 $this->notes['ml']['changes'] = <<<ML
 The Horde Team is pleased to announce the final release of the Turba Contact
-Manager version H3 (2.2.1).
+Manager version H3 (2.3.1). The original release was packaged with the wrong
+name; this second release of 2.3.1 has no changes to the package or the code,
+only to the version name.
 
-This is a bugfix release that also fixes an XSS (cross site scripting)
-vulnerability in the contact view.
+This is a minor security release that fixes unescaped output in the test
+script. All users are encouraged to upgrade to this release. In addition all
+users are encouraged to disable test.php in production, per the install
+documentation.
 
 Turba is the Horde contact management application. It is a production level
 address book, and makes heavy use of the Horde framework to provide
 integration with IMP and other Horde applications. It supports SQL, LDAP,
 Kolab, and IMSP address books.
 
-Major new changes in this release:
-    * Fixed unescaped output in the contact view.
-    * Improved importing of phone numbers.
-    * Fixed upgrade script.
+The major changes compared to the Turba version H3 (2.3) are:
+    * SECURITY: Escape output in test.php
+    * A SquirrelMail contact import script.
+    * Kolab fixes for free/busy URLs and photos.
+    * prototype.js has been upgraded to 1.6.0.3.
 ML;
 
 /* Freshmeat release notes, not more than 600 characters. */
 $this->notes['fm']['changes'] = <<<FM
-Unescaped output in the contact view has been fixed.
-Importing of phone numbers has been improved.
-The upgrade script has been fixed.
+This release fixes unescaped output in a utility test script. In addition, an
+import script for SquirrelMail contacts has been added, several Kolab issues
+have been fixed, and prototype.js has been upgraded to 1.6.0.3.
 FM;
 
 $this->notes['name'] = 'Turba';
diff --git a/docs/UPGRADING b/docs/UPGRADING
index bdcbeaf..24f5648 100644
--- a/docs/UPGRADING
+++ b/docs/UPGRADING
@@ -2,11 +2,41 @@
  Upgrading Turba
 =================
 
-:Last update:   $Date: 2008/04/30 03:37:39 $
-:Revision:      $Revision: 1.3.6.15 $
+:Last update:   $Date: 2008/11/14 17:27:39 $
+:Revision:      $Revision: 1.3.6.27 $
 :Contact:       turba at lists.horde.org
 
 
+These are instructions to upgrade from earlier Turba versions. Please backup
+your existing data before running any of the steps described below. You can't
+use the updated data with your old Turba version anymore.
+
+
+Upgrading Turba from 2.2.1 to 2.3
+=================================
+
+
+Share Table Updates
+-------------------
+
+Some fields in the SQL share driver tables have been changed. Execute the
+provided SQL script to update your database if you are using the native SQL
+share driver.
+
+   mysql --user=root --password=<MySQL-root-password>  <db name> < scripts/upgrade/2.2.1_to_2.3.sql
+
+
+New fields
+----------
+
+Examples for a few new fields have been added to
+``config/attributes.php.dist`` and ``config/sources.php.dist``. These field
+require Horde 3.3 or later though, so they are disabled by default. If you
+update your ``attributes.php`` and ``sources.php`` files and are using a
+sufficiently recent Horde version, you may want to uncomment those
+examples. These are the ``photo``, ``phototype``, ``logo``, ``logotype``,
+``pgpPublicKey`` and ``smimePublicKey`` fields.
+
 
 Upgrading to Turba 2.2
 ======================
@@ -18,6 +48,7 @@ These are notes on upgrading from Turba 2.1.x to Turba 2.2.x.
                follow the upgrade steps described in `Upgrading to Turba 2.1`_
                first.
 
+
 New Default Schema
 ------------------
 
@@ -27,7 +58,7 @@ other address book applications and to ease synchronization with those.
 You can still use your old address book schema without any restrictions, but
 if you want to migrate existing address books to the new default scheme, you
 can use the provided upgrade script. This script assumes that you use the old
-default schema from Turba 2.1 and doesn't permanentely change any data unless
+default schema from Turba 2.1 and doesn't permanently change any data unless
 you edit it.
 
 To run the script you have to open it in any text editor and change the three
@@ -66,15 +97,15 @@ or "text".
 New Beta SQL Share Driver Support
 ---------------------------------
 
-A new beta-level SQL Horde_Share driver has been added in Horde 3.2. This driver
-offers significant performance improvements over the existing Datatree driver
-but it has not received the same level of testing, thus the beta designation.
-In order to make use of this driver, you must be running Horde 3.2-RC3 or
-later. To add the new SQL tables for this driver, execute the sql script
-``2.1_to_2.2_add_sql_share_tables`` that is appropriate for your RDBMS. You then
-must execute the ``2.1_to_2.2_datatree_to_sql_share_migration.php`` upgrade
-script to migrate your existing share data.  Both of these scripts can be found
-in the ``scripts/upgrades`` folder.
+A new beta-level SQL Horde_Share driver has been added in Horde 3.2. This
+driver offers significant performance improvements over the existing Datatree
+driver but it has not received the same level of testing, thus the beta
+designation.  In order to make use of this driver, you must be running Horde
+3.2-RC3 or later. To add the new SQL tables for this driver, execute the sql
+script ``2.1_to_2.2_add_sql_share_tables`` that is appropriate for your
+RDBMS. You then must execute the ``convert_datatree_shares_to_sql.php``
+upgrade script to migrate your existing share data.  Both of these scripts can
+be found in the ``scripts/upgrades`` folder.
 
 
 Flattened Horde Shares
@@ -94,7 +125,9 @@ Turba transparently during each user's first login after the update.
 Additionally, if you are currently using the configuration setting,
 ``Name of Client Address Book`` and have it set to a share enabled source, you
 will most likely have to change the entry.  The correct share key to enter here
-can be found by browsing the datatree via the administration UI.
+is the share name and can be found by browsing the datatree via the
+administration UI if using datatree shares, or by looking in the turba_shares
+table if using SQL based shares.
 
 
 IMSP Driver and Share Support
@@ -133,6 +166,7 @@ All hooks that are specific to Turba have been moved from the
 Upgrading to Turba 2.1
 ======================
 
+
 These are notes on upgrading from Turba 2.0.x to Turba 2.1.
 
 .. Important:: These upgrade instructions assume that you are upgrading from
@@ -140,6 +174,7 @@ These are notes on upgrading from Turba 2.0.x to Turba 2.1.
                follow the upgrade steps described in `Upgrading to Turba 2.0`_
                first.
 
+
 Synchronization Support
 -----------------------
 
diff --git a/js/prototype.js b/js/prototype.js
index ab8726c..3c2493f 100644
--- a/js/prototype.js
+++ b/js/prototype.js
@@ -1 +1 @@
-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(A){return A}};if(Prototype.Browser.MobileSafari){Prototype.BrowserFeatures.SpecificElementExtensions=false}var Class={create:function(){var E=null,D=$A(arguments);if(Object.isFunction(D[0])){E=D.shift()}function A(){this.initialize.apply(this,arguments)}Object.extend(A,Class.Methods);A.superclass=E;A.subclasses=[];if(E){var B=function(){};B.prototype=E.prototype;A.prototype=new B;E.subclasses.push(A)}for(var C=0;C<D.length;C++){A.addMethods(D[C])}if(!A.prototype.initialize){A.prototype.initialize=Prototype.emptyFunction}A.prototype.constructor=A;return A}};Class.Methods={addMethods:function(G){var C=this.superclass&&this.superclass.prototype;var B=Object.keys(G);if(!Object.keys({toString:true}).length){B.push("toString","valueOf")}for(var A=0,D=B.length;A<D;A++){var F=B[A],E=G[F];if(C&&Object.isFunction(E)&&E.argumentNames().first()=="$super"){var H=E,E=Object.extend((function(I){return function(){return C[I].apply(this,arguments)}})(F).wrap(H),{valueOf:function(){return H},toString:function(){return H.toString()}})}this.prototype[F]=E}return this}};var Abstract={};Object.extend=function(A,C){for(var B in C){A[B]=C[B]}return A};Object.extend(Object,{inspect:function(A){try{if(Object.isUndefined(A)){return"undefined"}if(A===null){return"null"}return A.inspect?A.inspect():String(A)}catch(B){if(B instanceof RangeError){return"..."}throw B}},toJSON:function(A){var C=typeof A;switch(C){case"undefined":case"function":case"unknown":return ;case"boolean":return A.toString()}if(A===null){return"null"}if(A.toJSON){return A.toJSON()}if(Object.isElement(A)){return }var B=[];for(var E in A){var D=Object.toJSON(A[E]);if(!Object.isUndefined(D)){B.push(E.toJSON()+": "+D)}}return"{"+B.join(", ")+"}"},toQueryString:function(A){return $H(A).toQueryString()},toHTML:function(A){return A&&A.toHTML?A.toHTML():String.interpret(A)},keys:function(A){var B=[];for(var C in A){B.push(C)}return B},values:function(B){var A=[];for(var C in B){A.push(B[C])}return A},clone:function(A){return Object.extend({},A)},isElement:function(A){return A&&A.nodeType==1},isArray:function(A){return A!=null&&typeof A=="object"&&"splice" in A&&"join" in A},isHash:function(A){return A instanceof Hash},isFunction:function(A){return typeof A=="function"},isString:function(A){return typeof A=="string"},isNumber:function(A){return typeof A=="number"},isUndefined:function(A){return typeof A=="undefined"}});Object.extend(Function.prototype,{argumentNames:function(){var A=this.toString().match(/^[\s\(]*function[^(]*\((.*?)\)/)[1].split(",").invoke("strip");return A.length==1&&!A[0]?[]:A},bind:function(){if(arguments.length<2&&Object.isUndefined(arguments[0])){return this}var A=this,C=$A(arguments),B=C.shift();return function(){return A.apply(B,C.concat($A(arguments)))}},bindAsEventListener:function(){var A=this,C=$A(arguments),B=C.shift();return function(D){return A.apply(B,[D||window.event].concat(C))}},curry:function(){if(!arguments.length){return this}var A=this,B=$A(arguments);return function(){return A.apply(this,B.concat($A(arguments)))}},delay:function(){var A=this,B=$A(arguments),C=B.shift()*1000;return window.setTimeout(function(){return A.apply(A,B)},C)},wrap:function(B){var A=this;return function(){return B.apply(this,[A.bind(this)].concat($A(arguments)))}},methodize:function(){if(this._methodized){return this._methodized}var A=this;return this._methodized=function(){return A.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 C;for(var B=0,D=arguments.length;B<D;B++){var A=arguments[B];try{C=A();break}catch(E){}}return C}};RegExp.prototype.match=RegExp.prototype.test;RegExp.escape=function(A){return String(A).replace(/([.*+?^=!:${}()|[\]\/\\])/g,"\\$1")};var PeriodicalExecuter=Class.create({initialize:function(B,A){this.callback=B;this.frequency=A;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(A){return A==null?"":String(A)},specialChar:{"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r","\\":"\\\\"}});Object.extend(String.prototype,{gsub:function(E,C){var A="",D=this,B;C=arguments.callee.prepareReplacement(C);while(D.length>0){if(B=D.match(E)){A+=D.slice(0,B.index);A+=String.interpret(C(B));D=D.slice(B.index+B[0].length)}else{A+=D,D=""}}return A},sub:function(C,A,B){A=this.gsub.prepareReplacement(A);B=Object.isUndefined(B)?1:B;return this.gsub(C,function(D){if(--B<0){return D[0]}return A(D)})},scan:function(B,A){this.gsub(B,A);return String(this)},truncate:function(B,A){B=B||30;A=Object.isUndefined(A)?"...":A;return this.length>B?this.slice(0,B-A.length)+A: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 B=new RegExp(Prototype.ScriptFragment,"img");var A=new RegExp(Prototype.ScriptFragment,"im");return(this.match(B)||[]).map(function(C){return(C.match(A)||["",""])[1]})},evalScripts:function(){return this.extractScripts().map(function(script){return eval(script)})},escapeHTML:function(){var A=arguments.callee;A.text.data=this;return A.div.innerHTML},unescapeHTML:function(){var A=new Element("div");A.innerHTML=this.stripTags();return A.childNodes[0]?(A.childNodes.length>1?$A(A.childNodes).inject("",function(B,C){return B+C.nodeValue}):A.childNodes[0].nodeValue):""},toQueryParams:function(B){var A=this.strip().match(/([^?#]*)(#.*)?$/);if(!A){return{}}return A[1].split(B||"&").inject({},function(E,F){if((F=F.split("="))[0]){var C=decodeURIComponent(F.shift());var D=F.length>1?F.join("="):F[0];if(D!=undefined){D=decodeURIComponent(D)}if(C in E){if(!Object.isArray(E[C])){E[C]=[E[C]]}E[C].push(D)}else{E[C]=D}}return E})},toArray:function(){return this.split("")},succ:function(){return this.slice(0,this.length-1)+String.fromCharCode(this.charCodeAt(this.length-1)+1)},times:function(A){return A<1?"":new Array(A+1).join(this)},camelize:function(){var D=this.split("-"),A=D.length;if(A==1){return D[0]}var C=this.charAt(0)=="-"?D[0].charAt(0).toUpperCase()+D[0].substring(1):D[0];for(var B=1;B<A;B++){C+=D[B].charAt(0).toUpperCase()+D[B].substring(1)}return C},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(B){var A=this.gsub(/[\x00-\x1f\\]/,function(C){var D=String.specialChar[C[0]];return D?D:"\\u00"+C[0].charCodeAt().toPaddedString(2,16)});if(B){return'"'+A.replace(/"/g,'\\"')+'"'}return"'"+A.replace(/'/g,"\\'")+"'"},toJSON:function(){return this.inspect(true)},unfilterJSON:function(A){return this.sub(A||Prototype.JSONFilter,"#{1}")},isJSON:function(){var A=this;if(A.blank()){return false}A=this.replace(/\\./g,"@").replace(/"[^"\\\n\r]*"/g,"");return(/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(A)},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(A){return this.indexOf(A)>-1},startsWith:function(A){return this.indexOf(A)===0},endsWith:function(A){var B=this.length-A.length;return B>=0&&this.lastIndexOf(A)===B},empty:function(){return this==""},blank:function(){return/^\s*$/.test(this)},interpolate:function(A,B){return new Template(this,B).evaluate(A)}});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(B){if(Object.isFunction(B)){return B}var A=new Template(B);return function(C){return A.evaluate(C)}};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(A,B){this.template=A.toString();this.pattern=B||Template.Pattern},evaluate:function(A){if(Object.isFunction(A.toTemplateReplacements)){A=A.toTemplateReplacements()}return this.template.gsub(this.pattern,function(D){if(A==null){return""}var F=D[1]||"";if(F=="\\"){return D[2]}var B=A,G=D[3];var E=/^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;D=E.exec(G);if(D==null){return F}while(D!=null){var C=D[1].startsWith("[")?D[2].gsub("\\\\]","]"):D[1];B=B[C];if(null==B||""==D[3]){break}G=G.substring("["==D[3]?D[1].length:D[0].length);D=E.exec(G)}return F+String.interpret(B)})}});Template.Pattern=/(^|.|\r|\n)(#\{(.*?)\})/;var $break={};var Enumerable={each:function(C,B){var A=0;C=C.bind(B);try{this._each(function(E){C(E,A++)})}catch(D){if(D!=$break){throw D}}return this},eachSlice:function(D,C,B){C=C?C.bind(B):Prototype.K;var A=-D,E=[],F=this.toArray();while((A+=D)<F.length){E.push(F.slice(A,A+D))}return E.collect(C,B)},all:function(C,B){C=C?C.bind(B):Prototype.K;var A=true;this.each(function(E,D){A=A&&!!C(E,D);if(!A){throw $break}});return A},any:function(C,B){C=C?C.bind(B):Prototype.K;var A=false;this.each(function(E,D){if(A=!!C(E,D)){throw $break}});return A},collect:function(C,B){C=C?C.bind(B):Prototype.K;var A=[];this.each(function(E,D){A.push(C(E,D))});return A},detect:function(C,B){C=C.bind(B);var A;this.each(function(E,D){if(C(E,D)){A=E;throw $break}});return A},findAll:function(C,B){C=C.bind(B);var A=[];this.each(function(E,D){if(C(E,D)){A.push(E)}});return A},grep:function(D,C,B){C=C?C.bind(B):Prototype.K;var A=[];if(Object.isString(D)){D=new RegExp(D)}this.each(function(F,E){if(D.match(F)){A.push(C(F,E))}});return A},include:function(A){if(Object.isFunction(this.indexOf)){if(this.indexOf(A)!=-1){return true}}var B=false;this.each(function(C){if(C==A){B=true;throw $break}});return B},inGroupsOf:function(B,A){A=Object.isUndefined(A)?null:A;return this.eachSlice(B,function(C){while(C.length<B){C.push(A)}return C})},inject:function(A,C,B){C=C.bind(B);this.each(function(E,D){A=C(A,E,D)});return A},invoke:function(B){var A=$A(arguments).slice(1);return this.map(function(C){return C[B].apply(C,A)})},max:function(C,B){C=C?C.bind(B):Prototype.K;var A;this.each(function(E,D){E=C(E,D);if(A==null||E>=A){A=E}});return A},min:function(C,B){C=C?C.bind(B):Prototype.K;var A;this.each(function(E,D){E=C(E,D);if(A==null||E<A){A=E}});return A},partition:function(D,B){D=D?D.bind(B):Prototype.K;var C=[],A=[];this.each(function(F,E){(D(F,E)?C:A).push(F)});return[C,A]},pluck:function(B){var A=[];this.each(function(C){A.push(C[B])});return A},reject:function(C,B){C=C.bind(B);var A=[];this.each(function(E,D){if(!C(E,D)){A.push(E)}});return A},sortBy:function(B,A){B=B.bind(A);return this.map(function(D,C){return{value:D,criteria:B(D,C)}}).sort(function(F,E){var D=F.criteria,C=E.criteria;return D<C?-1:D>C?1:0}).pluck("value")},toArray:function(){return this.map()},zip:function(){var B=Prototype.K,A=$A(arguments);if(Object.isFunction(A.last())){B=A.pop()}var C=[this].concat(A).map($A);return this.map(function(E,D){return B(C.pluck(D))})},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(C){if(!C){return[]}if(C.toArray){return C.toArray()}var B=C.length||0,A=new Array(B);while(B--){A[B]=C[B]}return A}if(Prototype.Browser.WebKit){$A=function(C){if(!C){return[]}if(!(Object.isFunction(C)&&C=="[object NodeList]")&&C.toArray){return C.toArray()}var B=C.length||0,A=new Array(B);while(B--){A[B]=C[B]}return A}}Array.from=$A;Object.extend(Array.prototype,Enumerable);if(!Array.prototype._reverse){Array.prototype._reverse=Array.prototype.reverse}Object.extend(Array.prototype,{_each:function(B){for(var A=0,C=this.length;A<C;A++){B(this[A])}},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(A){return A!=null})},flatten:function(){return this.inject([],function(B,A){return B.concat(Object.isArray(A)?A.flatten():[A])})},without:function(){var A=$A(arguments);return this.select(function(B){return !A.include(B)})},reverse:function(A){return(A!==false?this:this.toArray())._reverse()},reduce:function(){return this.length>1?this:this[0]},uniq:function(A){return this.inject([],function(D,C,B){if(0==B||(A?D.last()!=C:!D.include(C))){D.push(C)}return D})},intersect:function(A){return this.uniq().findAll(function(B){return A.detect(function(C){return B===C})})},clone:function(){return[].concat(this)},size:function(){return this.length},inspect:function(){return"["+this.map(Object.inspect).join(", ")+"]"},toJSON:function(){var A=[];this.each(function(B){var C=Object.toJSON(B);if(!Object.isUndefined(C)){A.push(C)}});return"["+A.join(", ")+"]"}});if(Object.isFunction(Array.prototype.forEach)){Array.prototype._each=Array.prototype.forEach}if(!Array.prototype.indexOf){Array.prototype.indexOf=function(C,A){A||(A=0);var B=this.length;if(A<0){A=B+A}for(;A<B;A++){if(this[A]===C){return A}}return -1}}if(!Array.prototype.lastIndexOf){Array.prototype.lastIndexOf=function(B,A){A=isNaN(A)?this.length:(A<0?this.length+A:A)+1;var C=this.slice(0,A).reverse().indexOf(B);return(C<0)?C:A-C-1}}Array.prototype.toArray=Array.prototype.clone;function $w(A){if(!Object.isString(A)){return[]}A=A.strip();return A?A.split(/\s+/):[]}if(Prototype.Browser.Opera){Array.prototype.concat=function(){var E=[];for(var B=0,C=this.length;B<C;B++){E.push(this[B])}for(var B=0,C=arguments.length;B<C;B++){if(Object.isArray(arguments[B])){for(var A=0,D=arguments[B].length;A<D;A++){E.push(arguments[B][A])}}else{E.push(arguments[B])}}return E}}Object.extend(Number.prototype,{toColorPart:function(){return this.toPaddedString(2,16)},succ:function(){return this+1},times:function(A){$R(0,this,true).each(A);return this},toPaddedString:function(C,B){var A=this.toString(B||10);return"0".times(C-A.length)+A},toJSON:function(){return isFinite(this)?this.toString():"null"}});$w("abs round ceil floor").each(function(A){Number.prototype[A]=Math[A].methodize()});function $H(A){return new Hash(A)}var Hash=Class.create(Enumerable,(function(){function A(B,C){if(Object.isUndefined(C)){return B}return B+"="+encodeURIComponent(String.interpret(C))}return{initialize:function(B){this._object=Object.isHash(B)?B.toObject():Object.clone(B)},_each:function(C){for(var B in this._object){var D=this._object[B],E=[B,D];E.key=B;E.value=D;C(E)}},set:function(B,C){return this._object[B]=C},get:function(B){return this._object[B]},unset:function(B){var C=this._object[B];delete this._object[B];return C},toObject:function(){return Object.clone(this._object)},keys:function(){return this.pluck("key")},values:function(){return this.pluck("value")},index:function(C){var B=this.detect(function(D){return D.value===C});return B&&B.key},merge:function(B){return this.clone().update(B)},update:function(B){return new Hash(B).inject(this,function(C,D){C.set(D.key,D.value);return C})},toQueryString:function(){return this.map(function(D){var C=encodeURIComponent(D.key),B=D.value;if(B&&typeof B=="object"){if(Object.isArray(B)){return B.map(A.curry(C)).join("&")}}return A(C,B)}).join("&")},inspect:function(){return"#<Hash:{"+this.map(function(B){return B.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(C,A,B){this.start=C;this.end=A;this.exclusive=B},_each:function(A){var B=this.start;while(this.include(B)){A(B);B=B.succ()}},include:function(A){if(A<this.start){return false}if(this.exclusive){return A<this.end}return A<=this.end}});var $R=function(C,A,B){return new ObjectRange(C,A,B)};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(A){this.responders._each(A)},register:function(A){if(!this.include(A)){this.responders.push(A)}},unregister:function(A){this.responders=this.responders.without(A)},dispatch:function(D,B,C,A){this.each(function(E){if(Object.isFunction(E[D])){try{E[D].apply(E,[B,C,A])}catch(F){}}})}};Object.extend(Ajax.Responders,Enumerable);Ajax.Responders.register({onCreate:function(){Ajax.activeRequestCount++},onComplete:function(){Ajax.activeRequestCount--}});Ajax.Base=Class.create({initialize:function(A){this.options={method:"post",asynchronous:true,contentType:"application/x-www-form-urlencoded",encoding:"UTF-8",parameters:"",evalJSON:true,evalJS:true};Object.extend(this.options,A||{});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,B,A){$super(A);this.transport=Ajax.getTransport();this.request(B)},request:function(B){this.url=B;this.method=this.options.method;var D=Object.clone(this.options.parameters);if(!["get","post"].include(this.method)){D._method=this.method;this.method="post"}this.parameters=D;if(D=Object.toQueryString(D)){if(this.method=="get"){this.url+=(this.url.include("?")?"&":"?")+D}else{if(/Konqueror|Safari|KHTML/.test(navigator.userAgent)){D+="&_="}}}try{var A=new Ajax.Response(this);if(this.options.onCreate){this.options.onCreate(A)}Ajax.Responders.dispatch("onCreate",this,A);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||D):null;this.transport.send(this.body);if(!this.options.asynchronous&&this.transport.overrideMimeType){this.onStateChange()}}catch(C){this.dispatchException(C)}},onStateChange:function(){var A=this.transport.readyState;if(A>1&&!((A==4)&&this._complete)){this.respondToReadyState(this.transport.readyState)}},setRequestHeaders:function(){var E={"X-Requested-With":"XMLHttpRequest","X-Prototype-Version":Prototype.Version,Accept:"text/javascript, text/html, application/xml, text/xml, */*"};if(this.method=="post"){E["Content-type"]=this.options.contentType+(this.options.encoding?"; charset="+this.options.encoding:"");if(this.transport.overrideMimeType&&(navigator.userAgent.match(/Gecko\/(\d{4})/)||[0,2005])[1]<2005){E.Connection="close"}}if(typeof this.options.requestHeaders=="object"){var C=this.options.requestHeaders;if(Object.isFunction(C.push)){for(var B=0,D=C.length;B<D;B+=2){E[C[B]]=C[B+1]}}else{$H(C).each(function(F){E[F.key]=F.value})}}for(var A in E){this.transport.setRequestHeader(A,E[A])}},success:function(){var A=this.getStatus();return !A||(A>=200&&A<300)},getStatus:function(){try{return this.transport.status||0}catch(A){return 0}},respondToReadyState:function(A){var C=Ajax.Request.Events[A],B=new Ajax.Response(this);if(C=="Complete"){try{this._complete=true;(this.options["on"+B.status]||this.options["on"+(this.success()?"Success":"Failure")]||Prototype.emptyFunction)(B,B.headerJSON)}catch(D){this.dispatchException(D)}var E=B.getHeader("Content-type");if(this.options.evalJS=="force"||(this.options.evalJS&&this.isSameOrigin()&&E&&E.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i))){this.evalResponse()}}try{(this.options["on"+C]||Prototype.emptyFunction)(B,B.headerJSON);Ajax.Responders.dispatch("on"+C,this,B,B.headerJSON)}catch(D){this.dispatchException(D)}if(C=="Complete"){this.transport.onreadystatechange=Prototype.emptyFunction}},isSameOrigin:function(){var A=this.url.match(/^\s*https?:\/\/[^\/]*/);return !A||(A[0]=="#{protocol}//#{domain}#{port}".interpolate({protocol:location.protocol,domain:document.domain,port:location.port?":"+location.port:""}))},getHeader:function(A){try{return this.transport.getResponseHeader(A)||null}catch(B){return null}},evalResponse:function(){try{return eval((this.transport.responseText||"").unfilterJSON())}catch(e){this.dispatchException(e)}},dispatchException:function(A){(this.options.onException||Prototype.emptyFunction)(this,A);Ajax.Responders.dispatch("onException",this,A)}});Ajax.Request.Events=["Uninitialized","Loading","Loaded","Interactive","Complete"];Ajax.Response=Class.create({initialize:function(C){this.request=C;var D=this.transport=C.transport,A=this.readyState=D.readyState;if((A>2&&!Prototype.Browser.IE)||A==4){this.status=this.getStatus();this.statusText=this.getStatusText();this.responseText=String.interpret(D.responseText);this.headerJSON=this._getHeaderJSON()}if(A==4){var B=D.responseXML;this.responseXML=Object.isUndefined(B)?null:B;this.responseJSON=this._getResponseJSON()}},status:0,statusText:"",getStatus:Ajax.Request.prototype.getStatus,getStatusText:function(){try{return this.transport.statusText||""}catch(A){return""}},getHeader:Ajax.Request.prototype.getHeader,getAllHeaders:function(){try{return this.getAllResponseHeaders()}catch(A){return null}},getResponseHeader:function(A){return this.transport.getResponseHeader(A)},getAllResponseHeaders:function(){return this.transport.getAllResponseHeaders()},_getHeaderJSON:function(){var A=this.getHeader("X-JSON");if(!A){return null}A=decodeURIComponent(escape(A));try{return A.evalJSON(this.request.options.sanitizeJSON||!this.request.isSameOrigin())}catch(B){this.request.dispatchException(B)}},_getResponseJSON:function(){var A=this.request.options;if(!A.evalJSON||(A.evalJSON!="force"&&!(this.getHeader("Content-type")||"").include("application/json"))||this.responseText.blank()){return null}try{return this.responseText.evalJSON(A.sanitizeJSON||!this.request.isSameOrigin())}catch(B){this.request.dispatchException(B)}}});Ajax.Updater=Class.create(Ajax.Request,{initialize:function($super,A,C,B){this.container={success:(A.success||A),failure:(A.failure||(A.success?null:A))};B=Object.clone(B);var D=B.onComplete;B.onComplete=(function(E,F){this.updateContent(E.responseText);if(Object.isFunction(D)){D(E,F)}}).bind(this);$super(C,B)},updateContent:function(D){var C=this.container[this.success()?"success":"failure"],A=this.options;if(!A.evalScripts){D=D.stripScripts()}if(C=$(C)){if(A.insertion){if(Object.isString(A.insertion)){var B={};B[A.insertion]=D;C.insert(B)}else{A.insertion(C,D)}}else{C.update(D)}}}});Ajax.PeriodicalUpdater=Class.create(Ajax.Base,{initialize:function($super,A,C,B){$super(B);this.onComplete=this.options.onComplete;this.frequency=(this.options.frequency||2);this.decay=(this.options.decay||1);this.updater={};this.container=A;this.url=C;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(A){if(this.options.decay){this.decay=(A.responseText==this.lastText?this.decay*this.options.decay:1);this.lastText=A.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 $(B){if(arguments.length>1){for(var A=0,D=[],C=arguments.length;A<C;A++){D.push($(arguments[A]))}return D}if(Object.isString(B)){B=document.getElementById(B)}return Element.extend(B)}if(Prototype.BrowserFeatures.XPath){document._getElementsByXPath=function(F,A){var C=[];var E=document.evaluate(F,$(A)||document,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);for(var B=0,D=E.snapshotLength;B<D;B++){C.push(Element.extend(E.snapshotItem(B)))}return C}}if(!window.Node){var Node={}}if(!Node.ELEMENT_NODE){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 A=this.Element;this.Element=function(D,C){C=C||{};D=D.toLowerCase();var B=Element.cache;if(Prototype.Browser.IE&&C.name){D="<"+D+' name="'+C.name+'">';delete C.name;return Element.writeAttribute(document.createElement(D),C)}if(!B[D]){B[D]=Element.extend(document.createElement(D))}return Element.writeAttribute(B[D].cloneNode(false),C)};Object.extend(this.Element,A||{})}).call(window);Element.cache={};Element.Methods={visible:function(A){return $(A).style.display!="none"},toggle:function(A){A=$(A);Element[Element.visible(A)?"hide":"show"](A);return A},hide:function(A){$(A).style.display="none";return A},show:function(A){$(A).style.display="";return A},remove:function(A){A=$(A);A.parentNode.removeChild(A);return A},update:function(A,B){A=$(A);if(B&&B.toElement){B=B.toElement()}if(Object.isElement(B)){return A.update().insert(B)}B=Object.toHTML(B);A.innerHTML=B.stripScripts();B.evalScripts.bind(B).defer();return A},replace:function(B,C){B=$(B);if(C&&C.toElement){C=C.toElement()}else{if(!Object.isElement(C)){C=Object.toHTML(C);var A=B.ownerDocument.createRange();A.selectNode(B);C.evalScripts.bind(C).defer();C=A.createContextualFragment(C.stripScripts())}}B.parentNode.replaceChild(C,B);return B},insert:function(C,E){C=$(C);if(Object.isString(E)||Object.isNumber(E)||Object.isElement(E)||(E&&(E.toElement||E.toHTML))){E={bottom:E}}var D,F,B,G;for(var A in E){D=E[A];A=A.toLowerCase();F=Element._insertionTranslations[A];if(D&&D.toElement){D=D.toElement()}if(Object.isElement(D)){F(C,D);continue}D=Object.toHTML(D);B=((A=="before"||A=="after")?C.parentNode:C).tagName.toUpperCase();G=Element._getContentFromAnonymousElement(B,D.stripScripts());if(A=="top"||A=="after"){G.reverse()}G.each(F.curry(C));D.evalScripts.bind(D).defer()}return C},wrap:function(B,C,A){B=$(B);if(Object.isElement(C)){$(C).writeAttribute(A||{})}else{if(Object.isString(C)){C=new Element(C,A)}else{C=new Element("div",C)}}if(B.parentNode){B.parentNode.replaceChild(C,B)}C.appendChild(B);return C},inspect:function(B){B=$(B);var A="<"+B.tagName.toLowerCase();$H({id:"id",className:"class"}).each(function(F){var E=F.first(),C=F.last();var D=(B[E]||"").toString();if(D){A+=" "+C+"="+D.inspect(true)}});return A+">"},recursivelyCollect:function(A,C){A=$(A);var B=[];while(A=A[C]){if(A.nodeType==1){B.push(Element.extend(A))}}return B},ancestors:function(A){return $(A).recursivelyCollect("parentNode")},descendants:function(A){return $(A).select("*")},firstDescendant:function(A){A=$(A).firstChild;while(A&&A.nodeType!=1){A=A.nextSibling}return $(A)},immediateDescendants:function(A){if(!(A=$(A).firstChild)){return[]}while(A&&A.nodeType!=1){A=A.nextSibling}if(A){return[A].concat($(A).nextSiblings())}return[]},previousSiblings:function(A){return $(A).recursivelyCollect("previousSibling")},nextSiblings:function(A){return $(A).recursivelyCollect("nextSibling")},siblings:function(A){A=$(A);return A.previousSiblings().reverse().concat(A.nextSiblings())},match:function(B,A){if(Object.isString(A)){A=new Selector(A)}return A.match($(B))},up:function(B,D,A){B=$(B);if(arguments.length==1){return $(B.parentNode)}var C=B.ancestors();return Object.isNumber(D)?C[D]:Selector.findElement(C,D,A)},down:function(B,C,A){B=$(B);if(arguments.length==1){return B.firstDescendant()}return Object.isNumber(C)?B.descendants()[C]:B.select(C)[A||0]},previous:function(B,D,A){B=$(B);if(arguments.length==1){return $(Selector.handlers.previousElementSibling(B))}var C=B.previousSiblings();return Object.isNumber(D)?C[D]:Selector.findElement(C,D,A)},next:function(C,D,B){C=$(C);if(arguments.length==1){return $(Selector.handlers.nextElementSibling(C))}var A=C.nextSiblings();return Object.isNumber(D)?A[D]:Selector.findElement(A,D,B)},select:function(){var A=$A(arguments),B=$(A.shift());return Selector.findChildElements(B,A)},adjacent:function(){var A=$A(arguments),B=$(A.shift());return Selector.findChildElements(B.parentNode,A).without(B)},identify:function(B){B=$(B);var C=B.readAttribute("id"),A=arguments.callee;if(C){return C}do{C="anonymous_element_"+A.counter++}while($(C));B.writeAttribute("id",C);return C},readAttribute:function(C,A){C=$(C);if(Prototype.Browser.IE){var B=Element._attributeTranslations.read;if(B.values[A]){return B.values[A](C,A)}if(B.names[A]){A=B.names[A]}if(A.include(":")){return(!C.attributes||!C.attributes[A])?null:C.attributes[A].value}}return C.getAttribute(A)},writeAttribute:function(E,C,F){E=$(E);var B={},D=Element._attributeTranslations.write;if(typeof C=="object"){B=C}else{B[C]=Object.isUndefined(F)?true:F}for(var A in B){C=D.names[A]||A;F=B[A];if(D.values[A]){C=D.values[A](E,F)}if(F===false||F===null){E.removeAttribute(C)}else{if(F===true){E.setAttribute(C,C)}else{E.setAttribute(C,F)}}}return E},getHeight:function(A){return $(A).getDimensions().height},getWidth:function(A){return $(A).getDimensions().width},classNames:function(A){return new Element.ClassNames(A)},hasClassName:function(A,B){if(!(A=$(A))){return }var C=A.className;return(C.length>0&&(C==B||new RegExp("(^|\\s)"+B+"(\\s|$)").test(C)))},addClassName:function(A,B){if(!(A=$(A))){return }if(!A.hasClassName(B)){A.className+=(A.className?" ":"")+B}return A},removeClassName:function(A,B){if(!(A=$(A))){return }A.className=A.className.replace(new RegExp("(^|\\s+)"+B+"(\\s+|$)")," ").strip();return A},toggleClassName:function(A,B){if(!(A=$(A))){return }return A[A.hasClassName(B)?"removeClassName":"addClassName"](B)},cleanWhitespace:function(B){B=$(B);var C=B.firstChild;while(C){var A=C.nextSibling;if(C.nodeType==3&&!/\S/.test(C.nodeValue)){B.removeChild(C)}C=A}return B},empty:function(A){return $(A).innerHTML.blank()},descendantOf:function(D,C){D=$(D),C=$(C);var F=C;if(D.compareDocumentPosition){return(D.compareDocumentPosition(C)&8)===8}if(D.sourceIndex&&!Prototype.Browser.Opera){var E=D.sourceIndex,B=C.sourceIndex,A=C.nextSibling;if(!A){do{C=C.parentNode}while(!(A=C.nextSibling)&&C.parentNode)}if(A&&A.sourceIndex){return(E>B&&E<A.sourceIndex)}}while(D=D.parentNode){if(D==F){return true}}return false},scrollTo:function(A){A=$(A);var B=A.cumulativeOffset();window.scrollTo(B[0],B[1]);return A},getStyle:function(B,C){B=$(B);C=C=="float"?"cssFloat":C.camelize();var D=B.style[C];if(!D){var A=document.defaultView.getComputedStyle(B,null);D=A?A[C]:null}if(C=="opacity"){return D?parseFloat(D):1}return D=="auto"?null:D},getOpacity:function(A){return $(A).getStyle("opacity")},setStyle:function(B,C){B=$(B);var E=B.style,A;if(Object.isString(C)){B.style.cssText+=";"+C;return C.include("opacity")?B.setOpacity(C.match(/opacity:\s*(\d?\.?\d*)/)[1]):B}for(var D in C){if(D=="opacity"){B.setOpacity(C[D])}else{E[(D=="float"||D=="cssFloat")?(Object.isUndefined(E.styleFloat)?"cssFloat":"styleFloat"):D]=C[D]}}return B},setOpacity:function(A,B){A=$(A);A.style.opacity=(B==1||B==="")?"":(B<0.00001)?0:B;return A},getDimensions:function(C){C=$(C);var G=$(C).getStyle("display");if(G!="none"&&G!=null){return{width:C.offsetWidth,height:C.offsetHeight}}var B=C.style;var F=B.visibility;var D=B.position;var A=B.display;B.visibility="hidden";B.position="absolute";B.display="block";var H=C.clientWidth;var E=C.clientHeight;B.display=A;B.position=D;B.visibility=F;return{width:H,height:E}},makePositioned:function(A){A=$(A);var B=Element.getStyle(A,"position");if(B=="static"||!B){A._madePositioned=true;A.style.position="relative";if(window.opera){A.style.top=0;A.style.left=0}}return A},undoPositioned:function(A){A=$(A);if(A._madePositioned){A._madePositioned=undefined;A.style.position=A.style.top=A.style.left=A.style.bottom=A.style.right=""}return A},makeClipping:function(A){A=$(A);if(A._overflow){return A}A._overflow=Element.getStyle(A,"overflow")||"auto";if(A._overflow!=="hidden"){A.style.overflow="hidden"}return A},undoClipping:function(A){A=$(A);if(!A._overflow){return A}A.style.overflow=A._overflow=="auto"?"":A._overflow;A._overflow=null;return A},cumulativeOffset:function(B){var A=0,C=0;do{A+=B.offsetTop||0;C+=B.offsetLeft||0;B=B.offsetParent}while(B);return Element._returnOffset(C,A)},positionedOffset:function(B){var A=0,D=0;do{A+=B.offsetTop||0;D+=B.offsetLeft||0;B=B.offsetParent;if(B){if(B.tagName=="BODY"){break}var C=Element.getStyle(B,"position");if(C!=="static"){break}}}while(B);return Element._returnOffset(D,A)},absolutize:function(B){B=$(B);if(B.getStyle("position")=="absolute"){return }var D=B.positionedOffset();var F=D[1];var E=D[0];var C=B.clientWidth;var A=B.clientHeight;B._originalLeft=E-parseFloat(B.style.left||0);B._originalTop=F-parseFloat(B.style.top||0);B._originalWidth=B.style.width;B._originalHeight=B.style.height;B.style.position="absolute";B.style.top=F+"px";B.style.left=E+"px";B.style.width=C+"px";B.style.height=A+"px";return B},relativize:function(A){A=$(A);if(A.getStyle("position")=="relative"){return }A.style.position="relative";var C=parseFloat(A.style.top||0)-(A._originalTop||0);var B=parseFloat(A.style.left||0)-(A._originalLeft||0);A.style.top=C+"px";A.style.left=B+"px";A.style.height=A._originalHeight;A.style.width=A._originalWidth;return A},cumulativeScrollOffset:function(B){var A=0,C=0;do{A+=B.scrollTop||0;C+=B.scrollLeft||0;B=B.parentNode}while(B);return Element._returnOffset(C,A)},getOffsetParent:function(B){B=$(B);var D=B.offsetParent,A=document.body,C=document.documentElement;if(D&&D!==C){return $(D)}if(D===C||B===C||B===A){return $(A)}while((B=B.parentNode)&&B!==A){if(Element.getStyle(B,"position")!="static"){return $(B)}}return $(A)},viewportOffset:function(D){D=$(D);var B=D,A=0,C=0;do{A+=B.offsetTop||0;C+=B.offsetLeft||0}while((B=B.getOffsetParent())!=document.body);B=D;do{if(!Prototype.Browser.Opera||B.tagName=="BODY"){A-=B.scrollTop||0;C-=B.scrollLeft||0}}while(B=B.parentNode);return Element._returnOffset(C,A)},clonePosition:function(B,D){var A=Object.extend({setLeft:true,setTop:true,setWidth:true,setHeight:true,offsetTop:0,offsetLeft:0},arguments[2]||{});D=$(D);var E=D.viewportOffset();B=$(B);var F=[0,0];var C=null;if(Element.getStyle(B,"position")=="absolute"){C=B.getOffsetParent();F=C.viewportOffset()}if(C==document.body){F[0]-=document.body.offsetLeft;F[1]-=document.body.offsetTop}if(A.setLeft){B.style.left=(E[0]-F[0]+A.offsetLeft)+"px"}if(A.setTop){B.style.top=(E[1]-F[1]+A.offsetTop)+"px"}if(A.setWidth){B.style.width=D.offsetWidth+"px"}if(A.setHeight){B.style.height=D.offsetHeight+"px"}return B}};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(D,B,C){switch(C){case"left":case"top":case"right":case"bottom":if(D(B,"position")==="static"){return null}case"height":case"width":if(!Element.visible(B)){return null}var E=parseInt(D(B,C),10);if(E!==B["offset"+C.capitalize()]){return E+"px"}var A;if(C==="height"){A=["border-top-width","padding-top","padding-bottom","border-bottom-width"]}else{A=["border-left-width","padding-left","padding-right","border-right-width"]}return A.inject(E,function(F,G){var H=D(B,G);return H===null?F:F-parseInt(H,10)})+"px";default:return D(B,C)}});Element.Methods.readAttribute=Element.Methods.readAttribute.wrap(function(C,A,B){if(B==="title"){return A.title}return C(A,B)})}else{if(Prototype.Browser.IE){Element.Methods.getOffsetParent=Element.Methods.getOffsetParent.wrap(function(C,B){B=$(B);var A=B.getStyle("position");if(A!=="static"){return C(B)}B.setStyle({position:"relative"});var D=C(B);B.setStyle({position:A});return D});$w("positionedOffset viewportOffset").each(function(A){Element.Methods[A]=Element.Methods[A].wrap(function(E,C){C=$(C);var B=C.getStyle("position");if(B!=="static"){return E(C)}var D=C.getOffsetParent();if(D&&D.getStyle("position")==="fixed"){D.setStyle({zoom:1})}C.setStyle({position:"relative"});var F=E(C);C.setStyle({position:B});return F})});Element.Methods.getStyle=function(A,B){A=$(A);B=(B=="float"||B=="cssFloat")?"styleFloat":B.camelize();var C=A.style[B];if(!C&&A.currentStyle){C=A.currentStyle[B]}if(B=="opacity"){if(C=(A.getStyle("filter")||"").match(/alpha\(opacity=(.*)\)/)){if(C[1]){return parseFloat(C[1])/100}}return 1}if(C=="auto"){if((B=="width"||B=="height")&&(A.getStyle("display")!="none")){return A["offset"+B.capitalize()]+"px"}return null}return C};Element.Methods.setOpacity=function(B,E){function F(G){return G.replace(/alpha\([^\)]*\)/gi,"")}B=$(B);var A=B.currentStyle;if((A&&!A.hasLayout)||(!A&&B.style.zoom=="normal")){B.style.zoom=1}var D=B.getStyle("filter"),C=B.style;if(E==1||E===""){(D=F(D))?C.filter=D:C.removeAttribute("filter");return B}else{if(E<0.00001){E=0}}C.filter=F(D)+"alpha(opacity="+(E*100)+")";return B};Element._attributeTranslations={read:{names:{"class":"className","for":"htmlFor"},values:{_getAttr:function(A,B){return A.getAttribute(B,2)},_getAttrNode:function(A,C){var B=A.getAttributeNode(C);return B?B.value:""},_getEv:function(A,B){B=A.getAttribute(B);return B?B.toString().slice(23,-2):null},_flag:function(A,B){return $(A).hasAttribute(B)?B:null},style:function(A){return A.style.cssText.toLowerCase()},title:function(A){return A.title}}}};Element._attributeTranslations.write={names:Object.extend({cellpadding:"cellPadding",cellspacing:"cellSpacing"},Element._attributeTranslations.read.names),values:{checked:function(A,B){A.checked=!!B},style:function(A,B){A.style.cssText=B?B:""}}};Element._attributeTranslations.has={};$w("colSpan rowSpan vAlign dateTime accessKey tabIndex encType maxLength readOnly longDesc").each(function(A){Element._attributeTranslations.write.names[A.toLowerCase()]=A;Element._attributeTranslations.has[A.toLowerCase()]=A});(function(A){Object.extend(A,{href:A._getAttr,src:A._getAttr,type:A._getAttr,action:A._getAttrNode,disabled:A._flag,checked:A._flag,readonly:A._flag,multiple:A._flag,onload:A._getEv,onunload:A._getEv,onclick:A._getEv,ondblclick:A._getEv,onmousedown:A._getEv,onmouseup:A._getEv,onmouseover:A._getEv,onmousemove:A._getEv,onmouseout:A._getEv,onfocus:A._getEv,onblur:A._getEv,onkeypress:A._getEv,onkeydown:A._getEv,onkeyup:A._getEv,onsubmit:A._getEv,onreset:A._getEv,onselect:A._getEv,onchange:A._getEv})})(Element._attributeTranslations.read.values)}else{if(Prototype.Browser.Gecko&&/rv:1\.8\.0/.test(navigator.userAgent)){Element.Methods.setOpacity=function(A,B){A=$(A);A.style.opacity=(B==1)?0.999999:(B==="")?"":(B<0.00001)?0:B;return A}}else{if(Prototype.Browser.WebKit){Element.Methods.setOpacity=function(A,B){A=$(A);A.style.opacity=(B==1||B==="")?"":(B<0.00001)?0:B;if(B==1){if(A.tagName=="IMG"&&A.width){A.width++;A.width--}else{try{var D=document.createTextNode(" ");A.appendChild(D);A.removeChild(D)}catch(C){}}}return A};Element.Methods.cumulativeOffset=function(B){var A=0,C=0;do{A+=B.offsetTop||0;C+=B.offsetLeft||0;if(B.offsetParent==document.body){if(Element.getStyle(B,"position")=="absolute"){break}}B=B.offsetParent}while(B);return Element._returnOffset(C,A)}}}}}if(Prototype.Browser.IE||Prototype.Browser.Opera){Element.Methods.update=function(B,C){B=$(B);if(C&&C.toElement){C=C.toElement()}if(Object.isElement(C)){return B.update().insert(C)}C=Object.toHTML(C);var A=B.tagName.toUpperCase();if(A in Element._insertionTranslations.tags){$A(B.childNodes).each(function(D){B.removeChild(D)});Element._getContentFromAnonymousElement(A,C.stripScripts()).each(function(D){B.appendChild(D)})}else{B.innerHTML=C.stripScripts()}C.evalScripts.bind(C).defer();return B}}if("outerHTML" in document.createElement("div")){Element.Methods.replace=function(C,E){C=$(C);if(E&&E.toElement){E=E.toElement()}if(Object.isElement(E)){C.parentNode.replaceChild(E,C);return C}E=Object.toHTML(E);var D=C.parentNode,B=D.tagName.toUpperCase();if(Element._insertionTranslations.tags[B]){var F=C.next();var A=Element._getContentFromAnonymousElement(B,E.stripScripts());D.removeChild(C);if(F){A.each(function(G){D.insertBefore(G,F)})}else{A.each(function(G){D.appendChild(G)})}}else{C.outerHTML=E.stripScripts()}E.evalScripts.bind(E).defer();return C}}Element._returnOffset=function(B,C){var A=[B,C];A.left=B;A.top=C;return A};Element._getContentFromAnonymousElement=function(C,B){var D=new Element("div"),A=Element._insertionTranslations.tags[C];if(A){D.innerHTML=A[0]+B+A[1];A[2].times(function(){D=D.firstChild})}else{D.innerHTML=B}return $A(D.childNodes)};Element._insertionTranslations={before:function(A,B){A.parentNode.insertBefore(B,A)},top:function(A,B){A.insertBefore(B,A.firstChild)},bottom:function(A,B){A.appendChild(B)},after:function(A,B){A.parentNode.insertBefore(B,A.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(A,C){C=Element._attributeTranslations.has[C]||C;var B=$(A).getAttributeNode(C);return B&&B.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 A={},B=Element.Methods.ByTag;var C=Object.extend(function(F){if(!F||F._extendedByPrototype||F.nodeType!=1||F==window){return F}var D=Object.clone(A),E=F.tagName,H,G;if(B[E]){Object.extend(D,B[E])}for(H in D){G=D[H];if(Object.isFunction(G)&&!(H in F)){F[H]=G.methodize()}}F._extendedByPrototype=Prototype.emptyFunction;return F},{refresh:function(){if(!Prototype.BrowserFeatures.ElementExtensions){Object.extend(A,Element.Methods);Object.extend(A,Element.Methods.Simulated)}}});C.refresh();return C})();Element.hasAttribute=function(A,B){if(A.hasAttribute){return A.hasAttribute(B)}return Element.Methods.Simulated.hasAttribute(A,B)};Element.addMethods=function(C){var I=Prototype.BrowserFeatures,D=Element.Methods.ByTag;if(!C){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 B=C;C=arguments[1]}if(!B){Object.extend(Element.Methods,C||{})}else{if(Object.isArray(B)){B.each(H)}else{H(B)}}function H(F){F=F.toUpperCase();if(!Element.Methods.ByTag[F]){Element.Methods.ByTag[F]={}}Object.extend(Element.Methods.ByTag[F],C)}function A(L,K,F){F=F||false;for(var N in L){var M=L[N];if(!Object.isFunction(M)){continue}if(!F||!(N in K)){K[N]=M.methodize()}}}function E(L){var F;var K={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(K[L]){F="HTML"+K[L]+"Element"}if(window[F]){return window[F]}F="HTML"+L+"Element";if(window[F]){return window[F]}F="HTML"+L.capitalize()+"Element";if(window[F]){return window[F]}window[F]={};window[F].prototype=document.createElement(L).__proto__;return window[F]}if(I.ElementExtensions){A(Element.Methods,HTMLElement.prototype);A(Element.Methods.Simulated,HTMLElement.prototype,true)}if(I.SpecificElementExtensions){for(var J in Element.Methods.ByTag){var G=E(J);if(Object.isUndefined(G)){continue}A(D[J],G.prototype)}}Object.extend(Element,Element.Methods);delete Element.ByTag;if(Element.extend.refresh){Element.extend.refresh()}Element.cache={}};document.viewport={getDimensions:function(){var A={};var C=Prototype.Browser;$w("width height").each(function(E){var B=E.capitalize();A[E]=(C.WebKit&&!document.evaluate)?self["inner"+B]:(C.Opera)?document.body["client"+B]:document.documentElement["client"+B]});return A},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)}};var Selector=Class.create({initialize:function(A){this.expression=A.strip();this.compileMatcher()},shouldUseXPath:function(){if(!Prototype.BrowserFeatures.XPath){return false}var A=this.expression;if(Prototype.Browser.WebKit&&(A.include("-of-type")||A.include(":empty"))){return false}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,F=Selector.patterns,B=Selector.xpath,D,A;if(Selector._cache[E]){this.xpath=Selector._cache[E];return }this.matcher=[".//*"];while(E&&D!=E&&(/\S/).test(E)){D=E;for(var C in F){if(A=E.match(F[C])){this.matcher.push(Object.isFunction(B[C])?B[C](A):new Template(B[C]).evaluate(A));E=E.replace(A[0],"");break}}}this.xpath=this.matcher.join("");Selector._cache[this.expression]=this.xpath},findElements:function(A){A=A||document;if(this.xpath){return document._getElementsByXPath(this.xpath,A)}return this.matcher(A)},match:function(H){this.tokens=[];var L=this.expression,A=Selector.patterns,E=Selector.assertions;var B,D,F;while(L&&B!==L&&(/\S/).test(L)){B=L;for(var I in A){D=A[I];if(F=L.match(D)){if(E[I]){this.tokens.push([I,Object.clone(F)]);L=L.replace(F[0],"")}else{return this.findElements(document).include(H)}}}}var K=true,C,J;for(var I=0,G;G=this.tokens[I];I++){C=G[0],J=G[1];if(!Selector.assertions[C](H,J)){K=false;break}}return K},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(A){if(A[1]=="*"){return""}return"[local-name()='"+A[1].toLowerCase()+"' or local-name()='"+A[1].toUpperCase()+"']"},className:"[contains(concat(' ', @class, ' '), ' #{1} ')]",id:"[@id='#{1}']",attrPresence:function(A){A[1]=A[1].toLowerCase();return new Template("[@#{1}]").evaluate(A)},attr:function(A){A[1]=A[1].toLowerCase();A[3]=A[5]||A[6];return new Template(Selector.xpath.operators[A[2]]).evaluate(A)},pseudo:function(A){var B=Selector.xpath.pseudos[A[1]];if(!B){return""}if(Object.isFunction(B)){return B(A)}return new Template(Selector.xpath.pseudos[A[1]]).evaluate(A)},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(B){var H=B[6],G=Selector.patterns,A=Selector.xpath,E,C;var F=[];while(H&&E!=H&&(/\S/).test(H)){E=H;for(var D in G){if(B=H.match(G[D])){C=Object.isFunction(A[D])?A[D](B):new Template(A[D]).evaluate(B);F.push("("+C.substring(1,C.length-1)+")");H=H.replace(B[0],"");break}}}return"[not("+F.join(" and ")+")]"},"nth-child":function(A){return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ",A)},"nth-last-child":function(A){return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ",A)},"nth-of-type":function(A){return Selector.xpath.pseudos.nth("position() ",A)},"nth-last-of-type":function(A){return Selector.xpath.pseudos.nth("(last() + 1 - position()) ",A)},"first-of-type":function(A){A[6]="1";return Selector.xpath.pseudos["nth-of-type"](A)},"last-of-type":function(A){A[6]="1";return Selector.xpath.pseudos["nth-last-of-type"](A)},"only-of-type":function(A){var B=Selector.xpath.pseudos;return B["first-of-type"](A)+B["last-of-type"](A)},nth:function(E,C){var F,G=C[6],B;if(G=="even"){G="2n+0"}if(G=="odd"){G="2n+1"}if(F=G.match(/^(\d+)$/)){return"["+E+"= "+F[1]+"]"}if(F=G.match(/^(-?\d*)?n(([+-])(\d+))?/)){if(F[1]=="-"){F[1]=-1}var D=F[1]?Number(F[1]):1;var A=F[2]?Number(F[2]):0;B="[((#{fragment} - #{b}) mod #{a} = 0) and ((#{fragment} - #{b}) div #{a} >= 0)]";return new Template(B).evaluate({fragment:E,a:D,b:A})}}}},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(A){A[3]=(A[5]||A[6]);return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); c = false;').evaluate(A)},pseudo:function(A){if(A[6]){A[6]=A[6].replace(/"/g,'\\"')}return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(A)},descendant:'c = "descendant";',child:'c = "child";',adjacent:'c = "adjacent";',laterSibling:'c = "laterSibling";'},patterns:{laterSibling:/^\s*~\s*/,child:/^\s*>\s*/,adjacent:/^\s*\+\s*/,descendant:/^\s/,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|([^'"][^\]]*?)))?\]/},assertions:{tagName:function(A,B){return B[1].toUpperCase()==A.tagName.toUpperCase()},className:function(A,B){return Element.hasClassName(A,B[1])},id:function(A,B){return A.id===B[1]},attrPresence:function(A,B){return Element.hasAttribute(A,B[1])},attr:function(B,C){var A=Element.readAttribute(B,C[1]);return A&&Selector.operators[C[2]](A,C[5]||C[6])}},handlers:{concat:function(B,A){for(var C=0,D;D=A[C];C++){B.push(D)}return B},mark:function(A){var D=Prototype.emptyFunction;for(var B=0,C;C=A[B];B++){C._countedByPrototype=D}return A},unmark:function(A){for(var B=0,C;C=A[B];B++){C._countedByPrototype=undefined}return A},index:function(A,D,G){A._countedByPrototype=Prototype.emptyFunction;if(D){for(var B=A.childNodes,E=B.length-1,C=1;E>=0;E--){var F=B[E];if(F.nodeType==1&&(!G||F._countedByPrototype)){F.nodeIndex=C++}}}else{for(var E=0,C=1,B=A.childNodes;F=B[E];E++){if(F.nodeType==1&&(!G||F._countedByPrototype)){F.nodeIndex=C++}}}},unique:function(B){if(B.length==0){return B}var D=[],E;for(var C=0,A=B.length;C<A;C++){if(!(E=B[C])._countedByPrototype){E._countedByPrototype=Prototype.emptyFunction;D.push(Element.extend(E))}}return Selector.handlers.unmark(D)},descendant:function(A){var D=Selector.handlers;for(var C=0,B=[],E;E=A[C];C++){D.concat(B,E.getElementsByTagName("*"))}return B},child:function(A){var E=Selector.handlers;for(var D=0,C=[],F;F=A[D];D++){for(var B=0,G;G=F.childNodes[B];B++){if(G.nodeType==1&&G.tagName!="!"){C.push(G)}}}return C},adjacent:function(A){for(var C=0,B=[],E;E=A[C];C++){var D=this.nextElementSibling(E);if(D){B.push(D)}}return B},laterSibling:function(A){var D=Selector.handlers;for(var C=0,B=[],E;E=A[C];C++){D.concat(B,Element.nextSiblings(E))}return B},nextElementSibling:function(A){while(A=A.nextSibling){if(A.nodeType==1){return A}}return null},previousElementSibling:function(A){while(A=A.previousSibling){if(A.nodeType==1){return A}}return null},tagName:function(A,H,C,B){var I=C.toUpperCase();var E=[],G=Selector.handlers;if(A){if(B){if(B=="descendant"){for(var F=0,D;D=A[F];F++){G.concat(E,D.getElementsByTagName(C))}return E}else{A=this[B](A)}if(C=="*"){return A}}for(var F=0,D;D=A[F];F++){if(D.tagName.toUpperCase()===I){E.push(D)}}return E}else{return H.getElementsByTagName(C)}},id:function(B,A,H,F){var G=$(H),D=Selector.handlers;if(!G){return[]}if(!B&&A==document){return[G]}if(B){if(F){if(F=="child"){for(var C=0,E;E=B[C];C++){if(G.parentNode==E){return[G]}}}else{if(F=="descendant"){for(var C=0,E;E=B[C];C++){if(Element.descendantOf(G,E)){return[G]}}}else{if(F=="adjacent"){for(var C=0,E;E=B[C];C++){if(Selector.handlers.previousElementSibling(G)==E){return[G]}}}else{B=D[F](B)}}}}for(var C=0,E;E=B[C];C++){if(E==G){return[G]}}return[]}return(G&&Element.descendantOf(G,A))?[G]:[]},className:function(B,A,C,D){if(B&&D){B=this[D](B)}return Selector.handlers.byClassName(B,A,C)},byClassName:function(C,B,F){if(!C){C=Selector.handlers.descendant([B])}var H=" "+F+" ";for(var E=0,D=[],G,A;G=C[E];E++){A=G.className;if(A.length==0){continue}if(A==F||(" "+A+" ").include(H)){D.push(G)}}return D},attrPresence:function(C,B,A,G){if(!C){C=B.getElementsByTagName("*")}if(C&&G){C=this[G](C)}var E=[];for(var D=0,F;F=C[D];D++){if(Element.hasAttribute(F,A)){E.push(F)}}return E},attr:function(A,I,H,J,C,B){if(!A){A=I.getElementsByTagName("*")}if(A&&B){A=this[B](A)}var K=Selector.operators[C],F=[];for(var E=0,D;D=A[E];E++){var G=Element.readAttribute(D,H);if(G===null){continue}if(K(G,J)){F.push(D)}}return F},pseudo:function(B,C,E,A,D){if(B&&D){B=this[D](B)}if(!B){B=A.getElementsByTagName("*")}return Selector.pseudos[C](B,E,A)}},pseudos:{"first-child":function(B,F,A){for(var D=0,C=[],E;E=B[D];D++){if(Selector.handlers.previousElementSibling(E)){continue}C.push(E)}return C},"last-child":function(B,F,A){for(var D=0,C=[],E;E=B[D];D++){if(Selector.handlers.nextElementSibling(E)){continue}C.push(E)}return C},"only-child":function(B,G,A){var E=Selector.handlers;for(var D=0,C=[],F;F=B[D];D++){if(!E.previousElementSibling(F)&&!E.nextElementSibling(F)){C.push(F)}}return C},"nth-child":function(B,C,A){return Selector.pseudos.nth(B,C,A)},"nth-last-child":function(B,C,A){return Selector.pseudos.nth(B,C,A,true)},"nth-of-type":function(B,C,A){return Selector.pseudos.nth(B,C,A,false,true)},"nth-last-of-type":function(B,C,A){return Selector.pseudos.nth(B,C,A,true,true)},"first-of-type":function(B,C,A){return Selector.pseudos.nth(B,"1",A,false,true)},"last-of-type":function(B,C,A){return Selector.pseudos.nth(B,"1",A,true,true)},"only-of-type":function(B,D,A){var C=Selector.pseudos;return C["last-of-type"](C["first-of-type"](B,D,A),D,A)},getIndices:function(B,A,C){if(B==0){return A>0?[A]:[]}return $R(1,C).inject([],function(D,E){if(0==(E-A)%B&&(E-A)/B>=0){D.push(E)}return D})},nth:function(A,L,N,K,C){if(A.length==0){return[]}if(L=="even"){L="2n+0"}if(L=="odd"){L="2n+1"}var J=Selector.handlers,I=[],B=[],E;J.mark(A);for(var H=0,D;D=A[H];H++){if(!D.parentNode._countedByPrototype){J.index(D.parentNode,K,C);B.push(D.parentNode)}}if(L.match(/^\d+$/)){L=Number(L);for(var H=0,D;D=A[H];H++){if(D.nodeIndex==L){I.push(D)}}}else{if(E=L.match(/^(-?\d*)?n(([+-])(\d+))?/)){if(E[1]=="-"){E[1]=-1}var O=E[1]?Number(E[1]):1;var M=E[2]?Number(E[2]):0;var P=Selector.pseudos.getIndices(O,M,A.length);for(var H=0,D,F=P.length;D=A[H];H++){for(var G=0;G<F;G++){if(D.nodeIndex==P[G]){I.push(D)}}}}}J.unmark(A);J.unmark(B);return I},empty:function(B,F,A){for(var D=0,C=[],E;E=B[D];D++){if(E.tagName=="!"||(E.firstChild&&!E.innerHTML.match(/^\s*$/))){continue}C.push(E)}return C},not:function(A,D,I){var G=Selector.handlers,J,C;var H=new Selector(D).findElements(I);G.mark(H);for(var F=0,E=[],B;B=A[F];F++){if(!B._countedByPrototype){E.push(B)}}G.unmark(H);return E},enabled:function(B,F,A){for(var D=0,C=[],E;E=B[D];D++){if(!E.disabled){C.push(E)}}return C},disabled:function(B,F,A){for(var D=0,C=[],E;E=B[D];D++){if(E.disabled){C.push(E)}}return C},checked:function(B,F,A){for(var D=0,C=[],E;E=B[D];D++){if(E.checked){C.push(E)}}return C}},operators:{"=":function(B,A){return B==A},"!=":function(B,A){return B!=A},"^=":function(B,A){return B.startsWith(A)},"$=":function(B,A){return B.endsWith(A)},"*=":function(B,A){return B.include(A)},"~=":function(B,A){return(" "+B+" ").include(" "+A+" ")},"|=":function(B,A){return("-"+B.toUpperCase()+"-").include("-"+A.toUpperCase()+"-")}},split:function(B){var A=[];B.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/,function(C){A.push(C[1].strip())});return A},matchElements:function(F,G){var E=$$(G),D=Selector.handlers;D.mark(E);for(var C=0,B=[],A;A=F[C];C++){if(A._countedByPrototype){B.push(A)}}D.unmark(E);return B},findElement:function(B,C,A){if(Object.isNumber(C)){A=C;C=false}return Selector.matchElements(B,C||"*")[A||0]},findChildElements:function(E,G){G=Selector.split(G.join(","));var D=[],F=Selector.handlers;for(var C=0,B=G.length,A;C<B;C++){A=new Selector(G[C].strip());F.concat(D,A.findElements(E))}return(B>1)?F.unique(D):D}});if(Prototype.Browser.IE){Object.extend(Selector.handlers,{concat:function(B,A){for(var C=0,D;D=A[C];C++){if(D.tagName!=="!"){B.push(D)}}return B},unmark:function(A){for(var B=0,C;C=A[B];B++){C.removeAttribute("_countedByPrototype")}return A}})}function $$(){return Selector.findChildElements(document,$A(arguments))}var Form={reset:function(A){$(A).reset();return A},serializeElements:function(G,B){if(typeof B!="object"){B={hash:!!B}}else{if(Object.isUndefined(B.hash)){B.hash=true}}var C,F,A=false,E=B.submit;var D=G.inject({},function(H,I){if(!I.disabled&&I.name){C=I.name;F=$(I).getValue();if(F!=null&&(I.type!="submit"||(!A&&E!==false&&(!E||C==E)&&(A=true)))){if(C in H){if(!Object.isArray(H[C])){H[C]=[H[C]]}H[C].push(F)}else{H[C]=F}}}return H});return B.hash?D:Object.toQueryString(D)}};Form.Methods={serialize:function(B,A){return Form.serializeElements(Form.getElements(B),A)},getElements:function(A){return $A($(A).getElementsByTagName("*")).inject([],function(B,C){if(Form.Element.Serializers[C.tagName.toLowerCase()]){B.push(Element.extend(C))}return B})},getInputs:function(G,C,D){G=$(G);var A=G.getElementsByTagName("input");if(!C&&!D){return $A(A).map(Element.extend)}for(var E=0,H=[],F=A.length;E<F;E++){var B=A[E];if((C&&B.type!=C)||(D&&B.name!=D)){continue}H.push(Element.extend(B))}return H},disable:function(A){A=$(A);Form.getElements(A).invoke("disable");return A},enable:function(A){A=$(A);Form.getElements(A).invoke("enable");return A},findFirstElement:function(B){var C=$(B).getElements().findAll(function(D){return"hidden"!=D.type&&!D.disabled});var A=C.findAll(function(D){return D.hasAttribute("tabIndex")&&D.tabIndex>=0}).sortBy(function(D){return D.tabIndex}).first();return A?A:C.find(function(D){return["input","select","textarea"].include(D.tagName.toLowerCase())})},focusFirstElement:function(A){A=$(A);A.findFirstElement().activate();return A},request:function(B,A){B=$(B),A=Object.clone(A||{});var D=A.parameters,C=B.readAttribute("action")||"";if(C.blank()){C=window.location.href}A.parameters=B.serialize(true);if(D){if(Object.isString(D)){D=D.toQueryParams()}Object.extend(A.parameters,D)}if(B.hasAttribute("method")&&!A.method){A.method=B.method}return new Ajax.Request(C,A)}};Form.Element={focus:function(A){$(A).focus();return A},select:function(A){$(A).select();return A}};Form.Element.Methods={serialize:function(A){A=$(A);if(!A.disabled&&A.name){var B=A.getValue();if(B!=undefined){var C={};C[A.name]=B;return Object.toQueryString(C)}}return""},getValue:function(A){A=$(A);var B=A.tagName.toLowerCase();return Form.Element.Serializers[B](A)},setValue:function(A,B){A=$(A);var C=A.tagName.toLowerCase();Form.Element.Serializers[C](A,B);return A},clear:function(A){$(A).value="";return A},present:function(A){return $(A).value!=""},activate:function(A){A=$(A);try{A.focus();if(A.select&&(A.tagName.toLowerCase()!="input"||!["button","reset","submit"].include(A.type))){A.select()}}catch(B){}return A},disable:function(A){A=$(A);A.blur();A.disabled=true;return A},enable:function(A){A=$(A);A.disabled=false;return A}};var Field=Form.Element;var $F=Form.Element.Methods.getValue;Form.Element.Serializers={input:function(A,B){switch(A.type.toLowerCase()){case"checkbox":case"radio":return Form.Element.Serializers.inputSelector(A,B);default:return Form.Element.Serializers.textarea(A,B)}},inputSelector:function(A,B){if(Object.isUndefined(B)){return A.checked?A.value:null}else{A.checked=!!B}},textarea:function(A,B){if(Object.isUndefined(B)){return A.value}else{A.value=B}},select:function(D,A){if(Object.isUndefined(A)){return this[D.type=="select-one"?"selectOne":"selectMany"](D)}else{var C,F,G=!Object.isArray(A);for(var B=0,E=D.length;B<E;B++){C=D.options[B];F=this.optionValue(C);if(G){if(F==A){C.selected=true;return }}else{C.selected=A.include(F)}}}},selectOne:function(B){var A=B.selectedIndex;return A>=0?this.optionValue(B.options[A]):null},selectMany:function(D){var A,E=D.length;if(!E){return null}for(var C=0,A=[];C<E;C++){var B=D.options[C];if(B.selected){A.push(this.optionValue(B))}}return A},optionValue:function(A){return Element.extend(A).hasAttribute("value")?A.value:A.text}};Abstract.TimedObserver=Class.create(PeriodicalExecuter,{initialize:function($super,A,B,C){$super(C,B);this.element=$(A);this.lastValue=this.getValue()},execute:function(){var A=this.getValue();if(Object.isString(this.lastValue)&&Object.isString(A)?this.lastValue!=A:String(this.lastValue)!=String(A)){this.callback(this.element,A);this.lastValue=A}}});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(A,B){this.element=$(A);this.callback=B;this.lastValue=this.getValue();if(this.element.tagName.toLowerCase()=="form"){this.registerFormCallbacks()}else{this.registerCallback(this.element)}},onElementEvent:function(){var A=this.getValue();if(this.lastValue!=A){this.callback(this.element,A);this.lastValue=A}},registerFormCallbacks:function(){Form.getElements(this.element).each(this.registerCallback,this)},registerCallback:function(A){if(A.type){switch(A.type.toLowerCase()){case"checkbox":case"radio":Event.observe(A,"click",this.onElementEvent.bind(this));break;default:Event.observe(A,"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(B){var A;switch(B.type){case"mouseover":A=B.fromElement;break;case"mouseout":A=B.toElement;break;default:return null}return Element.extend(A)}});Event.Methods=(function(){var A;if(Prototype.Browser.IE){var B={0:1,1:4,2:2};A=function(D,C){return D.button==B[C]}}else{if(Prototype.Browser.WebKit){A=function(D,C){switch(C){case 0:return D.which==1&&!D.metaKey;case 1:return D.which==1&&D.metaKey;default:return false}}}else{A=function(D,C){return D.which?(D.which===C+1):(D.button===C)}}}return{isLeftClick:function(C){return A(C,0)},isMiddleClick:function(C){return A(C,1)},isRightClick:function(C){return A(C,2)},element:function(D){var C=Event.extend(D).target;return Element.extend(C.nodeType==Node.TEXT_NODE?C.parentNode:C)},findElement:function(D,F){var C=Event.element(D);if(!F){return C}var E=[C].concat(C.ancestors());return Selector.findElement(E,F,0)},pointer:function(C){return{x:C.pageX||(C.clientX+(document.documentElement.scrollLeft||document.body.scrollLeft)),y:C.pageY||(C.clientY+(document.documentElement.scrollTop||document.body.scrollTop))}},pointerX:function(C){return Event.pointer(C).x},pointerY:function(C){return Event.pointer(C).y},stop:function(C){Event.extend(C);C.preventDefault();C.stopPropagation();C.stopped=true}}})();Event.extend=(function(){var A=Object.keys(Event.Methods).inject({},function(B,C){B[C]=Event.Methods[C].methodize();return B});if(Prototype.Browser.IE){Object.extend(A,{stopPropagation:function(){this.cancelBubble=true},preventDefault:function(){this.returnValue=false},inspect:function(){return"[object Event]"}});return function(B){if(!B){return false}if(B._extendedByPrototype){return B}B._extendedByPrototype=Prototype.emptyFunction;var C=Event.pointer(B);Object.extend(B,{target:B.srcElement,relatedTarget:Event.relatedTarget(B),pageX:C.x,pageY:C.y});return Object.extend(B,A)}}else{Event.prototype=Event.prototype||document.createEvent("HTMLEvents").__proto__;Object.extend(Event.prototype,A);return Prototype.K}})();Object.extend(Event,(function(){var B=Event.cache;function C(J){if(J._prototypeEventID){return J._prototypeEventID[0]}arguments.callee.id=arguments.callee.id||1;return J._prototypeEventID=[++arguments.callee.id]}function G(J){if(J&&J.include(":")){return"dataavailable"}return J}function A(J){return B[J]=B[J]||{}}function F(L,J){var K=A(L);return K[J]=K[J]||[]}function H(K,J,L){var O=C(K);var N=F(O,J);if(N.pluck("handler").include(L)){return false}var M=function(P){if(!Event||!Event.extend||(P.eventName&&P.eventName!=J)){return false}Event.extend(P);L.call(K,P)};M.handler=L;N.push(M);return M}function I(M,J,K){var L=F(M,J);return L.find(function(N){return N.handler==K})}function D(M,J,K){var L=A(M);if(!L[J]){return false}L[J]=L[J].without(I(M,J,K))}function E(){for(var K in B){for(var J in B[K]){B[K][J]=null}}}if(window.attachEvent){window.attachEvent("onunload",E)}return{observe:function(L,J,M){L=$(L);var K=G(J);var N=H(L,J,M);if(!N){return L}if(L.addEventListener){L.addEventListener(K,N,false)}else{L.attachEvent("on"+K,N)}return L},stopObserving:function(L,J,M){L=$(L);var O=C(L),K=G(J);if(!M&&J){F(O,J).each(function(P){L.stopObserving(J,P.handler)});return L}else{if(!J){Object.keys(A(O)).each(function(P){L.stopObserving(P)});return L}}var N=I(O,J,M);if(!N){return L}if(L.removeEventListener){L.removeEventListener(K,N,false)}else{L.detachEvent("on"+K,N)}D(O,J,M);return L},fire:function(L,K,J){L=$(L);if(L==document&&document.createEvent&&!L.dispatchEvent){L=document.documentElement}var M;if(document.createEvent){M=document.createEvent("HTMLEvents");M.initEvent("dataavailable",true,true)}else{M=document.createEventObject();M.eventType="ondataavailable"}M.eventName=K;M.memo=J||{};if(document.createEvent){L.dispatchEvent(M)}else{L.fireEvent(M.eventType,M)}return Event.extend(M)}}})());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(){var B;function A(){if(document.loaded){return }if(B){window.clearInterval(B)}document.fire("dom:loaded");document.loaded=true}if(document.addEventListener){if(Prototype.Browser.WebKit){B=window.setInterval(function(){if(/loaded|complete/.test(document.readyState)){A()}},0);Event.observe(window,"load",A)}else{document.addEventListener("DOMContentLoaded",A,false)}}else{document.write("<script id=__onDOMContentLoaded defer src=//:><\/script>");$("__onDOMContentLoaded").onreadystatechange=function(){if(this.readyState=="complete"){this.onreadystatechange=null;A()}}}})();Hash.toQueryString=Object.toQueryString;var Toggle={display:Element.toggle};Element.Methods.childOf=Element.Methods.descendantOf;var Insertion={Before:function(A,B){return Element.insert(A,{before:B})},Top:function(A,B){return Element.insert(A,{top:B})},Bottom:function(A,B){return Element.insert(A,{bottom:B})},After:function(A,B){return Element.insert(A,{after:B})}};var $continue=new Error('"throw $continue" is deprecated, use "return" instead');var Position={includeScrollOffsets:false,prepare:function(){this.deltaX=window.pageXOffset||document.documentElement.scrollLeft||document.body.scrollLeft||0;this.deltaY=window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop||0},within:function(B,A,C){if(this.includeScrollOffsets){return this.withinIncludingScrolloffsets(B,A,C)}this.xcomp=A;this.ycomp=C;this.offset=Element.cumulativeOffset(B);return(C>=this.offset[1]&&C<this.offset[1]+B.offsetHeight&&A>=this.offset[0]&&A<this.offset[0]+B.offsetWidth)},withinIncludingScrolloffsets:function(B,A,D){var C=Element.cumulativeScrollOffset(B);this.xcomp=A+C[0]-this.deltaX;this.ycomp=D+C[1]-this.deltaY;this.offset=Element.cumulativeOffset(B);return(this.ycomp>=this.offset[1]&&this.ycomp<this.offset[1]+B.offsetHeight&&this.xcomp>=this.offset[0]&&this.xcomp<this.offset[0]+B.offsetWidth)},overlap:function(B,A){if(!B){return 0}if(B=="vertical"){return((this.offset[1]+A.offsetHeight)-this.ycomp)/A.offsetHeight}if(B=="horizontal"){return((this.offset[0]+A.offsetWidth)-this.xcomp)/A.offsetWidth}},cumulativeOffset:Element.Methods.cumulativeOffset,positionedOffset:Element.Methods.positionedOffset,absolutize:function(A){Position.prepare();return Element.absolutize(A)},relativize:function(A){Position.prepare();return Element.relativize(A)},realOffset:Element.Methods.cumulativeScrollOffset,offsetParent:Element.Methods.getOffsetParent,page:Element.Methods.viewportOffset,clone:function(B,C,A){A=A||{};return Element.clonePosition(C,B,A)}};if(!document.getElementsByClassName){document.getElementsByClassName=function(B){function A(C){return C.blank()?null:"[contains(concat(' ', @class, ' '), ' "+C+" ')]"}B.getElementsByClassName=Prototype.BrowserFeatures.XPath?function(C,E){E=E.toString().strip();var D=/\s/.test(E)?$w(E).map(A).join(""):A(E);return D?document._getElementsByXPath(".//*"+D,C):[]}:function(E,F){F=F.toString().strip();var G=[],H=(/\s/.test(F)?$w(F):null);if(!H&&!F){return G}var C=$(E).getElementsByTagName("*");F=" "+F+" ";for(var D=0,J,I;J=C[D];D++){if(J.className&&(I=" "+J.className+" ")&&(I.include(F)||(H&&H.all(function(K){return !K.toString().blank()&&I.include(" "+K+" ")})))){G.push(Element.extend(J))}}return G};return function(D,C){return $(C||document.body).getElementsByClassName(D)}}(Element.Methods)}Element.ClassNames=Class.create();Element.ClassNames.prototype={initialize:function(A){this.element=$(A)},_each:function(A){this.element.className.split(/\s+/).select(function(B){return B.length>0})._each(A)},set:function(A){this.element.className=A},add:function(A){if(this.include(A)){return }this.set($A(this).concat(A).join(" "))},remove:function(A){if(!this.include(A)){return }this.set($A(this).without(A).join(" "))},toString:function(){return $A(this).join(" ")}};Object.extend(Element.ClassNames.prototype,Enumerable);Element.addMethods();
\ No newline at end of file
+var Prototype={Version:"1.6.0.3",Browser:{IE:!!(window.attachEvent&&navigator.userAgent.indexOf("Opera")===-1),Opera:navigator.userAgent.indexOf("Opera")>-1,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,SelectorsAPI:!!document.querySelector,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(a){return a}};if(Prototype.Browser.MobileSafari){Prototype.BrowserFeatures.SpecificElementExtensions=false}var Class={create:function(){var e=null,d=$A(arguments);if(Object.isFunction(d[0])){e=d.shift()}function a(){this.initialize.apply(this,arguments)}Object.extend(a,Class.Methods);a.superclass=e;a.subclasses=[];if(e){var b=function(){};b.prototype=e.prototype;a.prototype=new b;e.subclasses.push(a)}for(var c=0;c<d.length;c++){a.addMethods(d[c])}if(!a.prototype.initialize){a.prototype.initialize=Prototype.emptyFunction}a.prototype.constructor=a;return a}};Class.Methods={addMethods:function(g){var c=this.superclass&&this.superclass.prototype;var b=Object.keys(g);if(!Object.keys({toString:true}).length){b.push("toString","valueOf")}for(var a=0,d=b.length;a<d;a++){var f=b[a],e=g[f];if(c&&Object.isFunction(e)&&e.argumentNames().first()=="$super"){var h=e;e=(function(i){return function(){return c[i].apply(this,arguments)}})(f).wrap(h);e.valueOf=h.valueOf.bind(h);e.toString=h.toString.bind(h)}this.prototype[f]=e}return this}};var Abstract={};Object.extend=function(a,c){for(var b in c){a[b]=c[b]}return a};Object.extend(Object,{inspect:function(a){try{if(Object.isUndefined(a)){return"undefined"}if(a===null){return"null"}return a.inspect?a.inspect():String(a)}catch(b){if(b instanceof RangeError){return"..."}throw b}},toJSON:function(a){var c=typeof a;switch(c){case"undefined":case"function":case"unknown":return;case"boolean":return a.toString()}if(a===null){return"null"}if(a.toJSON){return a.toJSON()}if(Object.isElement(a)){return}var b=[];for(var e in a){var d=Object.toJSON(a[e]);if(!Object.isUndefined(d)){b.push(e.toJSON()+": "+d)}}return"{"+b.join(", ")+"}"},toQueryString:function(a){return $H(a).toQueryString()},toHTML:function(a){return a&&a.toHTML?a.toHTML():String.interpret(a)},keys:function(a){var b=[];for(var c in a){b.push(c)}return b},values:function(b){var a=[];for(var c in b){a.push(b[c])}return a},clone:function(a){return Object.extend({},a)},isElement:function(a){return!!(a&&a.nodeType==1)},isArray:function(a){return a!=null&&typeof a=="object"&&"splice"in a&&"join"in a},isHash:function(a){return a instanceof Hash},isFunction:function(a){return typeof a=="function"},isString:function(a){return typeof a=="string"},isNumber:function(a){return typeof a=="number"},isUndefined:function(a){return typeof a=="undefined"}});Object.extend(Function.prototype,{argumentNames:function(){var a=this.toString().match(/^[\s\(]*function[^(]*\(([^\)]*)\)/)[1].replace(/\s+/g,"").split(",");return a.length==1&&!a[0]?[]:a},bind:function(){if(arguments.length<2&&Object.isUndefined(arguments[0])){return this}var a=this,c=$A(arguments),b=c.shift();return function(){return a.apply(b,c.concat($A(arguments)))}},bindAsEventListener:function(){var a=this,c=$A(arguments),b=c.shift();return function(d){return a.apply(b,[d||window.event].concat(c))}},curry:function(){if(!arguments.length){return this}var a=this,b=$A(arguments);return function(){return a.apply(this,b.concat($A(arguments)))}},delay:function(){var a=this,b=$A(arguments),c=b.shift()*1000;return window.setTimeout(function(){return a.apply(a,b)},c)},defer:function(){var a=[0.01].concat($A(arguments));return this.delay.apply(this,a)},wrap:function(b){var a=this;return function(){return b.apply(this,[a.bind(this)].concat($A(arguments)))}},methodize:function(){if(this._methodized){return this._methodized}var a=this;return this._methodized=function(){return a.apply(null,[this].concat($A(arguments)))}}});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 c;for(var b=0,d=arguments.length;b<d;b++){var a=arguments[b];try{c=a();break}catch(f){}}return c}};RegExp.prototype.match=RegExp.prototype.test;RegExp.escape=function(a){return String(a).replace(/([.*+?^=!:${}()|[\]\/\\])/g,"\\$1")};var PeriodicalExecuter=Class.create({initialize:function(b,a){this.callback=b;this.frequency=a;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(a){return a==null?"":String(a)},specialChar:{"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r","\\":"\\\\"}});Object.extend(String.prototype,{gsub:function(e,c){var a="",d=this,b;c=arguments.callee.prepareReplacement(c);while(d.length>0){if(b=d.match(e)){a+=d.slice(0,b.index);a+=String.interpret(c(b));d=d.slice(b.index+b[0].length)}else{a+=d,d=""}}return a},sub:function(c,a,b){a=this.gsub.prepareReplacement(a);b=Object.isUndefined(b)?1:b;return this.gsub(c,function(d){if(--b<0){return d[0]}return a(d)})},scan:function(b,a){this.gsub(b,a);return String(this)},truncate:function(b,a){b=b||30;a=Object.isUndefined(a)?"...":a;return this.length>b?this.slice(0,b-a.length)+a: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 b=new RegExp(Prototype.ScriptFragment,"img");var a=new RegExp(Prototype.ScriptFragment,"im");return(this.match(b)||[]).map(function(c){return(c.match(a)||["",""])[1]})},evalScripts:function(){return this.extractScripts().map(function(script){return eval(script)})},escapeHTML:function(){var a=arguments.callee;a.text.data=this;return a.div.innerHTML},unescapeHTML:function(){var a=new Element("div");a.innerHTML=this.stripTags();return a.childNodes[0]?(a.childNodes.length>1?$A(a.childNodes).inject("",function(b,c){return b+c.nodeValue}):a.childNodes[0].nodeValue):""},toQueryParams:function(b){var a=this.strip().match(/([^?#]*)(#.*)?$/);if(!a){return{}}return a[1].split(b||"&").inject({},function(e,f){if((f=f.split("="))[0]){var c=decodeURIComponent(f.shift());var d=f.length>1?f.join("="):f[0];if(d!=undefined){d=decodeURIComponent(d)}if(c in e){if(!Object.isArray(e[c])){e[c]=[e[c]]}e[c].push(d)}else{e[c]=d}}return e})},toArray:function(){return this.split("")},succ:function(){return this.slice(0,this.length-1)+String.fromCharCode(this.charCodeAt(this.length-1)+1)},times:function(a){return a<1?"":new Array(a+1).join(this)},camelize:function(){var d=this.split("-"),a=d.length;if(a==1){return d[0]}var c=this.charAt(0)=="-"?d[0].charAt(0).toUpperCase()+d[0].substring(1):d[0];for(var b=1;b<a;b++){c+=d[b].charAt(0).toUpperCase()+d[b].substring(1)}return c},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(b){var a=this.gsub(/[\x00-\x1f\\]/,function(c){var d=String.specialChar[c[0]];return d?d:"\\u00"+c[0].charCodeAt().toPaddedString(2,16)});if(b){return'"'+a.replace(/"/g,'\\"')+'"'}return"'"+a.replace(/'/g,"\\'")+"'"},toJSON:function(){return this.inspect(true)},unfilterJSON:function(a){return this.sub(a||Prototype.JSONFilter,"#{1}")},isJSON:function(){var a=this;if(a.blank()){return false}a=this.replace(/\\./g,"@").replace(/"[^"\\\n\r]*"/g,"");return(/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(a)},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(a){return this.indexOf(a)>-1},startsWith:function(a){return this.indexOf(a)===0},endsWith:function(a){var b=this.length-a.length;return b>=0&&this.lastIndexOf(a)===b},empty:function(){return this==""},blank:function(){return/^\s*$/.test(this)},interpolate:function(a,b){return new Template(this,b).evaluate(a)}});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.stripTags().replace(/&amp;/g,"&").replace(/&lt;/g,"<").replace(/&gt;/g,">")}})}String.prototype.gsub.prepareReplacement=function(b){if(Object.isFunction(b)){return b}var a=new Template(b);return function(c){return a.evaluate(c)}};String.prototype.parseQuery=String.prototype.toQueryParams;Object.extend(String.prototype.escapeHTML,{div:document.createElement("div"),text:document.createTextNode("")});String.prototype.escapeHTML.div.appendChild(String.prototype.escapeHTML.text);var Template=Class.create({initialize:function(a,b){this.template=a.toString();this.pattern=b||Template.Pattern},evaluate:function(a){if(Object.isFunction(a.toTemplateReplacements)){a=a.toTemplateReplacements()}return this.template.gsub(this.pattern,function(d){if(a==null){return""}var f=d[1]||"";if(f=="\\"){return d[2]}var b=a,g=d[3];var e=/^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;d=e.exec(g);if(d==null){return f}while(d!=null){var c=d[1].startsWith("[")?d[2].gsub("\\\\]","]"):d[1];b=b[c];if(null==b||""==d[3]){break}g=g.substring("["==d[3]?d[1].length:d[0].length);d=e.exec(g)}return f+String.interpret(b)})}});Template.Pattern=/(^|.|\r|\n)(#\{(.*?)\})/;var $break={};var Enumerable={each:function(c,b){var a=0;try{this._each(function(e){c.call(b,e,a++)})}catch(d){if(d!=$break){throw d}}return this},eachSlice:function(d,c,b){var a=-d,e=[],f=this.toArray();if(d<1){return f}while((a+=d)<f.length){e.push(f.slice(a,a+d))}return e.collect(c,b)},all:function(c,b){c=c||Prototype.K;var a=true;this.each(function(e,d){a=a&&!!c.call(b,e,d);if(!a){throw $break}});return a},any:function(c,b){c=c||Prototype.K;var a=false;this.each(function(e,d){if(a=!!c.call(b,e,d)){throw $break}});return a},collect:function(c,b){c=c||Prototype.K;var a=[];this.each(function(e,d){a.push(c.call(b,e,d))});return a},detect:function(c,b){var a;this.each(function(e,d){if(c.call(b,e,d)){a=e;throw $break}});return a},findAll:function(c,b){var a=[];this.each(function(e,d){if(c.call(b,e,d)){a.push(e)}});return a},grep:function(d,c,b){c=c||Prototype.K;var a=[];if(Object.isString(d)){d=new RegExp(d)}this.each(function(f,e){if(d.match(f)){a.push(c.call(b,f,e))}});return a},include:function(a){if(Object.isFunction(this.indexOf)){if(this.indexOf(a)!=-1){return true}}var b=false;this.each(function(c){if(c==a){b=true;throw $break}});return b},inGroupsOf:function(b,a){a=Object.isUndefined(a)?null:a;return this.eachSlice(b,function(c){while(c.length<b){c.push(a)}return c})},inject:function(a,c,b){this.each(function(e,d){a=c.call(b,a,e,d)});return a},invoke:function(b){var a=$A(arguments).slice(1);return this.map(function(c){return c[b].apply(c,a)})},max:function(c,b){c=c||Prototype.K;var a;this.each(function(e,d){e=c.call(b,e,d);if(a==null||e>=a){a=e}});return a},min:function(c,b){c=c||Prototype.K;var a;this.each(function(e,d){e=c.call(b,e,d);if(a==null||e<a){a=e}});return a},partition:function(d,b){d=d||Prototype.K;var c=[],a=[];this.each(function(f,e){(d.call(b,f,e)?c:a).push(f)});return[c,a]},pluck:function(b){var a=[];this.each(function(c){a.push(c[b])});return a},reject:function(c,b){var a=[];this.each(function(e,d){if(!c.call(b,e,d)){a.push(e)}});return a},sortBy:function(b,a){return this.map(function(d,c){return{value:d,criteria:b.call(a,d,c)}}).sort(function(f,e){var d=f.criteria,c=e.criteria;return d<c?-1:d>c?1:0}).pluck("value")},toArray:function(){return this.map()},zip:function(){var b=Prototype.K,a=$A(arguments);if(Object.isFunction(a.last())){b=a.pop()}var c=[this].concat(a).map($A);return this.map(function(e,d){return b(c.pluck(d))})},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(c){if(!c){return[]}if(c.toArray){return c.toArray()}var b=c.length||0,a=new Array(b);while(b--){a[b]=c[b]}return a}if(Prototype.Browser.WebKit){$A=function(c){if(!c){return[]}if(!(typeof c==="function"&&typeof c.length==="number"&&typeof c.item==="function")&&c.toArray){return c.toArray()}var b=c.length||0,a=new Array(b);while(b--){a[b]=c[b]}return a}}Array.from=$A;Object.extend(Array.prototype,Enumerable);if(!Array.prototype._reverse){Array.prototype._reverse=Array.prototype.reverse}Object.extend(Array.prototype,{_each:function(b){for(var a=0,c=this.length;a<c;a++){b(this[a])}},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(a){return a!=null})},flatten:function(){return this.inject([],function(b,a){return b.concat(Object.isArray(a)?a.flatten():[a])})},without:function(){var a=$A(arguments);return this.select(function(b){return!a.include(b)})},reverse:function(a){return(a!==false?this:this.toArray())._reverse()},reduce:function(){return this.length>1?this:this[0]},uniq:function(a){return this.inject([],function(d,c,b){if(0==b||(a?d.last()!=c:!d.include(c))){d.push(c)}return d})},intersect:function(a){return this.uniq().findAll(function(b){return a.detect(function(c){return b===c})})},clone:function(){return[].concat(this)},size:function(){return this.length},inspect:function(){return"["+this.map(Object.inspect).join(", ")+"]"},toJSON:function(){var a=[];this.each(function(b){var c=Object.toJSON(b);if(!Object.isUndefined(c)){a.push(c)}});return"["+a.join(", ")+"]"}});if(Object.isFunction(Array.prototype.forEach)){Array.prototype._each=Array.prototype.forEach}if(!Array.prototype.indexOf){Array.prototype.indexOf=function(c,a){a||(a=0);var b=this.length;if(a<0){a=b+a}for(;a<b;a++){if(this[a]===c){return a}}return-1}}if(!Array.prototype.lastIndexOf){Array.prototype.lastIndexOf=function(b,a){a=isNaN(a)?this.length:(a<0?this.length+a:a)+1;var c=this.slice(0,a).reverse().indexOf(b);return(c<0)?c:a-c-1}}Array.prototype.toArray=Array.prototype.clone;function $w(a){if(!Object.isString(a)){return[]}a=a.strip();return a?a.split(/\s+/):[]}if(Prototype.Browser.Opera){Array.prototype.concat=function(){var e=[];for(var b=0,c=this.length;b<c;b++){e.push(this[b])}for(var b=0,c=arguments.length;b<c;b++){if(Object.isArray(arguments[b])){for(var a=0,d=arguments[b].length;a<d;a++){e.push(arguments[b][a])}}else{e.push(arguments[b])}}return e}}Object.extend(Number.prototype,{toColorPart:function(){return this.toPaddedString(2,16)},succ:function(){return this+1},times:function(b,a){$R(0,this,true).each(b,a);return this},toPaddedString:function(c,b){var a=this.toString(b||10);return"0".times(c-a.length)+a},toJSON:function(){return isFinite(this)?this.toString():"null"}});$w("abs round ceil floor").each(function(a){Number.prototype[a]=Math[a].methodize()});function $H(a){return new Hash(a)}var Hash=Class.create(Enumerable,(function(){function a(b,c){if(Object.isUndefined(c)){return b}return b+"="+encodeURIComponent(String.interpret(c))}return{initialize:function(b){this._object=Object.isHash(b)?b.toObject():Object.clone(b)},_each:function(c){for(var b in this._object){var d=this._object[b],e=[b,d];e.key=b;e.value=d;c(e)}},set:function(b,c){return this._object[b]=c},get:function(b){if(this._object[b]!==Object.prototype[b]){return this._object[b]}},unset:function(b){var c=this._object[b];delete this._object[b];return c},toObject:function(){return Object.clone(this._object)},keys:function(){return this.pluck("key")},values:function(){return this.pluck("value")},index:function(c){var b=this.detect(function(d){return d.value===c});return b&&b.key},merge:function(b){return this.clone().update(b)},update:function(b){return new Hash(b).inject(this,function(c,d){c.set(d.key,d.value);return c})},toQueryString:function(){return this.inject([],function(d,e){var c=encodeURIComponent(e.key),b=e.value;if(b&&typeof b=="object"){if(Object.isArray(b)){return d.concat(b.map(a.curry(c)))}}else{d.push(a(c,b))}return d}).join("&")},inspect:function(){return"#<Hash:{"+this.map(function(b){return b.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(c,a,b){this.start=c;this.end=a;this.exclusive=b},_each:function(a){var b=this.start;while(this.include(b)){a(b);b=b.succ()}},include:function(a){if(a<this.start){return false}if(this.exclusive){return a<this.end}return a<=this.end}});var $R=function(c,a,b){return new ObjectRange(c,a,b)};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(a){this.responders._each(a)},register:function(a){if(!this.include(a)){this.responders.push(a)}},unregister:function(a){this.responders=this.responders.without(a)},dispatch:function(d,b,c,a){this.each(function(f){if(Object.isFunction(f[d])){try{f[d].apply(f,[b,c,a])}catch(g){}}})}};Object.extend(Ajax.Responders,Enumerable);Ajax.Responders.register({onCreate:function(){Ajax.activeRequestCount++},onComplete:function(){Ajax.activeRequestCount--}});Ajax.Base=Class.create({initialize:function(a){this.options={method:"post",asynchronous:true,contentType:"application/x-www-form-urlencoded",encoding:"UTF-8",parameters:"",evalJSON:true,evalJS:true};Object.extend(this.options,a||{});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,b,a){$super(a);this.transport=Ajax.getTransport();this.request(b)},request:function(b){this.url=b;this.method=this.options.method;var d=Object.clone(this.options.parameters);if(!["get","post"].include(this.method)){d._method=this.method;this.method="post"}this.parameters=d;if(d=Object.toQueryString(d)){if(this.method=="get"){this.url+=(this.url.include("?")?"&":"?")+d}else{if(/Konqueror|Safari|KHTML/.test(navigator.userAgent)){d+="&_="}}}try{var a=new Ajax.Response(this);if(this.options.onCreate){this.options.onCreate(a)}Ajax.Responders.dispatch("onCreate",this,a);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||d):null;this.transport.send(this.body);if(!this.options.asynchronous&&this.transport.overrideMimeType){this.onStateChange()}}catch(c){this.dispatchException(c)}},onStateChange:function(){var a=this.transport.readyState;if(a>1&&!((a==4)&&this._complete)){this.respondToReadyState(this.transport.readyState)}},setRequestHeaders:function(){var e={"X-Requested-With":"XMLHttpRequest","X-Prototype-Version":Prototype.Version,Accept:"text/javascript, text/html, application/xml, text/xml, */*"};if(this.method=="post"){e["Content-type"]=this.options.contentType+(this.options.encoding?"; charset="+this.options.encoding:"");if(this.transport.overrideMimeType&&(navigator.userAgent.match(/Gecko\/(\d{4})/)||[0,2005])[1]<2005){e.Connection="close"}}if(typeof this.options.requestHeaders=="object"){var c=this.options.requestHeaders;if(Object.isFunction(c.push)){for(var b=0,d=c.length;b<d;b+=2){e[c[b]]=c[b+1]}}else{$H(c).each(function(f){e[f.key]=f.value})}}for(var a in e){this.transport.setRequestHeader(a,e[a])}},success:function(){var a=this.getStatus();return!a||(a>=200&&a<300)},getStatus:function(){try{return this.transport.status||0}catch(a){return 0}},respondToReadyState:function(a){var c=Ajax.Request.Events[a],b=new Ajax.Response(this);if(c=="Complete"){try{this._complete=true;(this.options["on"+b.status]||this.options["on"+(this.success()?"Success":"Failure")]||Prototype.emptyFunction)(b,b.headerJSON)}catch(d){this.dispatchException(d)}var f=b.getHeader("Content-type");if(this.options.evalJS=="force"||(this.options.evalJS&&this.isSameOrigin()&&f&&f.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i))){this.evalResponse()}}try{(this.options["on"+c]||Prototype.emptyFunction)(b,b.headerJSON);Ajax.Responders.dispatch("on"+c,this,b,b.headerJSON)}catch(d){this.dispatchException(d)}if(c=="Complete"){this.transport.onreadystatechange=Prototype.emptyFunction}},isSameOrigin:function(){var a=this.url.match(/^\s*https?:\/\/[^\/]*/);return!a||(a[0]=="#{protocol}//#{domain}#{port}".interpolate({protocol:location.protocol,domain:document.domain,port:location.port?":"+location.port:""}))},getHeader:function(a){try{return this.transport.getResponseHeader(a)||null}catch(b){return null}},evalResponse:function(){try{return eval((this.transport.responseText||"").unfilterJSON())}catch(e){this.dispatchException(e)}},dispatchException:function(a){(this.options.onException||Prototype.emptyFunction)(this,a);Ajax.Responders.dispatch("onException",this,a)}});Ajax.Request.Events=["Uninitialized","Loading","Loaded","Interactive","Complete"];Ajax.Response=Class.create({initialize:function(c){this.request=c;var d=this.transport=c.transport,a=this.readyState=d.readyState;if((a>2&&!Prototype.Browser.IE)||a==4){this.status=this.getStatus();this.statusText=this.getStatusText();this.responseText=String.interpret(d.responseText);this.headerJSON=this._getHeaderJSON()}if(a==4){var b=d.responseXML;this.responseXML=Object.isUndefined(b)?null:b;this.responseJSON=this._getResponseJSON()}},status:0,statusText:"",getStatus:Ajax.Request.prototype.getStatus,getStatusText:function(){try{return this.transport.statusText||""}catch(a){return""}},getHeader:Ajax.Request.prototype.getHeader,getAllHeaders:function(){try{return this.getAllResponseHeaders()}catch(a){return null}},getResponseHeader:function(a){return this.transport.getResponseHeader(a)},getAllResponseHeaders:function(){return this.transport.getAllResponseHeaders()},_getHeaderJSON:function(){var a=this.getHeader("X-JSON");if(!a){return null}a=decodeURIComponent(escape(a));try{return a.evalJSON(this.request.options.sanitizeJSON||!this.request.isSameOrigin())}catch(b){this.request.dispatchException(b)}},_getResponseJSON:function(){var a=this.request.options;if(!a.evalJSON||(a.evalJSON!="force"&&!(this.getHeader("Content-type")||"").include("application/json"))||this.responseText.blank()){return null}try{return this.responseText.evalJSON(a.sanitizeJSON||!this.request.isSameOrigin())}catch(b){this.request.dispatchException(b)}}});Ajax.Updater=Class.create(Ajax.Request,{initialize:function($super,a,c,b){this.container={success:(a.success||a),failure:(a.failure||(a.success?null:a))};b=Object.clone(b);var d=b.onComplete;b.onComplete=(function(e,f){this.updateContent(e.responseText);if(Object.isFunction(d)){d(e,f)}}).bind(this);$super(c,b)},updateContent:function(d){var c=this.container[this.success()?"success":"failure"],a=this.options;if(!a.evalScripts){d=d.stripScripts()}if(c=$(c)){if(a.insertion){if(Object.isString(a.insertion)){var b={};b[a.insertion]=d;c.insert(b)}else{a.insertion(c,d)}}else{c.update(d)}}}});Ajax.PeriodicalUpdater=Class.create(Ajax.Base,{initialize:function($super,a,c,b){$super(b);this.onComplete=this.options.onComplete;this.frequency=(this.options.frequency||2);this.decay=(this.options.decay||1);this.updater={};this.container=a;this.url=c;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(a){if(this.options.decay){this.decay=(a.responseText==this.lastText?this.decay*this.options.decay:1);this.lastText=a.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 $(b){if(arguments.length>1){for(var a=0,d=[],c=arguments.length;a<c;a++){d.push($(arguments[a]))}return d}if(Object.isString(b)){b=document.getElementById(b)}return Element.extend(b)}if(Prototype.BrowserFeatures.XPath){document._getElementsByXPath=function(f,a){var c=[];var e=document.evaluate(f,$(a)||document,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);for(var b=0,d=e.snapshotLength;b<d;b++){c.push(Element.extend(e.snapshotItem(b)))}return c}}if(!window.Node){var Node={}}if(!Node.ELEMENT_NODE){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 a=this.Element;this.Element=function(d,c){c=c||{};d=d.toLowerCase();var b=Element.cache;if(Prototype.Browser.IE&&c.name){d="<"+d+' name="'+c.name+'">';delete c.name;return Element.writeAttribute(document.createElement(d),c)}if(!b[d]){b[d]=Element.extend(document.createElement(d))}return Element.writeAttribute(b[d].cloneNode(false),c)};Object.extend(this.Element,a||{});if(a){this.Element.prototype=a.prototype}}).call(window);Element.cache={};Element.Methods={visible:function(a){return $(a).style.display!="none"},toggle:function(a){a=$(a);Element[Element.visible(a)?"hide":"show"](a);return a},hide:function(a){a=$(a);a.style.display="none";return a},show:function(a){a=$(a);a.style.display="";return a},remove:function(a){a=$(a);a.parentNode.removeChild(a);return a},update:function(a,b){a=$(a);if(b&&b.toElement){b=b.toElement()}if(Object.isElement(b)){return a.update().insert(b)}b=Object.toHTML(b);a.innerHTML=b.stripScripts();b.evalScripts.bind(b).defer();return a},replace:function(b,c){b=$(b);if(c&&c.toElement){c=c.toElement()}else{if(!Object.isElement(c)){c=Object.toHTML(c);var a=b.ownerDocument.createRange();a.selectNode(b);c.evalScripts.bind(c).defer();c=a.createContextualFragment(c.stripScripts())}}b.parentNode.replaceChild(c,b);return b},insert:function(c,e){c=$(c);if(Object.isString(e)||Object.isNumber(e)||Object.isElement(e)||(e&&(e.toElement||e.toHTML))){e={bottom:e}}var d,f,b,g;for(var a in e){d=e[a];a=a.toLowerCase();f=Element._insertionTranslations[a];if(d&&d.toElement){d=d.toElement()}if(Object.isElement(d)){f(c,d);continue}d=Object.toHTML(d);b=((a=="before"||a=="after")?c.parentNode:c).tagName.toUpperCase();g=Element._getContentFromAnonymousElement(b,d.stripScripts());if(a=="top"||a=="after"){g.reverse()}g.each(f.curry(c));d.evalScripts.bind(d).defer()}return c},wrap:function(b,c,a){b=$(b);if(Object.isElement(c)){$(c).writeAttribute(a||{})}else{if(Object.isString(c)){c=new Element(c,a)}else{c=new Element("div",c)}}if(b.parentNode){b.parentNode.replaceChild(c,b)}c.appendChild(b);return c},inspect:function(b){b=$(b);var a="<"+b.tagName.toLowerCase();$H({id:"id",className:"class"}).each(function(f){var e=f.first(),c=f.last();var d=(b[e]||"").toString();if(d){a+=" "+c+"="+d.inspect(true)}});return a+">"},recursivelyCollect:function(a,c){a=$(a);var b=[];while(a=a[c]){if(a.nodeType==1){b.push(Element.extend(a))}}return b},ancestors:function(a){return $(a).recursivelyCollect("parentNode")},descendants:function(a){return $(a).select("*")},firstDescendant:function(a){a=$(a).firstChild;while(a&&a.nodeType!=1){a=a.nextSibling}return $(a)},immediateDescendants:function(a){if(!(a=$(a).firstChild)){return[]}while(a&&a.nodeType!=1){a=a.nextSibling}if(a){return[a].concat($(a).nextSiblings())}return[]},previousSiblings:function(a){return $(a).recursivelyCollect("previousSibling")},nextSiblings:function(a){return $(a).recursivelyCollect("nextSibling")},siblings:function(a){a=$(a);return a.previousSiblings().reverse().concat(a.nextSiblings())},match:function(b,a){if(Object.isString(a)){a=new Selector(a)}return a.match($(b))},up:function(b,d,a){b=$(b);if(arguments.length==1){return $(b.parentNode)}var c=b.ancestors();return Object.isNumber(d)?c[d]:Selector.findElement(c,d,a)},down:function(b,c,a){b=$(b);if(arguments.length==1){return b.firstDescendant()}return Object.isNumber(c)?b.descendants()[c]:Element.select(b,c)[a||0]},previous:function(b,d,a){b=$(b);if(arguments.length==1){return $(Selector.handlers.previousElementSibling(b))}var c=b.previousSiblings();return Object.isNumber(d)?c[d]:Selector.findElement(c,d,a)},next:function(c,d,b){c=$(c);if(arguments.length==1){return $(Selector.handlers.nextElementSibling(c))}var a=c.nextSiblings();return Object.isNumber(d)?a[d]:Selector.findElement(a,d,b)},select:function(){var a=$A(arguments),b=$(a.shift());return Selector.findChildElements(b,a)},adjacent:function(){var a=$A(arguments),b=$(a.shift());return Selector.findChildElements(b.parentNode,a).without(b)},identify:function(b){b=$(b);var c=b.readAttribute("id"),a=arguments.callee;if(c){return c}do{c="anonymous_element_"+a.counter++}while($(c));b.writeAttribute("id",c);return c},readAttribute:function(c,a){c=$(c);if(Prototype.Browser.IE){var b=Element._attributeTranslations.read;if(b.values[a]){return b.values[a](c,a)}if(b.names[a]){a=b.names[a]}if(a.include(":")){return(!c.attributes||!c.attributes[a])?null:c.attributes[a].value}}return c.getAttribute(a)},writeAttribute:function(e,c,f){e=$(e);var b={},d=Element._attributeTranslations.write;if(typeof c=="object"){b=c}else{b[c]=Object.isUndefined(f)?true:f}for(var a in b){c=d.names[a]||a;f=b[a];if(d.values[a]){c=d.values[a](e,f)}if(f===false||f===null){e.removeAttribute(c)}else{if(f===true){e.setAttribute(c,c)}else{e.setAttribute(c,f)}}}return e},getHeight:function(a){return $(a).getDimensions().height},getWidth:function(a){return $(a).getDimensions().width},classNames:function(a){return new Element.ClassNames(a)},hasClassName:function(a,b){if(!(a=$(a))){return}var c=a.className;return(c.length>0&&(c==b||new RegExp("(^|\\s)"+b+"(\\s|$)").test(c)))},addClassName:function(a,b){if(!(a=$(a))){return}if(!a.hasClassName(b)){a.className+=(a.className?" ":"")+b}return a},removeClassName:function(a,b){if(!(a=$(a))){return}a.className=a.className.replace(new RegExp("(^|\\s+)"+b+"(\\s+|$)")," ").strip();return a},toggleClassName:function(a,b){if(!(a=$(a))){return}return a[a.hasClassName(b)?"removeClassName":"addClassName"](b)},cleanWhitespace:function(b){b=$(b);var c=b.firstChild;while(c){var a=c.nextSibling;if(c.nodeType==3&&!/\S/.test(c.nodeValue)){b.removeChild(c)}c=a}return b},empty:function(a){return $(a).innerHTML.blank()},descendantOf:function(b,a){b=$(b),a=$(a);if(b.compareDocumentPosition){return(b.compareDocumentPosition(a)&8)===8}if(a.contains){return a.contains(b)&&a!==b}while(b=b.parentNode){if(b==a){return true}}return false},scrollTo:function(a){a=$(a);var b=a.cumulativeOffset();window.scrollTo(b[0],b[1]);return a},getStyle:function(b,c){b=$(b);c=c=="float"?"cssFloat":c.camelize();var d=b.style[c];if(!d||d=="auto"){var a=document.defaultView.getComputedStyle(b,null);d=a?a[c]:null}if(c=="opacity"){return d?parseFloat(d):1}return d=="auto"?null:d},getOpacity:function(a){return $(a).getStyle("opacity")},setStyle:function(b,c){b=$(b);var e=b.style,a;if(Object.isString(c)){b.style.cssText+=";"+c;return c.include("opacity")?b.setOpacity(c.match(/opacity:\s*(\d?\.?\d*)/)[1]):b}for(var d in c){if(d=="opacity"){b.setOpacity(c[d])}else{e[(d=="float"||d=="cssFloat")?(Object.isUndefined(e.styleFloat)?"cssFloat":"styleFloat"):d]=c[d]}}return b},setOpacity:function(a,b){a=$(a);a.style.opacity=(b==1||b==="")?"":(b<0.00001)?0:b;return a},getDimensions:function(c){c=$(c);var g=c.getStyle("display");if(g!="none"&&g!=null){return{width:c.offsetWidth,height:c.offsetHeight}}var b=c.style;var f=b.visibility;var d=b.position;var a=b.display;b.visibility="hidden";b.position="absolute";b.display="block";var h=c.clientWidth;var e=c.clientHeight;b.display=a;b.position=d;b.visibility=f;return{width:h,height:e}},makePositioned:function(a){a=$(a);var b=Element.getStyle(a,"position");if(b=="static"||!b){a._madePositioned=true;a.style.position="relative";if(Prototype.Browser.Opera){a.style.top=0;a.style.left=0}}return a},undoPositioned:function(a){a=$(a);if(a._madePositioned){a._madePositioned=undefined;a.style.position=a.style.top=a.style.left=a.style.bottom=a.style.right=""}return a},makeClipping:function(a){a=$(a);if(a._overflow){return a}a._overflow=Element.getStyle(a,"overflow")||"auto";if(a._overflow!=="hidden"){a.style.overflow="hidden"}return a},undoClipping:function(a){a=$(a);if(!a._overflow){return a}a.style.overflow=a._overflow=="auto"?"":a._overflow;a._overflow=null;return a},cumulativeOffset:function(b){var a=0,c=0;do{a+=b.offsetTop||0;c+=b.offsetLeft||0;b=b.offsetParent}while(b);return Element._returnOffset(c,a)},positionedOffset:function(b){var a=0,d=0;do{a+=b.offsetTop||0;d+=b.offsetLeft||0;b=b.offsetParent;if(b){if(b.tagName.toUpperCase()=="BODY"){break}var c=Element.getStyle(b,"position");if(c!=="static"){break}}}while(b);return Element._returnOffset(d,a)},absolutize:function(b){b=$(b);if(b.getStyle("position")=="absolute"){return b}var d=b.positionedOffset();var f=d[1];var e=d[0];var c=b.clientWidth;var a=b.clientHeight;b._originalLeft=e-parseFloat(b.style.left||0);b._originalTop=f-parseFloat(b.style.top||0);b._originalWidth=b.style.width;b._originalHeight=b.style.height;b.style.position="absolute";b.style.top=f+"px";b.style.left=e+"px";b.style.width=c+"px";b.style.height=a+"px";return b},relativize:function(a){a=$(a);if(a.getStyle("position")=="relative"){return a}a.style.position="relative";var c=parseFloat(a.style.top||0)-(a._originalTop||0);var b=parseFloat(a.style.left||0)-(a._originalLeft||0);a.style.top=c+"px";a.style.left=b+"px";a.style.height=a._originalHeight;a.style.width=a._originalWidth;return a},cumulativeScrollOffset:function(b){var a=0,c=0;do{a+=b.scrollTop||0;c+=b.scrollLeft||0;b=b.parentNode}while(b);return Element._returnOffset(c,a)},getOffsetParent:function(b){b=$(b);var d=b.offsetParent,a=document.body,c=document.documentElement;if(d&&d!==c){return $(d)}if(d===c||b===c||b===a){return $(a)}while((b=b.parentNode)&&b!==a){if(Element.getStyle(b,"position")!="static"){return $(b)}}return $(a)},viewportOffset:function(d){d=$(d);var b=d,a=0,c=0;do{a+=b.offsetTop||0;c+=b.offsetLeft||0}while((b=b.getOffsetParent())!=document.body);b=d;do{if(!Prototype.Browser.Opera||(b.tagName&&(b.tagName.toUpperCase()=="BODY"))){a-=b.scrollTop||0;c-=b.scrollLeft||0}}while(b=b.parentNode);return Element._returnOffset(c,a)},clonePosition:function(b,d){var a=Object.extend({setLeft:true,setTop:true,setWidth:true,setHeight:true,offsetTop:0,offsetLeft:0},arguments[2]||{});d=$(d);var e=d.viewportOffset();b=$(b);var f=[0,0];var c=null;if(Element.getStyle(b,"position")=="absolute"){c=b.getOffsetParent();f=c.viewportOffset()}if(c==document.body){f[0]-=document.body.offsetLeft;f[1]-=document.body.offsetTop}if(a.setLeft){b.style.left=(e[0]-f[0]+a.offsetLeft)+"px"}if(a.setTop){b.style.top=(e[1]-f[1]+a.offsetTop)+"px"}if(a.setWidth){b.style.width=d.offsetWidth+"px"}if(a.setHeight){b.style.height=d.offsetHeight+"px"}return b}};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(d,b,c){switch(c){case"left":case"top":case"right":case"bottom":if(d(b,"position")==="static"){return null}case"height":case"width":if(!Element.visible(b)){return null}var e=parseInt(d(b,c),10);if(e!==b["offset"+c.capitalize()]){return e+"px"}var a;if(c==="height"){a=["border-top-width","padding-top","padding-bottom","border-bottom-width"]}else{a=["border-left-width","padding-left","padding-right","border-right-width"]}return a.inject(e,function(f,g){var h=d(b,g);return h===null?f:f-parseInt(h,10)})+"px";default:return d(b,c)}});Element.Methods.readAttribute=Element.Methods.readAttribute.wrap(function(c,a,b){if(b==="title"){return a.title}return c(a,b)})}else{if(Prototype.Browser.IE){Element.Methods.getOffsetParent=Element.Methods.getOffsetParent.wrap(function(c,b){b=$(b);try{b.offsetParent}catch(f){return $(document.body)}var a=b.getStyle("position");if(a!=="static"){return c(b)}b.setStyle({position:"relative"});var d=c(b);b.setStyle({position:a});return d});$w("positionedOffset viewportOffset").each(function(a){Element.Methods[a]=Element.Methods[a].wrap(function(f,c){c=$(c);try{c.offsetParent}catch(h){return Element._returnOffset(0,0)}var b=c.getStyle("position");if(b!=="static"){return f(c)}var d=c.getOffsetParent();if(d&&d.getStyle("position")==="fixed"){d.setStyle({zoom:1})}c.setStyle({position:"relative"});var g=f(c);c.setStyle({position:b});return g})});Element.Methods.cumulativeOffset=Element.Methods.cumulativeOffset.wrap(function(b,a){try{a.offsetParent}catch(c){return Element._returnOffset(0,0)}return b(a)});Element.Methods.getStyle=function(a,b){a=$(a);b=(b=="float"||b=="cssFloat")?"styleFloat":b.camelize();var c=a.style[b];if(!c&&a.currentStyle){c=a.currentStyle[b]}if(b=="opacity"){if(c=(a.getStyle("filter")||"").match(/alpha\(opacity=(.*)\)/)){if(c[1]){return parseFloat(c[1])/100}}return 1}if(c=="auto"){if((b=="width"||b=="height")&&(a.getStyle("display")!="none")){return a["offset"+b.capitalize()]+"px"}return null}return c};Element.Methods.setOpacity=function(b,e){function f(g){return g.replace(/alpha\([^\)]*\)/gi,"")}b=$(b);var a=b.currentStyle;if((a&&!a.hasLayout)||(!a&&b.style.zoom=="normal")){b.style.zoom=1}var d=b.getStyle("filter"),c=b.style;if(e==1||e===""){(d=f(d))?c.filter=d:c.removeAttribute("filter");return b}else{if(e<0.00001){e=0}}c.filter=f(d)+"alpha(opacity="+(e*100)+")";return b};Element._attributeTranslations={read:{names:{"class":"className","for":"htmlFor"},values:{_getAttr:function(a,b){return a.getAttribute(b,2)},_getAttrNode:function(a,c){var b=a.getAttributeNode(c);return b?b.value:""},_getEv:function(a,b){b=a.getAttribute(b);return b?b.toString().slice(23,-2):null},_flag:function(a,b){return $(a).hasAttribute(b)?b:null},style:function(a){return a.style.cssText.toLowerCase()},title:function(a){return a.title}}}};Element._attributeTranslations.write={names:Object.extend({cellpadding:"cellPadding",cellspacing:"cellSpacing"},Element._attributeTranslations.read.names),values:{checked:function(a,b){a.checked=!!b},style:function(a,b){a.style.cssText=b?b:""}}};Element._attributeTranslations.has={};$w("colSpan rowSpan vAlign dateTime accessKey tabIndex encType maxLength readOnly longDesc frameBorder").each(function(a){Element._attributeTranslations.write.names[a.toLowerCase()]=a;Element._attributeTranslations.has[a.toLowerCase()]=a});(function(a){Object.extend(a,{href:a._getAttr,src:a._getAttr,type:a._getAttr,action:a._getAttrNode,disabled:a._flag,checked:a._flag,readonly:a._flag,multiple:a._flag,onload:a._getEv,onunload:a._getEv,onclick:a._getEv,ondblclick:a._getEv,onmousedown:a._getEv,onmouseup:a._getEv,onmouseover:a._getEv,onmousemove:a._getEv,onmouseout:a._getEv,onfocus:a._getEv,onblur:a._getEv,onkeypress:a._getEv,onkeydown:a._getEv,onkeyup:a._getEv,onsubmit:a._getEv,onreset:a._getEv,onselect:a._getEv,onchange:a._getEv})})(Element._attributeTranslations.read.values)}else{if(Prototype.Browser.Gecko&&/rv:1\.8\.0/.test(navigator.userAgent)){Element.Methods.setOpacity=function(a,b){a=$(a);a.style.opacity=(b==1)?0.999999:(b==="")?"":(b<0.00001)?0:b;return a}}else{if(Prototype.Browser.WebKit){Element.Methods.setOpacity=function(a,b){a=$(a);a.style.opacity=(b==1||b==="")?"":(b<0.00001)?0:b;if(b==1){if(a.tagName.toUpperCase()=="IMG"&&a.width){a.width++;a.width--}else{try{var d=document.createTextNode(" ");a.appendChild(d);a.removeChild(d)}catch(c){}}}return a};Element.Methods.cumulativeOffset=function(b){var a=0,c=0;do{a+=b.offsetTop||0;c+=b.offsetLeft||0;if(b.offsetParent==document.body){if(Element.getStyle(b,"position")=="absolute"){break}}b=b.offsetParent}while(b);return Element._returnOffset(c,a)}}}}}if(Prototype.Browser.IE||Prototype.Browser.Opera){Element.Methods.update=function(b,c){b=$(b);if(c&&c.toElement){c=c.toElement()}if(Object.isElement(c)){return b.update().insert(c)}c=Object.toHTML(c);var a=b.tagName.toUpperCase();if(a in Element._insertionTranslations.tags){$A(b.childNodes).each(function(d){b.removeChild(d)});Element._getContentFromAnonymousElement(a,c.stripScripts()).each(function(d){b.appendChild(d)})}else{b.innerHTML=c.stripScripts()}c.evalScripts.bind(c).defer();return b}}if("outerHTML"in document.createElement("div")){Element.Methods.replace=function(c,e){c=$(c);if(e&&e.toElement){e=e.toElement()}if(Object.isElement(e)){c.parentNode.replaceChild(e,c);return c}e=Object.toHTML(e);var d=c.parentNode,b=d.tagName.toUpperCase();if(Element._insertionTranslations.tags[b]){var f=c.next();var a=Element._getContentFromAnonymousElement(b,e.stripScripts());d.removeChild(c);if(f){a.each(function(g){d.insertBefore(g,f)})}else{a.each(function(g){d.appendChild(g)})}}else{c.outerHTML=e.stripScripts()}e.evalScripts.bind(e).defer();return c}}Element._returnOffset=function(b,c){var a=[b,c];a.left=b;a.top=c;return a};Element._getContentFromAnonymousElement=function(c,b){var d=new Element("div"),a=Element._insertionTranslations.tags[c];if(a){d.innerHTML=a[0]+b+a[1];a[2].times(function(){d=d.firstChild})}else{d.innerHTML=b}return $A(d.childNodes)};Element._insertionTranslations={before:function(a,b){a.parentNode.insertBefore(b,a)},top:function(a,b){a.insertBefore(b,a.firstChild)},bottom:function(a,b){a.appendChild(b)},after:function(a,b){a.parentNode.insertBefore(b,a.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(a,c){c=Element._attributeTranslations.has[c]||c;var b=$(a).getAttributeNode(c);return!!(b&&b.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 a={},b=Element.Methods.ByTag;var c=Object.extend(function(f){if(!f||f._extendedByPrototype||f.nodeType!=1||f==window){return f}var d=Object.clone(a),e=f.tagName.toUpperCase(),h,g;if(b[e]){Object.extend(d,b[e])}for(h in d){g=d[h];if(Object.isFunction(g)&&!(h in f)){f[h]=g.methodize()}}f._extendedByPrototype=Prototype.emptyFunction;return f},{refresh:function(){if(!Prototype.BrowserFeatures.ElementExtensions){Object.extend(a,Element.Methods);Object.extend(a,Element.Methods.Simulated)}}});c.refresh();return c})();Element.hasAttribute=function(a,b){if(a.hasAttribute){return a.hasAttribute(b)}return Element.Methods.Simulated.hasAttribute(a,b)};Element.addMethods=function(c){var h=Prototype.BrowserFeatures,d=Element.Methods.ByTag;if(!c){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 b=c;c=arguments[1]}if(!b){Object.extend(Element.Methods,c||{})}else{if(Object.isArray(b)){b.each(g)}else{g(b)}}function g(j){j=j.toUpperCase();if(!Element.Methods.ByTag[j]){Element.Methods.ByTag[j]={}}Object.extend(Element.Methods.ByTag[j],c)}function a(l,k,j){j=j||false;for(var n in l){var m=l[n];if(!Object.isFunction(m)){continue}if(!j||!(n in k)){k[n]=m.methodize()}}}function e(l){var j;var k={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(k[l]){j="HTML"+k[l]+"Element"}if(window[j]){return window[j]}j="HTML"+l+"Element";if(window[j]){return window[j]}j="HTML"+l.capitalize()+"Element";if(window[j]){return window[j]}window[j]={};window[j].prototype=document.createElement(l)["__proto__"];return window[j]}if(h.ElementExtensions){a(Element.Methods,HTMLElement.prototype);a(Element.Methods.Simulated,HTMLElement.prototype,true)}if(h.SpecificElementExtensions){for(var i in Element.Methods.ByTag){var f=e(i);if(Object.isUndefined(f)){continue}a(d[i],f.prototype)}}Object.extend(Element,Element.Methods);delete Element.ByTag;if(Element.extend.refresh){Element.extend.refresh()}Element.cache={}};document.viewport={getDimensions:function(){var a={},b=Prototype.Browser;$w("width height").each(function(e){var c=e.capitalize();if(b.WebKit&&!document.evaluate){a[e]=self["inner"+c]}else{if(b.Opera&&parseFloat(window.opera.version())<9.5){a[e]=document.body["client"+c]}else{a[e]=document.documentElement["client"+c]}}});return a},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)}};var Selector=Class.create({initialize:function(a){this.expression=a.strip();if(this.shouldUseSelectorsAPI()){this.mode="selectorsAPI"}else{if(this.shouldUseXPath()){this.mode="xpath";this.compileXPathMatcher()}else{this.mode="normal";this.compileMatcher()}}},shouldUseXPath:function(){if(!Prototype.BrowserFeatures.XPath){return false}var a=this.expression;if(Prototype.Browser.WebKit&&(a.include("-of-type")||a.include(":empty"))){return false}if((/(\[[\w-]*?:|:checked)/).test(a)){return false}return true},shouldUseSelectorsAPI:function(){if(!Prototype.BrowserFeatures.SelectorsAPI){return false}if(!Selector._div){Selector._div=new Element("div")}try{Selector._div.querySelector(this.expression)}catch(a){return false}return true},compileMatcher:function(){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 f=this.expression,g=Selector.patterns,b=Selector.xpath,d,a;if(Selector._cache[f]){this.xpath=Selector._cache[f];return}this.matcher=[".//*"];while(f&&d!=f&&(/\S/).test(f)){d=f;for(var c in g){if(a=f.match(g[c])){this.matcher.push(Object.isFunction(b[c])?b[c](a):new Template(b[c]).evaluate(a));f=f.replace(a[0],"");break}}}this.xpath=this.matcher.join("");Selector._cache[this.expression]=this.xpath},findElements:function(a){a=a||document;var c=this.expression,b;switch(this.mode){case"selectorsAPI":if(a!==document){var d=a.id,f=$(a).identify();c="#"+f+" "+c}b=$A(a.querySelectorAll(c)).map(Element.extend);a.id=d;return b;case"xpath":return document._getElementsByXPath(this.xpath,a);default:return this.matcher(a)}},match:function(j){this.tokens=[];var o=this.expression,a=Selector.patterns,f=Selector.assertions;var b,d,g;while(o&&b!==o&&(/\S/).test(o)){b=o;for(var k in a){d=a[k];if(g=o.match(d)){if(f[k]){this.tokens.push([k,Object.clone(g)]);o=o.replace(g[0],"")}else{return this.findElements(document).include(j)}}}}var n=true,c,l;for(var k=0,h;h=this.tokens[k];k++){c=h[0],l=h[1];if(!Selector.assertions[c](j,l)){n=false;break}}return n},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(a){if(a[1]=="*"){return""}return"[local-name()='"+a[1].toLowerCase()+"' or local-name()='"+a[1].toUpperCase()+"']"},className:"[contains(concat(' ', @class, ' '), ' #{1} ')]",id:"[@id='#{1}']",attrPresence:function(a){a[1]=a[1].toLowerCase();return new Template("[@#{1}]").evaluate(a)},attr:function(a){a[1]=a[1].toLowerCase();a[3]=a[5]||a[6];return new Template(Selector.xpath.operators[a[2]]).evaluate(a)},pseudo:function(a){var b=Selector.xpath.pseudos[a[1]];if(!b){return""}if(Object.isFunction(b)){return b(a)}return new Template(Selector.xpath.pseudos[a[1]]).evaluate(a)},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)]",checked:"[@checked]",disabled:"[(@disabled) and (@type!='hidden')]",enabled:"[not(@disabled) and (@type!='hidden')]",not:function(b){var j=b[6],h=Selector.patterns,a=Selector.xpath,f,c;var g=[];while(j&&f!=j&&(/\S/).test(j)){f=j;for(var d in h){if(b=j.match(h[d])){c=Object.isFunction(a[d])?a[d](b):new Template(a[d]).evaluate(b);g.push("("+c.substring(1,c.length-1)+")");j=j.replace(b[0],"");break}}}return"[not("+g.join(" and ")+")]"},"nth-child":function(a){return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ",a)},"nth-last-child":function(a){return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ",a)},"nth-of-type":function(a){return Selector.xpath.pseudos.nth("position() ",a)},"nth-last-of-type":function(a){return Selector.xpath.pseudos.nth("(last() + 1 - position()) ",a)},"first-of-type":function(a){a[6]="1";return Selector.xpath.pseudos["nth-of-type"](a)},"last-of-type":function(a){a[6]="1";return Selector.xpath.pseudos["nth-last-of-type"](a)},"only-of-type":function(a){var b=Selector.xpath.pseudos;return b["first-of-type"](a)+b["last-of-type"](a)},nth:function(g,e){var h,i=e[6],d;if(i=="even"){i="2n+0"}if(i=="odd"){i="2n+1"}if(h=i.match(/^(\d+)$/)){return"["+g+"= "+h[1]+"]"}if(h=i.match(/^(-?\d*)?n(([+-])(\d+))?/)){if(h[1]=="-"){h[1]=-1}var f=h[1]?Number(h[1]):1;var c=h[2]?Number(h[2]):0;d="[((#{fragment} - #{b}) mod #{a} = 0) and ((#{fragment} - #{b}) div #{a} >= 0)]";return new Template(d).evaluate({fragment:g,a:f,b:c})}}}},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(a){a[3]=(a[5]||a[6]);return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); c = false;').evaluate(a)},pseudo:function(a){if(a[6]){a[6]=a[6].replace(/"/g,'\\"')}return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(a)},descendant:'c = "descendant";',child:'c = "child";',adjacent:'c = "adjacent";',laterSibling:'c = "laterSibling";'},patterns:{laterSibling:/^\s*~\s*/,child:/^\s*>\s*/,adjacent:/^\s*\+\s*/,descendant:/^\s/,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]+:)?[\w]+)\]/,attr:/\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/},assertions:{tagName:function(a,b){return b[1].toUpperCase()==a.tagName.toUpperCase()},className:function(a,b){return Element.hasClassName(a,b[1])},id:function(a,b){return a.id===b[1]},attrPresence:function(a,b){return Element.hasAttribute(a,b[1])},attr:function(b,c){var a=Element.readAttribute(b,c[1]);return a&&Selector.operators[c[2]](a,c[5]||c[6])}},handlers:{concat:function(d,c){for(var e=0,f;f=c[e];e++){d.push(f)}return d},mark:function(a){var d=Prototype.emptyFunction;for(var b=0,c;c=a[b];b++){c._countedByPrototype=d}return a},unmark:function(a){for(var b=0,c;c=a[b];b++){c._countedByPrototype=undefined}return a},index:function(a,d,g){a._countedByPrototype=Prototype.emptyFunction;if(d){for(var b=a.childNodes,e=b.length-1,c=1;e>=0;e--){var f=b[e];if(f.nodeType==1&&(!g||f._countedByPrototype)){f.nodeIndex=c++}}}else{for(var e=0,c=1,b=a.childNodes;f=b[e];e++){if(f.nodeType==1&&(!g||f._countedByPrototype)){f.nodeIndex=c++}}}},unique:function(b){if(b.length==0){return b}var d=[],e;for(var c=0,a=b.length;c<a;c++){if(!(e=b[c])._countedByPrototype){e._countedByPrototype=Prototype.emptyFunction;d.push(Element.extend(e))}}return Selector.handlers.unmark(d)},descendant:function(a){var d=Selector.handlers;for(var c=0,b=[],e;e=a[c];c++){d.concat(b,e.getElementsByTagName("*"))}return b},child:function(a){var e=Selector.handlers;for(var d=0,c=[],f;f=a[d];d++){for(var b=0,g;g=f.childNodes[b];b++){if(g.nodeType==1&&g.tagName!="!"){c.push(g)}}}return c},adjacent:function(a){for(var c=0,b=[],e;e=a[c];c++){var d=this.nextElementSibling(e);if(d){b.push(d)}}return b},laterSibling:function(a){var d=Selector.handlers;for(var c=0,b=[],e;e=a[c];c++){d.concat(b,Element.nextSiblings(e))}return b},nextElementSibling:function(a){while(a=a.nextSibling){if(a.nodeType==1){return a}}return null},previousElementSibling:function(a){while(a=a.previousSibling){if(a.nodeType==1){return a}}return null},tagName:function(a,j,c,b){var k=c.toUpperCase();var e=[],g=Selector.handlers;if(a){if(b){if(b=="descendant"){for(var f=0,d;d=a[f];f++){g.concat(e,d.getElementsByTagName(c))}return e}else{a=this[b](a)}if(c=="*"){return a}}for(var f=0,d;d=a[f];f++){if(d.tagName.toUpperCase()===k){e.push(d)}}return e}else{return j.getElementsByTagName(c)}},id:function(b,a,j,f){var g=$(j),d=Selector.handlers;if(!g){return[]}if(!b&&a==document){return[g]}if(b){if(f){if(f=="child"){for(var c=0,e;e=b[c];c++){if(g.parentNode==e){return[g]}}}else{if(f=="descendant"){for(var c=0,e;e=b[c];c++){if(Element.descendantOf(g,e)){return[g]}}}else{if(f=="adjacent"){for(var c=0,e;e=b[c];c++){if(Selector.handlers.previousElementSibling(g)==e){return[g]}}}else{b=d[f](b)}}}}for(var c=0,e;e=b[c];c++){if(e==g){return[g]}}return[]}return(g&&Element.descendantOf(g,a))?[g]:[]},className:function(b,a,c,d){if(b&&d){b=this[d](b)}return Selector.handlers.byClassName(b,a,c)},byClassName:function(c,b,f){if(!c){c=Selector.handlers.descendant([b])}var h=" "+f+" ";for(var e=0,d=[],g,a;g=c[e];e++){a=g.className;if(a.length==0){continue}if(a==f||(" "+a+" ").include(h)){d.push(g)}}return d},attrPresence:function(c,b,a,g){if(!c){c=b.getElementsByTagName("*")}if(c&&g){c=this[g](c)}var e=[];for(var d=0,f;f=c[d];d++){if(Element.hasAttribute(f,a)){e.push(f)}}return e},attr:function(a,j,h,k,c,b){if(!a){a=j.getElementsByTagName("*")}if(a&&b){a=this[b](a)}var l=Selector.operators[c],f=[];for(var e=0,d;d=a[e];e++){var g=Element.readAttribute(d,h);if(g===null){continue}if(l(g,k)){f.push(d)}}return f},pseudo:function(b,c,e,a,d){if(b&&d){b=this[d](b)}if(!b){b=a.getElementsByTagName("*")}return Selector.pseudos[c](b,e,a)}},pseudos:{"first-child":function(b,f,a){for(var d=0,c=[],e;e=b[d];d++){if(Selector.handlers.previousElementSibling(e)){continue}c.push(e)}return c},"last-child":function(b,f,a){for(var d=0,c=[],e;e=b[d];d++){if(Selector.handlers.nextElementSibling(e)){continue}c.push(e)}return c},"only-child":function(b,g,a){var e=Selector.handlers;for(var d=0,c=[],f;f=b[d];d++){if(!e.previousElementSibling(f)&&!e.nextElementSibling(f)){c.push(f)}}return c},"nth-child":function(b,c,a){return Selector.pseudos.nth(b,c,a)},"nth-last-child":function(b,c,a){return Selector.pseudos.nth(b,c,a,true)},"nth-of-type":function(b,c,a){return Selector.pseudos.nth(b,c,a,false,true)},"nth-last-of-type":function(b,c,a){return Selector.pseudos.nth(b,c,a,true,true)},"first-of-type":function(b,c,a){return Selector.pseudos.nth(b,"1",a,false,true)},"last-of-type":function(b,c,a){return Selector.pseudos.nth(b,"1",a,true,true)},"only-of-type":function(b,d,a){var c=Selector.pseudos;return c["last-of-type"](c["first-of-type"](b,d,a),d,a)},getIndices:function(d,c,e){if(d==0){return c>0?[c]:[]}return $R(1,e).inject([],function(a,b){if(0==(b-c)%d&&(b-c)/d>=0){a.push(b)}return a})},nth:function(c,s,u,r,e){if(c.length==0){return[]}if(s=="even"){s="2n+0"}if(s=="odd"){s="2n+1"}var q=Selector.handlers,p=[],d=[],g;q.mark(c);for(var o=0,f;f=c[o];o++){if(!f.parentNode._countedByPrototype){q.index(f.parentNode,r,e);d.push(f.parentNode)}}if(s.match(/^\d+$/)){s=Number(s);for(var o=0,f;f=c[o];o++){if(f.nodeIndex==s){p.push(f)}}}else{if(g=s.match(/^(-?\d*)?n(([+-])(\d+))?/)){if(g[1]=="-"){g[1]=-1}var v=g[1]?Number(g[1]):1;var t=g[2]?Number(g[2]):0;var w=Selector.pseudos.getIndices(v,t,c.length);for(var o=0,f,k=w.length;f=c[o];o++){for(var n=0;n<k;n++){if(f.nodeIndex==w[n]){p.push(f)}}}}}q.unmark(c);q.unmark(d);return p},empty:function(b,f,a){for(var d=0,c=[],e;e=b[d];d++){if(e.tagName=="!"||e.firstChild){continue}c.push(e)}return c},not:function(a,d,k){var g=Selector.handlers,l,c;var j=new Selector(d).findElements(k);g.mark(j);for(var f=0,e=[],b;b=a[f];f++){if(!b._countedByPrototype){e.push(b)}}g.unmark(j);return e},enabled:function(b,f,a){for(var d=0,c=[],e;e=b[d];d++){if(!e.disabled&&(!e.type||e.type!=="hidden")){c.push(e)}}return c},disabled:function(b,f,a){for(var d=0,c=[],e;e=b[d];d++){if(e.disabled){c.push(e)}}return c},checked:function(b,f,a){for(var d=0,c=[],e;e=b[d];d++){if(e.checked){c.push(e)}}return c}},operators:{"=":function(b,a){return b==a},"!=":function(b,a){return b!=a},"^=":function(b,a){return b==a||b&&b.startsWith(a)},"$=":function(b,a){return b==a||b&&b.endsWith(a)},"*=":function(b,a){return b==a||b&&b.include(a)},"$=":function(b,a){return b.endsWith(a)},"*=":function(b,a){return b.include(a)},"~=":function(b,a){return(" "+b+" ").include(" "+a+" ")},"|=":function(b,a){return("-"+(b||"").toUpperCase()+"-").include("-"+(a||"").toUpperCase()+"-")}},split:function(b){var a=[];b.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/,function(c){a.push(c[1].strip())});return a},matchElements:function(f,g){var e=$$(g),d=Selector.handlers;d.mark(e);for(var c=0,b=[],a;a=f[c];c++){if(a._countedByPrototype){b.push(a)}}d.unmark(e);return b},findElement:function(b,c,a){if(Object.isNumber(c)){a=c;c=false}return Selector.matchElements(b,c||"*")[a||0]},findChildElements:function(e,g){g=Selector.split(g.join(","));var d=[],f=Selector.handlers;for(var c=0,b=g.length,a;c<b;c++){a=new Selector(g[c].strip());f.concat(d,a.findElements(e))}return(b>1)?f.unique(d):d}});if(Prototype.Browser.IE){Object.extend(Selector.handlers,{concat:function(d,c){for(var e=0,f;f=c[e];e++){if(f.tagName!=="!"){d.push(f)}}return d},unmark:function(a){for(var b=0,c;c=a[b];b++){c.removeAttribute("_countedByPrototype")}return a}})}function $$(){return Selector.findChildElements(document,$A(arguments))}var Form={reset:function(a){$(a).reset();return a},serializeElements:function(g,b){if(typeof b!="object"){b={hash:!!b}}else{if(Object.isUndefined(b.hash)){b.hash=true}}var c,f,a=false,e=b.submit;var d=g.inject({},function(h,i){if(!i.disabled&&i.name){c=i.name;f=$(i).getValue();if(f!=null&&i.type!="file"&&(i.type!="submit"||(!a&&e!==false&&(!e||c==e)&&(a=true)))){if(c in h){if(!Object.isArray(h[c])){h[c]=[h[c]]}h[c].push(f)}else{h[c]=f}}}return h});return b.hash?d:Object.toQueryString(d)}};Form.Methods={serialize:function(b,a){return Form.serializeElements(Form.getElements(b),a)},getElements:function(a){return $A($(a).getElementsByTagName("*")).inject([],function(b,c){if(Form.Element.Serializers[c.tagName.toLowerCase()]){b.push(Element.extend(c))}return b})},getInputs:function(g,c,d){g=$(g);var a=g.getElementsByTagName("input");if(!c&&!d){return $A(a).map(Element.extend)}for(var e=0,h=[],f=a.length;e<f;e++){var b=a[e];if((c&&b.type!=c)||(d&&b.name!=d)){continue}h.push(Element.extend(b))}return h},disable:function(a){a=$(a);Form.getElements(a).invoke("disable");return a},enable:function(a){a=$(a);Form.getElements(a).invoke("enable");return a},findFirstElement:function(b){var c=$(b).getElements().findAll(function(d){return"hidden"!=d.type&&!d.disabled});var a=c.findAll(function(d){return d.hasAttribute("tabIndex")&&d.tabIndex>=0}).sortBy(function(d){return d.tabIndex}).first();return a?a:c.find(function(d){return["input","select","textarea"].include(d.tagName.toLowerCase())})},focusFirstElement:function(a){a=$(a);a.findFirstElement().activate();return a},request:function(b,a){b=$(b),a=Object.clone(a||{});var d=a.parameters,c=b.readAttribute("action")||"";if(c.blank()){c=window.location.href}a.parameters=b.serialize(true);if(d){if(Object.isString(d)){d=d.toQueryParams()}Object.extend(a.parameters,d)}if(b.hasAttribute("method")&&!a.method){a.method=b.method}return new Ajax.Request(c,a)}};Form.Element={focus:function(a){$(a).focus();return a},select:function(a){$(a).select();return a}};Form.Element.Methods={serialize:function(a){a=$(a);if(!a.disabled&&a.name){var b=a.getValue();if(b!=undefined){var c={};c[a.name]=b;return Object.toQueryString(c)}}return""},getValue:function(a){a=$(a);var b=a.tagName.toLowerCase();return Form.Element.Serializers[b](a)},setValue:function(a,b){a=$(a);var c=a.tagName.toLowerCase();Form.Element.Serializers[c](a,b);return a},clear:function(a){$(a).value="";return a},present:function(a){return $(a).value!=""},activate:function(a){a=$(a);try{a.focus();if(a.select&&(a.tagName.toLowerCase()!="input"||!["button","reset","submit"].include(a.type))){a.select()}}catch(b){}return a},disable:function(a){a=$(a);a.disabled=true;return a},enable:function(a){a=$(a);a.disabled=false;return a}};var Field=Form.Element;var $F=Form.Element.Methods.getValue;Form.Element.Serializers={input:function(a,b){switch(a.type.toLowerCase()){case"checkbox":case"radio":return Form.Element.Serializers.inputSelector(a,b);default:return Form.Element.Serializers.textarea(a,b)}},inputSelector:function(a,b){if(Object.isUndefined(b)){return a.checked?a.value:null}else{a.checked=!!b}},textarea:function(a,b){if(Object.isUndefined(b)){return a.value}else{a.value=b}},select:function(c,f){if(Object.isUndefined(f)){return this[c.type=="select-one"?"selectOne":"selectMany"](c)}else{var b,d,g=!Object.isArray(f);for(var a=0,e=c.length;a<e;a++){b=c.options[a];d=this.optionValue(b);if(g){if(d==f){b.selected=true;return}}else{b.selected=f.include(d)}}}},selectOne:function(b){var a=b.selectedIndex;return a>=0?this.optionValue(b.options[a]):null},selectMany:function(d){var a,e=d.length;if(!e){return null}for(var c=0,a=[];c<e;c++){var b=d.options[c];if(b.selected){a.push(this.optionValue(b))}}return a},optionValue:function(a){return Element.extend(a).hasAttribute("value")?a.value:a.text}};Abstract.TimedObserver=Class.create(PeriodicalExecuter,{initialize:function($super,a,b,c){$super(c,b);this.element=$(a);this.lastValue=this.getValue()},execute:function(){var a=this.getValue();if(Object.isString(this.lastValue)&&Object.isString(a)?this.lastValue!=a:String(this.lastValue)!=String(a)){this.callback(this.element,a);this.lastValue=a}}});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(a,b){this.element=$(a);this.callback=b;this.lastValue=this.getValue();if(this.element.tagName.toLowerCase()=="form"){this.registerFormCallbacks()}else{this.registerCallback(this.element)}},onElementEvent:function(){var a=this.getValue();if(this.lastValue!=a){this.callback(this.element,a);this.lastValue=a}},registerFormCallbacks:function(){Form.getElements(this.element).each(this.registerCallback,this)},registerCallback:function(a){if(a.type){switch(a.type.toLowerCase()){case"checkbox":case"radio":Event.observe(a,"click",this.onElementEvent.bind(this));break;default:Event.observe(a,"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(b){var a;switch(b.type){case"mouseover":a=b.fromElement;break;case"mouseout":a=b.toElement;break;default:return null}return Element.extend(a)}});Event.Methods=(function(){var a;if(Prototype.Browser.IE){var b={0:1,1:4,2:2};a=function(d,c){return d.button==b[c]}}else{if(Prototype.Browser.WebKit){a=function(d,c){switch(c){case 0:return d.which==1&&!d.metaKey;case 1:return d.which==1&&d.metaKey;default:return false}}}else{a=function(d,c){return d.which?(d.which===c+1):(d.button===c)}}}return{isLeftClick:function(c){return a(c,0)},isMiddleClick:function(c){return a(c,1)},isRightClick:function(c){return a(c,2)},element:function(e){e=Event.extend(e);var d=e.target,c=e.type,f=e.currentTarget;if(f&&f.tagName){if(c==="load"||c==="error"||(c==="click"&&f.tagName.toLowerCase()==="input"&&f.type==="radio")){d=f}}if(d.nodeType==Node.TEXT_NODE){d=d.parentNode}return Element.extend(d)},findElement:function(d,f){var c=Event.element(d);if(!f){return c}var e=[c].concat(c.ancestors());return Selector.findElement(e,f,0)},pointer:function(e){var d=document.documentElement,c=document.body||{scrollLeft:0,scrollTop:0};return{x:e.pageX||(e.clientX+(d.scrollLeft||c.scrollLeft)-(d.clientLeft||0)),y:e.pageY||(e.clientY+(d.scrollTop||c.scrollTop)-(d.clientTop||0))}},pointerX:function(c){return Event.pointer(c).x},pointerY:function(c){return Event.pointer(c).y},stop:function(c){Event.extend(c);c.preventDefault();c.stopPropagation();c.stopped=true}}})();Event.extend=(function(){var a=Object.keys(Event.Methods).inject({},function(b,c){b[c]=Event.Methods[c].methodize();return b});if(Prototype.Browser.IE){Object.extend(a,{stopPropagation:function(){this.cancelBubble=true},preventDefault:function(){this.returnValue=false},inspect:function(){return"[object Event]"}});return function(b){if(!b){return false}if(b._extendedByPrototype){return b}b._extendedByPrototype=Prototype.emptyFunction;var c=Event.pointer(b);Object.extend(b,{target:b.srcElement,relatedTarget:Event.relatedTarget(b),pageX:c.x,pageY:c.y});return Object.extend(b,a)}}else{Event.prototype=Event.prototype||document.createEvent("HTMLEvents")["__proto__"];Object.extend(Event.prototype,a);return Prototype.K}})();Object.extend(Event,(function(){var b=Event.cache;function c(j){if(j._prototypeEventID){return j._prototypeEventID[0]}arguments.callee.id=arguments.callee.id||1;return j._prototypeEventID=[++arguments.callee.id]}function g(j){if(j&&j.include(":")){return"dataavailable"}return j}function a(j){return b[j]=b[j]||{}}function f(l,j){var k=a(l);return k[j]=k[j]||[]}function h(k,j,l){var o=c(k);var n=f(o,j);if(n.pluck("handler").include(l)){return false}var m=function(p){if(!Event||!Event.extend||(p.eventName&&p.eventName!=j)){return false}Event.extend(p);l.call(k,p)};m.handler=l;n.push(m);return m}function i(m,j,k){var l=f(m,j);return l.find(function(n){return n.handler==k})}function d(m,j,k){var l=a(m);if(!l[j]){return false}l[j]=l[j].without(i(m,j,k))}function e(){for(var k in b){for(var j in b[k]){b[k][j]=null}}}if(window.attachEvent){window.attachEvent("onunload",e)}if(Prototype.Browser.WebKit){window.addEventListener("unload",Prototype.emptyFunction,false)}return{observe:function(l,j,m){l=$(l);var k=g(j);var n=h(l,j,m);if(!n){return l}if(l.addEventListener){l.addEventListener(k,n,false)}else{l.attachEvent("on"+k,n)}return l},stopObserving:function(l,j,m){l=$(l);var o=c(l),k=g(j);if(!m&&j){f(o,j).each(function(p){l.stopObserving(j,p.handler)});return l}else{if(!j){Object.keys(a(o)).each(function(p){l.stopObserving(p)});return l}}var n=i(o,j,m);if(!n){return l}if(l.removeEventListener){l.removeEventListener(k,n,false)}else{l.detachEvent("on"+k,n)}d(o,j,m);return l},fire:function(l,k,j){l=$(l);if(l==document&&document.createEvent&&!l.dispatchEvent){l=document.documentElement}var m;if(document.createEvent){m=document.createEvent("HTMLEvents");m.initEvent("dataavailable",true,true)}else{m=document.createEventObject();m.eventType="ondataavailable"}m.eventName=k;m.memo=j||{};if(document.createEvent){l.dispatchEvent(m)}else{l.fireEvent(m.eventType,m)}return Event.extend(m)}}})());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(){var b;function a(){if(document.loaded){return}if(b){window.clearInterval(b)}document.fire("dom:loaded");document.loaded=true}if(document.addEventListener){if(Prototype.Browser.WebKit){b=window.setInterval(function(){if(/loaded|complete/.test(document.readyState)){a()}},0);Event.observe(window,"load",a)}else{document.addEventListener("DOMContentLoaded",a,false)}}else{document.write("<script id=__onDOMContentLoaded defer src=//:><\/script>");$("__onDOMContentLoaded").onreadystatechange=function(){if(this.readyState=="complete"){this.onreadystatechange=null;a()}}}})();Hash.toQueryString=Object.toQueryString;var Toggle={display:Element.toggle};Element.Methods.childOf=Element.Methods.descendantOf;var Insertion={Before:function(a,b){return Element.insert(a,{before:b})},Top:function(a,b){return Element.insert(a,{top:b})},Bottom:function(a,b){return Element.insert(a,{bottom:b})},After:function(a,b){return Element.insert(a,{after:b})}};var $continue=new Error('"throw $continue" is deprecated, use "return" instead');var Position={includeScrollOffsets:false,prepare:function(){this.deltaX=window.pageXOffset||document.documentElement.scrollLeft||document.body.scrollLeft||0;this.deltaY=window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop||0},within:function(b,a,c){if(this.includeScrollOffsets){return this.withinIncludingScrolloffsets(b,a,c)}this.xcomp=a;this.ycomp=c;this.offset=Element.cumulativeOffset(b);return(c>=this.offset[1]&&c<this.offset[1]+b.offsetHeight&&a>=this.offset[0]&&a<this.offset[0]+b.offsetWidth)},withinIncludingScrolloffsets:function(b,a,d){var c=Element.cumulativeScrollOffset(b);this.xcomp=a+c[0]-this.deltaX;this.ycomp=d+c[1]-this.deltaY;this.offset=Element.cumulativeOffset(b);return(this.ycomp>=this.offset[1]&&this.ycomp<this.offset[1]+b.offsetHeight&&this.xcomp>=this.offset[0]&&this.xcomp<this.offset[0]+b.offsetWidth)},overlap:function(b,a){if(!b){return 0}if(b=="vertical"){return((this.offset[1]+a.offsetHeight)-this.ycomp)/a.offsetHeight}if(b=="horizontal"){return((this.offset[0]+a.offsetWidth)-this.xcomp)/a.offsetWidth}},cumulativeOffset:Element.Methods.cumulativeOffset,positionedOffset:Element.Methods.positionedOffset,absolutize:function(a){Position.prepare();return Element.absolutize(a)},relativize:function(a){Position.prepare();return Element.relativize(a)},realOffset:Element.Methods.cumulativeScrollOffset,offsetParent:Element.Methods.getOffsetParent,page:Element.Methods.viewportOffset,clone:function(b,c,a){a=a||{};return Element.clonePosition(c,b,a)}};if(!document.getElementsByClassName){document.getElementsByClassName=function(b){function a(c){return c.blank()?null:"[contains(concat(' ', @class, ' '), ' "+c+" ')]"}b.getElementsByClassName=Prototype.BrowserFeatures.XPath?function(c,e){e=e.toString().strip();var d=/\s/.test(e)?$w(e).map(a).join(""):a(e);return d?document._getElementsByXPath(".//*"+d,c):[]}:function(e,f){f=f.toString().strip();var g=[],h=(/\s/.test(f)?$w(f):null);if(!h&&!f){return g}var c=$(e).getElementsByTagName("*");f=" "+f+" ";for(var d=0,k,j;k=c[d];d++){if(k.className&&(j=" "+k.className+" ")&&(j.include(f)||(h&&h.all(function(i){return!i.toString().blank()&&j.include(" "+i+" ")})))){g.push(Element.extend(k))}}return g};return function(d,c){return $(c||document.body).getElementsByClassName(d)}}(Element.Methods)}Element.ClassNames=Class.create();Element.ClassNames.prototype={initialize:function(a){this.element=$(a)},_each:function(a){this.element.className.split(/\s+/).select(function(b){return b.length>0})._each(a)},set:function(a){this.element.className=a},add:function(a){if(this.include(a)){return}this.set($A(this).concat(a).join(" "))},remove:function(a){if(!this.include(a)){return}this.set($A(this).without(a).join(" "))},toString:function(){return $A(this).join(" ")}};Object.extend(Element.ClassNames.prototype,Enumerable);Element.addMethods();
\ No newline at end of file
diff --git a/js/src/prototype.js b/js/src/prototype.js
index 0bb8807..a7fd383 100644
--- a/js/src/prototype.js
+++ b/js/src/prototype.js
@@ -1,4 +1,4 @@
-/*  Prototype JavaScript framework, version 1.6.0.2
+/*  Prototype JavaScript framework, version 1.6.0.3
  *  (c) 2005-2008 Sam Stephenson
  *
  *  Prototype is freely distributable under the terms of an MIT-style license.
@@ -7,23 +7,26 @@
  *--------------------------------------------------------------------------*/
 
 var Prototype = {
-  Version: '1.6.0.2',
+  Version: '1.6.0.3',
 
   Browser: {
-    IE:     !!(window.attachEvent && !window.opera),
-    Opera:  !!window.opera,
+    IE:     !!(window.attachEvent &&
+      navigator.userAgent.indexOf('Opera') === -1),
+    Opera:  navigator.userAgent.indexOf('Opera') > -1,
     WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
-    Gecko:  navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1,
+    Gecko:  navigator.userAgent.indexOf('Gecko') > -1 &&
+      navigator.userAgent.indexOf('KHTML') === -1,
     MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/)
   },
 
   BrowserFeatures: {
     XPath: !!document.evaluate,
+    SelectorsAPI: !!document.querySelector,
     ElementExtensions: !!window.HTMLElement,
     SpecificElementExtensions:
-      document.createElement('div').__proto__ &&
-      document.createElement('div').__proto__ !==
-        document.createElement('form').__proto__
+      document.createElement('div')['__proto__'] &&
+      document.createElement('div')['__proto__'] !==
+        document.createElement('form')['__proto__']
   },
 
   ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
@@ -83,12 +86,13 @@ Class.Methods = {
       var property = properties[i], value = source[property];
       if (ancestor && Object.isFunction(value) &&
           value.argumentNames().first() == "$super") {
-        var method = value, value = Object.extend((function(m) {
+        var method = value;
+        value = (function(m) {
           return function() { return ancestor[m].apply(this, arguments) };
-        })(property).wrap(method), {
-          valueOf:  function() { return method },
-          toString: function() { return method.toString() }
-        });
+        })(property).wrap(method);
+
+        value.valueOf = method.valueOf.bind(method);
+        value.toString = method.toString.bind(method);
       }
       this.prototype[property] = value;
     }
@@ -167,7 +171,7 @@ Object.extend(Object, {
   },
 
   isElement: function(object) {
-    return object && object.nodeType == 1;
+    return !!(object && object.nodeType == 1);
   },
 
   isArray: function(object) {
@@ -198,7 +202,8 @@ Object.extend(Object, {
 
 Object.extend(Function.prototype, {
   argumentNames: function() {
-    var names = this.toString().match(/^[\s\(]*function[^(]*\((.*?)\)/)[1].split(",").invoke("strip");
+    var names = this.toString().match(/^[\s\(]*function[^(]*\(([^\)]*)\)/)[1]
+      .replace(/\s+/g, '').split(',');
     return names.length == 1 && !names[0] ? [] : names;
   },
 
@@ -232,6 +237,11 @@ Object.extend(Function.prototype, {
     }, timeout);
   },
 
+  defer: function() {
+    var args = [0.01].concat($A(arguments));
+    return this.delay.apply(this, args);
+  },
+
   wrap: function(wrapper) {
     var __method = this;
     return function() {
@@ -248,8 +258,6 @@ Object.extend(Function.prototype, {
   }
 });
 
-Function.prototype.defer = Function.prototype.delay.curry(0.01);
-
 Date.prototype.toJSON = function() {
   return '"' + this.getUTCFullYear() + '-' +
     (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
@@ -530,7 +538,7 @@ if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.proto
     return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
   },
   unescapeHTML: function() {
-    return this.replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
+    return this.stripTags().replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
   }
 });
 
@@ -547,7 +555,7 @@ Object.extend(String.prototype.escapeHTML, {
   text: document.createTextNode('')
 });
 
-with (String.prototype.escapeHTML) div.appendChild(text);
+String.prototype.escapeHTML.div.appendChild(String.prototype.escapeHTML.text);
 
 var Template = Class.create({
   initialize: function(template, pattern) {
@@ -589,10 +597,9 @@ var $break = { };
 var Enumerable = {
   each: function(iterator, context) {
     var index = 0;
-    iterator = iterator.bind(context);
     try {
       this._each(function(value) {
-        iterator(value, index++);
+        iterator.call(context, value, index++);
       });
     } catch (e) {
       if (e != $break) throw e;
@@ -601,47 +608,46 @@ var Enumerable = {
   },
 
   eachSlice: function(number, iterator, context) {
-    iterator = iterator ? iterator.bind(context) : Prototype.K;
     var index = -number, slices = [], array = this.toArray();
+    if (number < 1) return array;
     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;
+    iterator = iterator || Prototype.K;
     var result = true;
     this.each(function(value, index) {
-      result = result && !!iterator(value, index);
+      result = result && !!iterator.call(context, value, index);
       if (!result) throw $break;
     });
     return result;
   },
 
   any: function(iterator, context) {
-    iterator = iterator ? iterator.bind(context) : Prototype.K;
+    iterator = iterator || Prototype.K;
     var result = false;
     this.each(function(value, index) {
-      if (result = !!iterator(value, index))
+      if (result = !!iterator.call(context, value, index))
         throw $break;
     });
     return result;
   },
 
   collect: function(iterator, context) {
-    iterator = iterator ? iterator.bind(context) : Prototype.K;
+    iterator = iterator || Prototype.K;
     var results = [];
     this.each(function(value, index) {
-      results.push(iterator(value, index));
+      results.push(iterator.call(context, value, index));
     });
     return results;
   },
 
   detect: function(iterator, context) {
-    iterator = iterator.bind(context);
     var result;
     this.each(function(value, index) {
-      if (iterator(value, index)) {
+      if (iterator.call(context, value, index)) {
         result = value;
         throw $break;
       }
@@ -650,17 +656,16 @@ var Enumerable = {
   },
 
   findAll: function(iterator, context) {
-    iterator = iterator.bind(context);
     var results = [];
     this.each(function(value, index) {
-      if (iterator(value, index))
+      if (iterator.call(context, value, index))
         results.push(value);
     });
     return results;
   },
 
   grep: function(filter, iterator, context) {
-    iterator = iterator ? iterator.bind(context) : Prototype.K;
+    iterator = iterator || Prototype.K;
     var results = [];
 
     if (Object.isString(filter))
@@ -668,7 +673,7 @@ var Enumerable = {
 
     this.each(function(value, index) {
       if (filter.match(value))
-        results.push(iterator(value, index));
+        results.push(iterator.call(context, value, index));
     });
     return results;
   },
@@ -696,9 +701,8 @@ var Enumerable = {
   },
 
   inject: function(memo, iterator, context) {
-    iterator = iterator.bind(context);
     this.each(function(value, index) {
-      memo = iterator(memo, value, index);
+      memo = iterator.call(context, memo, value, index);
     });
     return memo;
   },
@@ -711,10 +715,10 @@ var Enumerable = {
   },
 
   max: function(iterator, context) {
-    iterator = iterator ? iterator.bind(context) : Prototype.K;
+    iterator = iterator || Prototype.K;
     var result;
     this.each(function(value, index) {
-      value = iterator(value, index);
+      value = iterator.call(context, value, index);
       if (result == null || value >= result)
         result = value;
     });
@@ -722,10 +726,10 @@ var Enumerable = {
   },
 
   min: function(iterator, context) {
-    iterator = iterator ? iterator.bind(context) : Prototype.K;
+    iterator = iterator || Prototype.K;
     var result;
     this.each(function(value, index) {
-      value = iterator(value, index);
+      value = iterator.call(context, value, index);
       if (result == null || value < result)
         result = value;
     });
@@ -733,10 +737,10 @@ var Enumerable = {
   },
 
   partition: function(iterator, context) {
-    iterator = iterator ? iterator.bind(context) : Prototype.K;
+    iterator = iterator || Prototype.K;
     var trues = [], falses = [];
     this.each(function(value, index) {
-      (iterator(value, index) ?
+      (iterator.call(context, value, index) ?
         trues : falses).push(value);
     });
     return [trues, falses];
@@ -751,19 +755,20 @@ var Enumerable = {
   },
 
   reject: function(iterator, context) {
-    iterator = iterator.bind(context);
     var results = [];
     this.each(function(value, index) {
-      if (!iterator(value, index))
+      if (!iterator.call(context, 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)};
+      return {
+        value: value,
+        criteria: iterator.call(context, value, index)
+      };
     }).sort(function(left, right) {
       var a = left.criteria, b = right.criteria;
       return a < b ? -1 : a > b ? 1 : 0;
@@ -815,8 +820,12 @@ function $A(iterable) {
 if (Prototype.Browser.WebKit) {
   $A = function(iterable) {
     if (!iterable) return [];
-    if (!(Object.isFunction(iterable) && iterable == '[object NodeList]') &&
-        iterable.toArray) return iterable.toArray();
+    // In Safari, only use the `toArray` method if it's not a NodeList.
+    // A NodeList is a function, has an function `item` property, and a numeric
+    // `length` property. Adapted from Google Doctype.
+    if (!(typeof iterable === 'function' && typeof iterable.length ===
+        'number' && typeof iterable.item === 'function') && iterable.toArray)
+      return iterable.toArray();
     var length = iterable.length || 0, results = new Array(length);
     while (length--) results[length] = iterable[length];
     return results;
@@ -963,8 +972,8 @@ Object.extend(Number.prototype, {
     return this + 1;
   },
 
-  times: function(iterator) {
-    $R(0, this, true).each(iterator);
+  times: function(iterator, context) {
+    $R(0, this, true).each(iterator, context);
     return this;
   },
 
@@ -1011,7 +1020,9 @@ var Hash = Class.create(Enumerable, (function() {
     },
 
     get: function(key) {
-      return this._object[key];
+      // simulating poorly supported hasOwnProperty
+      if (this._object[key] !== Object.prototype[key])
+        return this._object[key];
     },
 
     unset: function(key) {
@@ -1051,14 +1062,14 @@ var Hash = Class.create(Enumerable, (function() {
     },
 
     toQueryString: function() {
-      return this.map(function(pair) {
+      return this.inject([], function(results, 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);
+            return results.concat(values.map(toQueryPair.curry(key)));
+        } else results.push(toQueryPair(key, values));
+        return results;
       }).join('&');
     },
 
@@ -1558,6 +1569,7 @@ if (!Node.ELEMENT_NODE) {
     return Element.writeAttribute(cache[tagName].cloneNode(false), attributes);
   };
   Object.extend(this.Element, element || { });
+  if (element) this.Element.prototype = element.prototype;
 }).call(window);
 
 Element.cache = { };
@@ -1574,12 +1586,14 @@ Element.Methods = {
   },
 
   hide: function(element) {
-    $(element).style.display = 'none';
+    element = $(element);
+    element.style.display = 'none';
     return element;
   },
 
   show: function(element) {
-    $(element).style.display = '';
+    element = $(element);
+    element.style.display = '';
     return element;
   },
 
@@ -1733,7 +1747,7 @@ Element.Methods = {
     element = $(element);
     if (arguments.length == 1) return element.firstDescendant();
     return Object.isNumber(expression) ? element.descendants()[expression] :
-      element.select(expression)[index || 0];
+      Element.select(element, expression)[index || 0];
   },
 
   previous: function(element, expression, index) {
@@ -1863,24 +1877,16 @@ Element.Methods = {
 
   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);
-    }
+    if (ancestor.contains)
+      return ancestor.contains(element) && ancestor !== element;
 
     while (element = element.parentNode)
-      if (element == originalAncestor) return true;
+      if (element == ancestor) return true;
+
     return false;
   },
 
@@ -1895,7 +1901,7 @@ Element.Methods = {
     element = $(element);
     style = style == 'float' ? 'cssFloat' : style.camelize();
     var value = element.style[style];
-    if (!value) {
+    if (!value || value == 'auto') {
       var css = document.defaultView.getComputedStyle(element, null);
       value = css ? css[style] : null;
     }
@@ -1934,7 +1940,7 @@ Element.Methods = {
 
   getDimensions: function(element) {
     element = $(element);
-    var display = $(element).getStyle('display');
+    var display = element.getStyle('display');
     if (display != 'none' && display != null) // Safari bug
       return {width: element.offsetWidth, height: element.offsetHeight};
 
@@ -1963,7 +1969,7 @@ Element.Methods = {
       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) {
+      if (Prototype.Browser.Opera) {
         element.style.top = 0;
         element.style.left = 0;
       }
@@ -2018,7 +2024,7 @@ Element.Methods = {
       valueL += element.offsetLeft || 0;
       element = element.offsetParent;
       if (element) {
-        if (element.tagName == 'BODY') break;
+        if (element.tagName.toUpperCase() == 'BODY') break;
         var p = Element.getStyle(element, 'position');
         if (p !== 'static') break;
       }
@@ -2028,7 +2034,7 @@ Element.Methods = {
 
   absolutize: function(element) {
     element = $(element);
-    if (element.getStyle('position') == 'absolute') return;
+    if (element.getStyle('position') == 'absolute') return element;
     // Position.prepare(); // To be done manually by Scripty when it needs it.
 
     var offsets = element.positionedOffset();
@@ -2052,7 +2058,7 @@ Element.Methods = {
 
   relativize: function(element) {
     element = $(element);
-    if (element.getStyle('position') == 'relative') return;
+    if (element.getStyle('position') == 'relative') return element;
     // Position.prepare(); // To be done manually by Scripty when it needs it.
 
     element.style.position = 'relative';
@@ -2077,15 +2083,15 @@ Element.Methods = {
   },
 
   getOffsetParent: function(element) {
-    element = $(element);  
-    var op = element.offsetParent, body = document.body, docEl = document.documentElement;  
-
-    /* IE with strict doctype may try to return documentElement as offsetParent  
-       on relatively positioned elements, we will return body instead */  
-    if (op && op !== docEl) return $(op);  
-    if (op === docEl || element === docEl || element === body) return $(body);  
-         
-    while ((element = element.parentNode) && element !== body)  
+    element = $(element);
+    var op = element.offsetParent, body = document.body, docEl = document.documentElement;
+
+    /* IE with strict doctype may try to return documentElement as offsetParent
+       on relatively positioned elements, we will return body instead */
+    if (op && op !== docEl) return $(op);
+    if (op === docEl || element === docEl || element === body) return $(body);
+
+    while ((element = element.parentNode) && element !== body)
       if (Element.getStyle(element, 'position') != 'static')
         return $(element);
 
@@ -2103,7 +2109,7 @@ Element.Methods = {
 
     element = forElement;
     do {
-      if (!Prototype.Browser.Opera || element.tagName == 'BODY') {
+      if (!Prototype.Browser.Opera || (element.tagName && (element.tagName.toUpperCase() == 'BODY'))) {
         valueT -= element.scrollTop  || 0;
         valueL -= element.scrollLeft || 0;
       }
@@ -2218,6 +2224,9 @@ else if (Prototype.Browser.IE) {
   Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap(
     function(proceed, element) {
       element = $(element);
+      // IE throws an error if element is not in document
+      try { element.offsetParent }
+      catch(e) { return $(document.body) }
       var position = element.getStyle('position');
       if (position !== 'static') return proceed(element);
       element.setStyle({ position: 'relative' });
@@ -2231,6 +2240,8 @@ else if (Prototype.Browser.IE) {
     Element.Methods[method] = Element.Methods[method].wrap(
       function(proceed, element) {
         element = $(element);
+        try { element.offsetParent }
+        catch(e) { return Element._returnOffset(0,0) }
         var position = element.getStyle('position');
         if (position !== 'static') return proceed(element);
         // Trigger hasLayout on the offset parent so that IE6 reports
@@ -2246,6 +2257,14 @@ else if (Prototype.Browser.IE) {
     );
   });
 
+  Element.Methods.cumulativeOffset = Element.Methods.cumulativeOffset.wrap(
+    function(proceed, element) {
+      try { element.offsetParent }
+      catch(e) { return Element._returnOffset(0,0) }
+      return proceed(element);
+    }
+  );
+
   Element.Methods.getStyle = function(element, style) {
     element = $(element);
     style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
@@ -2337,7 +2356,7 @@ else if (Prototype.Browser.IE) {
   Element._attributeTranslations.has = {};
 
   $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +
-      'encType maxLength readOnly longDesc').each(function(attr) {
+      'encType maxLength readOnly longDesc frameBorder').each(function(attr) {
     Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;
     Element._attributeTranslations.has[attr.toLowerCase()] = attr;
   });
@@ -2390,7 +2409,7 @@ else if (Prototype.Browser.WebKit) {
       (value < 0.00001) ? 0 : value;
 
     if (value == 1)
-      if(element.tagName == 'IMG' && element.width) {
+      if(element.tagName.toUpperCase() == 'IMG' && element.width) {
         element.width++; element.width--;
       } else try {
         var n = document.createTextNode(' ');
@@ -2521,7 +2540,7 @@ Element.Methods.Simulated = {
   hasAttribute: function(element, attribute) {
     attribute = Element._attributeTranslations.has[attribute] || attribute;
     var node = $(element).getAttributeNode(attribute);
-    return node && node.specified;
+    return !!(node && node.specified);
   }
 };
 
@@ -2530,9 +2549,9 @@ Element.Methods.ByTag = { };
 Object.extend(Element, Element.Methods);
 
 if (!Prototype.BrowserFeatures.ElementExtensions &&
-    document.createElement('div').__proto__) {
+    document.createElement('div')['__proto__']) {
   window.HTMLElement = { };
-  window.HTMLElement.prototype = document.createElement('div').__proto__;
+  window.HTMLElement.prototype = document.createElement('div')['__proto__'];
   Prototype.BrowserFeatures.ElementExtensions = true;
 }
 
@@ -2547,7 +2566,7 @@ Element.extend = (function() {
         element.nodeType != 1 || element == window) return element;
 
     var methods = Object.clone(Methods),
-      tagName = element.tagName, property, value;
+      tagName = element.tagName.toUpperCase(), property, value;
 
     // extend methods for specific tags
     if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);
@@ -2643,7 +2662,7 @@ Element.addMethods = function(methods) {
     if (window[klass]) return window[klass];
 
     window[klass] = { };
-    window[klass].prototype = document.createElement(tagName).__proto__;
+    window[klass].prototype = document.createElement(tagName)['__proto__'];
     return window[klass];
   }
 
@@ -2669,12 +2688,18 @@ Element.addMethods = function(methods) {
 
 document.viewport = {
   getDimensions: function() {
-    var dimensions = { };
-    var B = Prototype.Browser;
+    var dimensions = { }, 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];
+      if (B.WebKit && !document.evaluate) {
+        // Safari <3.0 needs self.innerWidth/Height
+        dimensions[d] = self['inner' + D];
+      } else if (B.Opera && parseFloat(window.opera.version()) < 9.5) {
+        // Opera <9.5 needs document.body.clientWidth/Height
+        dimensions[d] = document.body['client' + D]
+      } else {
+        dimensions[d] = document.documentElement['client' + D];
+      }
     });
     return dimensions;
   },
@@ -2693,14 +2718,24 @@ document.viewport = {
       window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop);
   }
 };
-/* Portions of the Selector class are derived from Jack Slocum’s DomQuery,
+/* 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();
+
+    if (this.shouldUseSelectorsAPI()) {
+      this.mode = 'selectorsAPI';
+    } else if (this.shouldUseXPath()) {
+      this.mode = 'xpath';
+      this.compileXPathMatcher();
+    } else {
+      this.mode = "normal";
+      this.compileMatcher();
+    }
+
   },
 
   shouldUseXPath: function() {
@@ -2715,16 +2750,29 @@ var Selector = Class.create({
 
     // XPath can't do namespaced attributes, nor can it read
     // the "checked" property from DOM nodes
-    if ((/(\[[\w-]*?:|:checked)/).test(this.expression))
+    if ((/(\[[\w-]*?:|:checked)/).test(e))
       return false;
 
     return true;
   },
 
-  compileMatcher: function() {
-    if (this.shouldUseXPath())
-      return this.compileXPathMatcher();
+  shouldUseSelectorsAPI: function() {
+    if (!Prototype.BrowserFeatures.SelectorsAPI) return false;
+
+    if (!Selector._div) Selector._div = new Element('div');
+
+    // Make sure the browser treats the selector as valid. Test on an
+    // isolated element to minimize cost of this check.
+    try {
+      Selector._div.querySelector(this.expression);
+    } catch(e) {
+      return false;
+    }
+
+    return true;
+  },
 
+  compileMatcher: function() {
     var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
         c = Selector.criteria, le, p, m;
 
@@ -2742,7 +2790,7 @@ var Selector = Class.create({
         p = ps[i];
         if (m = e.match(p)) {
           this.matcher.push(Object.isFunction(c[i]) ? c[i](m) :
-    	      new Template(c[i]).evaluate(m));
+            new Template(c[i]).evaluate(m));
           e = e.replace(m[0], '');
           break;
         }
@@ -2781,8 +2829,27 @@ var Selector = Class.create({
 
   findElements: function(root) {
     root = root || document;
-    if (this.xpath) return document._getElementsByXPath(this.xpath, root);
-    return this.matcher(root);
+    var e = this.expression, results;
+
+    switch (this.mode) {
+      case 'selectorsAPI':
+        // querySelectorAll queries document-wide, then filters to descendants
+        // of the context element. That's not what we want.
+        // Add an explicit context to the selector if necessary.
+        if (root !== document) {
+          var oldId = root.id, id = $(root).identify();
+          e = "#" + id + " " + e;
+        }
+
+        results = $A(root.querySelectorAll(e)).map(Element.extend);
+        root.id = oldId;
+
+        return results;
+      case 'xpath':
+        return document._getElementsByXPath(this.xpath, root);
+      default:
+       return this.matcher(root);
+    }
   },
 
   match: function(element) {
@@ -2873,10 +2940,10 @@ Object.extend(Selector, {
       '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', '') = '')]",
+      'empty':       "[count(*) = 0 and (count(text()) = 0)]",
       'checked':     "[@checked]",
-      'disabled':    "[@disabled]",
-      'enabled':     "[not(@disabled)]",
+      'disabled':    "[(@disabled) and (@type!='hidden')]",
+      'enabled':     "[not(@disabled) and (@type!='hidden')]",
       'not': function(m) {
         var e = m[6], p = Selector.patterns,
             x = Selector.xpath, le, v;
@@ -2968,7 +3035,7 @@ Object.extend(Selector, {
     className:    /^\.([\w\-\*]+)(\b|$)/,
     pseudo:
 /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/,
-    attrPresence: /^\[([\w]+)\]/,
+    attrPresence: /^\[((?:[\w]+:)?[\w]+)\]/,
     attr:         /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/
   },
 
@@ -3081,7 +3148,7 @@ Object.extend(Selector, {
 
     nextElementSibling: function(node) {
       while (node = node.nextSibling)
-	      if (node.nodeType == 1) return node;
+        if (node.nodeType == 1) return node;
       return null;
     },
 
@@ -3270,7 +3337,7 @@ Object.extend(Selector, {
     '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;
+        if (node.tagName == '!' || node.firstChild) continue;
         results.push(node);
       }
       return results;
@@ -3288,7 +3355,8 @@ Object.extend(Selector, {
 
     'enabled': function(nodes, value, root) {
       for (var i = 0, results = [], node; node = nodes[i]; i++)
-        if (!node.disabled) results.push(node);
+        if (!node.disabled && (!node.type || node.type !== 'hidden'))
+          results.push(node);
       return results;
     },
 
@@ -3308,11 +3376,14 @@ Object.extend(Selector, {
   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 == v || nv && nv.startsWith(v); },
+    '$=': function(nv, v) { return nv == v || nv && nv.endsWith(v); },
+    '*=': function(nv, v) { return nv == v || nv && nv.include(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() + '-'); }
+    '|=': function(nv, v) { return ('-' + (nv || "").toUpperCase() +
+     '-').include('-' + (v || "").toUpperCase() + '-'); }
   },
 
   split: function(expression) {
@@ -3386,7 +3457,7 @@ var Form = {
     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 &&
+        if (value != null && element.type != 'file' && (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
@@ -3547,7 +3618,6 @@ Form.Element.Methods = {
 
   disable: function(element) {
     element = $(element);
-    element.blur();
     element.disabled = true;
     return element;
   },
@@ -3587,22 +3657,22 @@ Form.Element.Serializers = {
     else element.value = value;
   },
 
-  select: function(element, index) {
-    if (Object.isUndefined(index))
+  select: function(element, value) {
+    if (Object.isUndefined(value))
       return this[element.type == 'select-one' ?
         'selectOne' : 'selectMany'](element);
     else {
-      var opt, value, single = !Object.isArray(index);
+      var opt, currentValue, single = !Object.isArray(value);
       for (var i = 0, length = element.length; i < length; i++) {
         opt = element.options[i];
-        value = this.optionValue(opt);
+        currentValue = this.optionValue(opt);
         if (single) {
-          if (value == index) {
+          if (currentValue == value) {
             opt.selected = true;
             return;
           }
         }
-        else opt.selected = index.include(value);
+        else opt.selected = value.include(currentValue);
       }
     }
   },
@@ -3773,8 +3843,23 @@ Event.Methods = (function() {
     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);
+      event = Event.extend(event);
+
+      var node          = event.target,
+          type          = event.type,
+          currentTarget = event.currentTarget;
+
+      if (currentTarget && currentTarget.tagName) {
+        // Firefox screws up the "click" event when moving between radio buttons
+        // via arrow keys. It also screws up the "load" and "error" events on images,
+        // reporting the document as the target instead of the original image.
+        if (type === 'load' || type === 'error' ||
+          (type === 'click' && currentTarget.tagName.toLowerCase() === 'input'
+            && currentTarget.type === 'radio'))
+              node = currentTarget;
+      }
+      if (node.nodeType == Node.TEXT_NODE) node = node.parentNode;
+      return Element.extend(node);
     },
 
     findElement: function(event, expression) {
@@ -3785,11 +3870,15 @@ Event.Methods = (function() {
     },
 
     pointer: function(event) {
+      var docElement = document.documentElement,
+      body = document.body || { scrollLeft: 0, scrollTop: 0 };
       return {
         x: event.pageX || (event.clientX +
-          (document.documentElement.scrollLeft || document.body.scrollLeft)),
+          (docElement.scrollLeft || body.scrollLeft) -
+          (docElement.clientLeft || 0)),
         y: event.pageY || (event.clientY +
-          (document.documentElement.scrollTop || document.body.scrollTop))
+          (docElement.scrollTop || body.scrollTop) -
+          (docElement.clientTop || 0))
       };
     },
 
@@ -3834,7 +3923,7 @@ Event.extend = (function() {
     };
 
   } else {
-    Event.prototype = Event.prototype || document.createEvent("HTMLEvents").__proto__;
+    Event.prototype = Event.prototype || document.createEvent("HTMLEvents")['__proto__'];
     Object.extend(Event.prototype, methods);
     return Prototype.K;
   }
@@ -3899,10 +3988,20 @@ Object.extend(Event, (function() {
         cache[id][eventName] = null;
   }
 
+
+  // Internet Explorer needs to remove event handlers on page unload
+  // in order to avoid memory leaks.
   if (window.attachEvent) {
     window.attachEvent("onunload", destroyCache);
   }
 
+  // Safari has a dummy event handler on page unload so that it won't
+  // use its bfcache. Safari <= 3.1 has an issue with restoring the "document"
+  // object when page is returned to via the back button using its bfcache.
+  if (Prototype.Browser.WebKit) {
+    window.addEventListener('unload', Prototype.emptyFunction, false);
+  }
+
   return {
     observe: function(element, eventName, handler) {
       element = $(element);
diff --git a/lib/Block/tree_menu.php b/lib/Block/tree_menu.php
index c82eaeb..10c52dc 100644
--- a/lib/Block/tree_menu.php
+++ b/lib/Block/tree_menu.php
@@ -4,7 +4,7 @@ $block_name = _("Menu List");
 $block_type = 'tree';
 
 /**
- * $Horde: turba/lib/Block/tree_menu.php,v 1.5.2.4 2008/06/11 17:45:54 mrubinsk Exp $
+ * $Horde: turba/lib/Block/tree_menu.php,v 1.5.2.5 2008/08/11 08:29:21 jan Exp $
  *
  * @package Horde_Block
  */
@@ -22,25 +22,29 @@ class Horde_Block_turba_tree_menu extends Horde_Block {
         $add = Horde::applicationUrl('add.php');
         $icondir = $registry->getImageDir() . '/menu';
 
-        $tree->addNode($parent . '__new',
-                       $parent,
-                       _("New Contact"),
-                       $indent + 1,
-                       false,
-                       array('icon' => 'new.png',
-                             'icondir' => $icondir,
-                             'url' => $add));
-
-        foreach (Turba::getAddressBooks(PERMS_EDIT) as $addressbook => $config) {
-            $tree->addNode($parent . $addressbook . '__new',
-                           $parent . '__new',
-                           sprintf(_("in %s"), $config['title']),
-                           $indent + 2,
+        if ($addSources) {
+            $tree->addNode($parent . '__new',
+                           $parent,
+                           _("New Contact"),
+                           $indent + 1,
                            false,
                            array('icon' => 'new.png',
                                  'icondir' => $icondir,
-                                 'url' => Util::addParameter($add, array('source' => $addressbook))));
+                                 'url' => $add));
+
+            foreach ($addSources as $addressbook => $config) {
+                $tree->addNode($parent . $addressbook . '__new',
+                               $parent . '__new',
+                               sprintf(_("in %s"), $config['title']),
+                               $indent + 2,
+                               false,
+                               array('icon' => 'new.png',
+                                     'icondir' => $icondir,
+                                     'url' => Util::addParameter($add, array('source' => $addressbook))));
+            }
+        }
 
+        foreach (Turba::getAddressBooks() as $addressbook => $config) {
             if (!empty($config['browse'])) {
                 $tree->addNode($parent . $addressbook,
                                $parent,
diff --git a/lib/Driver.php b/lib/Driver.php
index 226a199..a138d71 100644
--- a/lib/Driver.php
+++ b/lib/Driver.php
@@ -4,7 +4,7 @@
  * various directory search drivers.  It includes functions for searching,
  * adding, removing, and modifying directory entries.
  *
- * $Horde: turba/lib/Driver.php,v 1.57.2.60 2008/06/12 22:16:44 jan Exp $
+ * $Horde: turba/lib/Driver.php,v 1.57.2.83 2008/10/28 23:40:38 jan Exp $
  *
  * @author  Chuck Hagenbuch <chuck at horde.org>
  * @author  Jon Parise <jon at csh.rit.edu>
@@ -65,12 +65,19 @@ class Turba_Driver {
     var $approximate = array();
 
     /**
-     * Whether this source stores one address book, or multiple private address
-     * books.
+     * The name of a field to store contact list names in if not the default.
      *
-     * @var boolean
+     * @var string
+     */
+    var $listNameField = null;
+
+    /**
+     * The name of a field to use as an alternative to the name field if that
+     * one is empty.
+     *
+     * @var string
      */
-    var $public = false;
+    var $alternativeName = null;
 
     /**
      * Hash holding the driver's additional parameters.
@@ -94,14 +101,6 @@ class Turba_Driver {
     var $_count = null;
 
     /**
-     * Hold name of field to store contact list names in if not
-     * the default.
-     *
-     * @var string
-     */
-     var $_listNameField = null;
-
-    /**
      * Hold the value for the owner of this address book.
      *
      * @var string
@@ -142,6 +141,26 @@ class Turba_Driver {
     }
 
     /**
+     * Returns the attributes that are blob types.
+     *
+     * @return array  List of blob attributes in the array keys.
+     */
+    function getBlobs()
+    {
+        global $attributes;
+
+        $blobs = array();
+        foreach (array_keys($this->fields) as $attribute) {
+            if (isset($attributes[$attribute]) &&
+                $attributes[$attribute]['type'] == 'image') {
+                $blobs[$attribute] = true;
+            }
+        }
+
+        return $blobs;
+    }
+
+    /**
      * Translates the keys of the first hash from the generalized Turba
      * attributes to the driver-specific fields. The translation is based on
      * the contents of $this->map.
@@ -152,6 +171,17 @@ class Turba_Driver {
      */
     function toDriverKeys($hash)
     {
+        /* Handle category. */
+        if (!empty($hash['category']) &&
+            $GLOBALS['attributes']['category']['type'] == 'category') {
+            if (!empty($hash['category']['new'])) {
+                require_once 'Horde/Prefs/CategoryManager.php';
+                $cManager = new Prefs_CategoryManager();
+                $cManager->add($hash['category']['value']);
+            }
+            $hash['category'] = $hash['category']['value'];
+        }
+
         // Add composite fields to $hash if at least one field part exists
         // and the composite field will be saved to storage.
         // Otherwise composite fields won't be computed during an import.
@@ -170,10 +200,10 @@ class Turba_Driver {
             }
         }
 
-        if (!empty($hash['name']) && !empty($this->_listNameField) &&
+        if (!empty($hash['name']) && !empty($this->listNameField) &&
             !empty($hash['__type']) && is_array($this->map['name']) &&
             $hash['__type'] == 'Group') {
-                $hash[$this->_listNameField] = $hash['name'];
+                $hash[$this->listNameField] = $hash['name'];
                 unset($hash['name']);
         }
 
@@ -436,8 +466,8 @@ class Turba_Driver {
                      $search_type = 'AND', $return_fields = array(),
                      $custom_strict = array(), $match_begin = false)
     {
-        /* If we are not using Horde_Share, enfore the requirement that the
-           current user must be the owner of the addressbook. */
+        /* If we are not using Horde_Share, enforce the requirement that the
+         * current user must be the owner of the addressbook. */
         $search_criteria['__owner'] = $this->getContactOwner();
         $strict_fields = array($this->toDriver('__owner') => true);
 
@@ -575,9 +605,24 @@ class Turba_Driver {
             ++$t_object_end->mday;
             $t_object_end->correct();
             $key = $ob->getValue('__key');
-            $title = sprintf(_("%s of %s"), $GLOBALS['attributes'][$category]['label'],
+
+            // Calculate the age of the time object
+            if ($start->year == $end->year) {
+                $age = $start->year - $t_object->year;
+            } elseif ($t_object->month <= $end->month) {
+                // t_object must be in later year
+                $age = $end->year - $t_object->year;
+            } else {
+                // t_object must be in earlier year
+                $age = $start->year - $t_object->year;
+            }
+
+            $title = sprintf(_("%d. %s of %s"),
+                             $age,
+                             $GLOBALS['attributes'][$category]['label'],
                              $ob->getValue('name'));
 
+
             $t_objects[] = array(
                 'id' => $key,
                 'title' => $title,
@@ -645,7 +690,8 @@ class Turba_Driver {
     {
         $objects = $this->_read($this->map['__key'], $objectIds,
                                 $this->getContactOwner(),
-                                array_values($this->fields));
+                                array_values($this->fields),
+                                $this->toDriverKeys($this->getBlobs()));
         if (is_a($objects, 'PEAR_Error')) {
             return $objects;
         }
@@ -730,7 +776,7 @@ class Turba_Driver {
         $uid = $attributes['__uid'];
 
         $attributes = $this->toDriverKeys($attributes);
-        $result = $this->_add($attributes);
+        $result = $this->_add($attributes, $this->toDriverKeys($this->getBlobs()));
         if (is_a($result, 'PEAR_Error')) {
             return $result;
         }
@@ -802,11 +848,11 @@ class Turba_Driver {
      */
     function save($object)
     {
-        $attributes = $this->toDriverKeys($object->getAttributes());
-        $key = $this->toDriverKeys(array('__key' => $object->getValue('__key')));
-        list($object_key, $object_id) = each($key);
+        list($object_key, $object_id) = each($this->toDriverKeys(array('__key' => $object->getValue('__key'))));
 
-        $object_id = $this->_save($object_key, $object_id, $attributes);
+        $object_id = $this->_save($object_key, $object_id,
+                                  $this->toDriverKeys($object->getAttributes()),
+                                  $this->toDriverKeys($this->getBlobs()));
         if (is_a($object_id, 'PEAR_Error')) {
             return $object_id;
         }
@@ -897,6 +943,9 @@ class Turba_Driver {
         $geo = null;
 
         foreach ($hash as $key => $val) {
+            if (!strlen($val)) {
+                continue;
+            }
             if ($version != '2.1') {
                 $val = String::convertCharset($val, NLS::getCharset(), 'utf-8');
             }
@@ -912,6 +961,28 @@ class Turba_Driver {
                                      MIME::is8bit($val) ? $charset : array());
                 break;
 
+            case 'homeAddress':
+                if ($version == '2.1') {
+                    $vcard->setAttribute('LABEL', $val, array('HOME' => null));
+                } else {
+                    $vcard->setAttribute('LABEL', $val, array('TYPE' => 'HOME'));
+                }
+                break;
+            case 'workAddress':
+                if ($version == '2.1') {
+                    $vcard->setAttribute('LABEL', $val, array('HOME' => null));
+                } else {
+                    $vcard->setAttribute('LABEL', $val, array('TYPE' => 'WORK'));
+                }
+                break;
+            case 'otherAddress':
+                if ($version == '2.1') {
+                    $vcard->setAttribute('LABEL', $val);
+                } else {
+                    $vcard->setAttribute('LABEL', $val);
+                }
+                break;
+
             case 'phone':
                 if ($version == '2.1') {
                     $vcard->setAttribute('TEL', $val);
@@ -933,7 +1004,6 @@ class Turba_Driver {
                     $vcard->setAttribute('TEL', $val, array('TYPE' => 'WORK'));
                 }
                 break;
-
             case 'cellPhone':
                 if ($version == '2.1') {
                     $vcard->setAttribute('TEL', $val, array('CELL' => null));
@@ -1063,7 +1133,6 @@ class Turba_Driver {
                                          array('TYPE' => 'WORK'));
                 }
                 break;
-
             case 'emails':
                 $emails = explode(',', $val);
                 foreach ($emails as $email) {
@@ -1076,7 +1145,6 @@ class Turba_Driver {
                 $vcard->setAttribute('TITLE', $val,
                                      MIME::is8bit($val) ? $charset : array());
                 break;
-
             case 'role':
                 $vcard->setAttribute('ROLE', $val,
                                      MIME::is8bit($val) ? $charset : array());
@@ -1169,11 +1237,22 @@ class Turba_Driver {
                    }
                 }
                 break;
+
+            case 'photo':
+            case 'logo':
+                $params = array('ENCODING' => 'b');
+                if (isset($hash[$key . 'type'])) {
+                    $params['TYPE'] = $hash[$key . 'type'];
+                }
+                $vcard->setAttribute(String::upper($key),
+                                     base64_encode($val),
+                                     $params);
+                break;
             }
         }
 
         // No explicit firstname/lastname in data source: we have to guess.
-        if (!isset($hash['lastname'])) {
+        if (!isset($hash['lastname']) && isset($hash['name'])) {
             $i = strpos($hash['name'], ',');
             if (is_int($i)) {
                 // Assume Last, First
@@ -1216,15 +1295,17 @@ class Turba_Driver {
         if (isset($hash['department'])) {
             $org[] = $hash['department'];
         }
-        $val = implode(';', $org);
-        if ($version != '2.1') {
-            $val = String::convertCharset($val, NLS::getCharset(), 'utf-8');
-            $org = String::convertCharset($org, NLS::getCharset(), 'utf-8');
+        if (count($org)) {
+            $val = implode(';', $org);
+            if ($version != '2.1') {
+                $val = String::convertCharset($val, NLS::getCharset(), 'utf-8');
+                $org = String::convertCharset($org, NLS::getCharset(), 'utf-8');
+            }
+            $vcard->setAttribute('ORG', $val, MIME::is8bit($val) ? $charset : array(), false, $org);
         }
-        $vcard->setAttribute('ORG', $val, MIME::is8bit($val) ? $charset : array(), false, $org);
 
         if (isset($hash['commonAddress']) || isset($hash['commonStreet']) ||
-            isset($hash['commonPOBox']) || isset($hash['commonExtend']) ||
+            isset($hash['commonPOBox']) || isset($hash['commonExtended']) ||
             isset($hash['commonStreet']) || isset($hash['commonCity']) ||
             isset($hash['commonProvince']) ||
             isset($hash['commonPostalCode']) || isset($hash['commonCountry'])) {
@@ -1239,8 +1320,8 @@ class Turba_Driver {
             $a = array(
                 VCARD_ADR_POB      => isset($hash['commonPOBox'])
                     ? $hash['commonPOBox'] : '',
-                VCARD_ADR_EXTEND   => isset($hash['commonExtend'])
-                    ? $hash['commonExtend'] : '',
+                VCARD_ADR_EXTEND   => isset($hash['commonExtended'])
+                    ? $hash['commonExtended'] : '',
                 VCARD_ADR_STREET   => isset($hash['commonStreet'])
                     ? $hash['commonStreet'] : '',
                 VCARD_ADR_LOCALITY => isset($hash['commonCity'])
@@ -1250,7 +1331,7 @@ class Turba_Driver {
                 VCARD_ADR_POSTCODE => isset($hash['commonPostalCode'])
                     ? $hash['commonPostalCode'] : '',
                 VCARD_ADR_COUNTRY  => isset($hash['commonCountry'])
-                    ? $hash['commonCountry'] : '',
+                    ? Turba_Driver::getCountry($hash['commonCountry']) : '',
             );
 
             $val = implode(';', $a);
@@ -1268,7 +1349,7 @@ class Turba_Driver {
         }
 
         if (isset($hash['homeAddress']) || isset($hash['homeStreet']) ||
-            isset($hash['homePOBox']) || isset($hash['homeExtend']) ||
+            isset($hash['homePOBox']) || isset($hash['homeExtended']) ||
             isset($hash['homeStreet']) || isset($hash['homeCity']) ||
             isset($hash['homeProvince']) || isset($hash['homePostalCode']) ||
             isset($hash['homeCountry'])) {
@@ -1278,8 +1359,8 @@ class Turba_Driver {
             $a = array(
                 VCARD_ADR_POB      => isset($hash['homePOBox'])
                     ? $hash['homePOBox'] : '',
-                VCARD_ADR_EXTEND   => isset($hash['homeExtend'])
-                    ? $hash['homeExtend'] : '',
+                VCARD_ADR_EXTEND   => isset($hash['homeExtended'])
+                    ? $hash['homeExtended'] : '',
                 VCARD_ADR_STREET   => isset($hash['homeStreet'])
                     ? $hash['homeStreet'] : '',
                 VCARD_ADR_LOCALITY => isset($hash['homeCity'])
@@ -1289,7 +1370,7 @@ class Turba_Driver {
                 VCARD_ADR_POSTCODE => isset($hash['homePostalCode'])
                     ? $hash['homePostalCode'] : '',
                 VCARD_ADR_COUNTRY  => isset($hash['homeCountry'])
-                    ? $hash['homeCountry'] : '',
+                    ? Turba_Driver::getCountry($hash['homeCountry']) : '',
             );
 
             $val = implode(';', $a);
@@ -1307,7 +1388,7 @@ class Turba_Driver {
         }
 
         if (isset($hash['workAddress']) || isset($hash['workStreet']) ||
-            isset($hash['workPOBox']) || isset($hash['workExtend']) ||
+            isset($hash['workPOBox']) || isset($hash['workExtended']) ||
             isset($hash['workStreet']) || isset($hash['workCity']) ||
             isset($hash['workProvince']) || isset($hash['workPostalCode']) ||
             isset($hash['workCountry'])) {
@@ -1317,8 +1398,8 @@ class Turba_Driver {
             $a = array(
                 VCARD_ADR_POB      => isset($hash['workPOBox'])
                     ? $hash['workPOBox'] : '',
-                VCARD_ADR_EXTEND   => isset($hash['workExtend'])
-                    ? $hash['workExtend'] : '',
+                VCARD_ADR_EXTEND   => isset($hash['workExtended'])
+                    ? $hash['workExtended'] : '',
                 VCARD_ADR_STREET   => isset($hash['workStreet'])
                     ? $hash['workStreet'] : '',
                 VCARD_ADR_LOCALITY => isset($hash['workCity'])
@@ -1328,7 +1409,7 @@ class Turba_Driver {
                 VCARD_ADR_POSTCODE => isset($hash['workPostalCode'])
                     ? $hash['workPostalCode'] : '',
                 VCARD_ADR_COUNTRY  => isset($hash['workCountry'])
-                    ? $hash['workCountry'] : '',
+                    ? Turba_Driver::getCountry($hash['workCountry']) : '',
             );
 
             $val = implode(';', $a);
@@ -1349,6 +1430,24 @@ class Turba_Driver {
     }
 
     /**
+     * Returns the (localized) country name.
+     *
+     * @param string $country  The two-letter country code.
+     *
+     * @return string  The country name or the country code if a name cannot be
+     *                 found.
+     */
+    function getCountry($country)
+    {
+        static $countries;
+        if (!isset($countries)) {
+            include 'Horde/NLS/countries.php';
+        }
+
+        return isset($countries[$country]) ? $countries[$country] : $country;
+    }
+
+    /**
      * Function to convert a Horde_iCalendar_vcard object into a Turba
      * Object Hash with Turba attributes suitable as a parameter for add().
      *
@@ -1403,16 +1502,15 @@ class Turba_Driver {
 
             // We use LABEL but also support ADR.
             case 'LABEL':
-                if (isset($item['params']['HOME'])) {
+                if (isset($item['params']['HOME']) && !isset($hash['homeAddress'])) {
                     $hash['homeAddress'] = $item['value'];
-                } elseif (isset($item['params']['WORK'])) {
+                } elseif (isset($item['params']['WORK']) && !isset($hash['workAddress'])) {
                     $hash['workAddress'] = $item['value'];
-                } else {
+                } elseif (!isset($hash['commonAddress'])) {
                     $hash['commonAddress'] = $item['value'];
                 }
                 break;
 
-            // For vCard 3.0.
             case 'ADR':
                 if (isset($item['params']['TYPE'])) {
                     if (!is_array($item['params']['TYPE'])) {
@@ -1446,46 +1544,47 @@ class Turba_Driver {
                         $prefix = 'common';
                     }
 
-                    if ($prefix) {
-                        $hash[$prefix . 'Address'] = '';
+                    if (isset($hash[$prefix . 'Address'])) {
+                        continue;
+                    }
 
-                        if (!empty($address[VCARD_ADR_STREET])) {
-                            $hash[$prefix . 'Street'] = $address[VCARD_ADR_STREET];
-                            $hash[$prefix . 'Address'] .= $hash[$prefix . 'Street'] . "\n";
-                        }
-                        if (!empty($address[VCARD_ADR_EXTEND])) {
-                            $hash[$prefix . 'Extend'] = $address[VCARD_ADR_EXTEND];
-                            $hash[$prefix . 'Address'] .= $hash[$prefix . 'Extend'] . "\n";
-                        }
-                        if (!empty($address[VCARD_ADR_POB])) {
-                            $hash[$prefix . 'POBox'] = $address[VCARD_ADR_POB];
-                            $hash[$prefix . 'Address'] .= $hash[$prefix . 'POBox'] . "\n";
-                        }
-                        if (!empty($address[VCARD_ADR_LOCALITY])) {
-                            $hash[$prefix . 'City'] = $address[VCARD_ADR_LOCALITY];
-                            $hash[$prefix . 'Address'] .= $hash[$prefix . 'City'];
-                        }
-                        if (!empty($address[VCARD_ADR_REGION])) {
-                            $hash[$prefix . 'Province'] = $address[VCARD_ADR_REGION];
-                            $hash[$prefix . 'Address'] .= ', ' . $hash[$prefix . 'Province'];
-                        }
-                        if (!empty($address[VCARD_ADR_POSTCODE])) {
-                            $hash[$prefix . 'PostalCode'] = $address[VCARD_ADR_POSTCODE];
-                            $hash[$prefix . 'Address'] .= ' ' . $hash[$prefix . 'PostalCode'];
-                        }
-                        if (!empty($address[VCARD_ADR_COUNTRY])) {
-                            $hash[$prefix . 'Address'] .= "\n" . $address[VCARD_ADR_COUNTRY];
-                            include 'Horde/NLS/countries.php';
-                            $country = array_search($address[VCARD_ADR_COUNTRY], $countries);
-                            if ($country !== false) {
-                                $hash[$prefix . 'Country'] = $country;
-                            } else {
-                                $hash[$prefix . 'Country'] = $address[VCARD_ADR_COUNTRY];
-                            }
-                        }
+                    $hash[$prefix . 'Address'] = '';
 
-                        $hash[$prefix . 'Address'] = trim($hash[$prefix . 'Address']);
+                    if (!empty($address[VCARD_ADR_STREET])) {
+                        $hash[$prefix . 'Street'] = $address[VCARD_ADR_STREET];
+                        $hash[$prefix . 'Address'] .= $hash[$prefix . 'Street'] . "\n";
+                    }
+                    if (!empty($address[VCARD_ADR_EXTEND])) {
+                        $hash[$prefix . 'Extended'] = $address[VCARD_ADR_EXTEND];
+                        $hash[$prefix . 'Address'] .= $hash[$prefix . 'Extended'] . "\n";
+                    }
+                    if (!empty($address[VCARD_ADR_POB])) {
+                        $hash[$prefix . 'POBox'] = $address[VCARD_ADR_POB];
+                        $hash[$prefix . 'Address'] .= $hash[$prefix . 'POBox'] . "\n";
                     }
+                    if (!empty($address[VCARD_ADR_LOCALITY])) {
+                        $hash[$prefix . 'City'] = $address[VCARD_ADR_LOCALITY];
+                        $hash[$prefix . 'Address'] .= $hash[$prefix . 'City'];
+                    }
+                    if (!empty($address[VCARD_ADR_REGION])) {
+                        $hash[$prefix . 'Province'] = $address[VCARD_ADR_REGION];
+                        $hash[$prefix . 'Address'] .= ', ' . $hash[$prefix . 'Province'];
+                    }
+                    if (!empty($address[VCARD_ADR_POSTCODE])) {
+                        $hash[$prefix . 'PostalCode'] = $address[VCARD_ADR_POSTCODE];
+                        $hash[$prefix . 'Address'] .= ' ' . $hash[$prefix . 'PostalCode'];
+                    }
+                    if (!empty($address[VCARD_ADR_COUNTRY])) {
+                        include 'Horde/NLS/countries.php';
+                        $country = array_search($address[VCARD_ADR_COUNTRY], $countries);
+                        if ($country === false) {
+                            $country = $address[VCARD_ADR_COUNTRY];
+                        }
+                        $hash[$prefix . 'Country'] = $country;
+                        $hash[$prefix . 'Address'] .= "\n" . $address[VCARD_ADR_COUNTRY];
+                    }
+
+                    $hash[$prefix . 'Address'] = trim($hash[$prefix . 'Address']);
                 }
                 break;
 
@@ -1520,14 +1619,17 @@ class Turba_Driver {
 
             case 'TEL':
                 if (isset($item['params']['FAX'])) {
-                    if (isset($item['params']['WORK'])) {
+                    if (isset($item['params']['WORK']) &&
+                        !isset($hash['workFax'])) {
                         $hash['workFax'] = $item['value'];
-                    } elseif (isset($item['params']['HOME'])) {
+                    } elseif (isset($item['params']['HOME']) &&
+                              !isset($hash['homeFax'])) {
                         $hash['homeFax'] = $item['value'];
-                    } else {
+                    } elseif (!isset($hash['fax'])) {
                         $hash['fax'] = $item['value'];
                     }
-                } elseif (isset($item['params']['PAGER'])) {
+                } elseif (isset($item['params']['PAGER']) &&
+                          !isset($hash['pager'])) {
                     $hash['pager'] = $item['value'];
                 } elseif (isset($item['params']['TYPE'])) {
                     if (!is_array($item['params']['TYPE'])) {
@@ -1535,88 +1637,115 @@ class Turba_Driver {
                     }
                     // For vCard 3.0.
                     if (in_array('CELL', $item['params']['TYPE'])) {
-                        if (in_array('HOME', $item['params']['TYPE'])) {
+                        if (in_array('HOME', $item['params']['TYPE']) &&
+                            !isset($hash['homeCellPhone'])) {
                             $hash['homeCellPhone'] = $item['value'];
-                        } elseif (in_array('WORK', $item['params']['TYPE'])) {
+                        } elseif (in_array('WORK', $item['params']['TYPE']) &&
+                                  !isset($hash['workCellPhone'])) {
                             $hash['workCellPhone'] = $item['value'];
-                        } else {
+                        } elseif (!isset($hash['cellPhone'])) {
                             $hash['cellPhone'] = $item['value'];
                         }
                     } elseif (in_array('FAX', $item['params']['TYPE'])) {
-                        if (in_array('HOME', $item['params']['TYPE'])) {
+                        if (in_array('HOME', $item['params']['TYPE']) &&
+                            !isset($hash['homeFax'])) {
                             $hash['homeFax'] = $item['value'];
-                        } elseif (in_array('WORK', $item['params']['TYPE'])) {
+                        } elseif (in_array('WORK', $item['params']['TYPE']) &&
+                                  !isset($hash['workFax'])) {
                             $hash['workFax'] = $item['value'];
-                        } else {
+                        } elseif (!isset($hash['fax'])) {
                             $hash['fax'] = $item['value'];
                         }
                     } elseif (in_array('VIDEO', $item['params']['TYPE'])) {
-                        if (in_array('HOME', $item['params']['TYPE'])) {
+                        if (in_array('HOME', $item['params']['TYPE']) &&
+                            !isset($hash['homeVideoCall'])) {
                             $hash['homeVideoCall'] = $item['value'];
-                        } elseif (in_array('WORK', $item['params']['TYPE'])) {
+                        } elseif (in_array('WORK', $item['params']['TYPE']) &&
+                                  !isset($hash['workVideoCall'])) {
                             $hash['workVideoCall'] = $item['value'];
-                        } else {
+                        } elseif (!isset($hash['videoCall'])) {
                             $hash['videoCall'] = $item['value'];
                         }
-                    } elseif (in_array('PAGER', $item['params']['TYPE'])) {
+                    } elseif (in_array('PAGER', $item['params']['TYPE']) &&
+                              !isset($hash['pager'])) {
                         $hash['pager'] = $item['value'];
-                    } elseif (in_array('WORK', $item['params']['TYPE'])) {
+                    } elseif (in_array('WORK', $item['params']['TYPE']) &&
+                              !isset($hash['workPhone'])) {
                         $hash['workPhone'] = $item['value'];
-                    } elseif (in_array('HOME', $item['params']['TYPE'])) {
+                    } elseif (in_array('HOME', $item['params']['TYPE']) &&
+                              !isset($hash['homePhone'])) {
                         $hash['homePhone'] = $item['value'];
                     }
                 } elseif (isset($item['params']['CELL'])) {
-                    if (isset($item['params']['WORK'])) {
+                    if (isset($item['params']['WORK']) &&
+                        !isset($hash['workCellPhone'])) {
                         $hash['workCellPhone'] = $item['value'];
-                    } elseif (isset($item['params']['HOME'])) {
+                    } elseif (isset($item['params']['HOME']) &&
+                              !isset($hash['homeCellPhone'])) {
                         $hash['homeCellPhone'] = $item['value'];
-                    } else {
+                    } elseif (!isset($hash['cellPhone'])) {
                         $hash['cellPhone'] = $item['value'];
                     }
                 } elseif (isset($item['params']['VIDEO'])) {
-                    if (isset($item['params']['WORK'])) {
+                    if (isset($item['params']['WORK']) &&
+                        !isset($hash['workVideoCall'])) {
                         $hash['workVideoCall'] = $item['value'];
-                    } elseif (isset($item['params']['HOME'])) {
+                    } elseif (isset($item['params']['HOME']) &&
+                              !isset($hash['homeVideoCall'])) {
                         $hash['homeVideoCall'] = $item['value'];
-                    } else {
+                    } elseif (!isset($hash['homeVideoCall'])) {
                         $hash['videoCall'] = $item['value'];
                     }
                 } elseif (count($item['params']) <= 1 ||
-                          (count($item['params']) <= 2 &&
-                           isset($item['params']['VOICE']))) {
+                          isset($item['params']['VOICE'])) {
                     // There might be e.g. SAT;WORK which must not overwrite
                     // WORK.
-                    if (isset($item['params']['WORK'])) {
+                    if (isset($item['params']['WORK']) &&
+                        !isset($hash['workPhone'])) {
                         $hash['workPhone'] = $item['value'];
-                    } elseif (isset($item['params']['HOME'])) {
+                    } elseif (isset($item['params']['HOME']) &&
+                              !isset($hash['homePhone'])) {
                         $hash['homePhone'] = $item['value'];
-                    } elseif (count($item['params']) == 0 ||
-                              (count($item['params']) == 1 &&
-                               isset($item['params']['VOICE']))) {
+                    } elseif ((count($item['params']) == 0 ||
+                               (count($item['params']) == 1 &&
+                                isset($item['params']['VOICE']))) &&
+                              !isset($hash['phone'])) {
                         $hash['phone'] = $item['value'];
                     }
                 }
                 break;
 
             case 'EMAIL':
-                if (isset($item['params']['PREF']) || !isset($hash['email'])) {
-                    $hash['email'] = Horde_iCalendar_vcard::getBareEmail($item['value']);
-                } elseif (isset($item['params']['HOME'])) {
+                $email_set = false;
+                if (isset($item['params']['HOME']) &&
+                    (!isset($hash['homeEmail']) ||
+                     isset($item['params']['PREF']))) {
                     $hash['homeEmail'] = Horde_iCalendar_vcard::getBareEmail($item['value']);
-                } elseif (isset($item['params']['WORK'])) {
+                    $email_set = true;
+                } elseif (isset($item['params']['WORK']) &&
+                          (!isset($hash['workEmail']) ||
+                           isset($item['params']['PREF']))) {
                     $hash['workEmail'] = Horde_iCalendar_vcard::getBareEmail($item['value']);
+                    $email_set = true;
                 } elseif (isset($item['params']['TYPE'])) {
                     if (!is_array($item['params']['TYPE'])) {
                         $item['params']['TYPE'] = array($item['params']['TYPE']);
                     }
-                    if (in_array('HOME', $item['params']['TYPE'])) {
+                    if (in_array('HOME', $item['params']['TYPE']) &&
+                        (!isset($hash['homeEmail']) ||
+                         in_array('PREF', $item['params']['TYPE']))) {
                         $hash['homeEmail'] = Horde_iCalendar_vcard::getBareEmail($item['value']);
-                    } elseif (in_array('WORK', $item['params']['TYPE'])) {
+                        $email_set = true;
+                    } elseif (in_array('WORK', $item['params']['TYPE']) &&
+                              (!isset($hash['workEmail']) ||
+                         in_array('PREF', $item['params']['TYPE']))) {
                         $hash['workEmail'] = Horde_iCalendar_vcard::getBareEmail($item['value']);
-                    } else {
-                        $hash['email'] = Horde_iCalendar_vcard::getBareEmail($item['value']);
+                        $email_set = true;
                     }
-                } else {
+                }
+                if (!$email_set &&
+                    (!isset($hash['email']) ||
+                     isset($item['params']['PREF']))) {
                     $hash['email'] = Horde_iCalendar_vcard::getBareEmail($item['value']);
                 }
 
@@ -1652,11 +1781,13 @@ class Turba_Driver {
                 break;
 
             case 'URL':
-                if (isset($item['params']['HOME'])) {
+                if (isset($item['params']['HOME']) &&
+                    !isset($hash['homeWebsite'])) {
                     $hash['homeWebsite'] = $item['value'];
-                } elseif (isset($item['params']['WORK'])) {
+                } elseif (isset($item['params']['WORK']) &&
+                          !isset($hash['workWebsite'])) {
                     $hash['workWebsite'] = $item['value'];
-                } else {
+                } elseif (!isset($hash['website'])) {
                     $hash['website'] = $item['value'];
                 }
                 break;
@@ -1665,14 +1796,37 @@ class Turba_Driver {
                 $hash['birthday'] = $item['value']['year'] . '-' . $item['value']['month'] . '-' .  $item['value']['mday'];
                 break;
 
+            case 'PHOTO':
+            case 'LOGO':
+                if (isset($item['params']['VALUE']) &&
+                    String::lower($item['params']['VALUE']) == 'uri') {
+                    // No support for URIs yet.
+                    break;
+                }
+                if (!isset($item['params']['ENCODING']) ||
+                    (String::lower($item['params']['ENCODING']) != 'b' &&
+                     String::upper($item['params']['ENCODING']) != 'BASE64')) {
+                    // Invalid property.
+                    break;
+                }
+                $type = String::lower($item['name']);
+                $hash[$type] = base64_decode($item['value']);
+                if (isset($item['params']['TYPE'])) {
+                    $hash[$type . 'type'] = $item['params']['TYPE'];
+                }
+                break;
+
             case 'X-SIP':
-                if (isset($item['params']['POC'])) {
+                if (isset($item['params']['POC']) &&
+                    !isset($hash['ptt'])) {
                     $hash['ptt'] = $item['value'];
-                } elseif (isset($item['params']['VOIP'])) {
+                } elseif (isset($item['params']['VOIP']) &&
+                          !isset($hash['voip'])) {
                     $hash['voip'] = $item['value'];
-                } elseif (isset($item['params']['SWIS'])) {
+                } elseif (isset($item['params']['SWIS']) &&
+                          !isset($hash['shareView'])) {
                     $hash['shareView'] = $item['value'];
-                } else {
+                } elseif (!isset($hash['sip'])) {
                     $hash['sip'] = $item['value'];
                 }
                 break;
@@ -1854,16 +2008,18 @@ class Turba_Driver {
             $driver->tabs = $config['tabs'];
         }
 
-        /* Store strict and approximate fields. */
+        /* Store remaining fields. */
         if (isset($config['strict'])) {
             $driver->strict = $config['strict'];
         }
         if (isset($config['approximate'])) {
             $driver->approximate = $config['approximate'];
         }
-
-        if (!empty($config['list_name_field'])) {
-            $driver->_listNameField = $config['list_name_field'];
+        if (isset($config['list_name_field'])) {
+            $driver->listNameField = $config['list_name_field'];
+        }
+        if (isset($config['alternative_name'])) {
+            $driver->alternativeName = $config['alternative_name'];
         }
 
         return $driver;
diff --git a/lib/Driver/favourites.php b/lib/Driver/favourites.php
index c54581d..ad17bc9 100644
--- a/lib/Driver/favourites.php
+++ b/lib/Driver/favourites.php
@@ -3,7 +3,7 @@
  * Read-only Turba directory driver implementation for favourite
  * recipients. Relies on the contacts/favouriteRecipients API method.
  *
- * $Horde: turba/lib/Driver/favourites.php,v 1.5.2.2 2008/04/25 04:58:31 chuck Exp $
+ * $Horde: turba/lib/Driver/favourites.php,v 1.5.2.3 2008/08/03 16:50:06 jan Exp $
  *
  * @author  Jan Schneider <jan at horde.org>
  * @since   Turba 2.1
@@ -124,7 +124,7 @@ class Turba_Driver_favourites extends Turba_Driver {
             return PEAR::raiseError(_("No source for favourite recipients exists."));
         }
 
-        $addresses = $registry->call('contacts/favouriteRecipients', $this->_params['limit']);
+        $addresses = $registry->call('contacts/favouriteRecipients', array($this->_params['limit']));
         if (is_a($addresses, 'PEAR_Error')) {
             return $addresses;
         }
diff --git a/lib/Driver/kolab.php b/lib/Driver/kolab.php
index 56a3c86..166c0a6 100644
--- a/lib/Driver/kolab.php
+++ b/lib/Driver/kolab.php
@@ -2,7 +2,7 @@
 /**
  * @package Turba
  *
- * $Horde: turba/lib/Driver/kolab.php,v 1.5.10.21 2008/06/12 22:12:51 jan Exp $
+ * $Horde: turba/lib/Driver/kolab.php,v 1.5.10.23 2008/11/07 13:15:23 wrobel Exp $
  */
 
 /** Kolab support class. */
@@ -184,7 +184,7 @@ class Turba_Driver_kolab extends Turba_Driver {
 /**
  * Horde Turba wrapper to distinguish between both Kolab driver implementations.
  *
- * $Horde: turba/lib/Driver/kolab.php,v 1.5.10.21 2008/06/12 22:12:51 jan Exp $
+ * $Horde: turba/lib/Driver/kolab.php,v 1.5.10.23 2008/11/07 13:15:23 wrobel Exp $
  *
  * Copyright 2004-2008 The Horde Project (http://www.horde.org/)
  *
@@ -742,11 +742,26 @@ class Turba_Driver_kolab_wrapper_new extends Turba_Driver_kolab_wrapper {
         $this->_store = &$this->_kolab->_storage;
 
         /* Fetch the contacts first */
-        $contacts = $this->_store->getObjectArray();
-        if (!$contacts) {
-            $contacts = array();
+        $raw_contacts = $this->_store->getObjectArray();
+        if (!$raw_contacts) {
+            $raw_contacts = array();
+        }
+        $contacts = array();
+        foreach ($raw_contacts as $id => $contact) {
+            if (isset($contact['email'])) {
+                unset($contact['email']);
+            }
+            if (isset($contact['picture'])) {
+                $name = $contact['picture'];
+                if (isset($contact['_attachments'][$name])) {
+                    $contact['photo'] =  $this->_store->_data->getAttachment($contact['_attachments'][$name]['key']);
+                    $contact['phototype'] = $contact['_attachments'][$name]['type'];
+                }
+            }
+
+            $contacts[$id] = $contact;
         }
-        
+
         /* Now we retrieve distribution-lists */
         $result = $this->_store->setObjectType('distribution-list');
         if (is_a($result, 'PEAR_Error')) {
@@ -1073,29 +1088,19 @@ class Turba_Driver_kolab_wrapper_new extends Turba_Driver_kolab_wrapper {
             return $result;
         }
 
-        $group = false;
-        if (isset($attributes['__type']) && $attributes['__type'] == 'Group') {
-            $group = true;
-            $result = $this->_store->setObjectType('distribution-list');
-            if (is_a($result, 'PEAR_Error')) {
-                return $result;
-            }
-            $this->_convertMembers($attributes);
+        $attributes['full-name'] = $attributes['last-name'];
+        if (isset($attributes['middle-names'])) {
+            $attributes['full-name'] = $attributes['middle-names'] . ' ' . $attributes['full-name'];
         }
-
-        $object_id = $this->_store->save($attributes, null);
-        if (is_a($object_id, 'PEAR_Error')) {
-            return $object_id;
+        if (isset($attributes['given-name'])) {
+            $attributes['full-name'] = $attributes['given-name'] . ' ' . $attributes['full-name'];
         }
 
-        if ($group) {
-            $result = $this->_store->setObjectType('contact');
-            if (is_a($result, 'PEAR_Error')) {
-                return $result;
-            }
+        $result = $this->_store($attributes);
+        if (is_a($result, 'PEAR_Error')) {
+            return $result;
         }
-
-        return $object_id;
+        return true;
     }
 
     /**
@@ -1111,9 +1116,19 @@ class Turba_Driver_kolab_wrapper_new extends Turba_Driver_kolab_wrapper {
         }
 
         if ($object_key != 'uid') {
-            return PEAR::raiseError(sprintf(_("Key for saving must be a UID not %s!"), $object_key));
+            return PEAR::raiseError(sprintf(_("Key for saving must be \'uid\' not %s!"), $object_key));
         }
 
+        return $this->_store($attributes, $object_id);
+    }
+
+    /**
+     * Stores an object in the Kolab message store.
+     *
+     * @return string  The object id, possibly updated.
+     */
+    function _store($attributes, $object_id = null)
+    {
         $group = false;
         if (isset($attributes['__type']) && $attributes['__type'] == 'Group') {
             $group = true;
@@ -1124,6 +1139,14 @@ class Turba_Driver_kolab_wrapper_new extends Turba_Driver_kolab_wrapper {
             $this->_convertMembers($attributes);
         }
 
+        if (isset($attributes['photo']) && isset($attributes['phototype'])) {
+            $attributes['_attachments']['photo.attachment'] = array('type' => $attributes['phototype'],
+                                                                    'content' => $attributes['photo']);
+            $attributes['picture'] = 'photo.attachment';
+            unset($attributes['photo']);
+            unset($attributes['phototype']);
+        }
+
         $result = $this->_store->save($attributes, $object_id);
         if (is_a($result, 'PEAR_Error')) {
             return $result;
diff --git a/lib/Driver/share.php b/lib/Driver/share.php
index aa6528d..57222a4 100644
--- a/lib/Driver/share.php
+++ b/lib/Driver/share.php
@@ -4,7 +4,7 @@
  * various directory search drivers.  It includes functions for searching,
  * adding, removing, and modifying directory entries.
  *
- * $Horde: turba/lib/Driver/share.php,v 1.11.2.4 2008/02/15 16:44:06 chuck Exp $
+ * $Horde: turba/lib/Driver/share.php,v 1.11.2.6 2008/08/11 16:34:17 jan Exp $
  *
  * @author  Chuck Hagenbuch <chuck at horde.org>
  * @author  Jon Parise <jon at csh.rit.edu>
@@ -116,17 +116,17 @@ class Turba_Driver_share extends Turba_Driver {
      *
      * @return array  Hash containing the search results.
      */
-    function _read($key, $ids, $owner, $fields)
+    function _read($key, $ids, $owner, $fields, $blob_fields = array())
     {
-        return $this->_driver->_read($key, $ids, $owner, $fields);
+        return $this->_driver->_read($key, $ids, $owner, $fields, $blob_fields);
     }
 
     /**
      * Adds the specified object to the SQL database.
      */
-    function _add($attributes)
+    function _add($attributes, $blob_fields = array())
     {
-        return $this->_driver->_add($attributes);
+        return $this->_driver->_add($attributes, $blob_fields);
     }
 
     /**
@@ -155,17 +155,14 @@ class Turba_Driver_share extends Turba_Driver {
      *
      * @return string  The object id, possibly updated.
      */
-    function _save($object_key, $object_id, $attributes)
+    function _save($object_key, $object_id, $attributes, $blob_fields = array())
     {
-        return $this->_driver->_save($object_key, $object_id, $attributes);
+        return $this->_driver->_save($object_key, $object_id, $attributes, $blob_fields);
     }
 
     /**
      * Stub for removing all data for a specific user - to be overridden
      * by child class.
-     *
-     * @TODO This should actually call _deleteAll for the underlying
-     * driver and remove the share.
      */
     function removeUserData($user)
     {
diff --git a/lib/Driver/sql.php b/lib/Driver/sql.php
index e50baaa..5bb64b3 100644
--- a/lib/Driver/sql.php
+++ b/lib/Driver/sql.php
@@ -3,7 +3,7 @@
  * Turba directory driver implementation for PHP's PEAR database abstraction
  * layer.
  *
- * $Horde: turba/lib/Driver/sql.php,v 1.59.10.23 2008/06/09 03:28:07 chuck Exp $
+ * $Horde: turba/lib/Driver/sql.php,v 1.59.10.29 2008/08/12 09:24:50 jan Exp $
  *
  * @author  Jon Parise <jon at csh.rit.edu>
  * @package Turba
@@ -204,7 +204,7 @@ class Turba_Driver_sql extends Turba_Driver {
      *
      * @return array  Hash containing the search results.
      */
-    function _read($key, $ids, $owner, $fields)
+    function _read($key, $ids, $owner, $fields, $blob_fields = array())
     {
         $values = array();
 
@@ -231,8 +231,8 @@ class Turba_Driver_sql extends Turba_Driver {
             $where .= ' AND ' . $this->_params['filter'];
         }
 
-        $query  = 'SELECT ' . implode(', ', $fields) . ' ';
-        $query .= 'FROM ' . $this->_params['table'] . ' WHERE ' . $where;
+        $query  = 'SELECT ' . implode(', ', $fields) . ' FROM '
+            . $this->_params['table'] . ' WHERE ' . $where;
 
         /* Log the query at a DEBUG log level. */
         Horde::logMessage('SQL query by Turba_Driver_sql::_read(): ' . $query,
@@ -246,15 +246,25 @@ class Turba_Driver_sql extends Turba_Driver {
 
         $results = array();
         $iMax = count($fields);
-        if (!is_a($result, 'PEAR_Error')) {
-            foreach ($result as $row) {
-                $entry = array();
-                for ($i=0; $i < $iMax; $i++) {
-                    $field = $fields[$i];
+        foreach ($result as $row) {
+            $entry = array();
+            for ($i = 0; $i < $iMax; $i++) {
+                $field = $fields[$i];
+                if (isset($blob_fields[$field])) {
+                    switch ($this->_db->dbsyntax) {
+                    case 'pgsql':
+                    case 'mssql':
+                        $entry[$field] = pack('H' . strlen($row[$i]), $row[$i]);
+                        break;
+                    default:
+                        $entry[$field] = $row[$i];
+                        break;
+                    }
+                } else {
                     $entry[$field] = $this->_convertFromDriver($row[$i]);
                 }
-                $results[] = $entry;
             }
+            $results[] = $entry;
         }
 
         return $results;
@@ -263,15 +273,24 @@ class Turba_Driver_sql extends Turba_Driver {
     /**
      * Adds the specified object to the SQL database.
      */
-    function _add($attributes)
+    function _add($attributes, $blob_fields = array())
     {
-        $fields = array();
-        $values = array();
+        $fields = $values = array();
         foreach ($attributes as $field => $value) {
             $fields[] = $field;
-            $values[] = $this->_convertToDriver($value);
+            if (!empty($value) && isset($blob_fields[$field])) {
+                switch ($this->_write_db->dbsyntax) {
+                case 'mssql':
+                case 'pgsql':
+                    $values[] = bin2hex($value);
+                    break;
+                default:
+                    $values[] = $value;
+                }
+            } else {
+                $values[] = $this->_convertToDriver($value);
+            }
         }
-
         $query  = 'INSERT INTO ' . $this->_params['table']
             . ' (' . implode(', ', $fields) . ')'
             . ' VALUES (' . str_repeat('?, ', count($values) - 1) . '?)';
@@ -338,16 +357,26 @@ class Turba_Driver_sql extends Turba_Driver {
      *
      * @return string  The object id, possibly updated.
      */
-    function _save($object_key, $object_id, $attributes)
+    function _save($object_key, $object_id, $attributes, $blob_fields = array())
     {
         $where = $object_key . ' = ?';
         unset($attributes[$object_key]);
 
-        $fields = array();
-        $values = array();
+        $fields = $values =  array();
         foreach ($attributes as $field => $value) {
             $fields[] = $field . ' = ?';
-            $values[] = $this->_convertToDriver($value);
+            if (!empty($value) && isset($blob_fields[$field])) {
+                switch ($this->_write_db->dbsyntax) {
+                case 'mssql':
+                case 'pgsql':
+                    $values[] = bin2hex($value);
+                    break;
+                default:
+                    $values[] = $value;
+                }
+            } else {
+                $values[] = $this->_convertToDriver($value);
+            }
         }
 
         $values[] = $object_id;
diff --git a/lib/Forms/AddContact.php b/lib/Forms/AddContact.php
index bd8e5f6..babc7da 100644
--- a/lib/Forms/AddContact.php
+++ b/lib/Forms/AddContact.php
@@ -2,7 +2,7 @@
 /**
  * @package Turba
  *
- * $Horde: turba/lib/Forms/AddContact.php,v 1.9.2.1 2007/12/20 14:34:30 jan Exp $
+ * $Horde: turba/lib/Forms/AddContact.php,v 1.9.2.3 2008/10/25 01:36:42 chuck Exp $
  */
 
 /** Turba_ContactForm */
@@ -68,7 +68,12 @@ class Turba_AddContactForm extends Turba_ContactForm {
         $this->getInfo($this->_vars, $info);
         $source = $info['source'];
         foreach ($info['object'] as $info_key => $info_val) {
-            $this->_contact->setValue($info_key, $info_val);
+            if ($GLOBALS['attributes'][$info_key]['type'] == 'image' && !empty($info_val['file'])) {
+                $this->_contact->setValue($info_key, file_get_contents($info_val['file']));
+                $this->_contact->setValue($info_key . 'type', $info_val['type']);
+            } else {
+                $this->_contact->setValue($info_key, $info_val);
+            }
         }
         $contact = $this->_contact->attributes;
         unset($contact['__owner']);
@@ -78,11 +83,16 @@ class Turba_AddContactForm extends Turba_ContactForm {
         if (is_a($key, 'PEAR_Error')) {
             Horde::logMessage($key, __FILE__, __LINE__, PEAR_LOG_ERR);
         } else {
-            $ob = $driver->getObject($key);
-            if (!is_a($ob, 'PEAR_Error')) {
-                $notification->push(sprintf(_("%s added."), $ob->getValue('name')), 'horde.success');
-                header('Location: ' . (!empty($info['url']) ? $info['url'] : $ob->url('Contact', true)));
-                exit;
+            // Try 3 times to get the new entry. We retry to allow setups like
+            // LDAP replication to work.
+            for ($i = 0; $i < 3; $i++) {
+                $ob = $driver->getObject($key);
+                if (!is_a($ob, 'PEAR_Error')) {
+                    $notification->push(sprintf(_("%s added."), $ob->getValue('name')), 'horde.success');
+                    header('Location: ' . (!empty($info['url']) ? $info['url'] : $ob->url('Contact', true)));
+                    exit;
+                }
+                sleep(1);
             }
             Horde::logMessage($ob, __FILE__, __LINE__, PEAR_LOG_ERR);
         }
diff --git a/lib/Forms/DeleteAddressBook.php b/lib/Forms/DeleteAddressBook.php
index 4641184..fae2552 100644
--- a/lib/Forms/DeleteAddressBook.php
+++ b/lib/Forms/DeleteAddressBook.php
@@ -2,7 +2,7 @@
 /**
  * Horde_Form for deleting address books.
  *
- * $Horde: turba/lib/Forms/DeleteAddressBook.php,v 1.1.2.1 2007/12/24 05:18:00 chuck Exp $
+ * $Horde: turba/lib/Forms/DeleteAddressBook.php,v 1.1.2.2 2008/08/25 17:10:16 jan Exp $
  *
  * See the enclosed file LICENSE for license information (ASL). If you
  * did not receive this file, see http://www.horde.org/licenses/asl.php.
@@ -81,6 +81,12 @@ class Turba_DeleteAddressBookForm extends Horde_Form {
             unset($_SESSION['turba']['source']);
         }
 
+        $abooks = explode("\n", $GLOBALS['prefs']->getValue('addressbooks'));
+        if (($pos = array_search($this->_addressbook->getName(), $abooks)) !== false) {
+            unset($abooks[$pos]);
+            $GLOBALS['prefs']->setValue('addressbooks', implode("\n", $abooks));
+        }
+
         return true;
     }
 
diff --git a/lib/Forms/EditContact.php b/lib/Forms/EditContact.php
index 34d6346..b3499c6 100644
--- a/lib/Forms/EditContact.php
+++ b/lib/Forms/EditContact.php
@@ -2,7 +2,7 @@
 /**
  * @package Turba
  *
- * $Horde: turba/lib/Forms/EditContact.php,v 1.6.2.2 2008/04/17 15:53:54 chuck Exp $
+ * $Horde: turba/lib/Forms/EditContact.php,v 1.6.2.5 2008/08/12 22:05:57 jan Exp $
  */
 
 /** Turba_ContactForm */
@@ -64,7 +64,14 @@ class Turba_EditContactForm extends Turba_ContactForm {
         /* Update the contact. */
         foreach ($info['object'] as $info_key => $info_val) {
             if ($info_key != '__key') {
-                $this->_contact->setValue($info_key, $info_val);
+                if ($GLOBALS['attributes'][$info_key]['type'] == 'image' && !empty($info_val['file'])) {
+                    $this->_contact->setValue($info_key, file_get_contents($info_val['file']));
+                    if (isset($info_val['type'])) {
+                        $this->_contact->setValue($info_key . 'type', $info_val['type']);
+                    }
+                } else {
+                    $this->_contact->setValue($info_key, $info_val);
+                }
             }
         }
 
diff --git a/lib/List.php b/lib/List.php
index 1997a44..dac18f0 100644
--- a/lib/List.php
+++ b/lib/List.php
@@ -3,7 +3,7 @@
  * The Turba_List:: class provides an interface for dealing with a
  * list of Turba_Objects.
  *
- * $Horde: turba/lib/List.php,v 1.41.10.5 2007/12/20 14:34:28 jan Exp $
+ * $Horde: turba/lib/List.php,v 1.41.10.8 2008/08/28 21:24:04 chuck Exp $
  *
  * @author  Chuck Hagenbuch <chuck at horde.org>
  * @author  Jon Parise <jon at csh.rit.edu>
@@ -106,8 +106,13 @@ class Turba_List {
         }
 
         $need_lastname = false;
-        foreach ($order as $sort_order) {
-            if ($sort_order['field'] == 'lastname') {
+        $last_first = $GLOBALS['prefs']->getValue('name_format') == 'last_first';
+        foreach (array_keys($order) as $key) {
+            if ($last_first && $order[$key]['field'] == 'name') {
+                $order[$key]['field'] = 'lastname';
+            }
+            if ($order[$key]['field'] == 'lastname') {
+                $order[$key]['field'] = '__lastname';
                 $need_lastname = true;
                 break;
             }
@@ -118,11 +123,11 @@ class Turba_List {
         } else {
             $sorted_objects = array();
             foreach ($this->objects as $key => $object) {
-                if (!$object->getValue('lastname')) {
-                    $object->setValue(
-                        'lastname',
-                        Turba::guessLastname($object->getValue('name')));
+                $lastname = $object->getValue('lastname');
+                if (!$lastname) {
+                    $lastname = Turba::guessLastname($object->getValue('name'));
                 }
+                $object->setValue('__lastname', $lastname);
                 $sorted_objects[$key] = $object;
             }
         }
diff --git a/lib/ListView.php b/lib/ListView.php
index 1f59365..38c62d4 100644
--- a/lib/ListView.php
+++ b/lib/ListView.php
@@ -3,7 +3,7 @@
  * The Turba_ListView:: class provides an interface for objects that
  * visualize Turba_lists.
  *
- * $Horde: turba/lib/ListView.php,v 1.17.10.19 2008/04/24 03:42:56 mrubinsk Exp $
+ * $Horde: turba/lib/ListView.php,v 1.17.10.21 2008/07/20 08:01:45 jan Exp $
  *
  * @author  Chuck Hagenbuch <chuck at horde.org>
  * @author  Jon Parise <jon at csh.rit.edu>
@@ -305,7 +305,7 @@ class Turba_ListView {
      */
     function getSortInfoForColumn($i)
     {
-        $sortorder = Turba::getPreferredSortOrder($this->columns);
+        $sortorder = Turba::getPreferredSortOrder();
         $column_name = $this->getColumnName($i);
         $i = 0;
         foreach ($sortorder as $sortfield) {
@@ -337,7 +337,7 @@ class Turba_ListView {
     function getSortOrderDescription()
     {
         $description = array();
-        $sortorder = Turba::getPreferredSortOrder($this->columns);
+        $sortorder = Turba::getPreferredSortOrder();
         foreach ($sortorder as $elt) {
             $field = $elt['field'];
             if ($field == 'lastname') {
@@ -389,6 +389,7 @@ class Turba_ListView {
     {
         ob_start();
         $width = floor(90 / (count($this->columns) + 1));
+        @list($own_source, $own_id) = explode(';', $GLOBALS['prefs']->getValue('own_contact'));
 
         include TURBA_TEMPLATES . '/browse/column_headers.inc';
 
diff --git a/lib/Object.php b/lib/Object.php
index bc49f29..57f21f3 100644
--- a/lib/Object.php
+++ b/lib/Object.php
@@ -3,7 +3,7 @@
  * The Turba_Object:: class provides a base implementation for Turba
  * objects - people, groups, restaurants, etc.
  *
- * $Horde: turba/lib/Object.php,v 1.17.10.8 2008/03/10 09:17:35 jan Exp $
+ * $Horde: turba/lib/Object.php,v 1.17.10.10 2008/11/07 13:20:23 wrobel Exp $
  *
  * @author  Chuck Hagenbuch <chuck at horde.org>
  * @author  Jon Parise <jon at csh.rit.edu>
@@ -118,8 +118,14 @@ class Turba_Object {
                 $args[] = $this->getValue($field);
             }
             return trim(vsprintf($this->driver->map[$attribute]['format'], $args), " \t\n\r\0\x0B,");
+        } elseif (!isset($this->attributes[$attribute])) {
+            return null;
+        } elseif (isset($GLOBALS['attributes'][$attribute]) &&
+                  $GLOBALS['attributes'][$attribute]['type'] == 'image') {
+            return array('load' => array('file' => basename(tempnam(Horde::getTempDir(), 'horde_form_')),
+                                         'data' => $this->attributes[$attribute]));
         } else {
-            return isset($this->attributes[$attribute]) ? $this->attributes[$attribute] : null;
+            return $this->attributes[$attribute];
         }
     }
 
@@ -243,7 +249,10 @@ class Turba_Object {
      */
     function addFile($info)
     {
-        $this->_vfsInit();
+        $result = $this->_vfsInit();
+        if (is_a($result, 'PEAR_Error')) {
+            return $result;
+        }
 
         $dir = TURBA_VFS_PATH . '/' . $this->getValue('__uid');
         $file = $info['name'];
diff --git a/lib/Turba.php b/lib/Turba.php
index da2437f..e785172 100644
--- a/lib/Turba.php
+++ b/lib/Turba.php
@@ -1,6 +1,6 @@
 <?php
 /**
- * $Horde: turba/lib/Turba.php,v 1.59.4.39 2008/06/13 09:59:43 jan Exp $
+ * $Horde: turba/lib/Turba.php,v 1.59.4.43 2008/10/07 12:56:17 jan Exp $
  *
  * @package Turba
  */
@@ -153,20 +153,9 @@ class Turba {
     /**
      * Returns the sort order selected by the user
      */
-    function getPreferredSortOrder($columns)
+    function getPreferredSortOrder()
     {
-        global $prefs;
-
-        $order = @unserialize($prefs->getValue('sortorder'));
-        if ($prefs->getValue('name_format') == 'last_first') {
-            for ($i = 0, $i_max = count($order); $i < $i_max; ++$i) {
-                if ($order[$i]['field'] == 'name') {
-                    $order[$i]['field'] = 'lastname';
-                }
-            }
-        }
-
-        return $order;
+        return @unserialize($GLOBALS['prefs']->getValue('sortorder'));
     }
 
     /**
@@ -174,15 +163,7 @@ class Turba {
      */
     function getColumnName($i, $columns)
     {
-        if ($i == 0) {
-            if ($GLOBALS['prefs']->getValue('name_format') == 'first_last') {
-                return 'name';
-            } else {
-                return 'lastname';
-            }
-        } else {
-            return $columns[$i - 1];
-        }
+        return $i == 0 ? 'name' : $columns[$i - 1];
     }
 
     /**
@@ -234,7 +215,7 @@ class Turba {
                 $name = $namelist[$nameindex];
             }
         }
-        return $name;
+        return strlen($name) ? $name : null;
     }
 
     /**
@@ -592,14 +573,19 @@ class Turba {
 
         /* Update share_id as backends like Kolab change it to the IMAP folder
          * name. */
-        $share_id = $share->getId();
-
-        /* Add the new addressbook to the user's list of visible
-         * address books. */
-        $prefs = explode("\n", $GLOBALS['prefs']->getValue('addressbooks'));
-        if (array_search($share_id, $prefs) === false) {
-            $GLOBALS['prefs']->setValue('addressbooks', $GLOBALS['prefs']->getValue('addressbooks') . "\n" . $share_id);
+        $share_id = $share->getName();
+
+        /* Add the new addressbook to the user's list of visible address
+         * books. */
+        $prefs = $GLOBALS['prefs']->getValue('addressbooks');
+        if ($prefs) {
+            $prefs = explode("\n", $prefs);
+            if (array_search($share_id, $prefs) === false) {
+                $prefs[] = $share_id;
+                $GLOBALS['prefs']->setValue('addressbooks', implode("\n", $prefs));
+            }
         }
+
         return $share;
     }
 
diff --git a/lib/Views/Browse.php b/lib/Views/Browse.php
index 2068e60..c1433cf 100644
--- a/lib/Views/Browse.php
+++ b/lib/Views/Browse.php
@@ -1,6 +1,6 @@
 <?php
 /**
- * $Horde: turba/lib/Views/Browse.php,v 1.12.2.6 2008/04/04 20:13:34 jan Exp $
+ * $Horde: turba/lib/Views/Browse.php,v 1.12.2.8 2008/09/11 08:46:20 jan Exp $
  *
  * Copyright 2000-2008 The Horde Project (http://www.horde.org/)
  *
@@ -43,7 +43,7 @@ class Turba_View_Browse {
     {
         extract($this->_params, EXTR_REFS);
 
-        if (($sortby = $vars->get('sortby')) !== null && $sortby != '') {
+        if (strlen($sortby = $vars->get('sortby'))) {
             $sources = Turba::getColumns();
             $columns = isset($sources[$source]) ? $sources[$source] : array();
             $column_name = Turba::getColumnName($sortby, $columns);
@@ -51,7 +51,7 @@ class Turba_View_Browse {
             $append = true;
             $ascending = ($vars->get('sortdir') == 0);
             if ($vars->get('sortadd')) {
-                $sortorder = Turba::getPreferredSortOrder($columns);
+                $sortorder = Turba::getPreferredSortOrder();
                 foreach ($sortorder as $i => $elt) {
                     if ($elt['field'] == $column_name) {
                         $sortorder[$i]['ascending'] = $ascending;
@@ -220,6 +220,13 @@ class Turba_View_Browse {
                         if (is_a($sourceDriver->delete($objectKey), 'PEAR_Error')) {
                             $notification->push(sprintf(_("There was an error deleting \"%s\" from the source address book."), $object->getValue('name')), 'horde.error');
                         }
+
+                        /* Log the adding of this item in the history again,
+                         * because otherwise the delete log would be after the
+                         * add log. */
+                        $history = &Horde_History::singleton();
+                        $history->log('turba:' . $targetDriver->getName() . ':' . $objAttributes['__uid'],
+                                      array('action' => 'add'), true);
                     }
                 }
                 break;
@@ -329,7 +336,7 @@ class Turba_View_Browse {
             // Read the columns to display from the preferences.
             $sources = Turba::getColumns();
             $columns = isset($sources[$source]) ? $sources[$source] : array();
-            $sortorder = Turba::getPreferredSortOrder($columns);
+            $sortorder = Turba::getPreferredSortOrder();
 
             if ($vars->get('key')) {
                 // We are displaying a list.
diff --git a/lib/api.php b/lib/api.php
index 9c327f6..b4d18d1 100644
--- a/lib/api.php
+++ b/lib/api.php
@@ -2,7 +2,7 @@
 /**
  * Turba external API interface.
  *
- * $Horde: turba/lib/api.php,v 1.120.2.49 2008/05/31 20:20:51 mrubinsk Exp $
+ * $Horde: turba/lib/api.php,v 1.120.2.61 2008/09/18 17:25:41 mrubinsk Exp $
  *
  * This file defines Turba's external API interface. Other applications can
  * interact with Turba through this API.
@@ -69,6 +69,11 @@ $_services['export'] = array(
     'type' => 'string',
 );
 
+$_services['ownVCard'] = array(
+    'args' => array(),
+    'type' => 'string',
+);
+
 $_services['delete'] = array(
     'args' => array('uid' => 'string'),
     'type' => 'boolean',
@@ -83,7 +88,8 @@ $_services['search'] = array(
     'args' => array('names' => '{urn:horde}stringArray',
                     'sources' => '{urn:horde}stringArray',
                     'fields' => '{urn:horde}stringArray',
-                    'matchBegin' => 'boolean'),
+                    'matchBegin' => 'boolean',
+                    'forceSource' => 'boolean'),
     'type' => '{urn:horde}stringArray',
 );
 
@@ -201,33 +207,50 @@ function _turba_removeUserData($user)
     /* We need a clean copy of the $cfgSources array here.*/
     require TURBA_BASE . '/config/sources.php';
     $hasError = false;
-    $shares = null;
 
     foreach ($cfgSources as $source) {
         if (empty($source['use_shares'])) {
             // Shares not enabled for this source
             $driver = &Turba_Driver::singleton($source);
             if (is_a($driver, 'PEAR_Error')) {
+                Horde::logMessage($driver, __FILE__, __LINE__, PEAR_LOG_ERR);
                 $hasError = true;
             } else {
                 $result = $driver->removeUserData($user);
                 if (is_a($result, 'PEAR_Error')) {
-                    $hasError = true;
+                    Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
                 }
             }
         }
     }
 
-    $shares = &$GLOBALS['turba_shares']->listShares($user,
-                                                    PERMS_EDIT,
-                                                    $user);
-    foreach ($shares as $share) {
-        $params = @unserialize($share->get('params'));
-        $config = Turba::getSourceFromShare($share);
-        $driver = &Turba_Driver::singleton($config);
-        $result = $driver->removeUserData($user);
-        if (is_a($result, 'PEAR_Error')) {
-            $hasError = true;
+    /* Only attempt share removal if we have shares configured */
+    if ($_SESSION['turba']['has_share']) {
+        $shares = &$GLOBALS['turba_shares']->listShares(
+            $user, PERMS_EDIT, $user);
+
+        /* Look for the deleted user's default share and remove it */
+        foreach ($shares as $share) {
+            $params = @unserialize($share->get('params'));
+            /* Only attempt to delete the user's default share */
+            if (!empty($params['default'])) {
+                $config = Turba::getSourceFromShare($share);
+                $driver = &Turba_Driver::singleton($config);
+                $result = $driver->removeUserData($user);
+                if (is_a($result, 'PEAR_Error')) {
+                    Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+                    $hasError = true;
+                }
+            }
+        }
+        
+        /* Get a list of all shares this user has perms to and remove the perms */
+        $shares = $GLOBALS['turba_shares']->listShares($user);
+        if (is_a($shares, 'PEAR_Error')) {
+            Horde::logMessage($shares, __FILE__, __LINE__, PEAR_LOG_ERR);
+        }
+        foreach ($shares as $share) {
+            $share->removeUser($user);
         }
     }
 
@@ -809,7 +832,8 @@ function _turba_getActionTimestamp($uid, $action, $sources = null)
  *
  * @param string $content      The content of the contact.
  * @param string $contentType  What format is the data in? Currently supports
- *                             array and text/(x-)vcard.
+ *                             array, text/directory, text/vcard and
+ *                             text/x-vcard.
  * @param string $source       The source into which the contact will be
  *                             imported.
  *
@@ -858,6 +882,7 @@ function _turba_import($content, $contentType = 'array', $import_source = null)
 
         case 'text/x-vcard':
         case 'text/vcard':
+        case 'text/directory':
             require_once 'Horde/iCalendar.php';
             $iCal = new Horde_iCalendar();
             if (!$iCal->parsevCalendar($content)) {
@@ -936,9 +961,10 @@ function _turba_import($content, $contentType = 'array', $import_source = null)
  * @param string $uid            Identify the contact to export.
  * @param mixed $contentType     What format should the data be in?
  *                               Either a string with one of:
+ *                               - text/directory
  *                               - text/vcard
  *                               - text/x-vcard
- *                               The first produces a vcard3.0 (rfc2426),
+ *                               The first two produce a vcard3.0 (rfc2426),
  *                               the second produces a vcard in old 2.1 format
  *                               defined by imc.org
  * @param string|array $sources  The source(s) from which the contact will be
@@ -992,16 +1018,16 @@ function _turba_export($uid, $contentType, $sources = null)
         }
 
         $version = '3.0';
+        list($contentType,) = explode(';', $contentType);
         switch ($contentType) {
-        case 'text/x-vcard;version=2.1':
         case 'text/x-vcard':
             $version = '2.1';
         case 'text/vcard':
+        case 'text/directory':
             require_once 'Horde/iCalendar.php';
             $export = '';
             foreach ($result->objects as $obj) {
                 $vcard = $driver->tovCard($obj, $version);
-                $vcard->setAttribute('VERSION', $version);
                 /* vCards are not enclosed in BEGIN:VCALENDAR..END:VCALENDAR.
                  * Export the individual cards instead. */
                 $export .= $vcard->exportvCalendar();
@@ -1016,6 +1042,47 @@ function _turba_export($uid, $contentType, $sources = null)
 }
 
 /**
+ * Exports the user's own contact as a vCard string.
+ *
+ * @return string  The requested vCard data or PEAR_Error.
+ */
+function _turba_ownVCard()
+{
+    require_once dirname(__FILE__) . '/base.php';
+    global $cfgSources;
+
+    $own_contact = $GLOBALS['prefs']->getValue('own_contact');
+    if (empty($own_contact)) {
+        return PEAR::raiseError(_("You didn't mark a contact as your own yet."));
+    }
+    @list($source, $id) = explode(';', $own_contact);
+
+    if (!isset($cfgSources[$source])) {
+        return PEAR::raiseError(_("The address book with your own contact doesn't exist anymore."));
+    }
+
+    $driver = &Turba_Driver::singleton($source);
+    if (is_a($driver, 'PEAR_Error')) {
+        return PEAR::raiseError(sprintf(_("Connection failed: %s"), $driver->getMessage()));
+    }
+
+    if (!$driver->hasPermission(PERMS_READ)) {
+        return PEAR::raiseError(_("You don't have sufficient permissions to read the address book that contains your own contact."));
+    }
+
+    $contact = $driver->getObject($id);
+    if (is_a($contact, 'PEAR_Error')) {
+        return PEAR::raiseError(_("Your own contact cannot be found in the address book."));
+    }
+
+    require_once 'Horde/iCalendar.php';
+    $vcard = $driver->tovCard($contact, '3.0');
+    $vcard->setAttribute('VERSION', '3.0');
+
+    return $vcard->exportvCalendar();
+}
+
+/**
  * Deletes a contact identified by UID.
  *
  * @param string|array $uid      Identify the contact to delete, either a
@@ -1097,7 +1164,8 @@ function _turba_delete($uid, $sources = null)
  * @param string $uid            Idenfity the contact to replace.
  * @param string $content        The content of the contact.
  * @param string $contentType    What format is the data in? Currently supports
- *                               array and text/(x-)vcard.
+ *                               array, text/directory, text/vcard and
+ *                               text/x-vcard.
  * @param string|array $sources  The source(s) where the contact will be
  *                               replaced.
  *
@@ -1155,6 +1223,7 @@ function _turba_replace($uid, $content, $contentType, $sources = null)
 
         case 'text/x-vcard':
         case 'text/vcard':
+        case 'text/directory':
             require_once 'Horde/iCalendar.php';
             $iCal = new Horde_iCalendar();
             if (!$iCal->parsevCalendar($content)) {
@@ -1198,11 +1267,13 @@ function _turba_replace($uid, $content, $contentType, $sources = null)
  * @param array $sources        The sources to serach in
  * @param array $fields         The fields to serach on
  * @param boolean $matchBegin   Match word boundaries only
+ * @param boolean $forceSource  Whether to use the specified sources, even if
+ *                              they have been disabled in the preferences.
  *
  * @return array  Hash containing the search results.
  */
 function _turba_search($names = array(), $sources = array(), $fields = array(),
-                       $matchBegin = false)
+                       $matchBegin = false, $forceSource = false)
 {
     require_once dirname(__FILE__) . '/base.php';
     require_once 'Horde/MIME.php';
@@ -1216,11 +1287,13 @@ function _turba_search($names = array(), $sources = array(), $fields = array(),
         $names = is_null($names) ? array() : array($names);
     }
 
-    // Make sure the selected source is activated in Turba.
-    $addressbooks = array_keys(Turba::getAddressBookOrder());
-    foreach (array_keys($sources) as $id) {
-        if (!in_array($sources[$id], $addressbooks)) {
-            unset($sources[$id]);
+    if (!$forceSource) {
+        // Make sure the selected source is activated in Turba.
+        $addressbooks = array_keys(Turba::getAddressBooks());
+        foreach (array_keys($sources) as $id) {
+            if (!in_array($sources[$id], $addressbooks)) {
+                unset($sources[$id]);
+            }
         }
     }
 
@@ -1266,7 +1339,7 @@ function _turba_search($names = array(), $sources = array(), $fields = array(),
                 $criteria['name'] = trim($name);
             }
 
-            $search = $driver->search($criteria, Turba::getPreferredSortOrder($columns), 'OR', array(), array(), $matchBegin);
+            $search = $driver->search($criteria, Turba::getPreferredSortOrder(), 'OR', array(), array(), $matchBegin);
             if (!is_a($search, 'Turba_List')) {
                 continue;
             }
@@ -1301,9 +1374,15 @@ function _turba_search($names = array(), $sources = array(), $fields = array(),
                         }
                     }
 
+                    if ($ob->hasValue('name') ||
+                        !isset($ob->driver->alternativeName)) {
+                        $display_name = Turba::formatName($ob);
+                    } else {
+                        $display_name = $ob->getValue($ob->driver->alternativeName);
+                    }
                     if (count($email)) {
                         for ($i = 0; $i < count($email); $i++) {
-                            $seen_key = trim(String::lower($ob->getValue('name'))) . '/' . trim(String::lower($email[$i]));
+                            $seen_key = trim(String::lower($display_name)) . '/' . trim(String::lower($email[$i]));
                             if (!empty($seen[$seen_key])) {
                                 continue;
                             }
@@ -1313,7 +1392,7 @@ function _turba_search($names = array(), $sources = array(), $fields = array(),
                             }
                             $results[$name][] = array_merge($att,
                                 array('id' => $att['__key'],
-                                      'name' => Turba::formatName($ob),
+                                      'name' => $display_name,
                                       'email' => $email[$i],
                                       '__type' => 'Object',
                                       'source' => $source));
@@ -1324,7 +1403,7 @@ function _turba_search($names = array(), $sources = array(), $fields = array(),
                         }
                         $results[$name][] = array_merge($att,
                             array('id' => $att['__key'],
-                                  'name' => Turba::formatName($ob),
+                                  'name' => $display_name,
                                   'email' => null,
                                   '__type' => 'Object',
                                   'source' => $source));
@@ -1666,14 +1745,16 @@ function _turba_deleteClient($objectId = '')
  */
 function _turba_searchClients($names = array(), $fields = array(), $matchBegin = false)
 {
-    return _turba_search($names,
-                         array($GLOBALS['conf']['client']['addressbook']),
-                         array($GLOBALS['conf']['client']['addressbook'] => $fields),
-                         $matchBegin);
+    return _turba_search(
+        $names,
+        array($GLOBALS['conf']['client']['addressbook']),
+        array($GLOBALS['conf']['client']['addressbook'] => $fields),
+        $matchBegin,
+        true);
 }
 
 /**
- * Sets the value of the specified attribute of an contact
+ * Sets the value of the specified attribute of a contact
  *
  * @param string $address  Contact email address
  * @param string $name     Contact name
diff --git a/lib/prefs.php b/lib/prefs.php
index 741d7b1..b8416df 100644
--- a/lib/prefs.php
+++ b/lib/prefs.php
@@ -1,6 +1,6 @@
 <?php
 /**
- * $Horde: turba/lib/prefs.php,v 1.2.10.9 2008/04/10 13:37:07 jan Exp $
+ * $Horde: turba/lib/prefs.php,v 1.2.10.10 2008/08/25 17:10:33 jan Exp $
  *
  * Copyright 2001-2008 The Horde Project (http://www.horde.org/)
  *
@@ -22,7 +22,7 @@ function handle_columnselect($updated)
 function handle_addressbookselect($updated)
 {
     $addressbooks = Util::getFormData('addressbooks');
-    $GLOBALS['prefs']->setValue('addressbooks', $addressbooks);
+    $GLOBALS['prefs']->setValue('addressbooks', str_replace("\r", '', $addressbooks));
     return true;
 }
 
diff --git a/lib/tests/KolabTest.php b/lib/tests/KolabTest.php
new file mode 100644
index 0000000..5c697f1
--- /dev/null
+++ b/lib/tests/KolabTest.php
@@ -0,0 +1,203 @@
+<?php
+
+require_once dirname(__FILE__) . '/KolabTestBase.php';
+
+/**
+ * $Horde: turba/lib/tests/KolabTest.php,v 1.1.2.2 2008/11/07 15:03:33 selsky Exp $
+ *
+ * @author  Jason M. Felice <jason.m.felice at gmail.com>
+ * @package Turba
+ * @subpackage UnitTests
+ */
+class Turba_KolabTest extends Turba_KolabTestBase {
+
+    /**
+     * Test setup.
+     *
+     * @return NULL
+     */
+    public function setUp()
+    {
+        $this->prepareTurba();
+
+        $this->_kolab = &new Kolab();
+    }
+
+    function testBug5476()
+    {
+        /* Open our addressbook */
+        $this->_kolab->open('INBOX/Contacts', 1);
+
+        $object = array(
+            'uid' => 1,
+            'given-name' => 'test',
+            'last-name' => 'test',
+            'full-name' => 'test  test',
+        );
+
+        // Save the contact
+        $this->_kolab->_storage->save($object);
+
+        $object = array(
+            'uid' => 2,
+            'given-name' => 'test2',
+            'last-name' => 'test2',
+            'full-name' => 'test2  test2',
+        );
+
+        // Save the contact
+        $this->_kolab->_storage->save($object);
+
+        // Check that the driver can be created
+        $turba = Turba_Driver::singleton('wrobel at example.org');
+        $this->assertNoError($turba);
+
+        $result = $turba->search(array(), array('last-name'));
+        $this->assertNoError($result);
+        $this->assertEquals(2, $result->count());
+
+        $turba = Turba_Driver::singleton('INBOX%2Ftest2');
+        $result = $turba->search(array(), array('last-name'));
+
+        $this->assertEquals(0, $result->count());
+    }
+
+    function testPhoto()
+    {
+        /* Open our addressbook */
+        $this->_kolab->open('INBOX/Contacts', 1);
+
+        $object = array(
+            'uid' => 1,
+            'given-name' => 'photo',
+            'last-name' => 'photo',
+            'full-name' => 'photo photo',
+            'photo' => 'abcd',
+            'phototype' => 'image/jpeg',
+        );
+
+        // Save the contact
+        $turba = Turba_Driver::singleton('wrobel at example.org');
+        $this->assertNoError($turba);
+
+        $this->assertNoError($turba->_add($object));
+
+        $list = Kolab_List::singleton();
+        $share = &$list->getByShare('INBOX/Contacts', 'contact');
+        $data = &$share->getData();
+        $object = $data->getObject('1');
+        $this->assertTrue(isset($object['_attachments'][$object['picture']]));
+        $attachment = $data->getAttachment($object['_attachments'][$object['picture']]['key']);
+        $this->assertEquals("abcd\n", $attachment);
+    }
+
+    function testAttachments()
+    {
+        /* Open our addressbook */
+        $this->_kolab->open('INBOX/Contacts', 1);
+
+        $object = array(
+            'uid' => 'a',
+            'given-name' => 'atc',
+            'last-name' => 'atc',
+            'full-name' => 'atc atc',
+        );
+
+        // Save the contact
+        $turba = Turba_Driver::singleton('wrobel at example.org');
+        $this->assertNoError($turba);
+
+        $this->assertNoError($turba->_add($object));
+
+        $contact = $turba->getObject('a');
+        $this->assertNoError($contact);
+
+        $list = Kolab_List::singleton();
+        $share = &$list->getByShare('INBOX/Contacts', 'contact');
+        $data = &$share->getData();
+
+        $atc1 = Util::getTempFile();
+        $fh = fopen($atc1, 'w');
+        fwrite($fh, 'test');
+        fclose($fh);
+
+        $info = array('tmp_name' => $atc1,
+                      'name' => 'test.txt');
+        $this->assertNoError($contact->addFile($info));
+
+        $objects = $data->getObjects();
+        $this->assertEquals(1, count($objects));
+        $object = $data->getObject('a');
+        $this->assertTrue(isset($object['_attachments']));
+        $this->assertEquals(1, count($object['_attachments']));
+        $this->assertEquals(1, count($object['link-attachment']));
+        $this->assertContains('test.txt', $object['link-attachment']);
+        $attachment = $data->getAttachment($object['_attachments']['test.txt']['key']);
+        $this->assertEquals("test\n", $attachment);
+
+        $atc1 = Util::getTempFile();
+        $fh = fopen($atc1, 'w');
+        fwrite($fh, 'hhhh');
+        fclose($fh);
+
+        $info = array('tmp_name' => $atc1,
+                      'name' => 'test.txt');
+        $this->assertNoError($contact->addFile($info));
+
+        $objects = $data->getObjects();
+        $this->assertEquals(1, count($objects));
+        $object = $data->getObject('a');
+        $this->assertTrue(isset($object['_attachments']));
+        $this->assertEquals(2, count($object['_attachments']));
+        $this->assertEquals(2, count($object['link-attachment']));
+        $this->assertContains('test[1].txt', $object['link-attachment']);
+        $attachment = $data->getAttachment($object['_attachments']['test[1].txt']['key']);
+        $this->assertEquals("hhhh\n", $attachment);
+
+        $atc1 = Util::getTempFile();
+        $fh = fopen($atc1, 'w');
+        fwrite($fh, 'dummy');
+        fclose($fh);
+
+        $info = array('tmp_name' => $atc1,
+                      'name' => 'dummy.txt');
+        $this->assertNoError($contact->addFile($info));
+
+        $objects = $data->getObjects();
+        $this->assertEquals(1, count($objects));
+        $object = $data->getObject('a');
+        $this->assertTrue(isset($object['_attachments']));
+        $this->assertEquals(3, count($object['_attachments']));
+        $this->assertEquals(3, count($object['link-attachment']));
+        $this->assertContains('dummy.txt', $object['link-attachment']);
+        $attachment = $data->getAttachment($object['_attachments']['dummy.txt']['key']);
+        $this->assertEquals("dummy\n", $attachment);
+
+        $this->assertError($contact->deleteFile('doesnotexist.txt'), "Unable to delete VFS file.");
+
+        $this->assertNoError($contact->deleteFile('test[1].txt'));
+
+        $objects = $data->getObjects();
+        $this->assertEquals(1, count($objects));
+        $object = $data->getObject('a');
+        $this->assertTrue(!isset($object['_attachments']['test[1].txt']));
+        $this->assertEquals(2, count($object['_attachments']));
+        $this->assertEquals(2, count($object['link-attachment']));
+        $this->assertNotContains('test[1].txt', $object['link-attachment']);
+
+        $files = $contact->listFiles();
+        $this->assertNoError($files);
+        
+        $this->assertContains('test.txt', array_keys($files));
+        $this->assertContains('dummy.txt', array_keys($files));
+
+        $this->assertNoError($contact->deleteFiles());
+
+        $objects = $data->getObjects();
+        $this->assertEquals(1, count($objects));
+        $object = $data->getObject('a');
+        $this->assertTrue(!isset($object['_attachments']['test.txt']));
+        $this->assertTrue(!isset($object['_attachments']));
+        $this->assertTrue(!isset($object['link-attachment']));
+    }
+}
diff --git a/lib/tests/KolabTestBase.php b/lib/tests/KolabTestBase.php
new file mode 100644
index 0000000..ab1048e
--- /dev/null
+++ b/lib/tests/KolabTestBase.php
@@ -0,0 +1,185 @@
+<?php
+/**
+ * Base for PHPUnit scenarios.
+ *
+ * $Horde: turba/lib/tests/KolabTestBase.php,v 1.1.2.1 2008/11/07 13:20:23 wrobel Exp $
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Test
+ * @author   Gunnar Wrobel <wrobel at pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Storage
+ */
+
+/**
+ *  We need the unit test framework
+ */
+require_once 'Horde/Kolab/Test/Storage.php';
+
+/**
+ *  We need some additional tools for Turba
+ */
+require_once 'Horde/Share.php';
+require_once 'Horde/Kolab.php';
+
+/**
+ * Base for PHPUnit scenarios.
+ *
+ * $Horde: turba/lib/tests/KolabTestBase.php,v 1.1.2.1 2008/11/07 13:20:23 wrobel Exp $
+ *
+ * Copyright 2008 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Kolab
+ * @package  Kolab_Test
+ * @author   Gunnar Wrobel <wrobel at pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Storage
+ */
+class Turba_KolabTestBase extends Horde_Kolab_Test_Storage
+{
+    /**
+     * Handle a "given" step.
+     *
+     * @param array  &$world    Joined "world" of variables.
+     * @param string $action    The description of the step.
+     * @param array  $arguments Additional arguments to the step.
+     *
+     * @return mixed The outcome of the step.
+     */
+    public function runGiven(&$world, $action, $arguments)
+    {
+        switch($action) {
+        default:
+            return parent::runGiven($world, $action, $arguments);
+        }
+    }
+
+    /**
+     * Handle a "when" step.
+     *
+     * @param array  &$world    Joined "world" of variables.
+     * @param string $action    The description of the step.
+     * @param array  $arguments Additional arguments to the step.
+     *
+     * @return mixed The outcome of the step.
+     */
+    public function runWhen(&$world, $action, $arguments)
+    {
+        switch($action) {
+        default:
+            return parent::runWhen($world, $action, $arguments);
+        }
+    }
+
+    /**
+     * Handle a "then" step.
+     *
+     * @param array  &$world    Joined "world" of variables.
+     * @param string $action    The description of the step.
+     * @param array  $arguments Additional arguments to the step.
+     *
+     * @return mixed The outcome of the step.
+     */
+    public function runThen(&$world, $action, $arguments)
+    {
+        switch($action) {
+        default:
+            return parent::runThen($world, $action, $arguments);
+        }
+    }
+
+    /**
+     * Prepare the configuration.
+     *
+     * @return NULL
+     */
+    public function prepareConfiguration()
+    {
+    }
+
+    /**
+     * Prepare the registry.
+     *
+     * @return NULL
+     */
+    public function prepareRegistry()
+    {
+    }
+
+    /**
+     * Prepare the notification setup.
+     *
+     * @return NULL
+     */
+    public function prepareNotification()
+    {
+    }
+
+    /**
+     * Fix the read configuration.
+     *
+     * @return NULL
+     */
+    public function prepareFixedConfiguration()
+    {
+        $GLOBALS['conf'] = &$GLOBALS['registry']->_confCache['horde'];
+        $GLOBALS['conf']['kolab']['server']['driver'] = 'test';
+        $GLOBALS['conf']['documents']['type'] = 'horde';
+    }
+
+    /**
+     * Prepare the Turba setup.
+     *
+     * @return NULL
+     */
+    public function prepareTurba()
+    {
+        $world = &$this->prepareBasicSetup();
+
+        $this->assertTrue($world['auth']->authenticate('wrobel at example.org',
+                                                       array('password' => 'none')));
+
+        $GLOBALS['registry']->pushApp('turba');
+
+        // Find the base file path of Turba.
+        if (!defined('TURBA_BASE')) {
+            define('TURBA_BASE', dirname(__FILE__) . '/../..');
+        }
+
+        // Turba base libraries.
+        require_once TURBA_BASE . '/lib/Turba.php';
+        require_once TURBA_BASE . '/lib/Driver.php';
+        require_once TURBA_BASE . '/lib/Object.php';
+
+        // Turba source and attribute configuration.
+        include TURBA_BASE . '/config/attributes.php';
+        include TURBA_BASE . '/config/sources.php';
+        unset($cfgSources['kolab_global']);
+
+        $this->prepareNewFolder($world['storage'], 'Contacts', 'contact', true);
+        $this->prepareNewFolder($world['storage'], 'test2', 'contact');
+
+        $_SESSION['turba']['has_share'] =  true;
+        $GLOBALS['turba_shares'] = &Horde_Share::singleton('turba');
+
+        $GLOBALS['cfgSources'] = Turba::getConfigFromShares($cfgSources);
+
+        // Disable maintenance
+        $GLOBALS['prefs']->setValue('turba_maintenance_tasks',
+                                    serialize(array('upgradeprefs', 'upgradelists')));
+    }
+
+
+    function provideServerName() {
+        return 'localhost.localdomain';
+    }
+
+    function provideHordeBase() {
+        return dirname(__FILE__) . '/../../../';
+    }
+}
diff --git a/themes/graphics/az.png b/lib/tests/az.png
similarity index 100%
copy from themes/graphics/az.png
copy to lib/tests/az.png
diff --git a/lib/tests/tohash.phpt b/lib/tests/tohash.phpt
index 2e401bb..50c9652 100644
--- a/lib/tests/tohash.phpt
+++ b/lib/tests/tohash.phpt
@@ -22,6 +22,7 @@ EMAIL:jan at horde.org
 NICKNAME:yunosh
 TEL;HOME:+49 521 555123
 TEL;WORK:+49 521 555456
+TEL;WORK:+49 521 999999
 TEL;CELL:+49 177 555123
 TEL;FAX:+49 521 555789
 TEL;PAGER:+49 123 555789
@@ -42,7 +43,7 @@ ADR;HOME;CHARSET=ISO-8859-1;ENCODING=QUOTED-PRINTABLE:=
 ADR;WORK;CHARSET=ISO-8859-1;ENCODING=QUOTED-PRINTABLE:=
 ;;H=FCbschestr. 19;K=F6ln;Allg=E4u;;D=E4nemark
 TZ;VALUE=text:+02:00; Europe/Berlin
-GEO:52.516276;13.377778
+GEO:13.377778,52.516276
 BODY:
 END:VCARD
 ';
@@ -103,6 +104,95 @@ TEL;VOICE;WORK:6
 END:VCARD
 ';
 
+$vcard6 = '
+BEGIN:VCARD
+VERSION:2.1
+N:Lastname;Firstname;;;
+FN:Lastname, Firstname
+TITLE:
+ORG:Company Name;
+BDAY:
+TEL;HOME;VOICE;X-Synthesis-Ref1:(xxx) xxx-xxxx
+TEL;WORK;VOICE;X-Synthesis-Ref1:(xxx) xxx-xxxx
+TEL;CELL;VOICE;X-Synthesis-Ref1:(xxx) xxx-xxxx
+EMAIL:email at domain.com
+URL:
+CATEGORIES:Friends
+NOTE;ENCODING=QUOTED-PRINTABLE:
+EIN: xx-xxxxxxx
+ADR;HOME:;;Street address;City;St;12345;USA
+ADR;WORK:;;Street address;City;St;12345;USA
+PHOTO:
+END:VCARD
+';
+
+$vcard7 = '
+BEGIN:VCARD
+FN:Jan Schneider
+N:Schneider;Jan;;;
+PHOTO;ENCODING=b;TYPE=image/png:iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJAgMAAACd/+6D
+  AAAACVBMVEW6ABZmZmYAAACMtcxCAAAAAXRSTlMAQObYZgAAABpJREFUCFtjYACBBgYmRgEIZm
+  GBYAFGMAYBABVmAOEH9qP8AAAAAElFTkSuQmCC
+UID:nhCnPyv0u7
+VERSION:2.1
+END:VCARD
+';
+
+$vcard8 = '
+BEGIN:VCARD
+FN:Jan Schneider
+N:Schneider;Jan;;;
+EMAIL;WORK:work at example.com
+EMAIL;HOME:home at example.com
+EMAIL:mail at example.com
+EMAIL;PREF:pref at example.com
+UID:nhCnPyv0u7
+VERSION:2.1
+END:VCARD
+';
+
+$vcard9 = '
+BEGIN:VCARD
+VERSION:2.1
+N:Blow;Joe;;;
+FN:Blow, Joe
+TITLE:
+ORG:;
+BDAY:19700327
+TEL;HOME;VOICE;X-Synthesis-Ref1:302 834 9999
+TEL;CELL;VOICE;X-Synthesis-Ref1:302 521 9999
+EMAIL:Blow at somwhere.net
+URL:
+CATEGORIES:Personal
+NOTE:
+ADR;HOME:;;;;;;
+PHOTO:
+END:VCARD
+';
+
+// Invalid ENCODING value.
+$vcard10 = '
+BEGIN:VCARD
+VERSION:3.0
+PRODID:-//Synthesis AG//NONSGML SyncML Engine V3.1.6.10//EN
+REV:20081004T151032
+N:McTester;Testie;;;
+FN:Testie McTester
+ORG:Testers Inc;
+TEL;TYPE=VOICE,CELL,X-Synthesis-Ref0:+44 775550555
+TEL;TYPE=HOME,VOICE,X-Synthesis-Ref1:+44 205550555
+TEL;TYPE=WORK,VOICE,X-Synthesis-Ref2:+44 205550556
+EMAIL;TYPE=HOME,INTERNET,X-Synthesis-Ref0:test at example.org
+EMAIL;TYPE=WORK,INTERNET,X-Synthesis-Ref1:test at example.com
+ADR;TYPE=HOME,X-Synthesis-Ref0:;;111 One Street;London;;W1 1AA;
+BDAY:20081008
+PHOTO;TYPE=JPEG;ENCODING=BASE64:wolQTkcNChoKAAAADUlIRFIAAAAJAAAACQIDAAAAwp3
+ Dv8OuwoMAAAAJUExURcK6ABZmZmYAAADCjMK1w4xCAAAAAXRSTlMAQMOmw5hmAAAAGklEQVQIW2
+ NgAMKBBgYmRgEIZmHCgWABRjAGAQAVZgDDoQfDtsKjw7wAAAAASUVORMKuQmDCgg==
+ 
+END:VCARD
+';
+
 $driver = new Turba_Driver(array());
 $iCal = new Horde_iCalendar();
 
@@ -135,6 +225,30 @@ $iCal->parsevCalendar($vcard5);
 var_export($driver->toHash($iCal->getComponent(0)));
 echo "\n";
 
+$iCal->parsevCalendar($vcard6);
+var_export($driver->toHash($iCal->getComponent(0)));
+echo "\n";
+
+$iCal->parsevCalendar($vcard7);
+$hash = $driver->toHash($iCal->getComponent(0));
+var_export($hash['photo'] == file_get_contents(dirname(__FILE__) . '/az.png'));
+echo "\n";
+
+$iCal->parsevCalendar($vcard8);
+var_export($driver->toHash($iCal->getComponent(0)));
+echo "\n";
+
+$iCal->parsevCalendar($vcard9);
+var_export($driver->toHash($iCal->getComponent(0)));
+echo "\n";
+
+$iCal->parsevCalendar($vcard10);
+$hash = $driver->toHash($iCal->getComponent(0));
+var_export(strlen($hash['photo']));
+echo "\n";
+unset($hash['photo']);
+var_export($hash);
+
 ?>
 --EXPECT--
 array (
@@ -245,3 +359,84 @@ array (
   'workPhone' => '6',
   'name' => 'A  B',
 )
+array (
+  'lastname' => 'Lastname',
+  'firstname' => 'Firstname',
+  'name' => 'Lastname, Firstname',
+  'company' => 'Company Name',
+  'department' => '',
+  'homePhone' => '(xxx) xxx-xxxx',
+  'workPhone' => '(xxx) xxx-xxxx',
+  'cellPhone' => '(xxx) xxx-xxxx',
+  'email' => 'email at domain.com',
+  'emails' => 'email at domain.com',
+  'category' => 'Friends',
+  'businessCategory' => 'Friends',
+  'homeAddress' => 'Street address
+City, St 12345
+USA',
+  'homeStreet' => 'Street address',
+  'homeCity' => 'City',
+  'homeProvince' => 'St',
+  'homePostalCode' => '12345',
+  'homeCountry' => 'USA',
+  'workAddress' => 'Street address
+City, St 12345
+USA',
+  'workStreet' => 'Street address',
+  'workCity' => 'City',
+  'workProvince' => 'St',
+  'workPostalCode' => '12345',
+  'workCountry' => 'USA',
+)
+true
+array (
+  'name' => 'Jan Schneider',
+  'lastname' => 'Schneider',
+  'firstname' => 'Jan',
+  'workEmail' => 'work at example.com',
+  'emails' => 'work at example.com, home at example.com, mail at example.com, pref at example.com',
+  'homeEmail' => 'home at example.com',
+  'email' => 'pref at example.com',
+)
+array (
+  'lastname' => 'Blow',
+  'firstname' => 'Joe',
+  'name' => 'Blow, Joe',
+  'company' => '',
+  'department' => '',
+  'birthday' => '1970-03-27',
+  'homePhone' => '302 834 9999',
+  'cellPhone' => '302 521 9999',
+  'email' => 'Blow at somwhere.net',
+  'emails' => 'Blow at somwhere.net',
+  'category' => 'Personal',
+  'businessCategory' => 'Personal',
+  'homeAddress' => '',
+)
+136
+array (
+  'lastname' => 'McTester',
+  'firstname' => 'Testie',
+  'name' => 'Testie McTester',
+  'company' => 'Testers Inc',
+  'department' => '',
+  'cellPhone' => '+44 775550555',
+  'homePhone' => '+44 205550555',
+  'workPhone' => '+44 205550556',
+  'homeEmail' => 'test at example.org',
+  'emails' => 'test at example.org, test at example.com',
+  'workEmail' => 'test at example.com',
+  'homeAddress' => '111 One Street
+London W1 1AA',
+  'homeStreet' => '111 One Street',
+  'homeCity' => 'London',
+  'homePostalCode' => 'W1 1AA',
+  'commonAddress' => '111 One Street
+London W1 1AA',
+  'commonStreet' => '111 One Street',
+  'commonCity' => 'London',
+  'commonPostalCode' => 'W1 1AA',
+  'birthday' => '2008-10-08',
+  'phototype' => 'JPEG',
+)
diff --git a/lib/tests/tovcard.phpt b/lib/tests/tovcard.phpt
index de171e3..0eb9c2a 100644
--- a/lib/tests/tovcard.phpt
+++ b/lib/tests/tovcard.phpt
@@ -36,6 +36,8 @@ $attributes = array(
   'timezone' => 'Europe/Berlin',
   'latitude' => '52.516276',
   'longitude' => '13.377778',
+  'photo' => file_get_contents(dirname(__FILE__) . '/az.png'),
+  'phototype' => 'image/png',
 );
 
 $driver = new Turba_Driver(array());
@@ -52,6 +54,8 @@ VERSION:2.1
 FN;CHARSET=ISO-8859-1;ENCODING=QUOTED-PRINTABLE:Jan Schneider=F6
 EMAIL:jan at horde.org
 NICKNAME:yunosh
+LABEL;HOME;ENCODING=QUOTED-PRINTABLE;CHARSET=ISO-8859-1:Sch=F6nestr. 15=0D=0A=
+33604 Bielefeld
 TEL;HOME:+49 521 555123
 TEL;WORK:+49 521 555456
 TEL;CELL:+49 177 555123
@@ -63,13 +67,13 @@ ROLE;CHARSET=ISO-8859-1;ENCODING=QUOTED-PRINTABLE:Developer (=E4=F6=FC)
 NOTE;CHARSET=ISO-8859-1;ENCODING=QUOTED-PRINTABLE:A German guy (=E4=F6=FC)
 URL:http://janschneider.de
 TZ;VALUE=text:Europe/Berlin
-GEO:52.516276;13.377778
+GEO:13.377778,52.516276
+PHOTO;ENCODING=b;TYPE=image/png:iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJAgMAAACd/+6DAAAACVBMVEW6ABZmZmYAAACMtcxCAAAAAXRSTlMAQObYZgAAABpJREFUCFtjYACBBgYmRgEIZmGBYAFGMAYBABVmAOEH9qP8AAAAAElFTkSuQmCC
 N;CHARSET=ISO-8859-1;ENCODING=QUOTED-PRINTABLE:Schneider=F6;Jan;K.;Mr.;
 ORG;CHARSET=ISO-8859-1;ENCODING=QUOTED-PRINTABLE:Horde Project;=E4=F6=FC
 ADR;HOME;CHARSET=ISO-8859-1;ENCODING=QUOTED-PRINTABLE:;;Sch=F6nestr. 15=0D=0A=
 33604 Bielefeld;;;;
 ADR;WORK;CHARSET=ISO-8859-1;ENCODING=QUOTED-PRINTABLE:;;H=FCbschestr. 19;K=F6ln;Allg=E4u;;D=E4nemark
-BODY:
 END:VCARD
 
 BEGIN:VCARD
@@ -77,6 +81,7 @@ VERSION:3.0
 FN:Jan Schneiderö
 EMAIL:jan at horde.org
 NICKNAME:yunosh
+LABEL;TYPE=HOME:Schönestr. 15\n33604 Bielefeld
 TEL;TYPE=HOME:+49 521 555123
 TEL;TYPE=WORK:+49 521 555456
 TEL;TYPE=CELL:+49 177 555123
@@ -89,9 +94,11 @@ NOTE:A German guy (äöü)
 URL:http://janschneider.de
 TZ;VALUE=text:Europe/Berlin
 GEO:52.516276;13.377778
+PHOTO;ENCODING=b;TYPE=image/png:wolQTkcNChoKAAAADUlIRFIAAAAJAAAACQIDAAAAwp3
+ Dv8OuwoMAAAAJUExURcK6ABZmZmYAAADCjMK1w4xCAAAAAXRSTlMAQMOmw5hmAAAAGklEQVQIW2
+ NgAMKBBgYmRgEIZmHCgWABRjAGAQAVZgDDoQfDtsKjw7wAAAAASUVORMKuQmDCgg==
 N:Schneiderö;Jan;K.;Mr.;
 ORG:Horde Project;äöü
 ADR;TYPE=HOME:;;Schönestr. 15\n33604 Bielefeld;;;;
 ADR;TYPE=WORK:;;Hübschestr. 19;Köln;Allgäu;;Dänemark
-BODY:
 END:VCARD
diff --git a/lib/version.php b/lib/version.php
index 0d6f882..d7dfb6c 100644
--- a/lib/version.php
+++ b/lib/version.php
@@ -1 +1 @@
-<?php define('TURBA_VERSION', 'H3 (2.2.1)') ?>
+<?php define('TURBA_VERSION', 'H3 (2.3.1)') ?>
diff --git a/locale/de_DE/LC_MESSAGES/turba.mo b/locale/de_DE/LC_MESSAGES/turba.mo
index 2de8f6a..1884a7a 100644
Binary files a/locale/de_DE/LC_MESSAGES/turba.mo and b/locale/de_DE/LC_MESSAGES/turba.mo differ
diff --git a/locale/et_EE/LC_MESSAGES/turba.mo b/locale/et_EE/LC_MESSAGES/turba.mo
index 2f189e1..47f8d66 100644
Binary files a/locale/et_EE/LC_MESSAGES/turba.mo and b/locale/et_EE/LC_MESSAGES/turba.mo differ
diff --git a/locale/eu_ES/LC_MESSAGES/turba.mo b/locale/eu_ES/LC_MESSAGES/turba.mo
new file mode 100644
index 0000000..d1cc726
Binary files /dev/null and b/locale/eu_ES/LC_MESSAGES/turba.mo differ
diff --git a/locale/eu_ES/help.xml b/locale/eu_ES/help.xml
new file mode 100644
index 0000000..d7a47a8
--- /dev/null
+++ b/locale/eu_ES/help.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0"?>
+<!-- $Horde: turba/locale/eu_ES/help.xml,v 1.1.2.1 2008/07/17 21:09:27 jan Exp $ -->
+<help>
+
+<entry id="Overview" md5="290506ade4e6f749246f77bbe5d48835" state="uptodate">
+    <title>Informazio orokorra</title>
+    <heading>Sarrera</heading> <para>Helbide-liburuak kontaktuen informazioa gordetzeko metodo eta leku egokia eskaintzen ditu, informazio hori erraz eskuratzeko eta erabiltzeko. Administratzaileak sistema konfiguratzeko erabilitako moduaren arabera, helbide-liburu pribatu bat nahiz sareko direktorio-zerbitzu publikoetarako sarbidea izan dezakezu. Banaketa-zerrendak ere onartzen dituenez, zure "posta-zerrendak" sor ditzakezu.</para></entry>
+
+<entry id="menu-add" md5="3c1f92eb1a82839404d11d9b0a20b3ce" state="uptodate">
+    <title>Menua: Kontaktu berria</title>
+    <heading>Kontaktu berria</heading> <para>Helbide-liburuan beste erabiltzaile bat sartzea da. Helbide-liburuan, idazteko baimena eman dizuten erabiltzaileak sar ditzakezu soilik. Partekatutako helbide-liburuak edo helbide-liburu publikoak gaitu badira, askotan irakurtzeko soilik izaten dira; kasu horretan, ezingo duzu kontakturik sartu.</para></entry>
+
+<entry id="menu-search" md5="8daf81ab28602d772358a174571f9ad2" state="uptodate">
+    <title>Menua: Bilatu</title>
+    <heading>Bilatu</heading> <para>Helbide-liburu batean bilaketak egin ditzakezu informazioa eskuratzeko. Hori bilaketa erraza da, eta oinarrizko eremu batzuetan informazioa bilatzeko balio du. Askotan izena eta helbide elektronikoa eremuak erabili ohi dira, baina alda daitezke administratzaileak helbide-liburua konfiguratzeko erabilitako moduaren arabera.</para> <para>Bilaketa konplexuagoa egiteko "<ref module="turba" entry="menu-advanced-search">Bilaketa aurreratua</ref>" erabil dezakezu, eta horrela beste eremu batzuetan ere bilaketak egin ahal izango dituzu.</para></entry>
+
+<entry id="menu-advanced-search" md5="6be703ffeba614f6ada151749fa44ab5" state="uptodate">
+    <title>Menua: Bilaketa aurreratua</title>
+    <heading>Bilaketa aurreratua</heading> <para>Helbide-liburu batean bilaketak egin ditzakezu informazioa eskuratzeko. Bilaketa aurreratuaren bidez, hainbat eremutan egin ditzakezu bilaketak. Bilaketak egiteko zein eremu egongo diren erabilgarri, bilaketak egingo diren helbide-liburuaren arabera dago.</para></entry>
+
+<entry id="menu-import" md5="3c6c40f2a6213e9fc1f9a77827bfb5d4" state="uptodate">
+    <title>Menua: Inportatu/Esportatu</title>
+    <heading>Inportatu/Esportatu</heading>
+
+    <para>Menu honen bidez, helbide-liburura inportatu eta helbide-liburutik esporta dezakezu. Formatu hauek onartzen ditu: CSV (komaz bereizitako balioak), Outlook eta vCard formatuak, baita beste formatu ezagun batzuk ere.</para>
+
+    <heading>Inportatu helbide-liburua</heading> <para>Helbide-liburu bat inportatzeko, helbide-liburuko datuak makina lokalean eduki behar dituzu (web-arakatzailea bertan exekutatzen baita). Hori egiteko, aurrena helbide-liburuko informazioa esportatu behar duzu uneko posta elektronikotik.</para>
+
+    <para>Outlook Express erabiltzen baduzu:</para> <para>1) Hautatu Fitxategia &gt; Esportatu &gt; Helbide-liburua</para> <para>2) Hautatu "Helbide-liburua esportatzeko tresnak" aukerako "Testu-fitxategia" (Komaz bereizitako balioak). Egin klik "Esportatu" aukeran.</para> <para>3) "Gorde honela esportatutako fitxategia" eremuan, idatzi zure helbide-liburuaren izena. Egin klik "Hurrengoa" aukeran.</para> <para>4) Jarri hautamarka bat esportatu nahi duzun eremu bakoitzaren ezkerrean dagoen koadroan. Egin klik "Amaitu" aukeran.</para> <para>5) 'Helbide-liburuaren esportazio-prozesua amaitu da' adierazten duen mezu bat jasoko duzu. Sakatu 'Ados'.</para> <para>6) Sakatu ''Helbide-liburua esportatzeko tresnak" aukerako "Itxi" botoia eta itxi Outlook Express.</para>
+
+    <tip>Esportatutako helbide-liburuko fitxategia ez badago zure mahaigainean eta Windows sistema eragilea erabiltzen ari bazara, sakatu Hasi &gt; Bilatu&gt; Karpetak eta bilatu 3. urratsean idatzi duzun fitxategi-izena disko gogorrean. Idatzi ohar batean fitxategiaren kokapena.</tip>
+
+    <para>Helbide-liburu bat inportatzeko, ireki inportatu nahi duzun helbide-liburua, informazioa bertan jartzeko. Egin klik "Inportatu/Esportatu" ikonoan. Hautatu hautapen-zerrendatik inportatu nahi duzun datu-fitxategiko helbidearen informazioa. Egin klik "arakatzailea" botoian, helbidearen datu-fitxategia bilatzeko zure ordenagailuan. Hautatu fitxategia eta egin klik "Inportatu" botoian. Fitxategiko informazioa helbide-liburura inportatuko da.</para> <tip>Sarrera konplexu batzuk edo zure helbide-liburuko eremuekin bat ez datozen eremuak dauzkaten sarrera batzuk, agian, ez dira behar bezala inportatuko. Inportazio bat egin ondoren, inportatutako datuak egiaztatu beharko zenituzke.</tip></entry>
+
+<entry id="searching-search_results" md5="ffed8d72fa3b710df8e902c2493b254f" state="uptodate">
+    <title>Bilaketa: Bilaketaren emaitzak</title>
+    <heading>Bilaketaren emaitzak</heading> <para>Bilaketak emaitzaren bat ematen badu, "Bilaketaren emaitzak" arean azalduko dira.</para> <para>"Posta elektronikoa" sarreraren eremuan klik eginez, mezu berria idazteko leiho bat irekiko da, helbide horretara mezu bat bidaltzeko. "Izena" sarreraren eremuan klik eginez, izen horren helbide-liburua bistaratuko da. Horrela, sarrera hori editatu edo ezabatu ahal izango duzu.</para> <para>Zure administratzailearen sistema ezartzeko moduaren arabera, emaitzak metagarriak izan daitezke. Bilaketa bakoitzak ematen duen edozein emaitza lehendik dauden emaitzekin batera agertuko da. Hori lagungarria da zerrendak eraikitzean. Metatutako bilaketen emaitzak kentzeko eta beste bilaketa bat egiteko, egin klik "Garbitu bilaketa" estekan.</para>
+
+    <tip>Sarreren zerrenda bat ikustean, sarrerak ordena ditzakezu dauden eremuen arabera, zutabearen goiburu egokian klik eginez. Goranzko edo beheranzko ordenara aldatzeko, egin klik zutabearen izenburuko geziaren ikonoan.</tip></entry>
+
+</help>
diff --git a/locale/fi_FI/LC_MESSAGES/turba.mo b/locale/fi_FI/LC_MESSAGES/turba.mo
index 1ae252c..caca5ac 100644
Binary files a/locale/fi_FI/LC_MESSAGES/turba.mo and b/locale/fi_FI/LC_MESSAGES/turba.mo differ
diff --git a/locale/fr_FR/LC_MESSAGES/turba.mo b/locale/fr_FR/LC_MESSAGES/turba.mo
index e9598d1..5f48e18 100644
Binary files a/locale/fr_FR/LC_MESSAGES/turba.mo and b/locale/fr_FR/LC_MESSAGES/turba.mo differ
diff --git a/locale/ja_JP/LC_MESSAGES/turba.mo b/locale/ja_JP/LC_MESSAGES/turba.mo
index b3b2f0a..660c9d8 100644
Binary files a/locale/ja_JP/LC_MESSAGES/turba.mo and b/locale/ja_JP/LC_MESSAGES/turba.mo differ
diff --git a/locale/nl_NL/LC_MESSAGES/turba.mo b/locale/nl_NL/LC_MESSAGES/turba.mo
index 85c9269..6f94eb6 100644
Binary files a/locale/nl_NL/LC_MESSAGES/turba.mo and b/locale/nl_NL/LC_MESSAGES/turba.mo differ
diff --git a/locale/pt_BR/LC_MESSAGES/turba.mo b/locale/pt_BR/LC_MESSAGES/turba.mo
index c0b0fbd..47fcf24 100644
Binary files a/locale/pt_BR/LC_MESSAGES/turba.mo and b/locale/pt_BR/LC_MESSAGES/turba.mo differ
diff --git a/locale/sk_SK/LC_MESSAGES/turba.mo b/locale/sk_SK/LC_MESSAGES/turba.mo
index 805e9e8..847400a 100644
Binary files a/locale/sk_SK/LC_MESSAGES/turba.mo and b/locale/sk_SK/LC_MESSAGES/turba.mo differ
diff --git a/locale/tr_TR/LC_MESSAGES/turba.mo b/locale/tr_TR/LC_MESSAGES/turba.mo
index 0165106..d6264b2 100644
Binary files a/locale/tr_TR/LC_MESSAGES/turba.mo and b/locale/tr_TR/LC_MESSAGES/turba.mo differ
diff --git a/locale/uk_UA/LC_MESSAGES/turba.mo b/locale/uk_UA/LC_MESSAGES/turba.mo
index 843f0d5..967cb72 100644
Binary files a/locale/uk_UA/LC_MESSAGES/turba.mo and b/locale/uk_UA/LC_MESSAGES/turba.mo differ
diff --git a/po/de_DE.po b/po/de_DE.po
index 5a90f22..126ac3f 100644
--- a/po/de_DE.po
+++ b/po/de_DE.po
@@ -7,13 +7,14 @@ msgid ""
 msgstr ""
 "Project-Id-Version: Turba 2.2-cvs\n"
 "Report-Msgid-Bugs-To: dev at lists.horde.org\n"
-"POT-Creation-Date: 2008-06-13 23:43+0200\n"
-"PO-Revision-Date: 2008-06-13 23:45+0200\n"
+"POT-Creation-Date: 2008-09-25 12:32+0200\n"
+"PO-Revision-Date: 2008-09-06 12:20+0200\n"
 "Last-Translator: Jan Schneider <jan at horde.org>\n"
 "Language-Team: German <i18n at lists.horde.org>\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=iso-8859-1\n"
 "Content-Transfer-Encoding: 8-bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
 #: templates/prefs/imsp_opt.inc:40
 msgid " Add an IMSP Address Book"
@@ -23,19 +24,19 @@ msgstr "IMSP-Adressbuch hinzuf
 msgid " Delete IMSP Address Book"
 msgstr "IMSP-Adressbuch löschen"
 
-#: data.php:367
+#: data.php:455
 #, php-format
 msgid "\"%s\" already exists and was not imported."
 msgstr "\"%s\" existiert bereits und wurde nicht importiert."
 
-#: lib/Forms/EditContact.php:76
+#: lib/Forms/EditContact.php:83
 #, php-format
 msgid "\"%s\" updated, but saving the uploaded file failed: %s"
 msgstr ""
 "\"%s\" wurde aktualisiert, aber die hochgeladene Datei konnte nicht "
 "gespeichert werden: %s"
 
-#: lib/Forms/EditContact.php:78 lib/Forms/EditContact.php:81
+#: lib/Forms/EditContact.php:85 lib/Forms/EditContact.php:88
 #, php-format
 msgid "\"%s\" updated."
 msgstr "\"%s\" aktualisiert."
@@ -58,30 +59,30 @@ msgstr "\"Vorname Nachname\" (z.B. Max Mustermann)"
 msgid "\"Lastname, Firstname\" (ie. Doe, John)"
 msgstr "\"Nachname, Vorname\" (z.B. Mustermann, Max)"
 
-#: lib/Forms/AddContact.php:83
+#: lib/Driver.php:619
+#, php-format
+msgid "%d. %s of %s"
+msgstr "%d. %s von %s"
+
+#: lib/Forms/AddContact.php:88
 #, php-format
 msgid "%s added."
 msgstr "%s hinzugefügt."
 
-#: data.php:392
+#: data.php:483
 #, php-format
 msgid "%s file successfully imported."
 msgstr "Die %s-Datei wurde erfolgreich importiert."
 
-#: lib/Driver.php:578
-#, php-format
-msgid "%s of %s"
-msgstr "%s von %s"
-
 #: templates/list/numPager.inc:6
 #, php-format
 msgid "%s to %s of %s"
 msgstr "%s bis %s von %s"
 
-#: lib/Turba.php:560
+#: lib/Turba.php:541
 #, php-format
 msgid "%s's Address Book"
-msgstr "%ss Adressbuch"
+msgstr "Adressbuch von %s"
 
 #: lib/Block/minisearch.php:44
 msgid "A browser that supports iframes is required"
@@ -112,7 +113,7 @@ msgstr "Datei hinzuf
 msgid "Add to"
 msgstr "Hinzufügen zu"
 
-#: lib/Driver.php:1959 lib/Driver/null.php:57
+#: lib/Driver.php:2113 lib/Driver/null.php:57
 msgid "Adding contacts is not available."
 msgstr "Hinzufügen von Kontakten nicht unterstützt."
 
@@ -132,7 +133,7 @@ msgstr "Adressbuchliste"
 msgid "Address Books"
 msgstr "Adressbücher"
 
-#: data.php:347
+#: data.php:435
 msgid "Address book successfully purged."
 msgstr "Adressbuch wurde erfolgreich geleert."
 
@@ -141,7 +142,7 @@ msgstr "Adressbuch wurde erfolgreich geleert."
 msgid "Address books that will not be displayed:"
 msgstr "Adressbücher, die nicht angezeigt werden:"
 
-#: search.php:199 templates/browse/search.inc:47
+#: search.php:184 templates/browse/search.inc:47
 msgid "Advanced Search"
 msgstr "Erweiterte Suche"
 
@@ -153,7 +154,7 @@ msgstr "Spitzname"
 msgid "All"
 msgstr "Alle"
 
-#: lib/api.php:916
+#: lib/api.php:941
 msgid "Already Exists"
 msgstr "Existiert bereits"
 
@@ -174,11 +175,11 @@ msgstr "Sind Sie sicher, dass Sie %s l
 msgid "Are you sure that you want to delete the selected contacts?"
 msgstr "Sind Sie sicher, dass Sie die ausgewählten Kontakte löschen möchten?"
 
-#: config/attributes.php.dist:378
+#: config/attributes.php.dist:424
 msgid "Assistant"
 msgstr "Assistent"
 
-#: search.php:196 templates/browse/search.inc:46
+#: search.php:181 templates/browse/search.inc:46
 msgid "Basic Search"
 msgstr "Einfache Suche"
 
@@ -195,7 +196,7 @@ msgstr "Geburtstag"
 msgid "Birthdays"
 msgstr "Geburtstage"
 
-#: contact.php:106 templates/browse/row.inc:6
+#: contact.php:128 templates/browse/row.inc:10
 msgid "Blank name"
 msgstr "Leerer Name"
 
@@ -208,11 +209,11 @@ msgstr "Beides"
 msgid "Browse"
 msgstr "Liste"
 
-#: config/attributes.php.dist:288
+#: config/attributes.php.dist:300
 msgid "Business Category"
 msgstr "Geschäftskategorie"
 
-#: data.php:86
+#: data.php:101
 msgid "CSV"
 msgstr "CSV"
 
@@ -229,7 +230,7 @@ msgstr "Abbrechen"
 msgid "Cannot delete all address book entries for %s"
 msgstr "Es konnten nicht alle Adressbucheinträge für %s gelöscht werden"
 
-#: config/attributes.php.dist:350
+#: config/attributes.php.dist:388
 msgid "Category"
 msgstr "Kategorie"
 
@@ -297,58 +298,59 @@ msgstr "Kommagetrennte Werte"
 msgid "Comma separated values (Microsoft Outlook)"
 msgstr "Kommagetrennte Werte (Microsoft Outlook)"
 
-#: config/attributes.php.dist:476
+#: config/attributes.php.dist:522
 msgid "Common Address Extended"
 msgstr "Erweiterte Adresse allgemein"
 
-#: config/attributes.php.dist:494
+#: config/attributes.php.dist:540
 msgid "Common City"
 msgstr "Stadt allgemein"
 
-#: config/attributes.php.dist:512
+#: config/attributes.php.dist:558
 msgid "Common Country"
 msgstr "Land allgemein"
 
-#: config/attributes.php.dist:420
+#: config/attributes.php.dist:466
 msgid "Common Phone"
 msgstr "Telefon allgemein"
 
-#: config/attributes.php.dist:488
+#: config/attributes.php.dist:534
 msgid "Common Post Office Box"
 msgstr "Postfach allgemein"
 
-#: config/attributes.php.dist:506
+#: config/attributes.php.dist:552
 msgid "Common Postal Code"
 msgstr "Postleitzahl allgemein"
 
-#: config/attributes.php.dist:500
+#: config/attributes.php.dist:546
 msgid "Common State/Province"
 msgstr "Bundesstaat oder Provinz allgemein"
 
-#: config/attributes.php.dist:482
+#: config/attributes.php.dist:528
 msgid "Common Street"
 msgstr "Straße allgemein"
 
-#: config/attributes.php.dist:445
+#: config/attributes.php.dist:491
 msgid "Common Video Call"
 msgstr "Bildtelefon allgemein"
 
-#: config/sources.php.dist:243 config/sources.php.dist:922
+#: config/sources.php.dist:256 config/sources.php.dist:948
 msgid "Communications"
 msgstr "Kommunikation"
 
-#: config/attributes.php.dist:294
+#: config/attributes.php.dist:306
 msgid "Company"
 msgstr "Firma"
 
-#: config/attributes.php.dist:223
+#: config/attributes.php.dist:235
 msgid "Company Address"
 msgstr "Firmenadresse"
 
-#: scripts/import_squirrelmail_abook.php:112 lib/api.php:523 lib/api.php:577
-#: lib/api.php:630 lib/api.php:678 lib/api.php:733 lib/api.php:792
-#: lib/api.php:842 lib/api.php:978 lib/api.php:1070 lib/api.php:1136
-#: lib/api.php:1251 lib/api.php:1484 lib/api.php:1561 lib/api.php:1710
+#: scripts/import_squirrelmail_abook.php:112 lib/api.php:546 lib/api.php:600
+#: lib/api.php:653 lib/api.php:701 lib/api.php:756 lib/api.php:815
+#: lib/api.php:866 lib/api.php:1004 lib/api.php:1066 lib/api.php:1137
+#: lib/api.php:1204 lib/api.php:1324 lib/api.php:1563 lib/api.php:1640
+#: lib/api.php:1791
 #, php-format
 msgid "Connection failed: %s"
 msgstr "Verbindung fehlgeschlagen: %s"
@@ -361,7 +363,7 @@ msgstr "Verbindung fehlgeschlagen"
 msgid "Contact Search"
 msgstr "Kontaktsuche"
 
-#: config/sources.php.dist:851
+#: config/sources.php.dist:866
 msgid "Contacts"
 msgstr "Kontakte"
 
@@ -369,7 +371,7 @@ msgstr "Kontakte"
 msgid "Contacts displayed:"
 msgstr "Angezeigte Kontakte:"
 
-#: lib/Views/Browse.php:339
+#: lib/Views/Browse.php:346
 #, php-format
 msgid "Contacts in list: %s"
 msgstr "Kontakte in Liste: %s"
@@ -390,7 +392,7 @@ msgstr "Adressbuch Erstellen"
 msgid "Create a new Address Book"
 msgstr "Neues Adressbuch erstellen"
 
-#: lib/ListView.php:447
+#: lib/ListView.php:448
 msgid "Create a new Contact List in:"
 msgstr "Neue Kontaktliste anlegen in:"
 
@@ -398,7 +400,7 @@ msgstr "Neue Kontaktliste anlegen in:"
 msgid "Created"
 msgstr "Erstellt"
 
-#: contact.php:89
+#: contact.php:95
 msgid "De_lete"
 msgstr "_Löschen"
 
@@ -413,7 +415,7 @@ msgstr "L
 msgid "Delete %s"
 msgstr "%s löschen"
 
-#: lib/api.php:621
+#: lib/api.php:644
 msgid "Delete denied."
 msgstr "Löschen nicht erlaubt."
 
@@ -422,7 +424,7 @@ msgstr "L
 msgid "Delete failed: (%s) %s"
 msgstr "Löschen fehlgeschlagen: (%s) %s"
 
-#: lib/Driver.php:1967 lib/Driver/null.php:62
+#: lib/Driver.php:2121 lib/Driver/null.php:62
 msgid "Deleting contacts is not available."
 msgstr "Löschen von Kontakten nicht unterstützt."
 
@@ -430,7 +432,7 @@ msgstr "L
 msgid "Deletion failed"
 msgstr "Löschen ist fehlgeschlagen"
 
-#: config/attributes.php.dist:300
+#: config/attributes.php.dist:312
 msgid "Department"
 msgstr "Abteilung"
 
@@ -451,17 +453,17 @@ msgstr "Anzeige"
 msgid "Display Options"
 msgstr "Anzeige-Einstellungen"
 
-#: lib/Object.php:348
+#: lib/Object.php:354
 msgid "Download"
 msgstr "Herunterladen"
 
-#: templates/browse/row.inc:15 templates/browse/row.inc:16
+#: templates/browse/row.inc:27 templates/browse/row.inc:28
 #: templates/browse/contactrow.inc:25
 msgid "Download vCard"
 msgstr "vCard herunterladen"
 
-#: templates/addressbook_list.php:31 templates/browse/row.inc:28
-#: templates/browse/row.inc:29 templates/browse/column_headers.inc:11
+#: templates/addressbook_list.php:31 templates/browse/row.inc:40
+#: templates/browse/row.inc:41 templates/browse/column_headers.inc:11
 #: templates/browse/actions.inc:6 templates/browse/contactrow.inc:33
 #: addressbooks/index.php:40
 msgid "Edit"
@@ -477,20 +479,20 @@ msgstr "\"%s\" bearbeiten"
 msgid "Edit %s"
 msgstr "%s Bearbeiten"
 
-#: config/attributes.php.dist:237
+#: config/attributes.php.dist:249
 msgid "Email"
 msgstr "E-Mail-Adresse"
 
-#: config/attributes.php.dist:243
+#: config/attributes.php.dist:255
 msgid "Emails"
 msgstr "E-Mail-Adressen"
 
-#: lib/Views/Browse.php:263 lib/Views/Browse.php:303
+#: lib/Views/Browse.php:270 lib/Views/Browse.php:310
 #, php-format
 msgid "Error adding %d contact(s) to list."
 msgstr "Fehler beim Hinzufügen von %d Kontakten zur Liste."
 
-#: lib/Views/Browse.php:265 lib/Views/Browse.php:305
+#: lib/Views/Browse.php:272 lib/Views/Browse.php:312
 #, php-format
 msgid "Error adding %d of %d requested contact(s) to list."
 msgstr "Fehler beim Hinzufügen von %d von %d Kontakten zur Liste."
@@ -515,7 +517,7 @@ msgstr "Fehler beim Entfernen von %d Kontakten aus der Liste."
 msgid "Error removing %d of %d requested contact(s) from list."
 msgstr "Fehler beim Entfernen von %d von %d Kontakten aus der Liste."
 
-#: lib/api.php:684
+#: lib/api.php:707
 #, php-format
 msgid "Error searching the address book: %s"
 msgstr "Das Adressbuch konnte nicht durchsucht werden: %s"
@@ -536,7 +538,7 @@ msgstr "Nur die ausgew
 msgid "Export the following address book completely."
 msgstr "Das folgende Adressbuch komplett exportieren."
 
-#: data.php:159 data.php:258 data.php:336 add.php:42 search.php:121
+#: data.php:247 data.php:346 data.php:424 add.php:42 search.php:106
 #: lib/Views/Browse.php:84 lib/Views/Browse.php:152 lib/Views/Browse.php:180
 #, php-format
 msgid "Failed to access the address book: %s"
@@ -547,11 +549,11 @@ msgstr "Das Adressbuch konnte nicht ge
 msgid "Failed to add %s to %s: %s"
 msgstr "%s konnte nicht zu %s hinzugefügt werden: %s"
 
-#: lib/Views/Browse.php:345
+#: lib/Views/Browse.php:352
 msgid "Failed to browse list"
 msgstr "Die Liste konnte nicht angezeigt werden"
 
-#: lib/Views/Browse.php:376
+#: lib/Views/Browse.php:383
 msgid "Failed to browse the directory"
 msgstr "Das Verzeichnis konnte nicht angezeigt werden"
 
@@ -568,20 +570,20 @@ msgid "Failed to find object to be added: %s"
 msgstr ""
 "Das Objekt, das hinzugefügt werden sollte, konnte nicht gefunden werden: %s"
 
-#: search.php:190
+#: search.php:175
 msgid "Failed to search the address book"
 msgstr "Das Adressbuch konnte nicht durchsucht werden"
 
-#: data.php:174
+#: data.php:262
 #, php-format
 msgid "Failed to search the directory: %s"
 msgstr "Das Verzeichnis %s konnte nicht durchsucht werden."
 
-#: config/sources.php.dist:550
+#: config/sources.php.dist:565
 msgid "Favourite Recipients"
 msgstr "Häufigste Empfänger"
 
-#: config/attributes.php.dist:264
+#: config/attributes.php.dist:276
 msgid "Fax"
 msgstr "Fax"
 
@@ -593,7 +595,7 @@ msgstr "Dateien"
 msgid "Find"
 msgstr "Durchsuchen von "
 
-#: lib/Forms/EditContact.php:121 lib/Forms/EditContact.php:160
+#: lib/Forms/EditContact.php:128 lib/Forms/EditContact.php:167
 msgid "Finish"
 msgstr "Beenden"
 
@@ -601,7 +603,7 @@ msgstr "Beenden"
 msgid "First Name"
 msgstr "Vorname"
 
-#: config/attributes.php.dist:326
+#: config/attributes.php.dist:350
 msgid "Freebusy URL"
 msgstr "Frei-/Gebucht-URL"
 
@@ -609,83 +611,83 @@ msgstr "Frei-/Gebucht-URL"
 msgid "From"
 msgstr "Von"
 
-#: config/attributes.php.dist:384
+#: config/attributes.php.dist:430
 msgid "Gender"
 msgstr "Geschlecht"
 
-#: config/sources.php.dist:776
+#: config/sources.php.dist:791
 msgid "Global Address Book"
 msgstr "Allgemeines Adressbuch"
 
-#: templates/browse/row.inc:49 templates/browse/contactrow.inc:44
+#: templates/browse/row.inc:61 templates/browse/contactrow.inc:44
 msgid "Group"
 msgstr "Gruppe"
 
-#: config/attributes.php.dist:123
+#: config/attributes.php.dist:135
 msgid "Home Address"
 msgstr "Adresse privat"
 
-#: config/attributes.php.dist:546
+#: config/attributes.php.dist:592
 msgid "Home Address Extended"
 msgstr "Erweiterte Adresse privat"
 
-#: config/attributes.php.dist:141
+#: config/attributes.php.dist:153
 msgid "Home City"
 msgstr "Stadt privat"
 
-#: config/attributes.php.dist:159
+#: config/attributes.php.dist:171
 msgid "Home Country"
 msgstr "Land privat"
 
-#: config/attributes.php.dist:414
+#: config/attributes.php.dist:460
 msgid "Home Email"
 msgstr "E-Mail-Adresse privat"
 
-#: config/attributes.php.dist:430
+#: config/attributes.php.dist:476
 msgid "Home Fax"
 msgstr "Fax privat"
 
-#: config/attributes.php.dist:552
+#: config/attributes.php.dist:598
 msgid "Home Latitude"
 msgstr "Breitengrad privat"
 
-#: config/attributes.php.dist:557
+#: config/attributes.php.dist:603
 msgid "Home Longitude"
 msgstr "Längengrad privat"
 
-#: config/attributes.php.dist:440
+#: config/attributes.php.dist:486
 msgid "Home Mobile Phone"
 msgstr "Telefon mobil privat"
 
-#: config/attributes.php.dist:249
+#: config/attributes.php.dist:261
 msgid "Home Phone"
 msgstr "Telefon privat"
 
-#: config/attributes.php.dist:135
+#: config/attributes.php.dist:147
 msgid "Home Post Office Box"
 msgstr "Postfach privat"
 
-#: config/attributes.php.dist:153
+#: config/attributes.php.dist:165
 msgid "Home Postal Code"
 msgstr "Postleitzahl privat"
 
-#: config/attributes.php.dist:147
+#: config/attributes.php.dist:159
 msgid "Home State/Province"
 msgstr "Bundesstaat oder Provinz privat"
 
-#: config/attributes.php.dist:129
+#: config/attributes.php.dist:141
 msgid "Home Street Address"
 msgstr "Straße privat"
 
-#: config/attributes.php.dist:455
+#: config/attributes.php.dist:501
 msgid "Home Video Call"
 msgstr "Bildtelefon privat"
 
-#: config/attributes.php.dist:540
+#: config/attributes.php.dist:586
 msgid "Home Website URL"
 msgstr "Website-URL privat"
 
-#: config/sources.php.dist:667
+#: config/sources.php.dist:682
 msgid "IMSP"
 msgstr "IMSP"
 
@@ -694,38 +696,38 @@ msgstr "IMSP"
 msgid "Import Address Book, Step %d"
 msgstr "Adressbuch importieren, Schritt %d"
 
-#: data.php:410
+#: data.php:501
 msgid "Import/Export Address Books"
 msgstr "Adressbücher Import/Export"
 
-#: config/attributes.php.dist:360
+#: config/attributes.php.dist:406
 msgid "Initials"
 msgstr "Initialien"
 
-#: config/attributes.php.dist:366
+#: config/attributes.php.dist:412
 msgid "Instant Messenger"
 msgstr "Instant Messenger"
 
-#: lib/api.php:973 lib/api.php:1065
+#: lib/api.php:999 lib/api.php:1132
 msgid "Invalid ID"
 msgstr "Ungültige ID"
 
-#: scripts/import_squirrelmail_abook.php:105 lib/api.php:345 lib/api.php:673
-#: lib/api.php:728 lib/api.php:787 lib/api.php:835 lib/api.php:969
-#: lib/api.php:1061 lib/api.php:1126 lib/api.php:1693
+#: scripts/import_squirrelmail_abook.php:105 lib/api.php:368 lib/api.php:696
+#: lib/api.php:751 lib/api.php:810 lib/api.php:859 lib/api.php:995
+#: lib/api.php:1128 lib/api.php:1194 lib/api.php:1774
 #, php-format
 msgid "Invalid address book: %s"
 msgstr "Ungültiges Adressbuch: %s"
 
-#: lib/api.php:1130
+#: lib/api.php:1198
 msgid "Invalid contact unique ID"
 msgstr "Ungültige eindeutige Kontakt-ID"
 
-#: lib/api.php:1697 lib/api.php:1783 lib/api.php:1844
+#: lib/api.php:1778 lib/api.php:1864 lib/api.php:1925
 msgid "Invalid email"
 msgstr "Ungültige E-Mail-Adresse"
 
-#: lib/api.php:1705
+#: lib/api.php:1786
 msgid "Invalid entry"
 msgstr "Ungültiger Eintrag"
 
@@ -733,15 +735,15 @@ msgstr "Ung
 msgid "Invalid key specified."
 msgstr "Ungültiger Schlüssel angegeben."
 
-#: lib/api.php:1701
+#: lib/api.php:1782
 msgid "Invalid name"
 msgstr "Ungültiger Name"
 
-#: config/attributes.php.dist:276
+#: config/attributes.php.dist:288
 msgid "Job Title"
 msgstr "Position"
 
-#: lib/Driver/kolab.php:1114 lib/Driver/kolab.php:1180
+#: lib/Driver/kolab.php:1129 lib/Driver/kolab.php:1195
 #, php-format
 msgid "Key for saving must be a UID not %s!"
 msgstr "Der Schlüssel fürs Speichern muss eine UID sein, kein(e) %s!"
@@ -753,11 +755,11 @@ msgstr ""
 "LDAP-Unterstützung wird benötigt, aber das LDAP-Modul ist nicht verfügbar "
 "oder wurde nicht geladen."
 
-#: data.php:91 templates/data/import.inc:18 templates/data/export.inc:16
+#: data.php:106 templates/data/import.inc:18 templates/data/export.inc:16
 msgid "LDIF Address Book"
 msgstr "LDIF-Adressbuch"
 
-#: config/attributes.php.dist:390
+#: config/attributes.php.dist:436
 msgid "Language"
 msgstr "Sprache"
 
@@ -769,7 +771,7 @@ msgstr "Letzte 
 msgid "Last Name"
 msgstr "Nachname"
 
-#: config/attributes.php.dist:396
+#: config/attributes.php.dist:442
 msgid "Latitude"
 msgstr "Breitengrad"
 
@@ -777,15 +779,23 @@ msgstr "Breitengrad"
 msgid "List"
 msgstr "Liste"
 
-#: config/sources.php.dist:238 config/sources.php.dist:918
+#: config/sources.php.dist:251 config/sources.php.dist:944
 msgid "Location"
 msgstr "Ort"
 
-#: config/attributes.php.dist:401
+#: config/attributes.php.dist:324
+msgid "Logo"
+msgstr "Logo"
+
+#: config/attributes.php.dist:330
+msgid "Logo MIME Type"
+msgstr "Logo-Dateityp"
+
+#: config/attributes.php.dist:447
 msgid "Longitude"
 msgstr "Längengrad"
 
-#: lib/api.php:593
+#: lib/api.php:616
 msgid "Malformed request."
 msgstr "Ungültige Anfrage."
 
@@ -793,15 +803,19 @@ msgstr "Ung
 msgid "Manage Address Books"
 msgstr "Adressbücher-Verwaltung"
 
-#: config/attributes.php.dist:372
+#: config/attributes.php.dist:418
 msgid "Manager"
 msgstr "Vorgesetzter"
 
+#: contact.php:109
+msgid "Mark this as your own contact"
+msgstr "Diesen Kontakt als Ihre Visitenkarte markieren"
+
 #: templates/browse/search.inc:109
 msgid "Matching"
 msgstr "nach"
 
-#: lib/api.php:306
+#: lib/api.php:329
 msgid "Maximum Number of Contacts"
 msgstr "Maximale Anzahl an Kontakten"
 
@@ -821,7 +835,7 @@ msgstr "Weitere Namen"
 msgid "Missing DN in LDAP source configuration."
 msgstr "DN fehlt in der LDAP-Konfiguration."
 
-#: config/attributes.php.dist:259
+#: config/attributes.php.dist:271
 msgid "Mobile Phone"
 msgstr "Telefon mobil"
 
@@ -834,7 +848,7 @@ msgstr "
 msgid "More Options..."
 msgstr "Weitere Optionen..."
 
-#: lib/api.php:1821
+#: lib/api.php:1902
 msgid "More than 1 entry found"
 msgstr "Mehr als 1 Eintrag zurückgeliefert"
 
@@ -850,11 +864,11 @@ msgstr "Nach links"
 msgid "Move right"
 msgstr "Nach rechts"
 
-#: data.php:89 templates/data/import.inc:16
+#: data.php:104 templates/data/import.inc:16
 msgid "Mulberry Address Book"
 msgstr "Mulberry-Adressbuch"
 
-#: lib/api.php:1729
+#: lib/api.php:1810
 #, php-format
 msgid ""
 "Multiple persons with address [%s], but none with name [%s] already exist"
@@ -862,7 +876,7 @@ msgstr ""
 "Mehrere Personen mit der Adresse [%s], aber keine mit dem Namen [%s] im "
 "Adressbuch"
 
-#: config/sources.php.dist:162
+#: config/sources.php.dist:167
 msgid "My Address Book"
 msgstr "Mein Adressbuch"
 
@@ -887,12 +901,12 @@ msgstr "Titel nach dem Namen"
 msgid "Name:"
 msgstr "Name:"
 
-#: add.php:70 lib/Block/tree_menu.php:27
+#: add.php:70 lib/Block/tree_menu.php:28
 msgid "New Contact"
 msgstr "Neuer Kontakt"
 
-#: templates/data/import.inc:46 lib/Forms/EditContact.php:112
-#: lib/Forms/EditContact.php:119 lib/Forms/EditContact.php:174
+#: templates/data/import.inc:46 lib/Forms/EditContact.php:119
+#: lib/Forms/EditContact.php:126 lib/Forms/EditContact.php:181
 msgid "Next"
 msgstr "Weiter"
 
@@ -900,26 +914,26 @@ msgstr "Weiter"
 msgid "Nickname"
 msgstr "Spitzname"
 
-#: lib/api.php:1824 lib/api.php:1884
+#: lib/api.php:1905 lib/api.php:1965
 #, php-format
 msgid "No %s entry found for %s"
 msgstr "Es wurde kein %s Eintrag für %s gefunden"
 
-#: data.php:78
+#: data.php:93
 msgid ""
 "No Address Books are currently available. Import and Export is disabled."
 msgstr ""
 "Zur Zeit sind keine Adressbücher verfügbar. Import und Export stehen nicht "
 "zur Verfügung."
 
-#: search.php:107
+#: search.php:92
 msgid "No Address Books are currently available. Searching is disabled."
 msgstr ""
 "Zur Zeit sind keine Adressbücher verfügbar. Die Suche steht nicht zur "
 "Verfügung."
 
-#: lib/api.php:667 lib/api.php:722 lib/api.php:782 lib/api.php:964
-#: lib/api.php:1056 lib/api.php:1121
+#: lib/api.php:690 lib/api.php:745 lib/api.php:805 lib/api.php:990
+#: lib/api.php:1123 lib/api.php:1189
 msgid "No address book specified"
 msgstr "Kein Adressbuch angegeben"
 
@@ -939,7 +953,7 @@ msgstr "Keine passende Kontakte"
 msgid "No source for favourite recipients exists."
 msgstr "Es existiert keine Quelle für häufigste Empfänger."
 
-#: lib/api.php:868 lib/api.php:1166
+#: lib/api.php:893 lib/api.php:1235
 msgid "No vCard data was found."
 msgstr "Es wurden keine vCard-Daten gefunden."
 
@@ -956,7 +970,7 @@ msgstr "Nicht gefunden"
 msgid "Not found"
 msgstr "Nicht gefunden"
 
-#: config/attributes.php.dist:314
+#: config/attributes.php.dist:338
 msgid "Notes"
 msgstr "Bemerkungen"
 
@@ -964,20 +978,20 @@ msgstr "Bemerkungen"
 msgid "Number of items per page"
 msgstr "Anzahl der Einträge pro Seite"
 
-#: lib/api.php:1015 lib/api.php:1191
+#: lib/api.php:1041 lib/api.php:1260
 msgid "Object not found"
 msgstr "Objekt nicht gefunden"
 
-#: lib/Driver/kolab.php:1184
+#: lib/Driver/kolab.php:1199
 #, php-format
 msgid "Object with UID %s does not exist!"
 msgstr "Objekt mit der UID %s existiert nicht!"
 
-#: config/attributes.php.dist:282
+#: config/attributes.php.dist:294
 msgid "Occupation"
 msgstr "Beruf"
 
-#: config/attributes.php.dist:306
+#: config/attributes.php.dist:318
 msgid "Office"
 msgstr "Büro"
 
@@ -991,27 +1005,27 @@ msgstr ""
 "endgültig gelöscht. Wenn Sie das nicht möchten, ändern Sie Ihre Auswahl auf "
 "\"kein\"."
 
-#: lib/api.php:1174
+#: lib/api.php:1243
 msgid "Only one vcard supported."
 msgstr "Nur einzelne vCards werden unterstützt."
 
-#: config/sources.php.dist:245 config/sources.php.dist:924
+#: config/sources.php.dist:258 config/sources.php.dist:950
 msgid "Organization"
 msgstr "Firma"
 
-#: config/sources.php.dist:246 config/sources.php.dist:926
+#: config/sources.php.dist:259 config/sources.php.dist:952
 msgid "Other"
 msgstr "Sonstige"
 
-#: config/attributes.php.dist:332
+#: config/attributes.php.dist:356
 msgid "PGP Public Key"
 msgstr "Öffentlicher PGP-Schlüssel"
 
-#: config/attributes.php.dist:471
+#: config/attributes.php.dist:517
 msgid "PTT"
 msgstr "PTT"
 
-#: config/attributes.php.dist:269
+#: config/attributes.php.dist:281
 msgid "Pager"
 msgstr "Pager"
 
@@ -1019,16 +1033,24 @@ msgstr "Pager"
 msgid "Permanently delete this contact?"
 msgstr "Diesen Kontakt unwiederbringlich löschen?"
 
-#: deletefile.php:36 lib/Driver.php:759 lib/api.php:846 lib/api.php:1714
-#: lib/Driver/sql.php:486
+#: deletefile.php:36 lib/Driver.php:804 lib/api.php:870 lib/api.php:1795
+#: lib/Driver/sql.php:515
 msgid "Permission denied"
 msgstr "Zugriff verweigert"
 
-#: config/sources.php.dist:235 config/sources.php.dist:914
+#: config/sources.php.dist:248 config/sources.php.dist:940
 msgid "Personal"
 msgstr "Persönlich"
 
-#: data.php:90 templates/data/import.inc:17
+#: config/attributes.php.dist:121
+msgid "Photo"
+msgstr "Foto"
+
+#: config/attributes.php.dist:127
+msgid "Photo MIME Type"
+msgstr "Foto-Dateityp"
+
+#: data.php:105 templates/data/import.inc:17
 msgid "Pine Address Book"
 msgstr "Pine-Adressbuch"
 
@@ -1036,8 +1058,8 @@ msgstr "Pine-Adressbuch"
 msgid "Please name the new contact list:"
 msgstr "Bitte einen Namen für die neue Kontakliste angeben:"
 
-#: lib/Forms/EditContact.php:115 lib/Forms/EditContact.php:118
-#: lib/Forms/EditContact.php:167
+#: lib/Forms/EditContact.php:122 lib/Forms/EditContact.php:125
+#: lib/Forms/EditContact.php:174
 msgid "Previous"
 msgstr "Zurück"
 
@@ -1051,7 +1073,7 @@ msgstr "Abfrage fehlgeschlagen: (%s) %s"
 msgid "Read failed: (%s) %s"
 msgstr "Lesen fehlgeschlagen: (%s) %s"
 
-#: lib/Driver.php:1951 lib/Driver/null.php:52
+#: lib/Driver.php:2105 lib/Driver/null.php:52
 msgid "Reading contacts is not available."
 msgstr "Anzeigen von Kontakten nicht unterstützt."
 
@@ -1073,7 +1095,7 @@ msgstr "Adressbuch entfernen"
 msgid "Remove from this list"
 msgstr "Aus dieser Liste entfernen"
 
-#: lib/Driver.php:1989
+#: lib/Driver.php:2143
 msgid ""
 "Removing user data is not supported in the current address book storage "
 "driver."
@@ -1089,7 +1111,7 @@ msgstr ""
 "Existierendes Adressbuch mit dem importierten ersetzen? <strong>Warnung: "
 "Damit werden alle aktuellen Einträge in dem Adressbuch gelöscht.</strong>"
 
-#: lib/Driver.php:653
+#: lib/Driver.php:698
 msgid "Requested object not found."
 msgstr "Gewünschtes Objekt nicht gefunden."
 
@@ -1097,11 +1119,11 @@ msgstr "Gew
 msgid "Reset to Defaults"
 msgstr "Zurücksetzen"
 
-#: config/attributes.php.dist:338
+#: config/attributes.php.dist:362
 msgid "S/MIME Public Certificate"
 msgstr "Öffentliches S/MIME-Zertifikat"
 
-#: config/attributes.php.dist:465
+#: config/attributes.php.dist:511
 msgid "SIP"
 msgstr "SIP"
 
@@ -1118,21 +1140,21 @@ msgstr "Speichern"
 msgid "Save search as a virtual address book?"
 msgstr "Suche als Virtuelles Adressbuch speichern?"
 
-#: lib/Driver.php:1977 lib/Driver/null.php:67
+#: lib/Driver.php:2131 lib/Driver/null.php:67
 msgid "Saving contacts is not available."
 msgstr "Speichern von Kontakten nicht unterstützt."
 
 #: templates/browse/search.inc:111 templates/browse/search.inc:119
 #: templates/browse/header.inc:3 templates/block/minisearch.inc:25
-#: lib/Block/tree_menu.php:58 config/prefs.php.dist:116
+#: lib/Block/tree_menu.php:62 config/prefs.php.dist:116
 msgid "Search"
 msgstr "Suche"
 
-#: lib/api.php:1489
+#: lib/api.php:1568
 msgid "Search failed"
 msgstr "Suche fehlgeschlagen"
 
-#: lib/api.php:1719 lib/api.php:1725 lib/api.php:1734 lib/api.php:1747
+#: lib/api.php:1800 lib/api.php:1806 lib/api.php:1815 lib/api.php:1828
 #, php-format
 msgid "Search failed: %s"
 msgstr "Suche fehlgeschlagen: %s"
@@ -1141,11 +1163,11 @@ msgstr "Suche fehlgeschlagen: %s"
 msgid "Search for: "
 msgstr "Suchen nach: "
 
-#: lib/Driver.php:1936 lib/Driver/null.php:47
+#: lib/Driver.php:2090 lib/Driver/null.php:47
 msgid "Searching is not available."
 msgstr "Suchen von Kontakten nicht unterstützt."
 
-#: templates/browse/row.inc:40
+#: templates/browse/row.inc:52
 msgid "Select contact"
 msgstr "Kontakt auswählen"
 
@@ -1243,7 +1265,7 @@ msgstr "Sortieren nach %s, dann nach %s"
 msgid "Source:"
 msgstr "Quelle:"
 
-#: lib/api.php:299
+#: lib/api.php:322
 msgid "Sources"
 msgstr "Quellen"
 
@@ -1251,7 +1273,7 @@ msgstr "Quellen"
 msgid "Spouse"
 msgstr "Partner"
 
-#: lib/Views/Browse.php:261 lib/Views/Browse.php:301
+#: lib/Views/Browse.php:268 lib/Views/Browse.php:308
 #, php-format
 msgid "Successfully added %d contact(s) to list."
 msgstr "%d Kontakte erfolgreich zur Liste hinzugefügt."
@@ -1261,12 +1283,12 @@ msgstr "%d Kontakte erfolgreich zur Liste hinzugef
 msgid "Successfully added %s to %s"
 msgstr "%s wurde erfolgreich zu %s hinzugefügt"
 
-#: lib/Views/Browse.php:291
+#: lib/Views/Browse.php:298
 #, php-format
 msgid "Successfully created the contact list \"%s\"."
 msgstr "Die Kontaktliste \"%s\" wurde erfolgreich erstellt."
 
-#: search.php:165
+#: search.php:150
 #, php-format
 msgid "Successfully created virtual address book \"%s\""
 msgstr "Virtuelles Adressbuch \"%s\" erfolgreich erstellt."
@@ -1281,7 +1303,7 @@ msgstr "%d Kontakte erfolgreich gel
 msgid "Successfully removed %d contact(s) from list."
 msgstr "%d Kontakte erfolgreich aus der Liste entfernt."
 
-#: data.php:87
+#: data.php:102
 msgid "TSV"
 msgstr "TSV"
 
@@ -1297,7 +1319,7 @@ msgstr "Zieladressbuch"
 msgid "Target Contact List"
 msgstr "Zielkontaktliste"
 
-#: data.php:338
+#: data.php:426
 #, php-format
 msgid "The %s file didn't contain any contacts."
 msgstr "Die %s-Datei enthielt keine Kontakte."
@@ -1308,7 +1330,7 @@ msgstr ""
 "Das VFS-Backend muss konfiguriert sein, um das Hochladen von Dateien zu "
 "erlauben."
 
-#: lib/Driver.php:1907
+#: lib/Driver.php:2061
 #, php-format
 msgid "The address book \"%s\" does not exist."
 msgstr "Das Adressbuch \"%s\" existiert nicht."
@@ -1318,11 +1340,15 @@ msgstr "Das Adressbuch \"%s\" existiert nicht."
 msgid "The address book \"%s\" has been created."
 msgstr "Das Adressbuch \"%s\" wurde erzeugt."
 
-#: data.php:345
+#: data.php:433
 #, php-format
 msgid "The address book could not be purged: %s"
 msgstr "Das Adressbuch konnte nicht geleert werden: %s"
 
+#: lib/api.php:1061
+msgid "The address book with your own contact doesn't exist anymore."
+msgstr "Das Adressbuch mit Ihrer Visitenkarte existiert nicht mehr."
+
 #: addressbooks/delete.php:50
 #, php-format
 msgid "The addressbook \"%s\" has been deleted."
@@ -1347,7 +1373,7 @@ msgstr "Der ausgew
 msgid "The file \"%s\" has been deleted."
 msgstr "Die Datei \"%s\" wurde gelöscht."
 
-#: data.php:318
+#: data.php:406
 msgid "The import can be finished despite the warnings."
 msgstr "Der Import kann trotz der Warnungen abgeschlossen werden."
 
@@ -1371,20 +1397,22 @@ msgstr ""
 "dass es sich dabei um einen Fehler handelt, wenden Sie sich bitte an Ihren "
 "System-Administrator."
 
-#: lib/Views/Browse.php:349
+#: lib/Views/Browse.php:356
 #, php-format
 msgid "There is %d contact in this list that is not viewable to you"
 msgid_plural "There are %d contacts in this list that are not viewable to you"
 msgstr[0] ""
+"In dieser Liste befindet sich %d Kontakt, den Sie nicht einsehen dürfen"
 msgstr[1] ""
+"In dieser Liste befinden sich %d Kontakte, die Sie nicht einsehen dürfen"
 
-#: search.php:161
+#: search.php:146
 #, php-format
 msgid "There was a problem creating the virtual address book: %s"
 msgstr ""
 "Beim Erstellen des Virtuellen Adressbuchs ist ein Fehler aufgetreten: %s"
 
-#: lib/Forms/AddContact.php:90
+#: lib/Forms/AddContact.php:95
 msgid ""
 "There was an error adding the new contact. Contact your system administrator "
 "for further help."
@@ -1392,7 +1420,7 @@ msgstr ""
 "Beim Hinzufügen dieses Kontakts ist ein Fehler aufgetreten. Bitte wenden Sie "
 "sich an Ihren Systemadministrator für weitere Hilfe."
 
-#: lib/Views/Browse.php:311
+#: lib/Views/Browse.php:318
 msgid "There was an error creating a new list."
 msgstr "Beim Erstellen einer neuen Liste ist ein Fehler aufgetreten."
 
@@ -1406,25 +1434,25 @@ msgstr "Beim L
 msgid "There was an error deleting this contact: %s"
 msgstr "Beim Löschen des Kontakts ist ein Fehler aufgetreten: %s"
 
-#: lib/Views/Browse.php:355
+#: lib/Views/Browse.php:362
 msgid "There was an error displaying the list"
 msgstr "Beim Anzeigen der Liste ist ein Fehler aufgetreten"
 
-#: data.php:378
+#: data.php:469
 #, php-format
 msgid "There was an error importing the data: %s"
 msgstr "Beim Importieren der Daten ist ein Fehler aufgetreten: %s"
 
-#: lib/api.php:864 lib/api.php:1161
+#: lib/api.php:889 lib/api.php:1230
 msgid "There was an error importing the iCalendar data."
 msgstr "Beim Importieren der iCalendar Daten ist ein Fehler aufgetreten."
 
-#: lib/api.php:235
+#: lib/api.php:258
 #, php-format
 msgid "There was an error removing an address book for %s"
 msgstr "Beim Löschen eines Adressbuchs von %s ist ein Fehler aufgetreten"
 
-#: lib/Forms/EditContact.php:86
+#: lib/Forms/EditContact.php:93
 msgid ""
 "There was an error saving the contact. Contact your system administrator for "
 "further help."
@@ -1432,7 +1460,7 @@ msgstr ""
 "Beim Speichern des Kontaktes ist ein Fehler aufgetreten. Bitte wenden Sie "
 "sich an Ihren Systemadministrator für weitere Hilfe."
 
-#: data.php:204
+#: data.php:292
 msgid "There were no addresses to export."
 msgstr "Es konnten keine Adressen zum Exportieren gefunden werden."
 
@@ -1445,11 +1473,15 @@ msgstr "Diese Adressb
 msgid "This addressbook cannot be deleted"
 msgstr "Dieses Adressbuch kann nicht gelöscht werden"
 
-#: data.php:303
+#: contact.php:57
+msgid "This contact has been marked as your own."
+msgstr "Der Kontakt wurde als Ihre Visitenkarte markiert."
+
+#: data.php:391
 msgid "This file format is not supported."
 msgstr "Dieses Dateiformat wird nicht unterstützt."
 
-#: lib/api.php:1738 lib/api.php:1751
+#: lib/api.php:1819 lib/api.php:1832
 #, php-format
 msgid "This person already has a %s entry in the address book"
 msgstr "Diese Person hat bereits einen %s-Eintrag in Ihrem Adressbuch"
@@ -1461,7 +1493,7 @@ msgstr ""
 "Dies wird als Standardadressbuch für das Hinzufügen oder Importieren von "
 "Kontakten benutzt."
 
-#: config/attributes.php.dist:229
+#: config/attributes.php.dist:241
 msgid "Time Zone"
 msgstr "Zeitzone"
 
@@ -1469,7 +1501,7 @@ msgstr "Zeitzone"
 msgid "Unable to find contact owner."
 msgstr "Kontaktbesitzer konnte nicht gefunden werden."
 
-#: lib/Driver.php:1829 lib/Turba.php:676
+#: lib/Driver.php:1981 lib/Turba.php:659
 #, php-format
 msgid "Unable to load the definition of %s."
 msgstr "Der %s-Treiber konnte nicht geladen werden."
@@ -1483,11 +1515,11 @@ msgstr "Das Adressbuch \"%s\" kann nicht gespeichert werden: %s"
 msgid "Unable to search."
 msgstr "Suche nicht möglich."
 
-#: config/attributes.php.dist:353
+#: config/attributes.php.dist:391
 msgid "Unfiled"
 msgstr "Nicht zugeordnet"
 
-#: lib/api.php:902 lib/api.php:1012 lib/api.php:1179
+#: lib/api.php:927 lib/api.php:1038 lib/api.php:1248
 #, php-format
 msgid "Unsupported Content-Type: %s"
 msgstr "Nicht unterstützter Inhaltstyp: %s"
@@ -1505,75 +1537,75 @@ msgstr "Kontakt anzeigen"
 msgid "View to display by default:"
 msgstr "Standardansicht:"
 
-#: config/attributes.php.dist:460
+#: config/attributes.php.dist:506
 msgid "VoIP"
 msgstr "VoIP"
 
-#: config/attributes.php.dist:320
+#: config/attributes.php.dist:344
 msgid "Website URL"
 msgstr "Website-URL"
 
-#: config/attributes.php.dist:173
+#: config/attributes.php.dist:185
 msgid "Work Address"
 msgstr "Adresse geschäftlich"
 
-#: config/attributes.php.dist:524
+#: config/attributes.php.dist:570
 msgid "Work Address Extended"
 msgstr "Erweiterte Adresse geschäftlich"
 
-#: config/attributes.php.dist:191
+#: config/attributes.php.dist:203
 msgid "Work City"
 msgstr "Stadt geschäftlich"
 
-#: config/attributes.php.dist:209
+#: config/attributes.php.dist:221
 msgid "Work Country"
 msgstr "Land geschäftlich"
 
-#: config/attributes.php.dist:408
+#: config/attributes.php.dist:454
 msgid "Work Email"
 msgstr "E-Mail-Adresse geschäftlich"
 
-#: config/attributes.php.dist:425
+#: config/attributes.php.dist:471
 msgid "Work Fax"
 msgstr "Fax geschäftlich"
 
-#: config/attributes.php.dist:530
+#: config/attributes.php.dist:576
 msgid "Work Latitude"
 msgstr "Breitengrad geschäftlich"
 
-#: config/attributes.php.dist:535
+#: config/attributes.php.dist:581
 msgid "Work Longitude"
 msgstr "Längengrad geschäftlich"
 
-#: config/attributes.php.dist:435
+#: config/attributes.php.dist:481
 msgid "Work Mobile Phone"
 msgstr "Telefon mobil geschäftlich"
 
-#: config/attributes.php.dist:254
+#: config/attributes.php.dist:266
 msgid "Work Phone"
 msgstr "Telefon geschäftlich"
 
-#: config/attributes.php.dist:185
+#: config/attributes.php.dist:197
 msgid "Work Post Office Box"
 msgstr "Postfach geschäftlich"
 
-#: config/attributes.php.dist:203
+#: config/attributes.php.dist:215
 msgid "Work Postal Code"
 msgstr "Postleitzahl geschäftlich"
 
-#: config/attributes.php.dist:197
+#: config/attributes.php.dist:209
 msgid "Work State/Province"
 msgstr "Bundesstaat oder Provinz geschäftlich"
 
-#: config/attributes.php.dist:179
+#: config/attributes.php.dist:191
 msgid "Work Street Address"
 msgstr "Straße geschäftlich"
 
-#: config/attributes.php.dist:450
+#: config/attributes.php.dist:496
 msgid "Work Video Call"
 msgstr "Bildtelefon geschäftlich"
 
-#: config/attributes.php.dist:518
+#: config/attributes.php.dist:564
 msgid "Work Website URL"
 msgstr "Website-URL geschäftlich"
 
@@ -1581,7 +1613,7 @@ msgstr "Website-URL gesch
 msgid "You are not allowed to change this addressbook."
 msgstr "Sie dürfen dieses Adressbuch nicht ändern."
 
-#: data.php:267 add.php:48 lib/Views/Browse.php:159 lib/Views/Browse.php:274
+#: data.php:355 add.php:48 lib/Views/Browse.php:159 lib/Views/Browse.php:281
 #, php-format
 msgid "You are not allowed to create more than %d contacts in \"%s\"."
 msgstr "Sie dürfen nicht mehr als %d Kontakte in \"%s\" erstellen."
@@ -1590,7 +1622,7 @@ msgstr "Sie d
 msgid "You are not allowed to delete this addressbook."
 msgstr "Sie dürfen dieses Adressbuch nicht löschen."
 
-#: lib/api.php:198
+#: lib/api.php:204
 msgid "You are not allowed to remove user data."
 msgstr "Sie dürfen keine Benutzerdaten löschen."
 
@@ -1606,6 +1638,10 @@ msgstr "Sie k
 msgid "You cannot delete contacts from a virtual address book"
 msgstr "Sie können keine Kontakte aus Virtuellen Adressbüchern löschen"
 
+#: lib/api.php:1056
+msgid "You didn't mark a contact as your own yet."
+msgstr "Sie haben noch keinen Kontakt als Ihre Visitenkarte markiert."
+
 #: edit.php:66 view.php:38 lib/Views/EditContact.php:49
 #: lib/Views/DeleteContact.php:42
 msgid "You do not have permission to view this contact."
@@ -1619,6 +1655,14 @@ msgstr "Sie haben nicht gen
 msgid "You do not have permissions to delete this address book."
 msgstr "Sie haben nicht genügend Rechte, um dieses Adressbuch zu löschen."
 
+#: lib/api.php:1070
+msgid ""
+"You don't have sufficient permissions to read the address book that contains "
+"your own contact."
+msgstr ""
+"Sie haben nicht genügend Rechte, um das Adressbuch zu lesen, das Ihre "
+"Visitenkarte enthält."
+
 #: lib/Driver/ldap.php:741
 msgid ""
 "You must have the Net_LDAP PEAR library installed to use the schema check "
@@ -1627,7 +1671,7 @@ msgstr ""
 "Sie müssen die Net_LDAP PEAR-Bibliothek installieren, um die "
 "Schemaüberprüfung verwenden zu können."
 
-#: search.php:148
+#: search.php:133
 msgid "You must provide a name for virtual address books."
 msgstr "Sie müssen einen Namen für das Virtuelle Adressbuch angeben."
 
@@ -1648,15 +1692,24 @@ msgstr "Sie m
 msgid "You only have permission to view this contact."
 msgstr "Sie haben nur das Recht, diesen Kontakt anzusehen."
 
-#: lib/Views/Browse.php:362
+#: lib/Views/Browse.php:369
 msgid "Your default address book is not browseable."
 msgstr "Ihr Standard-Adressbuch kann nicht angezeigt werden."
 
+#: contact.php:101 contact.php:102 templates/browse/row.inc:14
+#: templates/browse/row.inc:15
+msgid "Your own contact"
+msgstr "Ihre Visitenkarte"
+
+#: lib/api.php:1075
+msgid "Your own contact cannot be found in the address book."
+msgstr "Ihre Visitenkarte konnte in dem Adressbuch nicht gefunden werden."
+
 #: templates/browse/contactrow.inc:12
 msgid "[no value]"
 msgstr "[kein Wert]"
 
-#: lib/Turba.php:618
+#: lib/Turba.php:601
 msgid "_Browse"
 msgstr "_Liste"
 
@@ -1668,11 +1721,11 @@ msgstr "_Kontakte"
 msgid "_Delete"
 msgstr "Lös_chen"
 
-#: contact.php:85
+#: contact.php:91
 msgid "_Edit"
 msgstr "_Bearbeiten"
 
-#: lib/Turba.php:627
+#: lib/Turba.php:610
 msgid "_Import/Export"
 msgstr "_Import/Export"
 
@@ -1680,15 +1733,15 @@ msgstr "_Import/Export"
 msgid "_Lists"
 msgstr "_Listen"
 
-#: lib/Turba.php:615
+#: lib/Turba.php:598
 msgid "_My Address Books"
 msgstr "_Meine Adressbücher"
 
-#: lib/Turba.php:621
+#: lib/Turba.php:604
 msgid "_New Contact"
 msgstr "_Neuer Kontakt"
 
-#: lib/Turba.php:632
+#: lib/Turba.php:615
 msgid "_Print"
 msgstr "_Drucken"
 
@@ -1696,11 +1749,11 @@ msgstr "_Drucken"
 msgid "_Remove from this list"
 msgstr "_Aus dieser Liste entfernen"
 
-#: lib/Turba.php:623
+#: lib/Turba.php:606
 msgid "_Search"
 msgstr "_Suche"
 
-#: contact.php:82
+#: contact.php:88
 msgid "_View"
 msgstr "_Anzeigen"
 
@@ -1721,19 +1774,19 @@ msgstr "durch mich"
 msgid "contact"
 msgstr "Kontakt"
 
-#: data.php:224 data.php:229 templates/data/export.inc:1
+#: data.php:312 data.php:317 templates/data/export.inc:1
 msgid "contacts.csv"
 msgstr "adressen.csv"
 
-#: data.php:249
+#: data.php:337
 msgid "contacts.ldif"
 msgstr "adressen.ldif"
 
-#: data.php:234
+#: data.php:322
 msgid "contacts.tsv"
 msgstr "adressen.tsv"
 
-#: data.php:244
+#: data.php:332
 msgid "contacts.vcf"
 msgstr "adressen.vcf"
 
@@ -1741,16 +1794,16 @@ msgstr "adressen.vcf"
 msgid "descending"
 msgstr "absteigend"
 
-#: config/attributes.php.dist:387
+#: config/attributes.php.dist:433
 msgid "female"
 msgstr "weiblich"
 
-#: lib/Block/tree_menu.php:37
+#: lib/Block/tree_menu.php:38
 #, php-format
 msgid "in %s"
 msgstr "in %s"
 
-#: config/attributes.php.dist:387
+#: config/attributes.php.dist:433
 msgid "male"
 msgstr "männlich"
 
@@ -1770,7 +1823,7 @@ msgstr "zu einer Kontaktliste"
 msgid "to a different Address Book"
 msgstr "zu einem anderen Adressbuch"
 
-#: data.php:88 templates/browse/column_headers.inc:14
+#: data.php:103 templates/browse/column_headers.inc:14
 #: templates/data/import.inc:15 templates/data/export.inc:15
 msgid "vCard"
 msgstr "vCard"
diff --git a/po/et_EE.po b/po/et_EE.po
index b8375ab..51c5da7 100644
--- a/po/et_EE.po
+++ b/po/et_EE.po
@@ -5,107 +5,118 @@ msgid ""
 msgstr ""
 "Project-Id-Version: Turba 2.1-cvs\n"
 "Report-Msgid-Bugs-To: dev at lists.horde.org\n"
-"POT-Creation-Date: 2005-10-18 15:12+0200\n"
-"PO-Revision-Date: 2006-03-27 23:09+0300\n"
-"Last-Translator: Toomas Aas <toomas.aas at mail.ee>\n"
+"POT-Creation-Date: 2008-05-29 15:48+0300\n"
+"PO-Revision-Date: 2008-06-05 19:27+0200\n"
+"Last-Translator: Alar SIng <alar.sing at err.ee>\n"
 "Language-Team: Estonian <dev at lists.horde.org>\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=ISO-8859-13\n"
-"Content-Transfer-Encoding: 8-bit\n"
+"Content-Transfer-Encoding: 8bit\n"
 
-#: templates/prefs/imsp_opt.inc:42
+#: templates/prefs/imsp_opt.inc:40
 msgid " Add an IMSP Address Book"
 msgstr " Lisa IMSP aadressiraamat"
 
-#: templates/prefs/imsp_opt.inc:31
+#: templates/prefs/imsp_opt.inc:29
 msgid " Delete IMSP Address Book"
 msgstr " Kustuta IMSP aadressiraamat"
 
-#: data.php:325
+#: data.php:351
 #, php-format
 msgid "\"%s\" already exists and was not imported."
 msgstr "\"%s\" ei imporditud, kuna see oli juba olemas."
 
-#: browse.php:126
+#: lib/Forms/EditContact.php:76
+#, php-format
+msgid "\"%s\" updated, but saving the uploaded file failed: %s"
+msgstr ""
+"\"%s\" kanne uuendati, kuid serverisse laetud faili salvestamine nurjus: %s"
+
+#: lib/Forms/EditContact.php:78 lib/Forms/EditContact.php:81
+#, php-format
+msgid "\"%s\" updated."
+msgstr "\"%s\" kanne muudetud."
+
+#: lib/Views/Browse.php:192
 #, php-format
 msgid "\"%s\" was not copied because it is a list."
 msgstr "\"%s\" ei kopeeritud, kuna see on list."
 
-#: browse.php:124
+#: lib/Views/Browse.php:190
 #, php-format
 msgid "\"%s\" was not moved because it is a list."
 msgstr "\"%s\" ei teisaldatud, kuna see on list."
 
-#: config/prefs.php.dist:137
+#: config/prefs.php.dist:118
 msgid "\"Firstname Lastname\"  (ie. John Doe)"
 msgstr "\"Eesnimi Perekonnanimi\" (nt. Jaan Kask)"
 
-#: config/prefs.php.dist:136
+#: config/prefs.php.dist:117
 msgid "\"Lastname, Firstname\" (ie. Doe, John)"
 msgstr "\"Perekonnanimi, Eesnimi\" (nt. Kask, Jaan)"
 
-#: add.php:109
+#: lib/Forms/AddContact.php:83
 #, php-format
 msgid "%s added."
 msgstr "%s lisatud."
 
-#: data.php:343
+#: data.php:370
 #, php-format
-msgid "%s file successfully imported"
+msgid "%s file successfully imported."
 msgstr "%s faili import õnnestus"
 
-#: templates/browse/footer.inc:5
+#: templates/list/numPager.inc:6
 #, php-format
 msgid "%s to %s of %s"
 msgstr "%s kuni %s, kokku %s"
 
-#: lib/Turba.php:446
+#: lib/Turba.php:531
 #, php-format
 msgid "%s's Address Book"
 msgstr "Kasutaja %s aadressiraamat"
 
-#: lib/Block/minisearch.php:45
+#: lib/Block/minisearch.php:42
 msgid "A browser that supports iFrames is required"
 msgstr "Vajalik on iFrame toetav veebilehitseja"
 
-#: config/prefs.php.dist:159
-msgid "Above search form"
-msgstr "Otsinguvormi kohal"
-
-#: view.php:52
+#: view.php:54
 #, php-format
 msgid "Access denied to %s"
 msgstr "Puudub õigus failile %s"
 
-#: add.php:56 templates/browse/actions.inc:22
+#: templates/browse/actions.inc:30 lib/Forms/AddContact.php:25
 msgid "Add"
 msgstr "Lisa"
 
-#: templates/prefs/addressbookselect.inc:174
+#: lib/Forms/AddContact.php:22
+msgid "Add Contact"
+msgstr "Lisa kontakt"
+
+#: templates/prefs/addressbookselect.inc:173
 msgid "Add address book"
 msgstr "Lisa aadressiraamat"
 
-#: config/prefs.php.dist:48
-msgid "Add and Delete IMSP address books"
-msgstr "Lisa ja kustuta IMSP aadressiraamatuid"
-
 #: templates/prefs/columnselect.inc:237
 msgid "Add column"
 msgstr "Lisa veerg"
 
-#: edit.php:128
+#: lib/Forms/EditContact.php:34
 msgid "Add file"
 msgstr "Lisa fail"
 
-#: templates/browse/actions.inc:22
+#: templates/browse/actions.inc:30
 msgid "Add to"
 msgstr "Lisa"
 
-#: templates/addressbooks.inc:86
+#: lib/Driver.php:1518
+msgid "Adding contacts is not available."
+msgstr "Kontaktide lisamine ei ole saadaval."
+
+#: templates/addressbooks.inc:105
 msgid "Address Book"
 msgstr "Aadressiraamat"
 
-#: browse.php:28 config/prefs.php.dist:125
+#: lib/Views/Browse.php:78 config/prefs.php.dist:106
 msgid "Address Book Listing"
 msgstr "Aadresside loend"
 
@@ -113,102 +124,141 @@ msgstr "Aadresside loend"
 msgid "Address Books"
 msgstr "Aadressiraamatud"
 
-#: add.php:108
-msgid "Address book entry"
-msgstr "Aadressiraamatu kanne"
-
-#: data.php:305
+#: data.php:331
 msgid "Address book successfully purged."
 msgstr "Aadressiraamat on tühjendatud."
 
-#: templates/addressbooks.inc:62
+#: templates/addressbooks.inc:80
 msgid "Address book to delete "
 msgstr "Kustutatav aadressiraamat "
 
-#: templates/prefs/addressbookselect.inc:170
+#: templates/prefs/addressbookselect.inc:167
+#: templates/prefs/addressbookselect.inc:169
 msgid "Address books that will not be displayed:"
 msgstr "Aadressiraamatud, mida ei näidata:"
 
-#: search.php:88 templates/browse/search.inc:37
+#: search.php:161 templates/browse/search.inc:47
 msgid "Advanced Search"
 msgstr "Põhjalik otsing"
 
-#: config/attributes.php.dist:64
+#: config/attributes.php.dist:77
 msgid "Alias"
 msgstr "Alias"
 
-#: templates/browse/footerAlpha.inc:7 templates/browse/footerAlpha.inc:10
-#: templates/browse/actions.inc:41
+#: templates/list/alphaPager.inc:7 templates/list/alphaPager.inc:10
 msgid "All"
 msgstr "Kõik"
 
-#: lib/api.php:552
+#: lib/api.php:725
 msgid "Already Exists"
 msgstr "On juba olemas"
 
-#: templates/browse/javascript.inc:93
+#: config/attributes.php.dist:95
+msgid "Anniversary"
+msgstr "Aastapäev"
+
+#: templates/browse/javascript.inc:85
 #, php-format
 msgid "Are you sure that you want to delete %s?"
 msgstr "Kas oled kindel, et soovid %s kustutada?"
 
-#: templates/browse/javascript.inc:67
-msgid "Are you sure that you want to delete the selected entries?"
+#: templates/browse/javascript.inc:66
+msgid "Are you sure that you want to delete the selected contacts?"
 msgstr "Kas oled kindel, et soovid märgitud kanded kustutada?"
 
-#: config/prefs.php.dist:96
-msgid "Ascending"
-msgstr "Kasvav"
+#: templates/addressbooks.inc:45
+msgid "Are you sure you want to delete the addressbook: "
+msgstr "Kas oled kindel, et soovid selle aadressi raamatu kustutada:"
 
-#: search.php:85 templates/browse/search.inc:36
+#: config/attributes.php.dist:368
+msgid "Assistant"
+msgstr "Assistent"
+
+#: search.php:158 templates/browse/search.inc:46
 msgid "Basic Search"
 msgstr "Lihtotsing"
 
-#: config/prefs.php.dist:158
-msgid "Below search form"
-msgstr "Otsinguvormi all"
-
-#: lib/Driver/ldap.php:77 lib/Driver/ldap.php:80
+#: lib/Driver/ldap.php:95 lib/Driver/ldap.php:98
 #, php-format
 msgid "Bind failed: (%s) %s"
 msgstr "Bind nurjus: (%s) %s"
 
-#: config/attributes.php.dist:178
+#: config/attributes.php.dist:89
 msgid "Birthday"
 msgstr "Sünnipäev"
 
-#: templates/menu.inc:7
+#: lib/Driver.php:524
+#, php-format
+msgid "Birthday of %s"
+msgstr "Sünnipäev %s"
+
+#: lib/api.php:1255
+msgid "Birthdays"
+msgstr "Sünnipäevad"
+
+#: contact.php:103 templates/browse/row.inc:6
+msgid "Blank name"
+msgstr "Tühi nimi"
+
+#: templates/browse/actions.inc:51
+msgid "Both"
+msgstr "Mõlemad"
+
+#: templates/menu.inc:8
 msgid "Browse"
 msgstr "Sirvi"
 
-#: config/attributes.php.dist:173
+#: config/attributes.php.dist:280
 msgid "Business Category"
 msgstr "Tegevusala"
 
-#: data.php:49
+#: data.php:60
 msgid "CSV"
 msgstr "CSV"
 
-#: lib/Object/Group.php:57
+#: lib/Object/Group.php:68
 msgid "Can't add a group to itself."
 msgstr "Gruppi ei saa lisada iseenda liikmeks."
 
+#: lib/Driver/kolab.php:620
+#, fuzzy, php-format
+msgid "Cannot delete all address book entries for %s"
+msgstr "Ava \"%s\" kanne aadressiraamatus"
+
+#: config/attributes.php.dist:342
+msgid "Category"
+msgstr "Kategooria"
+
+#: templates/browse/column_headers.inc:24
+#, php-format
+msgid "Change %s sort to %s"
+msgstr "Vaheta sorteerimine %s - %s vastu"
+
+#: templates/browse/column_headers.inc:7
+msgid "Check All/None"
+msgstr "Märgi/tühjenda kõik"
+
 #: templates/browse/column_headers.inc:7
 msgid "Check _All/None"
 msgstr "Märgi/tühjenda _kõik"
 
-#: templates/addressbooks.inc:44
+#: config/attributes.php.dist:107
+msgid "Children"
+msgstr "Lapsed"
+
+#: templates/addressbooks.inc:60
 msgid "Choose a name"
 msgstr "Vali nimi"
 
-#: add.php:48
+#: lib/Forms/AddContact.php:36
 msgid "Choose an address book"
 msgstr "Vali aadressiraamat"
 
-#: templates/prefs/columnselect.inc:221
+#: templates/prefs/columnselect.inc:218 templates/prefs/columnselect.inc:220
 msgid "Choose an address book:"
 msgstr "Vali aadressiraamat:"
 
-#: templates/prefs/addressbookselect.inc:165
+#: templates/prefs/addressbookselect.inc:163
 msgid "Choose which address books to display, and in what order:"
 msgstr "Vali, milliseid aadressiraamatuid näidata, ja millises järjestuses:"
 
@@ -216,15 +266,23 @@ msgstr "Vali, milliseid aadressiraamatuid n
 msgid "Choose which address books to use."
 msgstr "Vali, milliseid aadressiraamatuid kasutada."
 
-#: templates/prefs/columnselect.inc:228
+#: templates/prefs/columnselect.inc:227
 msgid "Choose which columns to display and in what order:"
 msgstr "Vali, milliseid veerge näidata, ja millises järjestuses."
 
+#: templates/block/minisearch.inc:27
+msgid "Close"
+msgstr "Sule"
+
+#: templates/browse/header.inc:7
+msgid "Close Search"
+msgstr "Sulge otsing"
+
 #: config/prefs.php.dist:17
 msgid "Column Options"
 msgstr "Veeru seaded"
 
-#: templates/prefs/columnselect.inc:233
+#: templates/prefs/columnselect.inc:231 templates/prefs/columnselect.inc:233
 msgid "Columns that will not be displayed:"
 msgstr "Veerud, mida ei näidata:"
 
@@ -236,101 +294,111 @@ msgstr "Komaga eraldatud v
 msgid "Comma separated values (Microsoft Outlook)"
 msgstr "Komaga eraldatud väljad (Microsoft Outlook)"
 
-#: config/attributes.php.dist:76
+#: config/sources.php.dist:228 config/sources.php.dist:901
+#, fuzzy
+msgid "Communications"
+msgstr "Veeru seaded"
+
+#: config/attributes.php.dist:286
 msgid "Company"
 msgstr "Firma"
 
-#: config/attributes.php.dist:144
+#: config/attributes.php.dist:215
 msgid "Company Address"
 msgstr "Töökoha aadress"
 
-#: lib/api.php:318 lib/api.php:363 lib/api.php:407 lib/api.php:491
-#: lib/api.php:601 lib/api.php:688 lib/api.php:741 lib/api.php:819
-#: lib/api.php:1006 lib/api.php:1094
+#: scripts/import_squirrelmail_abook.php:112 lib/api.php:394 lib/api.php:439
+#: lib/api.php:490 lib/api.php:525 lib/api.php:574 lib/api.php:620
+#: lib/api.php:661 lib/api.php:778 lib/api.php:865 lib/api.php:923
+#: lib/api.php:1012 lib/api.php:1231 lib/api.php:1289 lib/api.php:1371
 #, php-format
 msgid "Connection failed: %s"
 msgstr "Ühendus nurjus: %s"
 
-#: lib/Driver/ldap.php:58
+#: lib/Driver/ldap.php:71
 msgid "Connection failure"
 msgstr "Ühendus nurjus"
 
-#: browse.php:191 browse.php:232
-msgid "Contact added to list."
-msgstr "Aadress lisati listi."
+#: lib/Block/minisearch.php:3 lib/Block/minisearch.php:26
+#, fuzzy
+msgid "Contact Search"
+msgstr "Lihtotsing"
 
-#: browse.php:60
-msgid "Contact removed from list."
-msgstr "Aadress eemaldati listist."
+#: config/sources.php.dist:833
+msgid "Contacts"
+msgstr "Kontaktid"
 
-#: browse.php:266
+#: templates/list/numPager.inc:3
+#, fuzzy
+msgid "Contacts displayed:"
+msgstr "Näidatavad kanded:"
+
+#: lib/Views/Browse.php:336
 #, php-format
 msgid "Contacts in list: %s"
 msgstr "Aadressid listis: %s"
 
-#: templates/browse/actions.inc:12
+#: templates/browse/actions.inc:18
 msgid "Copy"
 msgstr "Kopeeri"
 
-#: templates/addressbooks.inc:53
+#: templates/addressbooks.inc:69
 msgid "Create"
 msgstr "Loo"
 
-#: templates/addressbooks.inc:40
+#: templates/addressbooks.inc:56
 msgid "Create Address Book"
 msgstr "Loo aadressiraamat"
 
-#: lib/ListView.php:315
+#: lib/ListView.php:443
 msgid "Create a new Contact List in:"
 msgstr "Loo uus list siia:"
 
-#: lib/ObjectView.php:133
+#: lib/Views/Contact.php:74
 msgid "Created"
 msgstr "Loodud"
 
-#: config/prefs.php.dist:98
-msgid "Default sorting direction:"
-msgstr "Vaikimisi järjestuse suund:"
+#: contact.php:86
+msgid "De_lete"
+msgstr "Kustuta"
 
-#: templates/addressbooks.inc:76 templates/browse/actions.inc:2
-#: lib/Object.php:328 lib/Renderer.php:47 lib/Renderer.php:50
+#: templates/addressbooks.inc:94 templates/browse/actions.inc:3
+#: lib/Views/DeleteContact.php:58
 msgid "Delete"
 msgstr "Kustuta"
 
-#: templates/addressbooks.inc:58
+#: lib/Views/DeleteContact.php:28
+#, php-format
+msgid "Delete %s"
+msgstr "Kustuta %s"
+
+#: templates/addressbooks.inc:76
 msgid "Delete Address Book"
 msgstr "Kustuta aadressiraamat"
 
-#: config/prefs.php.dist:38
-msgid "Delete Confirmation"
-msgstr "Kustutamise kinnitus"
-
-#: config/prefs.php.dist:39
-msgid "Delete button behaviour"
-msgstr "Kustutusnupu käitumine"
-
-#: lib/Driver/ldap.php:270
+#: lib/Driver/ldap.php:288
 #, php-format
 msgid "Delete failed: (%s) %s"
 msgstr "Kustutamine nurjus: (%s) %s"
 
-#: delete.php:38
+#: lib/Driver.php:1526
+#, fuzzy
+msgid "Deleting contacts is not available."
+msgstr "FTP laiendus ei ole saadaval."
+
+#: delete.php:40
 msgid "Deletion failed"
 msgstr "Kustutamine ebaõnnestus"
 
-#: config/attributes.php.dist:213
+#: config/attributes.php.dist:292
 msgid "Department"
 msgstr "Osakond"
 
-#: config/prefs.php.dist:97
-msgid "Descending"
-msgstr "Kahanev"
-
-#: templates/addressbooks.inc:115
+#: templates/addressbooks.inc:138
 msgid "Description"
 msgstr "Kirjeldus"
 
-#: templates/browse/search.inc:118
+#: templates/browse/search.inc:125
 msgid "Directory"
 msgstr "Kataloog"
 
@@ -343,63 +411,78 @@ msgstr "Esitus"
 msgid "Display Options"
 msgstr "Esituse seaded"
 
-#: config/prefs.php.dist:176
-msgid "Do you want to confirm deleting entries?"
-msgstr "Küsi kannete kustutamisel kinnitust"
-
-#: lib/Object.php:297
+#: lib/Object.php:348
 msgid "Download"
 msgstr "Salvesta"
 
-#: templates/browse/row.inc:17 templates/browse/contactrow.inc:25
+#: templates/browse/row.inc:13
 msgid "Download vCard"
 msgstr "Salvesta vCard"
 
-#: templates/addressbooks.inc:102 templates/browse/row.inc:25
-#: templates/browse/column_headers.inc:10 templates/browse/actions.inc:3
-#: templates/browse/contactrow.inc:33 lib/Renderer.php:42
+#: templates/addressbooks.inc:123 templates/browse/column_headers.inc:10
+#: templates/browse/row.inc:21 templates/browse/actions.inc:6
 msgid "Edit"
 msgstr "Muuda"
 
-#: templates/browse/row.inc:25 templates/browse/contactrow.inc:33
+#: edit.php:91
 #, php-format
 msgid "Edit \"%s\""
 msgstr "Muuda \"%s\""
 
-#: templates/addressbooks.inc:81
+#: lib/Views/EditContact.php:35
+#, php-format
+msgid "Edit %s"
+msgstr "Muuda %s"
+
+#: templates/addressbooks.inc:100
 msgid "Edit Address Books"
 msgstr "Muuda aadressiraamatuid"
 
-#: edit.php:78 edit.php:154
-#, php-format
-msgid "Edit entry for %s"
-msgstr "Muuda %s andmeid"
-
-#: config/attributes.php.dist:58
+#: config/attributes.php.dist:229
 msgid "Email"
 msgstr "E-post"
 
-#: edit.php:147
-#, php-format
-msgid "Entry for %s updated, but saving the uploaded file failed: %s"
-msgstr ""
-"%s kanne uuendati, kuid serverisse laetud faili salvestamine nurjus: %s"
+#: config/attributes.php.dist:235
+#, fuzzy
+msgid "Emails"
+msgstr "E-post"
+
+#: lib/Views/Browse.php:260 lib/Views/Browse.php:300
+#, fuzzy, php-format
+msgid "Error adding %d contact(s) to list."
+msgstr "%d sissekannet lisati listi."
+
+#: lib/Views/Browse.php:262 lib/Views/Browse.php:302
+#, fuzzy, php-format
+msgid "Error adding %d of %d requested contact(s) to list."
+msgstr "%d sissekannet lisati listi."
+
+#: lib/Views/Browse.php:134
+#, fuzzy, php-format
+msgid "Error deleting %d contact(s)."
+msgstr "Viga sisu laadimisel: %s"
 
-#: edit.php:149 edit.php:152
+#: lib/Views/Browse.php:136
 #, php-format
-msgid "Entry for %s updated."
-msgstr "%s kanne muudetud."
+msgid "Error deleting %d of %d requested contacts(s)."
+msgstr "Viga kustutamise %d'l kontakti %d'st. "
+
+#: lib/Views/Browse.php:117
+#, fuzzy, php-format
+msgid "Error removing %d contact(s) from list."
+msgstr "%d sissekanne(t) listist eemaldatud."
 
-#: browse.php:74
-msgid "Entry successfully deleted."
-msgstr "Kanne kustutatud."
+#: lib/Views/Browse.php:119
+#, fuzzy, php-format
+msgid "Error removing %d of %d requested contact(s) from list."
+msgstr "%d sissekanne(t) listist eemaldatud."
 
-#: lib/api.php:413
+#: lib/api.php:531
 #, php-format
 msgid "Error searching the address book: %s"
 msgstr "Aadressiraamatu otsingu viga: %s"
 
-#: templates/browse/actions.inc:6 templates/data/export.inc:41
+#: templates/browse/actions.inc:10 templates/data/export.inc:42
 msgid "Export"
 msgstr "Eksport"
 
@@ -407,168 +490,204 @@ msgstr "Eksport"
 msgid "Export Address Book"
 msgstr "Aadressiraamatu eksport"
 
-#: templates/data/export.inc:21
+#: templates/data/export.inc:22
 msgid "Export only the selected contacts."
 msgstr "Ekspordi ainult märgitud kanded."
 
-#: templates/data/export.inc:27
+#: templates/data/export.inc:28
 msgid "Export the following address book completely."
 msgstr "Ekspordi see aadressiraamat täies ulatuses."
 
-#: data.php:134 data.php:228 data.php:297 browse.php:35 browse.php:90
-#: browse.php:112 add.php:67 search.php:37
+#: search.php:86 add.php:42 data.php:143 data.php:242 data.php:320
+#: lib/Views/Browse.php:84 lib/Views/Browse.php:152 lib/Views/Browse.php:180
 #, php-format
 msgid "Failed to access the address book: %s"
 msgstr "Juurdepääs aadressiraamatule nurjus: %s"
 
-#: browse.php:139
+#: lib/Views/Browse.php:208
 #, php-format
 msgid "Failed to add %s to %s: %s"
 msgstr "%s lisamine %s-le nurjus: %s"
 
-#: browse.php:272
+#: lib/Views/Browse.php:342
 msgid "Failed to browse list"
 msgstr "Listi sirvimine nurjus"
 
-#: browse.php:310
+#: lib/Views/Browse.php:369
 msgid "Failed to browse the directory"
 msgstr "Kataloogi sirvimine ebaõnnestus"
 
-#: lib/Driver/ldap.php:309
+#: lib/Driver/ldap.php:321
 #, php-format
 msgid "Failed to change name: (%s) %s; Old DN = %s, New DN = %s, Root = %s"
 msgstr "Nime muutmine nurjus: (%s) %s; Vana DN = %s, uus DN = %s, Root = %s"
 
-#: browse.php:120
+#: lib/Views/Browse.php:186
 #, php-format
 msgid "Failed to find object to be added: %s"
 msgstr "Ei leidnud objekti, mida lisada: %s"
 
-#: search.php:79
+#: search.php:152
 msgid "Failed to search the address book"
 msgstr "Aadressiraamatu otsing nurjus"
 
-#: data.php:149
+#: data.php:158
 #, php-format
 msgid "Failed to search the directory: %s"
 msgstr "Otsing kataloogist ebaõnnestus: %s"
 
-#: config/attributes.php.dist:167
+#: config/sources.php.dist:532
+#, fuzzy
+msgid "Favourite Recipients"
+msgstr "Nähtamatud adressaadid"
+
+#: config/attributes.php.dist:256
 msgid "Fax"
 msgstr "Faks"
 
-#: edit.php:124 display.php:100
+#: lib/Forms/Contact.php:40
 msgid "Files"
 msgstr "Failid"
 
-#: templates/browse/search.inc:70
+#: templates/browse/search.inc:104
 msgid "Find"
 msgstr "Leia"
 
-#: edit.php:97 edit.php:187
+#: lib/Forms/EditContact.php:121 lib/Forms/EditContact.php:159
 msgid "Finish"
 msgstr "Lõpp"
 
-#: config/attributes.php.dist:48
+#: config/attributes.php.dist:47
 msgid "First Name"
 msgstr "Eesnimi"
 
-#: config/attributes.php.dist:202
+#: config/attributes.php.dist:318
 msgid "Freebusy URL"
 msgstr "Freebusy URL"
 
-#: templates/browse/search.inc:83
+#: templates/browse/search.inc:98
 msgid "From"
 msgstr "Kellelt"
 
-#: config/sources.php.dist:604
+#: config/attributes.php.dist:374
+msgid "Gender"
+msgstr "Sugu"
+
+#: config/sources.php.dist:758
 msgid "Global Address Book"
 msgstr "Globaalne aadressiraamat"
 
-#: templates/browse/row.inc:43 templates/browse/contactrow.inc:44
+#: templates/browse/row.inc:39
 msgid "Group"
 msgstr "Grupp"
 
-#: config/attributes.php.dist:82
+#: config/attributes.php.dist:115
 msgid "Home Address"
 msgstr "Kodune aadress"
 
-#: config/attributes.php.dist:93
+#: config/attributes.php.dist:133
 msgid "Home City"
 msgstr "Kodulinn"
 
-#: config/attributes.php.dist:108
+#: config/attributes.php.dist:151
 msgid "Home Country"
 msgstr "Kodumaa"
 
-#: config/attributes.php.dist:150
+#: config/attributes.php.dist:241
 msgid "Home Phone"
 msgstr "Kodune telefon"
 
-#: config/attributes.php.dist:103
+#: config/attributes.php.dist:127
+msgid "Home Post Office Box"
+msgstr "Kodune postiindeks"
+
+#: config/attributes.php.dist:145
 msgid "Home Postal Code"
 msgstr "Kodune postiindeks"
 
-#: config/attributes.php.dist:98
+#: config/attributes.php.dist:139
 msgid "Home State/Province"
 msgstr "Kodumaakond"
 
-#: config/attributes.php.dist:88
+#: config/attributes.php.dist:121
 msgid "Home Street Address"
 msgstr "Kodune tänav/maja"
 
-#: config/sources.php.dist:506
+#: config/sources.php.dist:649
 msgid "IMSP"
 msgstr "IMSP"
 
-#: config/prefs.php.dist:47
-msgid "IMSP Address Book Administration"
-msgstr "IMSP aadressiraamatu haldus"
-
 #: templates/data/import.inc:7
 #, php-format
 msgid "Import Address Book, Step %d"
 msgstr "Aadressiraamatu import, samm %d"
 
-#: data.php:361
+#: data.php:388
 msgid "Import/Export Address Books"
 msgstr "Aadressiraamatute import/eksport"
 
-#: lib/api.php:596 lib/api.php:683
+#: config/attributes.php.dist:350
+msgid "Initials"
+msgstr "Initsiaalid"
+
+#: config/attributes.php.dist:356
+msgid "Instant Messenger"
+msgstr "Otse suhtlus"
+
+#: lib/api.php:773 lib/api.php:860
 msgid "Invalid ID"
 msgstr "Vigane ID"
 
-#: lib/api.php:230 lib/api.php:402 lib/api.php:486 lib/api.php:592
-#: lib/api.php:679 lib/api.php:731 lib/api.php:1077
-msgid "Invalid address book"
-msgstr "Vigane aadressiraamat"
+#: scripts/import_squirrelmail_abook.php:105 lib/api.php:306 lib/api.php:520
+#: lib/api.php:569 lib/api.php:615 lib/api.php:656 lib/api.php:769
+#: lib/api.php:856 lib/api.php:913 lib/api.php:1354
+#, php-format
+msgid "Invalid address book: %s"
+msgstr "Vigane aadressiraamat: %s"
 
-#: lib/api.php:1081 lib/api.php:1154 lib/api.php:1207
+#: lib/api.php:1358 lib/api.php:1432 lib/api.php:1489
 msgid "Invalid email"
 msgstr "Vigane e-posti aadress"
 
-#: lib/api.php:1089
+#: lib/api.php:1366
 msgid "Invalid entry"
 msgstr "Vigane kanne"
 
-#: lib/Driver/ldap.php:266
+#: lib/Driver/ldap.php:284
 msgid "Invalid key specified."
 msgstr "Määrati vigane võti."
 
-#: lib/api.php:1085
+#: lib/api.php:1362
 msgid "Invalid name"
 msgstr "Vigane nimi"
 
-#: lib/api.php:735
+#: lib/api.php:917
 msgid "Invalid objectId"
 msgstr "Vigane objectId"
 
-#: lib/Driver/ldap.php:30
+#: config/attributes.php.dist:268
+msgid "Job Title"
+msgstr "Tiitel"
+
+#: lib/Driver/kolab.php:855 lib/Driver/kolab.php:877
+#, php-format
+msgid "Key for saving must be a UID not %s!"
+msgstr "Salvestamise võti peab olema UID mitte %s!"
+
+#: lib/Driver/ldap.php:67
 msgid ""
 "LDAP support is required but the LDAP module is not available or not loaded."
 msgstr "Vajalik on LDAP toetus, ent LDAP moodul puudub või pole laetud."
 
-#: lib/ObjectView.php:138
+#: data.php:65 templates/data/import.inc:18 templates/data/export.inc:16
+msgid "LDIF Address Book"
+msgstr "LDIF Aadressiraamat"
+
+#: config/attributes.php.dist:380
+msgid "Language"
+msgstr "Keel"
+
+#: lib/Views/Contact.php:80
 msgid "Last Modified"
 msgstr "Viimati muudetud"
 
@@ -576,19 +695,35 @@ msgstr "Viimati muudetud"
 msgid "Last Name"
 msgstr "Perekonnanimi"
 
+#: config/attributes.php.dist:386
+msgid "Latitude"
+msgstr "laiuskraad"
+
 #: templates/browse/column_headers.inc:16
 msgid "List"
 msgstr "Nimekiri"
 
-#: templates/browse/search.inc:77
+#: config/sources.php.dist:223 config/sources.php.dist:897
+msgid "Location"
+msgstr "Asukoht"
+
+#: config/attributes.php.dist:391
+msgid "Longitude"
+msgstr "Pikkuskraad"
+
+#: config/attributes.php.dist:362
+msgid "Manager"
+msgstr "Haldur"
+
+#: templates/browse/search.inc:109
 msgid "Matching"
 msgstr " mis sisaldab "
 
-#: lib/api.php:205
+#: lib/api.php:281
 msgid "Maximum Number of Contacts"
 msgstr "Kannete suurim arv"
 
-#: config/prefs.php.dist:107
+#: config/prefs.php.dist:88
 msgid "Maximum number of pages"
 msgstr "Lehekülgede suurim arv"
 
@@ -596,60 +731,65 @@ msgstr "Lehek
 msgid "Menu List"
 msgstr "Menüü nimekiri"
 
-#: lib/Block/minisearch.php:3
-msgid "Mini Search"
-msgstr "Miniotsing"
+#: config/attributes.php.dist:59
+msgid "Middle Names"
+msgstr "Kesknimi"
 
-#: lib/Driver/ldap.php:302
+#: lib/Driver/ldap.php:314
 msgid "Missing DN in LDAP source configuration."
 msgstr "LDAP allika seadistuses puudub DN."
 
-#: config/attributes.php.dist:162
+#: config/attributes.php.dist:251
 msgid "Mobile Phone"
 msgstr "Mobiiltelefon"
 
-#: lib/Driver/ldap.php:332 lib/Driver/ldap.php:348
+#: lib/Driver/ldap.php:343 lib/Driver/ldap.php:359
 #, php-format
 msgid "Modify failed: (%s) %s"
 msgstr "Muutmine nurjus: (%s) %s"
 
-#: lib/api.php:1193
+#: templates/browse/header.inc:8
+#, fuzzy
+msgid "More Options..."
+msgstr "Kirja seaded"
+
+#: lib/api.php:1474
 msgid "More than 1 entry found"
 msgstr "Leiti rohkem kui 1 kanne"
 
-#: templates/browse/actions.inc:11
+#: templates/browse/actions.inc:16
 msgid "Move"
 msgstr "Nihuta"
 
-#: templates/prefs/columnselect.inc:247
 #: templates/prefs/addressbookselect.inc:184
+#: templates/prefs/columnselect.inc:248
 msgid "Move left"
 msgstr "nihuta vasakule"
 
-#: templates/prefs/columnselect.inc:249
 #: templates/prefs/addressbookselect.inc:186
+#: templates/prefs/columnselect.inc:250
 msgid "Move right"
 msgstr "nihuta paremale"
 
-#: data.php:52 templates/data/import.inc:16
+#: data.php:63 templates/data/import.inc:16
 msgid "Mulberry Address Book"
 msgstr "Mulberry aadressiraamat"
 
-#: lib/api.php:1113
+#: lib/api.php:1390
 #, php-format
 msgid ""
 "Multiple persons with address [%s], but none with name [%s] already exist"
 msgstr "Kirjas on juba mitu inimest aadressiga [%s], kuid ühegi nimi pole [%s]"
 
-#: config/sources.php.dist:107 config/sources.php.dist:243
+#: config/sources.php.dist:161
 msgid "My Address Book"
 msgstr "Minu aadressiraamat"
 
-#: addressbooks.php:120
+#: addressbooks.php:174
 msgid "My Address Books"
 msgstr "Minu aadressiraamatud"
 
-#: templates/addressbooks.inc:107 config/attributes.php.dist:42
+#: templates/addressbooks.inc:130 config/attributes.php.dist:41
 msgid "Name"
 msgstr "Nimi"
 
@@ -657,61 +797,103 @@ msgstr "Nimi"
 msgid "Name Format"
 msgstr "Nime esitus"
 
-#: config/sources.php.dist:315
-msgid "Netcenter Member Directory"
-msgstr "Netcenteri liikmete kataloog"
+#: config/attributes.php.dist:65
+msgid "Name Prefixes"
+msgstr "Nime liides"
+
+#: config/attributes.php.dist:71
+msgid "Name Suffixes"
+msgstr "Nime liited."
+
+#: templates/browse/search_vbook.inc:6
+msgid "Name:"
+msgstr "Nimi:"
 
-#: add.php:36 add.php:124 lib/Block/minisearch.php:27
-#: lib/Block/tree_menu.php:27
+#: add.php:70 lib/Block/tree_menu.php:28
 msgid "New Contact"
 msgstr "Lisa uus sissekanne"
 
-#: edit.php:88 edit.php:95 edit.php:202 templates/data/import.inc:45
+#: templates/data/import.inc:46 lib/Forms/EditContact.php:112
+#: lib/Forms/EditContact.php:119 lib/Forms/EditContact.php:174
 msgid "Next"
 msgstr "Edasi"
 
-#: config/attributes.php.dist:218
+#: config/attributes.php.dist:83
 msgid "Nickname"
 msgstr "Hüüdnimi"
 
-#: lib/api.php:1195 lib/api.php:1244
+#: lib/api.php:1477 lib/api.php:1526
 #, php-format
 msgid "No %s entry found for %s"
 msgstr "%s kannet %s jaoks ei leitud"
 
-#: lib/api.php:1163
+#: data.php:52
+msgid ""
+"No Address Books are currently available. Import and Export is disabled."
+msgstr "Aadressi raamatuid ei ole saadaval. Import ja eksport on keelatud."
+
+#: search.php:72
+msgid "No Address Books are currently available. Searching is disabled."
+msgstr "Aadressi raamatuid ei ole saadaval. Otsimine on keelatud."
+
+#: lib/api.php:1441
 msgid "No address books found."
 msgstr "Ei leitud ühtki aadressiraamatut."
 
-#: minisearch.php:83
+#: minisearch.php:81
 msgid "No contacts found"
 msgstr "Kandeid ei leitud"
 
-#: lib/api.php:511 lib/api.php:766
+#: templates/browse/column_headers.inc:43
+msgid "No contacts match the current filter."
+msgstr "Ühtegi vastet ei leitud otsingule."
+
+#: lib/ListView.php:251
+msgid "No matching contacts"
+msgstr "Ühtegi vastet ei leitud."
+
+#: lib/Driver/favourites.php:120
+msgid "No source for favourite recipients exists."
+msgstr "Lemmik saajate allikat ei leidu."
+
+#: lib/api.php:682 lib/api.php:949
 msgid "No vCard data was found."
 msgstr "vCard andmeid ei leitud."
 
-#: templates/browse/footer.inc:7
+#: templates/list/numPager.inc:8
 msgid "None"
 msgstr "Puudub"
 
-#: config/attributes.php.dist:184
+#: lib/Views/EditContact.php:33 lib/Views/Contact.php:49
+#: lib/Views/DeleteContact.php:26
+msgid "Not Found"
+msgstr "Ei leitud"
+
+#: deletefile.php:22 edit.php:48
+msgid "Not found"
+msgstr "Ei leitud"
+
+#: config/attributes.php.dist:306
 msgid "Notes"
 msgstr "Märkmed"
 
-#: config/prefs.php.dist:115
+#: config/prefs.php.dist:96
 msgid "Number of items per page"
 msgstr "Kannete arv leheküljel"
 
-#: lib/api.php:614 lib/api.php:747
+#: lib/api.php:789 lib/api.php:929
 msgid "Object not found"
 msgstr "Objekti ei leitud"
 
-#: config/attributes.php.dist:223
+#: config/attributes.php.dist:274
+msgid "Occupation"
+msgstr "Amet"
+
+#: config/attributes.php.dist:298
 msgid "Office"
 msgstr "Töökoht"
 
-#: templates/prefs/imsp_opt.inc:23
+#: templates/prefs/imsp_opt.inc:22
 msgid ""
 "Once you save this option page, the address book will be permanently "
 "deleted. If this is not what you want, then you must change your selection "
@@ -720,69 +902,71 @@ msgstr ""
 "Selle seadistuse lehe salvestamisel kustutatakse aadressiraamat jäädavalt."
 "Kui sa seda ei taha, pead valima \"Puudub\"."
 
-#: lib/api.php:774
+#: lib/api.php:957
 msgid "Only one vcard supported."
 msgstr "Lubatud on ainult üks vCard."
 
-#: config/prefs.php.dist:37 config/prefs.php.dist:46
-msgid "Other Options"
-msgstr "Muud seaded"
+#: config/sources.php.dist:230 config/sources.php.dist:903
+msgid "Organization"
+msgstr "Organisatsioon"
+
+#: config/sources.php.dist:231 config/sources.php.dist:905
+msgid "Other"
+msgstr "Muu"
 
-#: config/attributes.php.dist:190
+#: config/attributes.php.dist:324
 msgid "PGP Public Key"
 msgstr "PGP avalik võti"
 
-#: lib/api.php:495 lib/api.php:605 lib/api.php:692 lib/api.php:934
-#: lib/api.php:1098
-msgid "Permission Denied"
-msgstr "Juurdepääs puudub"
+#: config/attributes.php.dist:261
+msgid "Pager"
+msgstr "Piipar"
+
+#: lib/Views/DeleteContact.php:57
+msgid "Permanently delete this contact?"
+msgstr "Kustutada see kontakt igaveseks?"
 
-#: lib/Driver.php:543
+#: deletefile.php:36 lib/Driver.php:676 lib/api.php:665 lib/api.php:782
+#: lib/api.php:869 lib/api.php:1159 lib/api.php:1375 lib/Driver/sql.php:481
 msgid "Permission denied"
 msgstr "Juurdepääs puudub"
 
-#: templates/addressbooks.inc:99
+#: templates/addressbooks.inc:120
 msgid "Permissions"
 msgstr "Õigused"
 
-#: data.php:53 templates/data/import.inc:17
+#: config/sources.php.dist:221 config/sources.php.dist:893
+msgid "Personal"
+msgstr "Isiklik"
+
+#: data.php:64 templates/data/import.inc:17
 msgid "Pine Address Book"
 msgstr "Pine aadressiraamat"
 
-#: templates/browse/javascript.inc:28
+#: templates/browse/javascript.inc:27
 msgid "Please name the new contact list:"
 msgstr "Sisesta uue listi nimi:"
 
-#: edit.php:91 edit.php:94 edit.php:194
+#: lib/Forms/EditContact.php:115 lib/Forms/EditContact.php:118
+#: lib/Forms/EditContact.php:166
 msgid "Previous"
 msgstr "Tagasi"
 
-#: config/sources.php.dist:416
-msgid "Private Address Book"
-msgstr "Isiklik aadressiraamat"
-
-#: lib/Driver/ldap.php:147
+#: lib/Driver/ldap.php:165
+#, php-format
 msgid "Query failed: (%s) %s"
 msgstr "Päring nurjus: (%s) %s"
 
-#: templates/block/minisearch.inc:18
-msgid "Quick Search"
-msgstr "Kiirotsing"
-
-#: lib/Driver/ldap.php:191 lib/Driver/ldap.php:199 lib/Driver/ldap.php:429
+#: lib/Driver/ldap.php:209 lib/Driver/ldap.php:217 lib/Driver/ldap.php:453
 #, php-format
 msgid "Read failed: (%s) %s"
 msgstr "Lugemine nurjus: (%s) %s"
 
-#: lib/Renderer.php:49
-msgid "Really delete this contact?"
-msgstr "Kas tõesti kustutada see kanne?"
-
-#: templates/browse/footer.inc:2
-msgid "Records displayed:"
-msgstr "Näidatavad kanded:"
+#: lib/Driver.php:1510
+msgid "Reading contacts is not available."
+msgstr "Kontaktide kuvamine ei ole võimalik."
 
-#: templates/prefs/addressbookselect.inc:176
+#: templates/prefs/addressbookselect.inc:175
 msgid "Remove address book"
 msgstr "Eemalda aadressiraamat"
 
@@ -790,11 +974,19 @@ msgstr "Eemalda aadressiraamat"
 msgid "Remove column"
 msgstr "eemalda veerg"
 
-#: templates/browse/actions.inc:2
+#: templates/browse/actions.inc:3
 msgid "Remove from this list"
 msgstr "Eemalda sellest listist"
 
-#: templates/data/import.inc:21
+#: lib/Driver.php:1548
+msgid ""
+"Removing user data is not supported in the current address book storage "
+"driver."
+msgstr ""
+"Kasutaja andmete kustutamine ei ole võimalik antud aadressi raamatu "
+"draiveriga."
+
+#: templates/data/import.inc:22
 msgid ""
 "Replace existing address book with the imported one? <strong>Warning: This "
 "deletes all entries in your current address book.</strong>"
@@ -802,75 +994,87 @@ msgstr ""
 "Kas asendame olemasoleva aadressiraamatu imporditavaga? <strong>Hoiatus: "
 "sellega hävivad kõik olemasolevad sissekanded aadressiraamatus.</strong>"
 
-#: lib/Driver.php:438
+#: lib/Driver.php:573
 msgid "Requested object not found."
 msgstr "Soovitud objekti ei leitud."
 
-#: add.php:37 templates/browse/search.inc:113
+#: templates/browse/search.inc:120
 msgid "Reset to Defaults"
 msgstr "Taasta vaikeväärtused"
 
-#: config/attributes.php.dist:196
+#: config/attributes.php.dist:330
 msgid "S/MIME Public Certificate"
 msgstr "S/MIME avalik sertifikaat"
 
-#: lib/Driver/ldap.php:69
+#: lib/Driver/ldap.php:87
 #, php-format
 msgid "STARTTLS failed: (%s) %s"
 msgstr "STARTTLS nurjus: (%s) %s"
 
-#: edit.php:99 add.php:37 templates/addressbooks.inc:124
+#: templates/addressbooks.inc:147 lib/Forms/EditContact.php:26
 msgid "Save"
 msgstr "Salvesta"
 
-#: templates/browse/search.inc:96 templates/browse/search.inc:112
-#: templates/block/minisearch.inc:21 lib/Block/tree_menu.php:49
-#: config/prefs.php.dist:126
+#: templates/browse/search_vbook.inc:4
+msgid "Save search as a virtual address book?"
+msgstr "Säilita otsingutulemus virtuaalkaustana?"
+
+#: lib/Driver.php:1536
+msgid "Saving contacts is not available."
+msgstr "Kontatkide salvestamine ei ole saadaval."
+
+#: templates/browse/header.inc:3 templates/browse/search.inc:111
+#: templates/browse/search.inc:119 templates/block/minisearch.inc:26
+#: lib/Block/tree_menu.php:59 config/prefs.php.dist:107
 msgid "Search"
 msgstr "Otsing"
 
-#: minisearch.php:63
-msgid "Search Results:"
-msgstr "Otsingu tulemus:"
-
-#: lib/api.php:1011
+#: lib/api.php:1236
 msgid "Search failed"
 msgstr "Otsing nurjus"
 
-#: lib/api.php:1103 lib/api.php:1109 lib/api.php:1118 lib/api.php:1131
+#: lib/api.php:1380 lib/api.php:1386 lib/api.php:1395 lib/api.php:1408
 #, php-format
 msgid "Search failed: %s"
 msgstr "Otsing nurjus: %s"
 
-#: templates/block/minisearch.inc:22
-msgid "Searching..."
-msgstr "Otsing käib..."
+#: templates/block/minisearch.inc:24
+msgid "Search for: "
+msgstr "Otsing: %s"
 
-#: templates/addressbooks.inc:66
+#: lib/Driver.php:1495
+msgid "Searching is not available."
+msgstr "Otismine ei ole saadaval."
+
+#: templates/addressbooks.inc:84
 msgid "Select an address book to delete"
 msgstr "Vali kustutamisele kuuluv aadressiraamat."
 
-#: templates/addressbooks.inc:90
+#: templates/addressbooks.inc:109
 msgid "Select an address book to edit"
 msgstr "Vali aadressiraamat, mida soovid muuta."
 
-#: templates/data/export.inc:32
+#: templates/browse/row.inc:30
+msgid "Select contact"
+msgstr "Vali kontakt"
+
+#: templates/data/export.inc:33
 msgid "Select the address book to export from:"
 msgstr "Kust eksportida:"
 
-#: templates/data/import.inc:26
+#: templates/data/import.inc:27
 msgid "Select the address book to import to:"
 msgstr "Kuhu importida:"
 
-#: templates/data/import.inc:35
+#: templates/data/import.inc:36
 msgid "Select the charset of the source file:"
 msgstr "Vali lähtefaili märgistik:"
 
 #: templates/data/export.inc:10
-msgid "Seect the export format:"
+msgid "Select the export format:"
 msgstr "Ekspordi vorming:"
 
-#: templates/data/import.inc:43
+#: templates/data/import.inc:44
 msgid "Select the file to import:"
 msgstr "Vali imporditav fail:"
 
@@ -878,7 +1082,7 @@ msgstr "Vali imporditav fail:"
 msgid "Select the format of the source file:"
 msgstr "Vali lähtefaili vorming:"
 
-#: config/prefs.php.dist:135
+#: config/prefs.php.dist:116
 msgid "Select the format used to display names:"
 msgstr "Vali nimede esitusviis:"
 
@@ -894,71 +1098,90 @@ msgstr "Vali, milliseid v
 msgid "Select which format to display names."
 msgstr "Vali, millisel kujul näidata nimesid."
 
-#: add.php:54
+#: lib/Forms/AddContact.php:42
 #, php-format
 msgid "Selected address book \"%s\"."
 msgstr "Valitud aadressiraamat \"%s\"."
 
-#: config/sources.php.dist:173
-msgid "Shared Directory"
-msgstr "Ühiskataloog"
-
-#: templates/browse/actions.inc:39
+#: templates/browse/actions.inc:49
 #, php-format
 msgid "Show %s"
 msgstr "Näita %s"
 
-#: templates/browse/actions.inc:41
-msgid "Show All"
-msgstr "Näita kõiki"
+#: templates/browse/actions.inc:51
+msgid "Show Both"
+msgstr "Näita mõlemaid"
 
-#: templates/browse/actions.inc:40
+#: templates/browse/actions.inc:50
 msgid "Show Contacts"
 msgstr "Näita sissekandeid"
 
-#: templates/browse/actions.inc:39
+#: templates/browse/actions.inc:49
 msgid "Show Lists"
 msgstr "Näita liste"
 
-#: templates/browse/column_headers.inc:21
-#: templates/browse/column_headers.inc:34
+#: lib/ListView.php:319 lib/ListView.php:325
 msgid "Sort Direction"
 msgstr "Järjestuse suund"
 
-#: lib/api.php:198
+#: templates/browse/column_headers.inc:31
+#, php-format
+msgid "Sort by %s"
+msgstr "Järjesta %s järgi"
+
+#: templates/browse/column_headers.inc:26
+#, php-format
+msgid "Sort by %s only"
+msgstr "Järjesta ainult %s järgi"
+
+#: templates/browse/column_headers.inc:29
+#, php-format
+msgid "Sort by %s, then by %s"
+msgstr "Sorteeri %s ja siis %s"
+
+#: templates/menu.inc:6
+msgid "Source:"
+msgstr "Allikad:"
+
+#: lib/api.php:274
 msgid "Sources"
 msgstr "Allikad"
 
-#: config/prefs.php.dist:157
-msgid "Specify where you want advanced search results to appear."
-msgstr "Määra, kus peaksid ilmuma põhjaliku otsingu tulemused."
+#: config/attributes.php.dist:101
+msgid "Spouse"
+msgstr "Abikaasa"
 
-#: browse.php:214 browse.php:262
+#: lib/Views/Browse.php:258 lib/Views/Browse.php:298
 #, php-format
 msgid "Successfully added %d contact(s) to list."
 msgstr "%d sissekannet lisati listi."
 
-#: browse.php:141
+#: lib/Views/Browse.php:212
 #, php-format
 msgid "Successfully added %s to %s"
 msgstr "%s lisati %s-i"
 
-#: browse.php:222
+#: lib/Views/Browse.php:288
 #, php-format
 msgid "Successfully created the contact list \"%s\"."
 msgstr "List \"%s\" loodud."
 
-#: browse.php:84
+#: search.php:127
+#, php-format
+msgid "Successfully created virtual address book \"%s\""
+msgstr "Virtuaal aadressiraamatu \"%s\" tegemine õnnestus"
+
+#: lib/Views/Browse.php:132
 #, php-format
 msgid "Successfully deleted %d contact(s)."
 msgstr "%d sissekanne(t) kustutatud."
 
-#: browse.php:64
+#: lib/Views/Browse.php:115
 #, php-format
 msgid "Successfully removed %d contact(s) from list."
 msgstr "%d sissekanne(t) listist eemaldatud."
 
-#: data.php:50
+#: data.php:61
 msgid "TSV"
 msgstr "TSV"
 
@@ -966,53 +1189,71 @@ msgstr "TSV"
 msgid "Tab separated values"
 msgstr "Tabulaatoriga eraldatud väljad"
 
-#: view.php:15
+#: templates/browse/actions.inc:20
+msgid "Target Address Book"
+msgstr "Siht aadressiraamat"
+
+#: templates/browse/actions.inc:31
+msgid "Target Contact List"
+msgstr "Siht kontaktide nimekiri"
+
+#: data.php:322
+#, php-format
+msgid "The %s file didn't contain any contacts."
+msgstr "Fail %s ei sisaldanud ühtegi kontakti."
+
+#: view.php:17
 msgid "The VFS backend needs to be configured to enable attachment uploads."
 msgstr "Lisandite üleslaadimiseks peab VFS olema seadistatud."
 
-#: lib/Driver.php:1054
+#: lib/Driver.php:1466
 #, php-format
 msgid "The address book \"%s\" does not exist."
 msgstr "Aadressiraamatut \"%s\" pole olemas."
 
-#: addressbooks.php:45
+#: addressbooks.php:104
 #, php-format
 msgid "The address book \"%s\" was created successfully."
 msgstr "Aadressiraamat \"%s\" loodud."
 
-#: addressbooks.php:70
+#: addressbooks.php:130
 #, php-format
 msgid "The address book \"%s\" was removed successfully."
 msgstr "Aadressiraamat \"%s\" kustutatud."
 
-#: addressbooks.php:89
+#: addressbooks.php:151
 #, php-format
 msgid "The address book \"%s\" was successfully updated."
 msgstr "Muudatused aadressiraamatus \"%s\" salvestatud."
 
-#: data.php:303
+#: data.php:329
 #, php-format
 msgid "The address book could not be purged: %s"
 msgstr "Aadressiraamatut ei saanud tühjendada: %s"
 
-#: edit.php:47 vcard.php:22 display.php:23 view.php:26
+#: contact.php:23 view.php:28 vcard.php:19
 msgid "The contact you requested does not exist."
 msgstr "Soovitud sissekannet pole olemas."
 
-#: edit.php:116
+#: deletefile.php:46
 #, php-format
-msgid "The file \"%s\" has successfully been deleted."
-msgstr "Faili \"%s\" kustutamine õnnestus."
+msgid "The file \"%s\" has been deleted."
+msgstr "Fail \"%s\" on kustutatud."
 
-#: data.php:284
+#: data.php:302
 msgid "The import can be finished despite the warnings."
 msgstr "Import on võimalik sooritada hoiatustele vaatamata."
 
-#: browse.php:31
+#: lib/Views/EditContact.php:43 lib/Views/Contact.php:59
+#: lib/Views/DeleteContact.php:36
+msgid "The requested contact was not found."
+msgstr "Otsitud kontakti ei leitud."
+
+#: lib/Views/Browse.php:80
 msgid "There are no browseable address books."
 msgstr "Ükski aadressiraamat ei luba sirvimist."
 
-#: add.php:29
+#: add.php:32
 msgid ""
 "There are no writeable address books. None of the available address books "
 "are configured to allow you to add new entries to them. If you believe this "
@@ -1021,7 +1262,12 @@ msgstr ""
 "Ükski kasutatav aadressiraamat ei võimalda sul aadresse lisada. Kui see on "
 "sinu arvates viga, võta ühendust süsteemi halduriga."
 
-#: add.php:121
+#: search.php:123
+#, php-format
+msgid "There was a problem creating the virtual address book: %s"
+msgstr "Selle aadressiraamatu loomisel tekkis viga: %s"
+
+#: lib/Forms/AddContact.php:90
 msgid ""
 "There was an error adding the new contact. Contact your system administrator "
 "for further help."
@@ -1029,275 +1275,334 @@ msgstr ""
 "Uue sissekande lisamisel tekkis viga. Abi saamiseks võta ühendust süsteemi "
 "halduriga."
 
-#: browse.php:189 browse.php:230
-msgid "There was an error adding this object."
-msgstr "Selle objekti lisamisel tekkis viga."
-
-#: browse.php:238
+#: lib/Views/Browse.php:308
 msgid "There was an error creating a new list."
 msgstr "Listi loomisel tekkis viga."
 
-#: addressbooks.php:35
+#: addressbooks.php:96 addressbooks.php:102
 #, php-format
 msgid "There was an error creating this address book: %s"
 msgstr "Selle aadressiraamatu loomisel tekkis viga: %s"
 
-#: browse.php:147
+#: lib/Views/Browse.php:218
 #, php-format
 msgid "There was an error deleting \"%s\" from the source address book."
 msgstr "\"%s\" kustutamisel lähteaadressiraamatust tekkis viga."
 
-#: delete.php:37
+#: delete.php:39
 #, php-format
 msgid "There was an error deleting this contact: %s"
 msgstr "Selle sissekande kustutamisel tekkis viga: %s"
 
-#: browse.php:72
-msgid "There was an error deleting this object."
-msgstr "Selle objekti kustutamisel tekkis viga."
-
-#: browse.php:278
+#: lib/Views/Browse.php:348
 msgid "There was an error displaying the list"
 msgstr "Listi näitamisel tekkis viga"
 
-#: data.php:335
+#: data.php:356
 #, php-format
 msgid "There was an error importing the data: %s"
 msgstr "Andmete impordil tekkis viga: %s"
 
-#: lib/api.php:507 lib/api.php:761
+#: lib/api.php:678 lib/api.php:944
 msgid "There was an error importing the iCalendar data."
 msgstr "iCalendar andmete impordil tekkis viga."
 
-#: browse.php:58
-msgid "There was an error removing a contact from the list."
-msgstr "Liikme eemaldamisel listist tekkis viga."
+#: lib/api.php:227
+#, php-format
+msgid "There was an error removing an address book for %s"
+msgstr "Selle aadressiraamatu eemaldamisel tekkis viga: %s"
 
-#: addressbooks.php:55 addressbooks.php:60 addressbooks.php:68
+#: addressbooks.php:113 addressbooks.php:120 addressbooks.php:128
 #, php-format
 msgid "There was an error removing this address book: %s"
 msgstr "Selle aadressiraamatu eemaldamisel tekkis viga: %s"
 
-#: addressbooks.php:81 addressbooks.php:87
+#: lib/Forms/EditContact.php:86
+msgid ""
+"There was an error saving the contact. Contact your system administrator for "
+"further help."
+msgstr ""
+"Uue sissekande lisamisel tekkis viga. Abi saamiseks võta ühendust süsteemi "
+"halduriga."
+
+#: addressbooks.php:149
 #, php-format
 msgid "There was an error updating this address book: %s"
 msgstr "Selle aadressiraamatu muutmisel tekkis viga: %s"
 
-#: edit.php:158
-#, php-format
-msgid "There was an error updating this entry: %s"
-msgstr "Andmete muutmisel tekkis viga: %s"
-
-#: data.php:179
+#: data.php:188
 msgid "There were no addresses to export."
 msgstr "Polnudki aadresse, mida eksportida."
 
+#: templates/prefs/addressbookselect.inc:178
 #: templates/prefs/addressbookselect.inc:180
 msgid "These address books will display in this order:"
 msgstr "Aadressiraamatuid näidatakse selles järjestuses:"
 
-#: templates/prefs/columnselect.inc:243
+#: templates/prefs/columnselect.inc:242 templates/prefs/columnselect.inc:244
 msgid "These columns will display in this order:"
 msgstr "Veerge näidatakse selles järjekorras:"
 
-#: data.php:269
+#: data.php:287
 msgid "This file format is not supported."
 msgstr "Seda failivormingut ei toetata."
 
-#: lib/api.php:1122 lib/api.php:1135
+#: lib/api.php:1399 lib/api.php:1412
 #, php-format
 msgid "This person already has a %s entry in the address book"
 msgstr "Sellel isikul on juba aadressiraamatus %s kanne"
 
-#: config/prefs.php.dist:147
+#: config/prefs.php.dist:129
 msgid ""
 "This will be the default address book when adding or importing contacts."
 msgstr ""
 "See on aadressiraamat, kuhu aadresse vaikimisi lisatakse ja imporditakse."
 
-#: config/attributes.php.dist:70
-msgid "Title"
-msgstr "Tiitel"
+#: config/attributes.php.dist:221
+msgid "Time Zone"
+msgstr "Ajatsoon"
 
-#: lib/Turba.php:435
-#, php-format
-msgid "Unable to create new share.  Missing source type."
-msgstr "Uue jao loomine nurjus. Puudub allika tüüp."
+#: lib/Driver/share.php:77
+msgid "Unable to find contact owner."
+msgstr "Kontakti omaniku ei leitud."
 
-#: lib/Driver.php:979
+#: lib/Turba.php:643 lib/Driver.php:1388
 #, php-format
 msgid "Unable to load the definition of %s."
 msgstr "Ei suuda laadida %s kirjeldust."
 
-#: edit.php:88 edit.php:91 edit.php:94 edit.php:99 edit.php:196
+#: lib/Driver/kolab.php:486
+msgid "Unable to search."
+msgstr "Otsing ei õnnestunud."
+
+#: lib/Forms/EditContact.php:26 lib/Forms/EditContact.php:112
+#: lib/Forms/EditContact.php:115 lib/Forms/EditContact.php:118
+#: lib/Forms/EditContact.php:168
 msgid "Undo Changes"
 msgstr "Võta tagasi"
 
-#: lib/api.php:540 lib/api.php:643 lib/api.php:779
+#: config/attributes.php.dist:340
+msgid "Unfiled"
+msgstr "Kategooriata"
+
+#: lib/api.php:711 lib/api.php:814 lib/api.php:962
 #, php-format
 msgid "Unsupported Content-Type: %s"
 msgstr "Toetamata Content-Type: %s"
 
-#: config/sources.php.dist:376
-msgid "Verisign Directory"
-msgstr "Verisigni kataloog"
-
-#: templates/browse/row.inc:19 templates/browse/contactrow.inc:27
-#, php-format
-msgid "View \"%s\""
-msgstr "Vaata \"%s\""
-
-#: minisearch.php:78
+#: minisearch.php:76
 msgid "View Contact"
 msgstr "Vaata sissekannet"
 
-#: config/prefs.php.dist:124
+#: config/prefs.php.dist:105
 msgid "View to display by default:"
 msgstr "Vaikimisi näidatav vaade:"
 
-#: config/attributes.php.dist:208
+#: config/attributes.php.dist:312
 msgid "Website URL"
 msgstr "Veebilehe URL"
 
-#: config/attributes.php.dist:113
+#: config/attributes.php.dist:165
 msgid "Work Address"
 msgstr "Tööaadress"
 
-#: config/attributes.php.dist:124
+#: config/attributes.php.dist:183
 msgid "Work City"
 msgstr "Töökoha linn"
 
-#: config/attributes.php.dist:139
+#: config/attributes.php.dist:201
 msgid "Work Country"
 msgstr "Töökoha riik"
 
-#: config/attributes.php.dist:156
+#: config/attributes.php.dist:246
 msgid "Work Phone"
 msgstr "Töötelefon"
 
-#: config/attributes.php.dist:134
+#: config/attributes.php.dist:177
+msgid "Work Post Office Box"
+msgstr "Töökoha postiindeks"
+
+#: config/attributes.php.dist:195
 msgid "Work Postal Code"
 msgstr "Töökoha postiindeks"
 
-#: config/attributes.php.dist:129
+#: config/attributes.php.dist:189
 msgid "Work State/Province"
 msgstr "Töökoha maakond"
 
-#: config/attributes.php.dist:119
+#: config/attributes.php.dist:171
 msgid "Work Street Address"
 msgstr "Töökoha tänav/maja"
 
-#: data.php:237 browse.php:93 browse.php:201 add.php:73
+#: add.php:48 data.php:251 lib/Views/Browse.php:159 lib/Views/Browse.php:271
 #, php-format
 msgid "You are not allowed to create more than %d contacts in \"%s\"."
 msgstr "Sa ei saa luua rohkem kui %d sissekannet \"%s\"-sse"
 
-#: edit.php:69 display.php:58 view.php:36
+#: lib/Driver/vbook.php:137
+msgid "You cannot add an entry to a virtual address book."
+msgstr "Virtuaal aadressiraamatusse ei saa lisada kirjeid."
+
+#: lib/Driver/vbook.php:121
+msgid "You cannot add new contacts to a virtual address book"
+msgstr "Virtuaal aadressiraamatusse ei saa lisada kirjeid."
+
+#: lib/Driver/vbook.php:129
+msgid "You cannot delete contacts from a virtual address book"
+msgstr "Virtuaal aadressiraamatust ei saa kustutada kirjeid."
+
+#: edit.php:66 view.php:38 lib/Views/EditContact.php:49
+#: lib/Views/DeleteContact.php:42
 msgid "You do not have permission to view this contact."
 msgstr "Sul puuduvad õigused selle sissekande vaatamiseks."
 
-#: vcard.php:40
+#: vcard.php:37
 msgid "You do not have permission to view this object."
 msgstr "Sul puuduvad õigused selle objekti vaatamiseks."
 
-#: templates/browse/javascript.inc:55
+#: addressbooks.php:59
+msgid "You do not have permissions to delete this source."
+msgstr "Sul puuduvad õigused selle objekti kustutamiseks."
+
+#: lib/Driver/ldap.php:708
+msgid ""
+"You must have the Net_LDAP PEAR library installed to use the schema check "
+"function."
+msgstr ""
+"Sul peab olema installeeritud PEAR pakket Net_LDAP kasutamaks raamistik "
+"kontroll funktsiooni."
+
+#: search.php:110
+msgid "You must provide a name for virtual address books."
+msgstr "Sa pead sisestama nime sellele virtuaal aadressiraamatule."
+
+#: templates/browse/javascript.inc:54
 msgid "You must select a target address book."
 msgstr "Sa pead valima sihtaadressiraamatu."
 
-#: templates/browse/javascript.inc:23
-msgid "You must select a target list."
+#: templates/browse/javascript.inc:22
+msgid "You must select a target contact list."
 msgstr "Sa pead valima sihtlisti."
 
-#: edit.php:32
+#: edit.php:31 templates/browse/javascript.inc:16
+#: templates/browse/javascript.inc:48 templates/browse/javascript.inc:71
 msgid "You must select at least one contact first."
 msgstr "Kõigepealt pead valima vähemalt ühe sissekande."
 
-#: templates/browse/javascript.inc:17 templates/browse/javascript.inc:49
-#: templates/browse/javascript.inc:72
-msgid "You must select at least one entry first."
-msgstr "Kõigepealt pead valima vähemalt ühe sissekande."
-
-#: edit.php:73
+#: edit.php:70 lib/Views/EditContact.php:52 lib/Views/DeleteContact.php:45
 msgid "You only have permission to view this contact."
 msgstr "Sul on õigus seda sissekannet ainult vaadata."
 
-#: browse.php:285
+#: lib/Views/Browse.php:355
 msgid "Your default address book is not browseable."
 msgstr "Sinu vaikeaadressiraamat ei võimalda sirvimist."
 
-#: templates/browse/row.inc:6 templates/browse/contactrow.inc:12
-msgid "[no value]"
-msgstr "[väärtus puudub]"
-
-#: lib/Turba.php:484
+#: lib/Turba.php:585
 msgid "_Browse"
 msgstr "Sir_vi"
 
-#: templates/browse/actions.inc:40
+#: templates/browse/actions.inc:50
 msgid "_Contacts"
 msgstr "_Kanded"
 
-#: templates/browse/actions.inc:2
+#: templates/browse/actions.inc:3
 msgid "_Delete"
 msgstr "K_ustuta"
 
-#: lib/Turba.php:493
+#: contact.php:82
+msgid "_Edit"
+msgstr "Muuda"
+
+#: lib/Turba.php:594
 msgid "_Import/Export"
 msgstr "_Import/Eksport"
 
-#: templates/browse/actions.inc:39
+#: templates/browse/actions.inc:49
 msgid "_Lists"
 msgstr "_Listid"
 
-#: lib/Turba.php:481
+#: lib/Turba.php:582
 msgid "_My Address Books"
 msgstr "_Minu aadressiraamatud"
 
-#: lib/Turba.php:487
+#: lib/Turba.php:588
 msgid "_New Contact"
 msgstr "_Uus sissekanne"
 
-#: templates/browse/actions.inc:2
+#: lib/Turba.php:599
+msgid "_Print"
+msgstr "_Prindi"
+
+#: templates/browse/actions.inc:3
 msgid "_Remove from this list"
 msgstr "_Eemalda sellest listist"
 
-#: lib/Turba.php:489
+#: lib/Turba.php:590
 msgid "_Search"
 msgstr "_Otsing"
 
-#: vcard.php:46
-msgid "contact.vcf"
-msgstr "aadress.vcf"
+#: contact.php:79
+msgid "_View"
+msgstr "Vaata"
 
-#: data.php:199 data.php:204 templates/data/export.inc:1
+#: templates/browse/column_headers.inc:24
+msgid "ascending"
+msgstr "Kasvav"
+
+#: vcard.php:45
+msgid "contact"
+msgstr "kontakt"
+
+#: data.php:208 data.php:213 templates/data/export.inc:1
 msgid "contacts.csv"
 msgstr "aadressid.csv"
 
-#: data.php:209
+#: data.php:233
+msgid "contacts.ldif"
+msgstr "contacts.ldif"
+
+#: data.php:218
 msgid "contacts.tsv"
 msgstr "aadressid.tsv"
 
-#: data.php:219
+#: data.php:228
 msgid "contacts.vcf"
 msgstr "aadressid.vcf"
 
-#: config/prefs.php.dist:138
+#: templates/browse/column_headers.inc:24
+msgid "descending"
+msgstr "Kahanev"
+
+#: config/attributes.php.dist:377
+msgid "female"
+msgstr "naine"
+
+#: lib/Block/tree_menu.php:38
+#, php-format
+msgid "in %s"
+msgstr "%s sees"
+
+#: config/attributes.php.dist:377
+msgid "male"
+msgstr "mees"
+
+#: config/prefs.php.dist:119
 msgid "no formatting"
 msgstr "suvaline"
 
-#: templates/prefs/imsp_opt.inc:34
+#: templates/prefs/imsp_opt.inc:32
 msgid "none"
 msgstr "puudub"
 
-#: templates/browse/actions.inc:24
+#: templates/browse/actions.inc:33
 msgid "to a Contact List"
 msgstr "listi"
 
-#: templates/browse/actions.inc:14
+#: templates/browse/actions.inc:22
 msgid "to a different Address Book"
 msgstr "teise aadressiraamatusse"
 
-#: data.php:51 templates/browse/column_headers.inc:13
+#: data.php:62 templates/browse/column_headers.inc:13
 #: templates/data/import.inc:15 templates/data/export.inc:15
 msgid "vCard"
 msgstr "vCard"
diff --git a/po/cs_CZ.po b/po/eu_ES.po
similarity index 57%
copy from po/cs_CZ.po
copy to po/eu_ES.po
index a8ed468..67a2ee3 100644
--- a/po/cs_CZ.po
+++ b/po/eu_ES.po
@@ -1,197 +1,188 @@
-# translation of turba.po to Cestina
-# Turba Czech Translation.
-# Copyright (C) 2004 Horde Project
-# This file is distributed under the same license as the Horde package.
-# Pavel Chytil <pavel at chytil.tk>, 2001-2004.
+# Basque translations for Turba.
+# Copyright (C) 2008 Horde Project
+# This file is distributed under the same license as the Turba package.
+# Euskal Herriko Unibertsitatea EHU/UPV <xabier.arrieta at ehu.es>, 2008.
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: turba\n"
+"Project-Id-Version: Turba H3 (2.1)\n"
 "Report-Msgid-Bugs-To: dev at lists.horde.org\n"
-"POT-Creation-Date: 2007-03-30 18:15+0200\n"
-"PO-Revision-Date: 2007-04-05 11:35+0200\n"
-"Last-Translator: Jan Krivanek <honza at atack.cz>\n"
-"Language-Team: Cestina <cs at li.org>\n"
+"POT-Creation-Date: 2008-01-16 09:33+0100\n"
+"PO-Revision-Date: 2008-07-17 14:28+0200\n"
+"Last-Translator: Euskal Herriko Unibertsitatea EHU/UPV <xabier.arrieta at ehu."
+"es>\n"
+"Language-Team: Euskal Herriko Unibertsitatea EHU/UPV <xabier.arrieta at ehu."
+"es>\n"
 "MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=ISO-8859-2\n"
+"Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms:  nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
-"X-Generator: KBabel 1.11.4\n"
 
 #: templates/prefs/imsp_opt.inc:40
 msgid " Add an IMSP Address Book"
-msgstr "Pøidat IMSP Adresáø"
+msgstr " Gehitu IMSP helbide-liburu bat"
 
 #: templates/prefs/imsp_opt.inc:29
 msgid " Delete IMSP Address Book"
-msgstr "Smazat IMSP Adresáø"
+msgstr " Ezabatu IMSP helbide-liburua"
 
-#: data.php:333
+#: data.php:339
 #, php-format
 msgid "\"%s\" already exists and was not imported."
-msgstr "\"%s\" ji¾ existuje a nebyl naimportován."
+msgstr "\"%s\" badago lehendik eta ez da inportatu."
 
 #: browse.php:140
 #, php-format
 msgid "\"%s\" was not copied because it is a list."
-msgstr "\"%s\" nebyl zkopírován, proto¾e je to seznam."
+msgstr "\"%s\" ez da kopiatu zerrenda bat delako."
 
 #: browse.php:138
 #, php-format
 msgid "\"%s\" was not moved because it is a list."
-msgstr "\"%s\" nebyl pøesunut, proto¾e je to seznam."
+msgstr "\"%s\" ez da lekuz aldatu zerrenda bat delako."
 
-#: config/prefs.php.dist:137
+#: config/prefs.php.dist:130
 msgid "\"Firstname Lastname\"  (ie. John Doe)"
-msgstr "\"Jméno pøíjmení\" (napø. Josef Novák)"
+msgstr "\"Izena Abizena\" (adib. Maialen Ugarte)"
 
-#: config/prefs.php.dist:136
+#: config/prefs.php.dist:129
 msgid "\"Lastname, Firstname\" (ie. Doe, John)"
-msgstr "\"Pøíjmení, jméno\" (napø. Novák, Josef) "
+msgstr "\"Abizena, Izena\" (adib. Ugarte, Maialen)"
 
 #: add.php:112
 #, php-format
 msgid "%s added."
-msgstr "%s pøidán."
+msgstr "%s gehitu da."
 
-#: data.php:357
+#: data.php:363
 #, php-format
-msgid "%s file successfully imported"
-msgstr "%s soubor byl úspì¹nì importován"
+msgid "%s file successfully imported."
+msgstr "%s fitxategia ondo inportatu da."
 
 #: templates/browse/footer.inc:5
 #, php-format
 msgid "%s to %s of %s"
-msgstr "%s a¾ %s z %s"
+msgstr "%s - %s / %s"
 
-#: lib/Turba.php:458
+#: lib/Turba.php:460
 #, php-format
 msgid "%s's Address Book"
-msgstr "Adresáø %s"
+msgstr "%s(r)en helbide-liburua"
 
 #: lib/Block/minisearch.php:42
 msgid "A browser that supports iFrames is required"
-msgstr "Je nutné pou¾ít prohlí¾eè podporující iFrames."
-
-#: config/prefs.php.dist:160
-msgid "Above search form"
-msgstr "Vý¹e uvedený formuláø pro vyhledávání"
+msgstr "iFrames onartzen duen arakatzailea behar da."
 
 #: view.php:52
 #, php-format
 msgid "Access denied to %s"
-msgstr "Do %s byl pøístup zamítnut"
+msgstr "%s(e)rako sarbidea ukatuta"
 
 #: add.php:56 templates/browse/actions.inc:29
 msgid "Add"
-msgstr "Pøidat"
+msgstr "Gehitu"
 
 #: templates/prefs/addressbookselect.inc:174
 msgid "Add address book"
-msgstr "Pøidat Adresáø"
+msgstr "Gehitu helbide-liburua"
 
-#: config/prefs.php.dist:48
+#: config/prefs.php.dist:41
 msgid "Add and Delete IMSP address books"
-msgstr "Pøidat a smazat IMSP adresáøe"
+msgstr "Gehitu eta ezabatu IMPSP helbide-liburua"
 
 #: templates/prefs/columnselect.inc:237
 msgid "Add column"
-msgstr "Pøidat sloupec"
+msgstr "Gehitu zutabea"
 
 #: edit.php:135
 msgid "Add file"
-msgstr "Pøidat soubor"
+msgstr "Gehitu fitxategia"
 
 #: templates/browse/actions.inc:29
 msgid "Add to"
-msgstr "Pøidat do"
+msgstr "Gehitu hemen"
 
 #: lib/Driver/null.php:57
 msgid "Adding contacts is not available."
-msgstr "Pøidávání kontaktù není k dispozici."
+msgstr "Kontaktu-gehitzea ez dago erabilgarri."
 
 #: templates/addressbooks.inc:93
 msgid "Address Book"
-msgstr "Adresáø"
+msgstr "Helbide-liburua"
 
-#: browse.php:28 config/prefs.php.dist:125
+#: browse.php:28 config/prefs.php.dist:118
 msgid "Address Book Listing"
-msgstr "Výpis adresáøe"
+msgstr "Helbide-liburuen zerrenda"
 
 #: config/prefs.php.dist:10
 msgid "Address Books"
-msgstr "Adresáøe"
+msgstr "Helbide-liburuak"
 
 #: add.php:111
 msgid "Address book entry"
-msgstr "Polo¾ka adresáøe"
+msgstr "Helbide-liburuko sarrera"
 
-#: data.php:313
+#: data.php:319
 msgid "Address book successfully purged."
-msgstr "Adresáø byl úspì¹nì vyèistìný."
+msgstr "Helbide-liburua behar bezala purgatu da."
 
 #: templates/addressbooks.inc:69
 msgid "Address book to delete "
-msgstr "Adresáø ke smazání "
+msgstr "Ezabatzeko helbide-liburua "
 
 #: templates/prefs/addressbookselect.inc:170
 msgid "Address books that will not be displayed:"
-msgstr "Adresáøe, které nebudou zobrazeny:"
+msgstr "Bistaratuko ez diren helbide-liburuak:"
 
 #: search.php:88 templates/browse/search.inc:38
 msgid "Advanced Search"
-msgstr "Pokroèilé vyhledávání"
+msgstr "Bilaketa aurreratua"
 
 #: config/attributes.php.dist:64
 msgid "Alias"
-msgstr "Alias"
+msgstr "Aliasa"
 
 #: templates/browse/footerAlpha.inc:7 templates/browse/footerAlpha.inc:10
-#: templates/browse/actions.inc:48
+#: templates/browse/actions.inc:49
 msgid "All"
-msgstr "V¹e"
+msgstr "Denak"
 
 #: lib/api.php:607
 msgid "Already Exists"
-msgstr "Ji¾ existuje"
+msgstr "Badago lehendik"
 
 #: templates/browse/javascript.inc:93
 #, php-format
 msgid "Are you sure that you want to delete %s?"
-msgstr "Jste si jistí, ¾e chcete smazat %s?"
+msgstr "Ziur zaude %s ezabatu nahi duzula?"
 
 #: templates/browse/javascript.inc:67
 msgid "Are you sure that you want to delete the selected entries?"
-msgstr "Jste si jistí, ¾e chcete smazat vybrané polo¾ky?"
+msgstr "Ziur zaude hautatutako sarrerak ezabatu nahi dituzula?"
 
-#: config/prefs.php.dist:96
+#: config/prefs.php.dist:89
 msgid "Ascending"
-msgstr "Vzestupnì"
+msgstr "Gorantz"
 
 #: search.php:85 templates/browse/search.inc:37
 msgid "Basic Search"
-msgstr "Vyhledat"
-
-#: config/prefs.php.dist:159
-msgid "Below search form"
-msgstr "Pod formuláøem pro vyhledávání"
+msgstr "Oinarrizko bilaketa"
 
 #: lib/Driver/ldap.php:77 lib/Driver/ldap.php:80
 #, php-format
 msgid "Bind failed: (%s) %s"
-msgstr "Spojení selhalo: (%s) %s"
+msgstr "Loturak huts egin du: (%s) %s"
 
 #: config/attributes.php.dist:178
 msgid "Birthday"
-msgstr "Narozeniny"
+msgstr "Urtebetetzea"
 
 #: templates/menu.inc:7
 msgid "Browse"
-msgstr "Prohlí¾et"
+msgstr "Arakatu"
 
 #: config/attributes.php.dist:173
 msgid "Business Category"
-msgstr "Obchodní kategorie"
+msgstr "Negozio kategoria"
 
 #: data.php:49
 msgid "CSV"
@@ -199,549 +190,541 @@ msgstr "CSV"
 
 #: lib/Object/Group.php:57
 msgid "Can't add a group to itself."
-msgstr "Nelze pøidat skupinu sama sobì."
+msgstr "Ezin zaio talde bat gehitu bere buruari."
 
 #: templates/browse/column_headers.inc:7
 msgid "Check _All/None"
-msgstr "Vybrat _v¹e/¾ádné"
+msgstr "Egiaztatu _guztiak/bat ere ez"
 
 #: templates/addressbooks.inc:51
 msgid "Choose a name"
-msgstr "Zvolte název"
+msgstr "Aukeratu izen bat"
 
 #: add.php:48
 msgid "Choose an address book"
-msgstr "Vyberte adresáø"
+msgstr "Aukeratu helbide-liburu bat"
 
 #: templates/prefs/columnselect.inc:221
 msgid "Choose an address book:"
-msgstr "Zvolte Adresáø:"
+msgstr "Aukeratu helbide-liburu bat:"
 
 #: templates/prefs/addressbookselect.inc:165
 msgid "Choose which address books to display, and in what order:"
-msgstr "Zvolte adresáøe k zobrazení a jejich poøadí:"
+msgstr "Aukeratu zein helbide-liburu bistaratuko diren eta zein ordenatan:"
 
 #: config/prefs.php.dist:11
 msgid "Choose which address books to use."
-msgstr "Vyberte adresáø, který chcete pou¾ívat."
+msgstr "Aukeratu zein helbide-liburu erabiliko diren."
 
 #: templates/prefs/columnselect.inc:228
 msgid "Choose which columns to display and in what order:"
-msgstr "Vyberte sloupce k zobrazení a jejich poøadí:"
+msgstr "Aukeratu zein zutabe bistaratuko diren eta zein ordenatan:"
 
 #: config/prefs.php.dist:17
 msgid "Column Options"
-msgstr "Mo¾nosti sloupce"
+msgstr "Zutabe-aukerak"
 
 #: templates/prefs/columnselect.inc:233
 msgid "Columns that will not be displayed:"
-msgstr "Sloupce, které nebudou zobrazeny:"
+msgstr "Bistaratuko ez diren zutabeak:"
 
-#: templates/data/import.inc:13 templates/data/export.inc:12
+#: templates/data/export.inc:12 templates/data/import.inc:13
 msgid "Comma separated values"
-msgstr "Èárkou oddìlené hodnoty"
+msgstr "Komaz bereizitako balioak"
 
 #: templates/data/export.inc:13
 msgid "Comma separated values (Microsoft Outlook)"
-msgstr "Èárkou oddìlené hodnoty (Microsoft Outlook)"
+msgstr "Komaz bereizitako balioak (Microsoft Outlook)"
 
 #: config/attributes.php.dist:76
 msgid "Company"
-msgstr "Firma"
+msgstr "Enpresa"
 
 #: config/attributes.php.dist:144
 msgid "Company Address"
-msgstr "Adresa firmy"
+msgstr "Enpresaren helbidea"
 
 #: lib/api.php:319 lib/api.php:364 lib/api.php:414 lib/api.php:546
 #: lib/api.php:660 lib/api.php:747 lib/api.php:805 lib/api.php:886
-#: lib/api.php:1086 lib/api.php:1175
+#: lib/api.php:1087 lib/api.php:1176
 #, php-format
 msgid "Connection failed: %s"
-msgstr "Spojení selhalo: %s"
+msgstr "Konexioak huts egin du: %s"
 
 #: lib/Driver/ldap.php:58
 msgid "Connection failure"
-msgstr "Spojení selhalo"
+msgstr "Konexioak huts egin du"
 
 #: lib/Block/minisearch.php:3 lib/Block/minisearch.php:26
 msgid "Contact Search"
-msgstr "Vyhledat kontakt"
+msgstr "Kontaktu-bilaketa"
 
-#: browse.php:306
+#: browse.php:310
 #, php-format
 msgid "Contacts in list: %s"
-msgstr "Kontakty v seznamu: %s"
+msgstr "Zerrendako kontaktuak: %s"
 
 #: templates/browse/actions.inc:18
 msgid "Copy"
-msgstr "Kopírovat"
+msgstr "Kopiatu"
 
 #: templates/addressbooks.inc:60
 msgid "Create"
-msgstr "Vytvoøit"
+msgstr "Sortu"
 
 #: templates/addressbooks.inc:47
 msgid "Create Address Book"
-msgstr "Vytvoøit Adresáø"
+msgstr "Sortu helbide-liburua"
 
-#: lib/ListView.php:348
+#: lib/ListView.php:350
 msgid "Create a new Contact List in:"
-msgstr "Vytvoøit nový seznam kontaktù v:"
+msgstr "Sortu kontaktu-zerrenda berria hemen:"
 
 #: lib/ObjectView.php:133
 msgid "Created"
-msgstr "Vytvoøeno"
+msgstr "Sortu da"
 
-#: config/prefs.php.dist:98
+#: config/prefs.php.dist:91
 msgid "Default sorting direction:"
-msgstr "Implicitní smìr tøídìní:"
+msgstr "Ordenatze-noranzko lehenetsia:"
 
 #: lib/Renderer.php:47 lib/Renderer.php:50 lib/Object.php:328
 #: templates/addressbooks.inc:83 templates/browse/actions.inc:3
 msgid "Delete"
-msgstr "Smazat"
+msgstr "Ezabatu"
 
 #: templates/addressbooks.inc:65
 msgid "Delete Address Book"
-msgstr "Smazat Adresáø"
-
-#: config/prefs.php.dist:38
-msgid "Delete Confirmation"
-msgstr "Potvrzení smazání"
-
-#: config/prefs.php.dist:39
-msgid "Delete button behaviour"
-msgstr "Vlastnosti tlaèítka pro smazání"
+msgstr "Ezabatu helbide-liburua"
 
 #: lib/Driver/ldap.php:270
 #, php-format
 msgid "Delete failed: (%s) %s"
-msgstr "Smazaní selhalo: (%s) %s"
+msgstr "Huts egin du ezabatzean: (%s) %s"
 
 #: lib/Driver/null.php:62
 msgid "Deleting contacts is not available."
-msgstr "Mazání kontaktù není k dispozici."
+msgstr "Kontaktu-ezabatzea ez dago erabilgarri."
 
 #: delete.php:38
 msgid "Deletion failed"
-msgstr "Smazaní selhalo"
+msgstr "Huts egin du ezabatzean"
 
 #: config/attributes.php.dist:213
 msgid "Department"
-msgstr "Oddìlìní"
+msgstr "Saila"
 
-#: config/prefs.php.dist:97
+#: config/prefs.php.dist:90
 msgid "Descending"
-msgstr "Sestupnì"
+msgstr "Beherantz"
 
 #: templates/addressbooks.inc:122
 msgid "Description"
-msgstr "Popis"
+msgstr "Azalpena"
 
-#: templates/browse/search.inc:119
+#: templates/browse/search.inc:106
 msgid "Directory"
-msgstr "Adresáø"
+msgstr "Direktorioa"
 
 #: config/prefs.php.dist:24
 msgid "Display"
-msgstr "Zobrazit"
+msgstr "Bistaratu"
 
 #: config/prefs.php.dist:9 config/prefs.php.dist:16 config/prefs.php.dist:23
 #: config/prefs.php.dist:30
 msgid "Display Options"
-msgstr "Zobrazit mo¾nosti"
-
-#: config/prefs.php.dist:177
-msgid "Do you want to confirm deleting entries?"
-msgstr "Chcete potvrdit smazání polo¾ek?"
+msgstr "Bistaratzeko aukerak"
 
 #: lib/Object.php:297
 msgid "Download"
-msgstr "Stáhnout"
+msgstr "Deskargatu"
 
 #: templates/browse/row.inc:17 templates/browse/contactrow.inc:25
 msgid "Download vCard"
-msgstr "Stahnout vCard"
+msgstr "Deskargatu vCard"
 
 #: lib/Renderer.php:42 templates/addressbooks.inc:109
 #: templates/browse/row.inc:25 templates/browse/column_headers.inc:10
 #: templates/browse/contactrow.inc:33 templates/browse/actions.inc:6
 msgid "Edit"
-msgstr "Upravit"
+msgstr "Editatu"
 
 #: templates/browse/row.inc:25 templates/browse/contactrow.inc:33
 #, php-format
 msgid "Edit \"%s\""
-msgstr "Upravit \"%s\""
+msgstr "Editatu \"%s\""
 
 #: templates/addressbooks.inc:88
 msgid "Edit Address Books"
-msgstr "Upravit Adresáøe"
+msgstr "Editatu helbide-liburuak"
 
 #: edit.php:85 edit.php:161
 #, php-format
 msgid "Edit entry for %s"
-msgstr "Upravit polo¾ku pro %s"
+msgstr "Editatu %s(r)en sarrera"
 
 #: config/attributes.php.dist:58
 msgid "Email"
-msgstr "Email"
+msgstr "Helb.el."
 
 #: edit.php:154
 #, php-format
 msgid "Entry for %s updated, but saving the uploaded file failed: %s"
 msgstr ""
-"Polo¾ka pro  %s byla zaktualizována, ale selhalo ulo¾ení poslaného soubory: %"
-"s"
+"%s(r)en sarrera eguneratu da, baina huts egin du eguneratutako fitxategia "
+"gordetzean: %s"
 
 #: edit.php:156 edit.php:159
 #, php-format
 msgid "Entry for %s updated."
-msgstr "Polo¾ky pro %s byly aktualizovány."
+msgstr "%s(r)en sarrera eguneratu da."
 
-#: browse.php:216 browse.php:264
+#: browse.php:220 browse.php:268
 #, php-format
 msgid "Error adding %d contact(s) to list."
-msgstr "Chyba pøi pøidávání %d kontaktu(ù) do seznamu."
+msgstr "Errorea %d kontaktu zerrendan gehitzean."
 
-#: browse.php:218 browse.php:266
+#: browse.php:222 browse.php:270
 #, php-format
 msgid "Error adding %d of %d requested contact(s) to list."
-msgstr "Chyba pøi pøidávání %d z %d po¾adovaných kontaktù do seznamu."
+msgstr "Errorea eskatutako %d / %d kontaktu zerrendan gehitzean."
 
 #: browse.php:86
 #, php-format
 msgid "Error deleting %d contact(s)."
-msgstr "Chyba pøi mazání %d kontaktu(ù)."
+msgstr "Errorea %d kontaktu ezabatzean."
 
 #: browse.php:88
 #, php-format
 msgid "Error deleting %d of %d requested contacts(s)."
-msgstr "Chyba pøi odstraòování %d z po¾adovaných %d kontaktù."
+msgstr "Errorea eskatutako %d / %d kontaktu ezabatzean."
 
 #: browse.php:66
 #, php-format
 msgid "Error removing %d contact(s) from list."
-msgstr "Chyba pøi odstraòování %d kontakt(ù) ze seznamu."
+msgstr "Errorea %d kontaktu zerrendatik kentzean."
 
 #: browse.php:68
 #, php-format
 msgid "Error removing %d of %d requested contact(s) from list."
-msgstr "Chyba pøi odstraòování %d z po¾adovaných %d kontaktù ze seznamu."
+msgstr "Errorea eskatutako %d / %d kontaktu zerrendatik kentzean."
 
 #: lib/api.php:420
 #, php-format
 msgid "Error searching the address book: %s"
-msgstr "Chyba pøi hledání v Adresáøi: %s"
+msgstr "Errorea helbide liburuan bilatzean: %s"
 
 #: templates/browse/actions.inc:10 templates/data/export.inc:41
 msgid "Export"
-msgstr "Exportovat"
+msgstr "Esportatu"
 
 #: templates/data/export.inc:6
 msgid "Export Address Book"
-msgstr "Exportovat adresáø"
+msgstr "Esportatu helbide-liburua"
 
 #: templates/data/export.inc:21
 msgid "Export only the selected contacts."
-msgstr "Exportovat pouze vybrané kontakty."
+msgstr "Esportatu hautatako kontaktuak bakarrik."
 
 #: templates/data/export.inc:27
 msgid "Export the following address book completely."
-msgstr "Kompletnì exportovat následující adresáø."
+msgstr "Esportatu helbide-liburu hau osorik."
 
-#: data.php:134 data.php:228 data.php:302 browse.php:35 browse.php:103
-#: browse.php:130 search.php:37 add.php:67
+#: add.php:67 search.php:37 browse.php:35 browse.php:103 browse.php:130
+#: data.php:134 data.php:228 data.php:302
 #, php-format
 msgid "Failed to access the address book: %s"
-msgstr "Nelze pøistoupit k adresáøi: %s"
+msgstr "Huts egin du helbide-liburua atzitzean: %s"
 
 #: browse.php:158
 #, php-format
 msgid "Failed to add %s to %s: %s"
-msgstr "Selhalo pøidání %s do %s: %s"
+msgstr "Huts egin du gehitzean %s --> %s: %s"
 
-#: browse.php:312
+#: browse.php:316
 msgid "Failed to browse list"
-msgstr "Nelze prohlédnout seznam"
+msgstr "Huts egin du zerrenda arakatzean"
 
-#: browse.php:350
+#: browse.php:354
 msgid "Failed to browse the directory"
-msgstr "Nelze prohlédnout adresáø"
+msgstr "Huts egin du direktorioa arakatzean"
 
 #: lib/Driver/ldap.php:303
 #, php-format
 msgid "Failed to change name: (%s) %s; Old DN = %s, New DN = %s, Root = %s"
-msgstr "Selhala zmìna jména: (%s) %s; staré DN = %s, nové DN = %s, Root = %s"
+msgstr ""
+"Huts egin du izena aldatzean: (%s) %s; DN zaharra = %s, DN berria = %s, Root "
+"= %s"
 
 #: browse.php:134
 #, php-format
 msgid "Failed to find object to be added: %s"
-msgstr "Nelze nalézt objekt k pøidání: %s"
+msgstr "Huts egin du gehitzeko objektua aurkitzean: %s"
 
 #: search.php:79
 msgid "Failed to search the address book"
-msgstr "Hledání v adresáøi je nedostupné"
+msgstr "Huts egin du helbide-liburuan bilatzean"
 
 #: data.php:149
 #, php-format
 msgid "Failed to search the directory: %s"
-msgstr "Nelze prohledat kontakty: %s"
+msgstr "Huts egin du direktorioan bilatzean: %s"
 
 #: config/attributes.php.dist:167
 msgid "Fax"
-msgstr "Fax"
+msgstr "Faxa"
 
-#: edit.php:131 display.php:99
+#: display.php:99 edit.php:131
 msgid "Files"
-msgstr "Soubory"
+msgstr "Fitxategiak"
 
-#: templates/browse/search.inc:71
+#: templates/browse/search.inc:77
 msgid "Find"
-msgstr "Hledat"
+msgstr "bilatu"
 
 #: edit.php:104 edit.php:195
 msgid "Finish"
-msgstr "Konec"
+msgstr "Amaitu"
 
 #: config/attributes.php.dist:48
 msgid "First Name"
-msgstr "Jméno"
+msgstr "Izena"
 
 #: config/attributes.php.dist:202
 msgid "Freebusy URL"
-msgstr "Free/Busy URL"
+msgstr "Libre/lanpetutaren URLa"
 
-#: templates/browse/search.inc:84
+#: templates/browse/search.inc:69
 msgid "From"
-msgstr "Od"
+msgstr "Hemen"
 
-#: config/sources.php.dist:589
+#: config/sources.php.dist:625
 msgid "Global Address Book"
-msgstr "Globální adresáø"
+msgstr "Helbide-liburu orokorra"
 
 #: templates/browse/row.inc:43 templates/browse/contactrow.inc:44
 msgid "Group"
-msgstr "Skupina"
+msgstr "Taldea"
 
 #: config/attributes.php.dist:82
 msgid "Home Address"
-msgstr "Adresa domù"
+msgstr "Helbidea (etxea)"
 
 #: config/attributes.php.dist:93
 msgid "Home City"
-msgstr "Mìsto adresy domù"
+msgstr "Herria (etxea)"
 
 #: config/attributes.php.dist:108
 msgid "Home Country"
-msgstr "Zemì adresy domù"
+msgstr "Estatua (etxea)"
 
 #: config/attributes.php.dist:150
 msgid "Home Phone"
-msgstr "Telefon domù"
+msgstr "Telefonoa (etxekoa)"
 
 #: config/attributes.php.dist:103
 msgid "Home Postal Code"
-msgstr "PSÈ domù"
+msgstr "Posta-kodea (etxea)"
 
 #: config/attributes.php.dist:98
 msgid "Home State/Province"
-msgstr "Stát/provincie adresy domù"
+msgstr "Herrialdea/probintzia (etxea)"
 
 #: config/attributes.php.dist:88
 msgid "Home Street Address"
-msgstr "Ulice adresy domù"
+msgstr "Kale-helbidea (etxea)"
 
-#: config/sources.php.dist:491
+#: config/sources.php.dist:527
 msgid "IMSP"
 msgstr "IMSP"
 
-#: config/prefs.php.dist:47
+#: config/prefs.php.dist:40
 msgid "IMSP Address Book Administration"
-msgstr "Správa IMSP Adresáøe"
+msgstr "IMSP helbide-liburuen administrazioa"
 
 #: templates/data/import.inc:7
 #, php-format
 msgid "Import Address Book, Step %d"
-msgstr "Importovat adresáø, krok %d"
+msgstr "Inportatu helbide-liburua, %d. urratsa"
 
-#: data.php:375
+#: data.php:381
 msgid "Import/Export Address Books"
-msgstr "Importovat/exportovat adresáøe"
+msgstr "Inportatu/esportatu helbide-liburuak"
 
 #: lib/api.php:655 lib/api.php:742
 msgid "Invalid ID"
-msgstr "Neplatné ID"
+msgstr "IDa ez da baliozkoa"
 
-#: lib/api.php:231 lib/api.php:1158
+#: lib/api.php:231 lib/api.php:1159
 msgid "Invalid address book"
-msgstr "Neplatný adresáø"
+msgstr "Helbide-liburua ez da baliozkoa"
 
 #: lib/api.php:409 lib/api.php:541 lib/api.php:651 lib/api.php:738
 #: lib/api.php:795
 #, php-format
 msgid "Invalid address book: %s"
-msgstr "Neplatný Adresáø: %s"
+msgstr "Helbide-liburua ez da baliozkoa: %s"
 
-#: lib/api.php:1162 lib/api.php:1236 lib/api.php:1292
+#: lib/api.php:1163 lib/api.php:1237 lib/api.php:1293
 msgid "Invalid email"
-msgstr "Neplatný email"
+msgstr "Helbide elektronikoa ez da baliozkoa"
 
-#: lib/api.php:1170
+#: lib/api.php:1171
 msgid "Invalid entry"
-msgstr "Neplatná polo¾ka"
+msgstr "Sarrera ez da baliozkoa"
 
 #: lib/Driver/ldap.php:266
 msgid "Invalid key specified."
-msgstr "Byl poskytnut neplatný klíè."
+msgstr "Gako baliogabea zehaztu da."
 
-#: lib/api.php:1166
+#: lib/api.php:1167
 msgid "Invalid name"
-msgstr "Neplatné jméno"
+msgstr "Izena ez da baliozkoa"
 
 #: lib/api.php:799
 msgid "Invalid objectId"
-msgstr "Neplatné ID objektu"
+msgstr "objectId ez da baliozkoa"
 
 #: lib/Driver/ldap.php:30
 msgid ""
 "LDAP support is required but the LDAP module is not available or not loaded."
-msgstr "Je vy¾adován LDAP, ale tento modul není dostupný nebo instalovaný."
+msgstr ""
+"LDAP euskarria behar da, baina LDAP modulua ez dago erabilgarri edo ez da "
+"kargatu."
 
 #: lib/ObjectView.php:138
 msgid "Last Modified"
-msgstr "Naposledy zmìnìno"
+msgstr "Aldatze-data"
 
 #: config/attributes.php.dist:53
 msgid "Last Name"
-msgstr "Pøíjmení"
+msgstr "Abizena"
 
 #: templates/browse/column_headers.inc:16
 msgid "List"
-msgstr "Seznam"
+msgstr "Zerrenda"
 
-#: templates/browse/search.inc:78
+#: templates/browse/search.inc:82
 msgid "Matching"
-msgstr "Shodující se s"
+msgstr "hau"
 
 #: lib/api.php:206
 msgid "Maximum Number of Contacts"
-msgstr "Maximální poèet kontaktù"
+msgstr "kontaktu gehienez"
 
-#: config/prefs.php.dist:107
+#: config/prefs.php.dist:100
 msgid "Maximum number of pages"
-msgstr "Maximální poèet stránek"
+msgstr "orri gehienez"
 
 #: lib/Block/tree_menu.php:3
 msgid "Menu List"
-msgstr "Seznam nabídky"
+msgstr "Menu-zerrenda"
 
 #: lib/Driver/ldap.php:296
 msgid "Missing DN in LDAP source configuration."
-msgstr "Chybìjící DN u konfigurace zdroje LDAP."
+msgstr "DN falta da LDAP iturburuen konfigurazioan."
 
 #: config/attributes.php.dist:162
 msgid "Mobile Phone"
-msgstr "Mobilní telefon"
+msgstr "Telefono mugikorra"
 
 #: lib/Driver/ldap.php:325 lib/Driver/ldap.php:341
 #, php-format
 msgid "Modify failed: (%s) %s"
-msgstr "Selhala úprava: (%s) %s"
+msgstr "Huts egin du aldaketak: (%s) %s"
 
-#: lib/api.php:1277
+#: lib/api.php:1278
 msgid "More than 1 entry found"
-msgstr "Byla nalezena více ne¾ 1 polo¾ka"
+msgstr "Sarrera bat baino gehiago aurkitu da"
 
 #: templates/browse/actions.inc:16
 msgid "Move"
-msgstr "Pøesunout"
+msgstr "Lekuz aldatu"
 
 #: templates/prefs/addressbookselect.inc:184
 #: templates/prefs/columnselect.inc:247
 msgid "Move left"
-msgstr "Pøesunout vlevo"
+msgstr "Eraman ezkerrera"
 
 #: templates/prefs/addressbookselect.inc:186
 #: templates/prefs/columnselect.inc:249
 msgid "Move right"
-msgstr "Pøesunout vpravo"
+msgstr "Mugitu eskuinera"
 
 #: data.php:52 templates/data/import.inc:16
 msgid "Mulberry Address Book"
-msgstr "Mulberry adresáø"
+msgstr "Mulberry helbide-liburua"
 
-#: lib/api.php:1194
+#: lib/api.php:1195
 #, php-format
 msgid ""
 "Multiple persons with address [%s], but none with name [%s] already exist"
-msgstr "Více osob s adresou [%s], ale se jménem [%s] neexistuje nikdo"
+msgstr "Hainbat pertsonak dute helbidea [%s], baina ez dago [%s] izena duenik"
 
-#: config/sources.php.dist:114
+#: config/sources.php.dist:132
 msgid "My Address Book"
-msgstr "Mùj Adresáø"
+msgstr "Nire helbide-liburua"
 
 #: addressbooks.php:119
 msgid "My Address Books"
-msgstr "Mé Adresáøe"
+msgstr "Nire helbide-liburuak"
 
 #: templates/addressbooks.inc:114 config/attributes.php.dist:42
 msgid "Name"
-msgstr "Jméno"
+msgstr "Izena"
 
 #: config/prefs.php.dist:31
 msgid "Name Format"
-msgstr "Formát jména"
+msgstr "Izen-formatua"
 
 #: add.php:36 add.php:127 lib/Block/tree_menu.php:27
 msgid "New Contact"
-msgstr "Nový kontakt"
+msgstr "Kontaktu berria"
 
 #: edit.php:95 edit.php:102 edit.php:210 templates/data/import.inc:45
 msgid "Next"
-msgstr "Dal¹í"
+msgstr "Hurrengoa"
 
 #: config/attributes.php.dist:218
 msgid "Nickname"
-msgstr "Pøezdívka/alias"
+msgstr "Goitizena"
 
-#: lib/api.php:1280 lib/api.php:1329
+#: lib/api.php:1281 lib/api.php:1330
 #, php-format
 msgid "No %s entry found for %s"
-msgstr "®ádná polo¾ka %s nebyla nalezena pro %s"
+msgstr "Ez da aurkitu %s sarrerarik %s(r)entzat"
 
-#: lib/api.php:1245
+#: lib/api.php:1246
 msgid "No address books found."
-msgstr "Nebyl nalezen ¾ádný adresáø."
+msgstr "Ez da aurkitu helbide-libururik."
 
-#: minisearch.php:81
+#: minisearch.php:83
 msgid "No contacts found"
-msgstr "Nebyly nalezeny ¾ádné kontakty."
+msgstr "Ez da aurkitu kontakturik"
 
 #: lib/api.php:566 lib/api.php:830
 msgid "No vCard data was found."
-msgstr "Nebyly nalezeny ¾ádná vCard data."
+msgstr "Ez da aurkitu vCard daturik."
 
 #: templates/browse/footer.inc:7
 msgid "None"
-msgstr "®ádný"
+msgstr "Bat ere ez"
 
 #: config/attributes.php.dist:184
 msgid "Notes"
-msgstr "Poznámka"
+msgstr "Oharrak"
 
-#: config/prefs.php.dist:115
+#: config/prefs.php.dist:108
 msgid "Number of items per page"
-msgstr "Poèet polo¾ek na stránku"
+msgstr "Elementu kopurua orriko"
 
 #: lib/api.php:671 lib/api.php:811
 msgid "Object not found"
-msgstr "Objekt nebyl nalezen"
+msgstr "Objektua ez da aurkitu"
 
 #: config/attributes.php.dist:223
 msgid "Office"
-msgstr "Kanceláø"
+msgstr "Bulegoa"
 
 #: templates/prefs/imsp_opt.inc:22
 msgid ""
@@ -749,306 +732,305 @@ msgid ""
 "deleted. If this is not what you want, then you must change your selection "
 "to \"None\"."
 msgstr ""
-"Po ulo¾ení tohoto nastavení bude Adresáø nenávratnì odstranìn. Jestli¾e si "
-"to nepøejete, musíte zmìnit Va¹i volbu na \"®ádný\"."
+"Aukera-orri hau gordetzen duzunean, helbide-liburua betiko ezabatuko da. Ez "
+"baduzu hori nahi, hautatu \"Bat ere ez\"."
 
 #: lib/api.php:838
 msgid "Only one vcard supported."
-msgstr "Je podporávána jen jedna vCard."
+msgstr "vcard bat bakarrik onartzen da."
 
-#: config/prefs.php.dist:37 config/prefs.php.dist:46
+#: config/prefs.php.dist:39
 msgid "Other Options"
-msgstr "Dal¹í mo¾nosti"
+msgstr "Beste aukera batzuk"
 
 #: config/attributes.php.dist:190
 msgid "PGP Public Key"
-msgstr "Veøejný PGP klíè"
+msgstr "PGP gako publikoa"
 
-#: lib/api.php:550 lib/api.php:664 lib/api.php:751 lib/api.php:1014
-#: lib/api.php:1179
+#: lib/api.php:550 lib/api.php:664 lib/api.php:751 lib/api.php:1015
+#: lib/api.php:1180
 msgid "Permission Denied"
-msgstr "Nepovolený pøístup"
+msgstr "Baimena ukatu da"
 
 #: lib/Driver.php:554
 msgid "Permission denied"
-msgstr "Nepovolený pøístup"
+msgstr "Baimena ukatuta"
 
 #: templates/addressbooks.inc:106
 msgid "Permissions"
-msgstr "Oprávnìní"
+msgstr "Baimenak"
 
 #: data.php:53 templates/data/import.inc:17
 msgid "Pine Address Book"
-msgstr "Pine adresáø"
+msgstr "Pine helbide-liburua"
 
 #: templates/browse/javascript.inc:28
 msgid "Please name the new contact list:"
-msgstr "Zadejte jméno nového seznamu kontaktù:"
+msgstr "Idatzi kontaktu-zerrenda berriaren izena:"
 
 #: edit.php:98 edit.php:101 edit.php:202
 msgid "Previous"
-msgstr "Pøedchozí"
+msgstr "Aurrekoa"
 
 #: lib/Driver/ldap.php:147
 #, php-format
 msgid "Query failed: (%s) %s"
-msgstr "Vyhledávání selhalo: (%s) %s"
+msgstr "Kontsulta honek huts egin du: (%s) %s"
 
 #: templates/block/minisearch.inc:15
 msgid "Quick Search"
-msgstr "Rychlé vyhledávání"
+msgstr "Bilaketa azkarra"
 
 #: lib/Driver/ldap.php:191 lib/Driver/ldap.php:199 lib/Driver/ldap.php:435
 #, php-format
 msgid "Read failed: (%s) %s"
-msgstr "Ètení selhalo: (%s) %s"
+msgstr "Huts egin du irakurtzean: (%s) %s"
 
 #: lib/Driver/null.php:52
 msgid "Reading contacts is not available."
-msgstr "Ètení kontaktù není dostupné."
+msgstr "Kontaktu-irakurtzea ez dago erabilgarri."
 
 #: lib/Renderer.php:49
 msgid "Really delete this contact?"
-msgstr "Skuteènì odstranit tento kontakt?"
+msgstr