merge 894685124c68
authorLaure Bourgois <Laure.Bourgois@logilab.fr>
Wed, 01 Apr 2009 17:26:36 +0200
branch894685124c68
changeset 1200 0d12d4371d11
parent 1199 7fa66717175b (current diff)
parent 1198 9df49357b0eb (diff)
child 1202 3a5d622f166f
child 1213 06789a89f91f
merge
--- a/_exceptions.py	Wed Apr 01 17:25:40 2009 +0200
+++ b/_exceptions.py	Wed Apr 01 17:26:36 2009 +0200
@@ -2,7 +2,7 @@
 
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
--- a/common/mixins.py	Wed Apr 01 17:25:40 2009 +0200
+++ b/common/mixins.py	Wed Apr 01 17:26:36 2009 +0200
@@ -2,7 +2,7 @@
 
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
--- a/common/uilib.py	Wed Apr 01 17:25:40 2009 +0200
+++ b/common/uilib.py	Wed Apr 01 17:26:36 2009 +0200
@@ -180,21 +180,27 @@
             if add_ellipsis:
                 return text + u'...'
             return text
-        
-def text_cut(text, nbwords=30):
+
+def text_cut(text, nbwords=30, gotoperiod=True):
     """from the given plain text, return a text with at least <nbwords> words,
     trying to go to the end of the current sentence.
 
+    :param nbwords: the minimum number of words required
+    :param gotoperiod: specifies if the function should try to go to
+                       the first period after the cut (i.e. finish
+                       the sentence if possible)
+
     Note that spaces are normalized.
     """
     if text is None:
         return u''
     words = text.split()
     text = u' '.join(words) # normalize spaces
-    minlength = len(' '.join(words[:nbwords]))
-    textlength = text.find('.', minlength) + 1
-    if textlength == 0: # no point found
-        textlength = minlength 
+    textlength = minlength = len(' '.join(words[:nbwords]))
+    if gotoperiod:
+        textlength = text.find('.', minlength) + 1
+        if textlength == 0: # no period found
+            textlength = minlength
     return text[:textlength]
 
 def cut(text, length):
--- a/cwconfig.py	Wed Apr 01 17:25:40 2009 +0200
+++ b/cwconfig.py	Wed Apr 01 17:26:36 2009 +0200
@@ -2,7 +2,7 @@
 """common configuration utilities for cubicweb
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
@@ -501,17 +501,16 @@
 class CubicWebConfiguration(CubicWebNoAppConfiguration):
     """base class for cubicweb server and web configurations"""
     
+    INSTANCE_DATA_DIR = None
     if CubicWebNoAppConfiguration.mode == 'test':
         root = os.environ['APYCOT_ROOT']
         REGISTRY_DIR = '%s/etc/cubicweb.d/' % root
-        INSTANCE_DATA_DIR = REGISTRY_DIR
         RUNTIME_DIR = '/tmp/'
         MIGRATION_DIR = '%s/local/share/cubicweb/migration/' % root
         if not exists(REGISTRY_DIR):
             os.makedirs(REGISTRY_DIR)
     elif CubicWebNoAppConfiguration.mode == 'dev':
         REGISTRY_DIR = expanduser('~/etc/cubicweb.d/')
-        INSTANCE_DATA_DIR = REGISTRY_DIR
         RUNTIME_DIR = '/tmp/'
         MIGRATION_DIR = join(CW_SOFTWARE_ROOT, 'misc', 'migration')
     else: #mode = 'installed'
@@ -574,7 +573,8 @@
     @classmethod
     def instance_data_dir(cls):
         """return the instance data directory"""
-        return env_path('CW_INSTANCE_DATA', cls.INSTANCE_DATA_DIR,
+        return env_path('CW_INSTANCE_DATA',
+                        cls.INSTANCE_DATA_DIR or cls.REGISTRY_DIR,
                         'additional data')
         
     @classmethod
--- a/cwvreg.py	Wed Apr 01 17:25:40 2009 +0200
+++ b/cwvreg.py	Wed Apr 01 17:26:36 2009 +0200
@@ -1,7 +1,7 @@
 """extend the generic VRegistry with some cubicweb specific stuff
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
@@ -12,7 +12,7 @@
 
 from rql import RQLHelper
 
-from cubicweb import Binary, UnknownProperty
+from cubicweb import Binary, UnknownProperty, UnknownEid
 from cubicweb.vregistry import VRegistry, ObjectNotFound, NoSelectableObject
 
 _ = unicode
@@ -337,7 +337,11 @@
         rqlst = self.rqlhelper.parse(rql)
         def type_from_eid(eid, session=session):
             return session.describe(eid)[0]
-        self.rqlhelper.compute_solutions(rqlst, {'eid': type_from_eid}, args)
+        try:
+            self.rqlhelper.compute_solutions(rqlst, {'eid': type_from_eid}, args)
+        except UnknownEid:
+            for select in rqlst.children:
+                select.solutions = []
         return rqlst
 
     @property
--- a/devtools/devctl.py	Wed Apr 01 17:25:40 2009 +0200
+++ b/devtools/devctl.py	Wed Apr 01 17:26:36 2009 +0200
@@ -513,17 +513,22 @@
             raise BadCommandUsage("no argument expected")
         import re
         requests = {}
-        for line in sys.stdin:
+        for lineno, line in enumerate(sys.stdin):
             if not ' WHERE ' in line:
                 continue
             #sys.stderr.write( line )
-            rql, time = line.split('--')
-            rql = re.sub("(\'\w+': \d*)", '', rql)
-            req = requests.setdefault(rql, [])
-            time.strip()
-            chunks = time.split()
-            cputime = float(chunks[-3])
-            req.append( cputime )
+            try:
+                rql, time = line.split('--')
+                rql = re.sub("(\'\w+': \d*)", '', rql)
+                if '{' in rql:
+                    rql = rql[:rql.index('{')]
+                req = requests.setdefault(rql, [])
+                time.strip()
+                chunks = time.split()
+                cputime = float(chunks[-3])
+                req.append( cputime )
+            except Exception, exc:
+                sys.stderr.write('Line %s: %s (%s)\n' % (lineno, exc, line))
 
         stat = []
         for rql, times in requests.items():
@@ -531,8 +536,11 @@
 
         stat.sort()
         stat.reverse()
+
+        total_time = sum(time for time, occ, rql in stat)*0.01
+        print 'Percentage;Cumulative Time;Occurences;Query'
         for time, occ, rql in stat:
-            print time, occ, rql
+            print '%.2f;%.2f;%s;%s' % (time/total_time, time, occ, rql)
         
 register_commands((UpdateCubicWebCatalogCommand,
                    UpdateTemplateCatalogCommand,
--- a/doc/book/en/B0010-define-schema.en.txt	Wed Apr 01 17:25:40 2009 +0200
+++ b/doc/book/en/B0010-define-schema.en.txt	Wed Apr 01 17:26:36 2009 +0200
@@ -4,14 +4,15 @@
 ================================
 
 The schema is the core piece of a `CubicWeb` application as it defines
-the data model handled. It is based on entities types already defined
-in the `CubicWeb` standard library and others, more specific, we would 
-expect to find in one or more Python files under the `schema` directory.
+the data model handled. It is based on entity types that are either already
+defined in the `CubicWeb` standard library; or more specific types, that 
+`CubicWeb` expects to find in one or more Python files under the directory 
+`schema`.
 
 At this point, it is important to make clear the difference between
-relation type and relation definition: a relation type is only a relation
+*relation type* and *relation definition*: a *relation type* is only a relation
 name with potentially other additionnal properties (see XXXX), whereas a 
-relation definition is a complete triplet 
+*relation definition* is a complete triplet 
 "<subject entity type> <relation type> <object entity type>". 
 A relation type could have been implied if none is related to a 
 relation definition of the schema.
--- a/doc/book/en/B0011-schema-stdlib.en.txt	Wed Apr 01 17:25:40 2009 +0200
+++ b/doc/book/en/B0011-schema-stdlib.en.txt	Wed Apr 01 17:26:36 2009 +0200
@@ -3,14 +3,14 @@
 Pre-defined schemas in the library
 ----------------------------------
 
-The library defines a set of entities schemas that are required by the system
+The library defines a set of entity schemas that are required by the system
 or commonly used in `CubicWeb` applications.
 Of course, you can extend those schemas if necessary.
 
 
 System schemas
 ``````````````
-The system entities available are :
+The system entities available are:
 
 * `EUser`, system users
 * `EGroup`, users groups
@@ -31,6 +31,9 @@
 * `Bookmark`, an entity type used to allow a user to customize his links within
   the application
 
+(The first 'E' in some of the names is the first letter of 'Erudi', 
+`CubicWeb`'s old name; it might be changed/removed some day.)
+
 Cubes available
 ```````````````
 
--- a/doc/book/en/B0012-schema-definition.en.txt	Wed Apr 01 17:25:40 2009 +0200
+++ b/doc/book/en/B0012-schema-definition.en.txt	Wed Apr 01 17:26:36 2009 +0200
@@ -3,26 +3,30 @@
 Entity type definition
 ----------------------
 
-An entity type is defined by a Python class which inherits `EntityType`. The
-class name correponds to the type name. Then the content of the class contains
-the description of attributes and relations for the defined entity type,
-for example ::
+An entity type is defined by a Python class which inherits from `EntityType`.
+The class definition contains the description of attributes and relations
+for the defined entity type.
+The class name corresponds to the entity type name.
 
-  class Personne(EntityType):
+For example ::
+
+  class Person(EntityType):
     """A person with the properties and the relations necessary for my
     application"""
 
     last_name = String(required=True, fulltextindexed=True)
     first_name = String(required=True, fulltextindexed=True)
-    title = String(vocabulary=('M', 'Mme', 'Mlle'))
+    title = String(vocabulary=('Mr', 'Mrs', 'Miss'))
     date_of_birth = Date()
     works_for = SubjectRelation('Company', cardinality='?*')
 
+
 * the name of the Python attribute corresponds to the name of the attribute
   or the relation in `CubicWeb` application.
 
-* all built-in types are available : `String`, `Int`, `Float`,
-  `Boolean`, `Date`, `Datetime`, `Time`, `Byte`.
+* all `CubicWeb` built-in types are available : `String`, `Int`, `Float`,
+  `Boolean`, `Date`, `Datetime`, `Time`, `Byte`; they are and implicitely
+  imported (as well as the special the function "_").
 
 * each entity type has at least the following meta-relations :
 
@@ -34,21 +38,19 @@
   
   - `created_by` (`EUser`) (which user created the entity)
   
-  - `owned_by` (`EUser`) (who does the entity belongs to, by default the 
-     creator but not necessary and it could have multiple owners)
+  - `owned_by` (`EUser`) (to whom the entity belongs; by default the 
+     creator but not necessary, and it could have multiple owners)
      
   - `is` (`EEType`)
 
-  
-* it is also possible to define relations of type object by using `ObjectRelation`
-  instead of `SubjectRelation`
 
-* the first argument of `SubjectRelation` and `ObjectRelation` gives respectively
+* relations can be defined by using `ObjectRelation` or `SubjectRelation`.
+  The first argument of `SubjectRelation` or `ObjectRelation` gives respectively
   the object/subject entity type of the relation. This could be :  
 
   * a string corresponding to an entity type
 
-  * a tuple of string correponding to multiple entities types
+  * a tuple of string corresponding to multiple entity types
 
   * special string such as follows :
 
@@ -62,20 +64,20 @@
 
 * optional properties for attributes and relations : 
 
-  - `description` : string describing an attribute or a relation. By default
+  - `description` : a string describing an attribute or a relation. By default
     this string will be used in the editing form of the entity, which means
     that it is supposed to help the end-user and should be flagged by the
     function `_` to be properly internationalized.
 
-  - `constraints` : list of conditions/constraints that the relation needs to
+  - `constraints` : a list of conditions/constraints that the relation has to
     satisfy (c.f. `Contraints`_)
 
-  - `cardinality` : two characters string which specify the cardinality of the
+  - `cardinality` : a two character string which specify the cardinality of the
     relation. The first character defines the cardinality of the relation on
-    the subject, the second on the object of the relation. When a relation
-    has multiple possible subjects or objects, the cardinality applies to all
-    and not on a one to one basis (so it must be consistent...). The possible
-    values are inspired from regular expressions syntax :
+    the subject, and the second on the object. When a relation can have 
+    multiple subjects or objects, the cardinality applies to all,
+    not on a one-to-one basis (so it must be consistent...). The possible
+    values are inspired from regular expression syntax :
 
     * `1`: 1..1
     * `?`: 0..1
@@ -85,7 +87,7 @@
   - `meta` : boolean indicating that the relation is a meta-relation (false by
     default)
 
-* optionnal properties for attributes : 
+* optional properties for attributes : 
 
   - `required` : boolean indicating if the attribute is required (false by default)
 
@@ -98,11 +100,11 @@
     attribute.
 
   - `default` : default value of the attribute. In case of date types, the values
-    which could be used correpond to the RQL keywords `TODAY` and `NOW`.
+    which could be used correspond to the RQL keywords `TODAY` and `NOW`.
   
   - `vocabulary` : specify static possible values of an attribute
 
-* optionnal properties of type `String` : 
+* optional properties of type `String` : 
 
   - `fulltextindexed` : boolean indicating if the attribute is part of
     the full text index (false by default) (*applicable on the type `Byte`
@@ -113,17 +115,17 @@
 
   - `maxsize` : integer providing the maximum size of the string (no limit by default)
 
-* optionnal properties for relations : 
+* optional properties for relations : 
 
   - `composite` : string indicating that the subject (composite == 'subject')
     is composed of the objects of the relations. For the opposite case (when
-    the object is composed of the subjects of the relation), we just need
-    to set 'object' as the value. The composition implies that when the relation
+    the object is composed of the subjects of the relation), we just set 
+    'object' as value. The composition implies that when the relation
     is deleted (so when the composite is deleted), the composed are also deleted.
 
 Contraints
 ``````````
-By default, the available constraints types are :
+By default, the available constraint types are :
 
 * `SizeConstraint` : allows to specify a minimum and/or maximum size on
   string (generic case of `maxsize`)
@@ -135,7 +137,7 @@
 
 * `StaticVocabularyConstraint` : identical to "vocabulary=(...)"
 
-* `RQLConstraint` : allows to specify a RQL query that needs to be satisfied
+* `RQLConstraint` : allows to specify a RQL query that has to be satisfied
   by the subject and/or the object of the relation. In this query the variables
   `S` and `O` are reserved for the entities subject and object of the 
   relation.
@@ -143,7 +145,7 @@
 * `RQLVocabularyConstraint` : similar to the previous type of constraint except
   that it does not express a "strong" constraint, which means it is only used to
   restrict the values listed in the drop-down menu of editing form, but it does
-  not prevent another entity to be selected
+  not prevent another entity to be selected.
 
 
 Relation definition
@@ -154,7 +156,7 @@
 A relation is defined by a Python class heriting `RelationType`. The name
 of the class corresponds to the name of the type. The class then contains
 a description of the properties of this type of relation, and could as well 
-contains a string for the subject and a string for the object. This allows to create
+contain a string for the subject and a string for the object. This allows to create
 new definition of associated relations, (so that the class can have the 
 definition properties from the relation) for example ::
 
@@ -174,14 +176,14 @@
   table for the relation. This applies to the relation when the cardinality
   of subject->relation->object is 0..1 (`?`) or 1..1 (`1`)
 
-* `symetric` : boolean indication that the relation is symetrical, which
+* `symmetric` : boolean indicating that the relation is symmetrical, which
   means `X relation Y` implies `Y relation X`
 
 In the case of simultaneous relations definitions, `subject` and `object`
 can both be equal to the value of the first argument of `SubjectRelation`
 and `ObjectRelation`.
 
-When a relation is not inlined and not symetrical, and it does not require
+When a relation is not inlined and not symmetrical, and it does not require
 specific permissions, its definition (by using `SubjectRelation` and
 `ObjectRelation`) is all we need.
 
@@ -375,12 +377,16 @@
 Updating your application with your new schema
 ``````````````````````````````````````````````
 
-You have to get a shell on your application ::
+If you modified your schema, the update is not automatic; this is 
+indeed in general not a good idea.
+Instead, we call a shell on  your application, which is a 
+an interactive python shell, with an appropriate
+cubicweb environment ::
 
-   cubicweb-ctl shell moninstance
+   cubicweb-ctl shell myinstance
 
 and type ::
 
-   add_entity_type('Personne')
+   add_entity_type('Person')
 
 And restart your application!
--- a/doc/book/en/B0040-migration.en.txt	Wed Apr 01 17:25:40 2009 +0200
+++ b/doc/book/en/B0040-migration.en.txt	Wed Apr 01 17:26:36 2009 +0200
@@ -5,10 +5,10 @@
 Migration
 =========
 
-One of the main concept in `CubicWeb` is to create incremental applications
-and for this purpose multiple actions are provided to facilitate the improvement
-of an application and in particular changes applied to the data model
-without loosing existing data.
+One of the main concept in `CubicWeb` is to create incremental applications.
+For this purpose, multiple actions are provided to facilitate the improvement
+of an application, and in particular to handle the changes to be applied
+to the data model, without loosing existing data.
 
 The current version of an application model is provided in the file
 `__pkginfo__.py` as a tuple of 3 integers.
@@ -16,8 +16,8 @@
 Migration scripts management
 ----------------------------
 
-Migration scripts needs to be located in the directory `migration` of your
-application and nammed accordingly:
+Migration scripts has to be located in the directory `migration` of your
+application and named accordingly:
 
 ::
 
@@ -25,26 +25,27 @@
 
 in which : 
 
-* X.Y.Z is the model version number to which the script enable to migrate
+* X.Y.Z is the model version number to which the script enables to migrate.
 
-* *mode* (between the last "_" and the extension ".py") indicates which part
-  of the application (RQL server, web server) the script applies to in case
-  of distributed installation. Its value could be :
+* *mode* (between the last "_" and the extension ".py") is used for 
+  distributed installation. It indicates to which part
+  of the application (RQL server, web server) the script applies.
+  Its value could be :
 
   * `common`, applies to the RQL server as well as the web server and updates
     files on the hard drive (configuration files migration for example).
 
-  * `web`, applies only to the web server and updates files on the hard drive
+  * `web`, applies only to the web server and updates files on the hard drive.
 
   * `repository`, applies only to the RQL server and updates files on the
-    hard drive
+    hard drive.
 
   * `Any`, applies only to the RQL server and updates data in the database
-    (schema and data migration for example)
+    (schema and data migration for example).
 
 Again in the directory `migration`, the file `depends.map` allows to indicate
-that to migrate to a particular model version, you always have to first migrate
-to a particular `CubicWeb` version. This file can contains comments (lines
+that for the migration to a particular model version, you always have to first 
+migrate to a particular `CubicWeb` version. This file can contain comments (lines
 starting by `#`) and a dependancy is listed as follows: ::
   
   <model version n° X.Y.Z> : <cubicweb version n° X.Y.Z>
@@ -53,33 +54,31 @@
 
   0.12.0: 2.26.0
   0.13.0: 2.27.0
-  # 0.14 works with 2.27 <= erudi <= 2.28 at least
+  # 0.14 works with 2.27 <= cubicweb <= 2.28 at least
   0.15.0: 2.28.0
 
 Base context
 ------------
 
-The following identifiers are pre-defined in the migration scripts:
+The following identifiers are pre-defined in migration scripts:
 
 * `config`, instance configuration
 
 * `interactive_mode`, boolean indicating that the script is executed in
-  an intercative mode or not 
+  an interactive mode or not 
 
 * `appltemplversion`, application model version of the instance
 
-* `applerudiversion`, cubicweb instance version
-
 * `templversion`, installed application model version
 
-* `erudiversion`, installed cubicweb version
+* `cubicwebversion`, installed cubicweb version
 
-* `confirm(question)`, function interrogating the user and returning true
-  if the user answers yes, false otherwise (always returns true when in a
+* `confirm(question)`, function asking the user and returning true
+  if the user answers yes, false otherwise (always returns true in
   non-interactive mode)
 
-* `_`, function fonction equivalent to `unicode` allowing to flag the strings
-  to internationalize in the migration scripts
+* the function `_`, it is equivalent to `unicode` allowing to flag the strings
+  to internationalize in the migration scripts.
 
 In the `repository` scripts, the following identifiers are also defined:
 
@@ -89,7 +88,7 @@
   current migration)
 
 * `newschema`, installed schema on the file system (e.g. schema of 
-  the updated model and erudi)
+  the updated model and cubicweb)
 
 * `sqlcursor`, SQL cursor for very rare cases where it is really
    necessary or beneficial to go through the sql
@@ -99,7 +98,7 @@
                         
 Schema migration
 ----------------
-The following functions for schema migration are available in the `repository`
+The following functions for schema migration are available in `repository`
 scripts:
 
 * `add_attribute(etype, attrname, attrtype=None, commit=True)`, adds a new
@@ -109,7 +108,7 @@
 * `drop_attribute(etype, attrname, commit=True)`, removes an attribute from an
   existing entity type.
 
-* `rename_attribute(etype, oldname, newname, commit=True)`, rename an attribute
+* `rename_attribute(etype, oldname, newname, commit=True)`, renames an attribute
             
 * `add_entity_type(etype, auto=True, commit=True)`, adds a new entity type.
   If `auto` is True, all the relations using this entity type and having a known
@@ -149,7 +148,7 @@
   or even relations definitions).
         
 * `change_relation_props(subjtype, rtype, objtype, commit=True, **kwargs)`, changes
-  properties of a relation definition by using the nammed parameters of the properties
+  properties of a relation definition by using the named parameters of the properties
   to change.
 
 * `set_widget(etype, rtype, widget, commit=True)`, changes the widget used for the
@@ -160,19 +159,19 @@
 
 Data migration
 --------------
-The following functions for data migration are available in the `repository` scripts:
+The following functions for data migration are available in `repository` scripts:
 
 * `rql(rql, kwargs=None, cachekey=None, ask_confirm=True)`, executes an arbitrary RQL
   query, either to interrogate or update. A result set object is returned.  
 
 * `add_entity(etype, *args, **kwargs)`, adds a nes entity type of the given
-  type. The attributes and relations values are specified using the nammed and
+  type. The attribute and relation values are specified using the named and
   positionned parameters.
 
 Workflow creation
 -----------------
 
-The following functions for workflow creation are available in the `repository`
+The following functions for workflow creation are available in `repository`
 scripts:
 
 * `add_state(name, stateof, initial=False, commit=False, **kwargs)`, adds a new state
@@ -186,10 +185,10 @@
 Configuration migration
 -----------------------
 
-The following functions for configuration migration are available in all the
+The following functions for configuration migration are available in all 
 scripts:
 
-* `option_renamed(oldname, newname)`, indicates that an option has been renammed
+* `option_renamed(oldname, newname)`, indicates that an option has been renamed
 
 * `option_group_change(option, oldgroup, newgroup)`, indicates that an option does not
   belong anymore to the same group.
@@ -203,7 +202,7 @@
 --------------------------
 Those functions are only used for low level operations that could not be 
 accomplished otherwise or to repair damaged databases during interactive 
-session. They are available in the `repository` scripts:
+session. They are available in `repository` scripts:
 
 * `sqlexec(sql, args=None, ask_confirm=True)`, executes an arbitrary SQL query
 * `add_entity_type_table(etype, commit=True)`
--- a/doc/book/en/B1020-define-views.en.txt	Wed Apr 01 17:25:40 2009 +0200
+++ b/doc/book/en/B1020-define-views.en.txt	Wed Apr 01 17:26:36 2009 +0200
@@ -163,7 +163,7 @@
 If you want to change the way a ``BlogEntry`` is displayed, just override 
 the method ``cell_call()`` of the view ``primary`` in ``BlogDemo/views.py`` ::
 
-  01. from ginco.web.views import baseviews
+  01. from cubicweb.web.views import baseviews
   02.
   03. class BlogEntryPrimaryView(baseviews.PrimaryView):
   04.
--- a/doc/book/en/B1060-templates.en.txt	Wed Apr 01 17:25:40 2009 +0200
+++ b/doc/book/en/B1060-templates.en.txt	Wed Apr 01 17:26:36 2009 +0200
@@ -84,7 +84,7 @@
 
 ::
 
- from ginco.web.views.basetemplates import HTMLPageHeader
+ from cubicweb.web.views.basetemplates import HTMLPageHeader
  class MyHTMLPageHeader(HTMLPageHeader):
     def main_header(self, view):
         """build the top menu with authentification info and the rql box"""
@@ -143,7 +143,7 @@
 for HTMLPageFooter and override it in your views file as in : 
 ::
 
-  form ginco.web.views.basetemplates import HTMLPageFooter
+  form cubicweb.web.views.basetemplates import HTMLPageFooter
   class MyHTMLPageFooter(HTMLPageFooter):
       def call(self, **kwargs):
           self.w(u'<div class="footer">')
@@ -211,5 +211,5 @@
 
 [TODO]
 Rajouter une section pour definir la terminologie utilisee.
-Dans ginco-doc rajouter une section pour cubciweb-ctl shell ou
+Dans cubicweb-doc rajouter une section pour cubciweb-ctl shell ou
 on liste les commandes dispos.
--- a/doc/book/en/B2052-install.en.txt	Wed Apr 01 17:25:40 2009 +0200
+++ b/doc/book/en/B2052-install.en.txt	Wed Apr 01 17:26:36 2009 +0200
@@ -107,10 +107,10 @@
   $ python myapp/bin/laxctl i18ncompile 
 
 Ignore the errors that print "No translation file found for domain
-'erudi'". They disappear after the first run of i18ncompile.
+'cubicweb'". They disappear after the first run of i18ncompile.
 
 .. note:: The command  myapp/bin/laxctl i18nupdate needs to be executed
-   only if your application is using cubes from ginco-apps.
+   only if your application is using cubes from cubicweb-apps.
    Otherwise, please skip it.
 
 You will never need to add new entries in the translation catalog. Instead we would
--- a/doc/book/en/C000-administration.en.txt	Wed Apr 01 17:25:40 2009 +0200
+++ b/doc/book/en/C000-administration.en.txt	Wed Apr 01 17:26:36 2009 +0200
@@ -15,4 +15,4 @@
    C020-site-config.en.txt
    C030-instance-config.en.txt
    C040-rql.en.txt
-   C041-rql-tutorial.en.txt
+
--- a/doc/book/en/C011-installation.en.txt	Wed Apr 01 17:25:40 2009 +0200
+++ b/doc/book/en/C011-installation.en.txt	Wed Apr 01 17:26:36 2009 +0200
@@ -29,7 +29,7 @@
   deb http://ftp.logilab.org/dists/ hardy/
 
 
-You can now install the required packages with the following command: ::
+You can now install the required packages with the following command::
 
   apt-get update 
   apt-get install cubicweb
@@ -41,7 +41,7 @@
 Install from source
 ```````````````````
 
-You can download the archive containing the sources from our `ftp site`_ at: ::
+You can download the archive containing the sources from our `ftp site`_ at::
 
   http://ftp.logilab.org/pub/cubicweb/
 
@@ -92,18 +92,35 @@
 Your new cubes will be placed in `/usr/share/cubicweb/cubes` and
 your applications will be placed in `/etc/cubicweb.d`.
 
+To use other directories you will have to configure the
+following environment variables as follows::
+
+    export CW_CUBES_PATH=~/lib/cubes
+    export CW_REGISTRY=~/etc/cubicweb.d/
+    export CW_INSTANCE_DATA=$CW_REGISTRY
+    export CW_RUNTIME=/tmp
+
+.. note::
+    The values given above are our suggestions but of course
+    can be different.
+
 .. _ConfigurationPostgres:
 
 Postgres configuration
 ----------------------
 
+.. note::
+    If you already have an existing cluster and postgres server
+    running you do not require to execute the initilization step
+    of your Postgres database.
+
 * First you have to initialize the database Postgres with the command ``initdb``.
   ::
 
     $ initdb -D /path/to/pgsql
 
   Once initialized, you can launch the database server Postgres 
-  with the command: ::
+  with the command::
   
     $ postgres -D /path/to/psql
 
@@ -113,17 +130,28 @@
  
     $ chown username /path/to/pgsql
 
-* Create a superuser for `CubicWeb` instance (**root**) ::
-
-    createuser -s username
+* Create a superuser for `CubicWeb` instance::
+    
+    $ su
+    $ su - postgres
+    $ createuser -s username
 
-  Initialize the password of the superuser you just created with 
-  ``su - postgres`` and ``psql``.
+  Initialize the password of the superuser you just created for your
+  database::
+    
+    $ su 
+    $ su - postgres
+    $ psql
 
-  This password will be asked to you later on where you will create an
-  instance with `cubicweb-ctl create`
+  And then execute de following query::
+    
+    ALTER USER username WITH PASSWORD `password`
 
-.. [XXX] Est-ce que ces etapes sont vraiment necessaires?  sand : lors de l'installation de ma bdd cela n'a pas ete fait et il semble que tout aille bien. Doit etre verifie avec les experts.
+  This login/password will be requested when you will create an
+  instance with `cubicweb-ctl create` to initialize the database of
+  your application.
+
+.. FIXME Are these steps really necessary? It seemed to work without.
 
 * installation of plain-text index extension ::
 
--- a/doc/book/en/C012-create-instance.en.txt	Wed Apr 01 17:25:40 2009 +0200
+++ b/doc/book/en/C012-create-instance.en.txt	Wed Apr 01 17:26:36 2009 +0200
@@ -62,6 +62,13 @@
 at the time the base is created (import_erschema('MyCube') will
 not properly work otherwise).
 
+.. note::
+    Please note that if you do not wish to use default directory
+    for your cubes library, then you want to use the option
+    --directory to specify where you would like to place
+    the source code of your cube:
+    ``cubicweb-ctl newcube --directory=/path/to/cubes/library cube_name``
+
 Instance creation
 -----------------
 
@@ -91,7 +98,7 @@
 possible, later on, to create others users for your final web application.
 
 When this command is completed, the definition of your instance is
-located in *~/etc/cubicweb.d/moninstance/*. To launch it, you just type ::
+located in *~/etc/cubicweb.d/myinstance/*. To launch it, you just type ::
 
   cubicweb-ctl start -D myinstance
 
--- a/doc/book/en/C040-rql.en.txt	Wed Apr 01 17:25:40 2009 +0200
+++ b/doc/book/en/C040-rql.en.txt	Wed Apr 01 17:26:36 2009 +0200
@@ -9,13 +9,13 @@
 Introduction
 ============
 
-Goals RQL
----------
+Goals of RQL
+------------
 
 The goal is to have a language emphasizing the way of browsing
 relations. As such, attributes will be regarded as cases of
-special relations (in terms of implementation, the user
-language not to see virtually no difference between an attribute and a
+special relations (in terms of implementation, the language
+user should see virtually no difference between an attribute and a
 relation).
 
 RQL is inspired by SQL but is the highest level. A knowledge of the 
@@ -34,7 +34,7 @@
 
 Versa
 `````
-Should I look in more detail, but here is already some ideas for
+We should look in more detail, but here are already some ideas for
 the moment ... Versa_ is the language most similar to what we wanted
 to do, but the model underlying data being RDF, there is some
 number of things such as namespaces or handling of the RDF types which 
@@ -54,19 +54,18 @@
 ------------------------------
 
 Search (`Any`)
-   This type of query can extract entities and attributes of entities.
+   Extract entities and attributes of entities.
 
-Inserting entities (`INSERT`)
-   This type of query is used to insert new entities in the database. It
-   will also create direct relationships entities newly created.
+Insert entities (`INSERT`)
+   Insert new entities or relations in the database.
+   It can also directly create relationships for the newly created entities.
 
-Update entities, relations creation (`SET`)
-   This type of query updates existing entities in the database,
+Update entities, create relations (`SET`)
+   Update existing entities in the database,
    or create relations between existing entities.
 
-Deletion of entities or relationship (`DELETE`)
-   This type of query allows for the removal of entities and relations existing
-   in the database.
+Delete entities or relationship (`DELETE`)
+   Remove entities or relations existing in the database.
 
 Search Query
 ------------
@@ -80,7 +79,7 @@
    Type of selected variables.
    The special type `Any` is equivalent to not specify a type.
 :restriction:
-   list of relations to go through whic follow the pattern
+   list of conditions to test successively 
      `V1 relation V2 | <static value>`
 :orderterms:
    Definition of the selection order: variable or column number followed by
@@ -92,13 +91,13 @@
 
 
 - *Search for the object of identifier 53*
-   ::
+  ::
 
         Any WHERE X
         X eid 53
 
 - *Search material such as comics, owned by syt and available*
-   ::
+  ::
 
         WHERE X Document
         X occurence_of F, F class C, C name 'Comics'
@@ -106,36 +105,36 @@
         X available true
 
 - *Looking for people working for eurocopter interested in training*
-   ::
+  ::
 
         Any P WHERE
-        P is Person, P work_for P, S name 'Eurocopter'
+        P is Person, P work_for S, S name 'Eurocopter'
         P interested_by T, T name 'training'
 
 - *Search note less than 10 days old written by jphc or ocy*
-   ::
+  ::
 
         Any N WHERE
         N is Note, N written_on D, D day> (today -10),
         N written_by P, P name 'jphc' or P name 'ocy'
 
 - *Looking for people interested in training or living in Paris*
-   ::
+  ::
 
         Any P WHERE
         P is Person, (P interested_by T, T name 'training') OR
         (P city 'Paris')
 
 - *The name and surname of all people*
-   ::
+  ::
 
         Any N, P WHERE
         X is Person, X name N, X first_name P
 
-   Note that the selection of several entities generally force
-   the use of "Any" because the type specification applies otherwise
-   to all the selected variables. We could write here
-   ::
+  Note that the selection of several entities generally force
+  the use of "Any" because the type specification applies otherwise
+  to all the selected variables. We could write here
+  ::
 
         String N, P WHERE
         X is Person, X name N, X first_name P
@@ -147,16 +146,16 @@
     `INSERT` <entity type> V1 (, <entity type> V2) \ * `:` <assignments>
     [ `WHERE` <restriction>]
 
-: assignments:
+:assignments:
    list of relations to assign in the form `V1 relationship V2 | <static value>`
 
 The restriction can define variables used in assignments.
 
 Caution, if a restriction is specified, the insertion is done for 
-*each line results returned by the restriction*.
+*each line result returned by the restriction*.
 
 - *Insert a new person named 'foo'*
-   ::
+  ::
 
         INSERT Person X: X name 'foo'
 
@@ -178,12 +177,12 @@
     [ `WHERE` <restriction>]
 
 Caution, if a restriction is specified, the update is done *for
-each line results returned by the restriction*.
+each result line returned by the restriction*.
 
 - *Renaming of the person named 'foo' to 'bar' with the first name changed*
   ::
 
-        SET X name 'bar', X first_name 'original' where X is Person X name 'foo'
+        SET X name 'bar', X first_name 'original' WHERE X is Person, X name 'foo'
 
 - *Insert a relation of type 'know' between objects linked by 
   the relation of type 'friend'*
@@ -198,7 +197,7 @@
     [ `WHERE` <restriction>]
 
 Caution, if a restriction is specified, the deletion is made *for
-each line results returned by the restriction*.
+each line result returned by the restriction*.
 
 - *Deletion of the person named 'foo'*
   ::
@@ -211,8 +210,8 @@
         DELETE X friend Y WHERE X is Person, X name 'foo'
 
 
-Undocumented (yet) type of queries
-----------------------------------
+(yet) Undocumented types of queries
+-----------------------------------
 
 **Limit / offset**
 ::
@@ -302,14 +301,14 @@
 ``````````````
 
 The base types supported are string (between double or single quotes),
-integers or floats (the separator is the'.'), dates and
+integers or floats (the separator is '.'), dates and
 boolean. We expect to receive a schema in which types String,
 Int, Float, Date and Boolean are defined.
 
 * `String` (literal: between double or single quotes).
 * `Int`, `Float` (separator being'.').
 * `Date`, `Datetime`, `Time` (literal: string YYYY/MM/DD [hh:mm] or keywords
-   `TODAY` and `NOW`).
+  `TODAY` and `NOW`).
 * `Boolean` (keywords `TRUE` and `FALSE`).
 * `Keyword` NULL.
 
@@ -318,10 +317,10 @@
 ---------
 
 Logical Operators
-```````````````````
+`````````````````
 ::
 
-     AND, OR, ','
+     AND, OR, NOT, ','
 
 ',' is equivalent to 'AND' but with the smallest among the priority
 of logical operators (see :ref:`PriorityOperators`).
@@ -336,7 +335,7 @@
 ````````````````````
 ::
 
-     =, <, <=, >=, > = ~, IN, LIKE
+     =, <, <=, >=, >, ~=, IN, LIKE
 
 * The operator `=` is the default operator.
 
@@ -345,7 +344,7 @@
   must start or finish by a prefix/suffix:
   ::
 
-     Any X WHERE X name =~ 'Th%'
+     Any X WHERE X name ~= 'Th%'
      Any X WHERE X name LIKE '%lt'
 
 * The operator `IN` provides a list of possible values:
@@ -354,30 +353,32 @@
     Any X WHERE X name IN ( 'chauvat', 'fayolle', 'di mascio', 'thenault')
 
 
-XXX nico: A trick <> 'bar' would not it be more convenient than NOT A
-trick 'bar'?
+XXX nico: "A trick <> 'bar'" wouldn't it be more convenient than 
+"NOT A trick 'bar'" ?
 
 .. _PriorityOperators:
 
-Operators priority
-``````````````````
+Operator priority
+`````````````````
 
 1. '*', '/'
 
 2. '+', '-'
 
-3. 'and'
+3. 'not'
+
+4 'and'
 
-4. 'or'
+5 'or'
 
-5. ','
+6 ','
 
 
 Advanced Features
 -----------------
 
-Functions aggregates
-````````````````````
+Aggregate Functions
+```````````````````
 ::
 
      COUNT, MIN, MAX, AVG, SUM
@@ -468,11 +469,11 @@
      constant    ::= KEYWORD | STRING | FLOAT | INT
 
      # tokens
-     LOGIC_OP ::= ',' | 'GOLD' | 'AND'
+     LOGIC_OP ::= ',' | 'OR' | 'AND'
      MATH_OP  ::= '+' | '-' | '/' | '*'
      COMP_OP  ::= '>' | '>=' | '=' | '<=' | '<' | '~=' | 'LIKE'
 
-     FUNCTION ::= 'MIN' | 'MAX' | 'SUM' | 'AVG' | 'COUNT' | 'upper' | 'LOWER'
+     FUNCTION ::= 'MIN' | 'MAX' | 'SUM' | 'AVG' | 'COUNT' | 'UPPER' | 'LOWER'
 
      VARIABLE ::= '[A-Z][A-Z0-9]*'
      E_TYPE   ::= '[A-Z]\w*'
@@ -541,13 +542,13 @@
 
 ::
 
-     Select         ((Relationship | And | Gold)?, Group?, Sort?)
-     Insert         (Relations | And | Gold)?
-     Delete         (Relationship | And | Gold)?
-     Update         (Relations | And | Gold)?
+     Select         ((Relationship | And | Or)?, Group?, Sort?)
+     Insert         (Relations | And | Or)?
+     Delete         (Relationship | And | Or)?
+     Update         (Relations | And | Or)?
 
-     And            ((Relationship | And | Gold), (Relationship | And | Gold))
-     Or             ((Relationship | And | Gold), (Relationship | And | Gold))
+     And            ((Relationship | And | Or), (Relationship | And | Or))
+     Or             ((Relationship | And | Or), (Relationship | And | Or))
 
      Relationship   ((VariableRef, Comparison))
 
@@ -578,20 +579,20 @@
 
      Point P where P abs X, P ord Y, P value X+Y
 
-   is valid, but::
+  is valid, but::
 
      Point P where P abs X, P value X+Y, P ord Y
 
-   is not.
+  is not.
 
 RQL logs
 --------
 
-You can configure the `CubicWeb` application so that you keep a log
-of the queries executed against your database. To do so, you want to
+You can configure the `CubicWeb` application to keep a log
+of the queries executed against your database. To do so, 
 edit the configuration file of your application 
 ``.../etc/cubicweb.d/myapp/all-in-one.conf`` and uncomment the
-variable ``query-log-file``: ::
+variable ``query-log-file``::
 
   # web application query log file
   query-log-file=/tmp/rql-myapp.log
--- a/doc/book/en/D000-annex.en.txt	Wed Apr 01 17:25:40 2009 +0200
+++ b/doc/book/en/D000-annex.en.txt	Wed Apr 01 17:26:36 2009 +0200
@@ -1,9 +1,9 @@
 .. -*- coding: utf-8 -*-
 
 
-=================
-Part IV - Annexes
-=================
+====================
+Part IV - Appendixes
+====================
 
 The following chapters are reference material.
  
--- a/doc/book/en/D010-faq.en.txt	Wed Apr 01 17:25:40 2009 +0200
+++ b/doc/book/en/D010-faq.en.txt	Wed Apr 01 17:26:36 2009 +0200
@@ -99,9 +99,9 @@
   the application `Error while publishing ...` for Rest text and plain text.
   The server returns a traceback like as follows ::
 
-      2008-10-06 15:05:08 - (erudi.rest) ERROR: error while publishing ReST text
+      2008-10-06 15:05:08 - (cubicweb.rest) ERROR: error while publishing ReST text
       Traceback (most recent call last):
-      File "/home/sandrine/src/blogdemo/ginco/common/rest.py", line 217, in rest_publish
+      File "/home/user/src/blogdemo/cubicweb/common/rest.py", line 217, in rest_publish
       File "/usr/lib/python2.5/codecs.py", line 817, in open
       file = __builtin__.open(filename, mode, buffering)
       TypeError: __init__() takes at most 3 arguments (4 given)
@@ -156,11 +156,11 @@
   ``all-in-one.conf`` file of your instance, define the anonymous user
   as follows ::
 
-    # login of the Erudi user account to use for anonymous user (if you want to
+    # login of the CubicWeb user account to use for anonymous user (if you want to
     # allow anonymous)
     anonymous-user=anon
 
-    # password of the Erudi user account matching login
+    # password of the CubicWeb user account matching login
     anonymous-password=anon
 
   You also must ensure that this `anon` user is a registered user of
@@ -206,7 +206,7 @@
     user-login-attr=uid
     # name of a group in which ldap users will be by default
     user-default-group=users
-    # map from ldap user attributes to erudi attributes
+    # map from ldap user attributes to cubicweb attributes
     user-attrs-map=gecos:email,uid:login
 
   Any change applied to configuration file requires to restart your
--- a/doc/book/en/D030-architecture.en.txt	Wed Apr 01 17:25:40 2009 +0200
+++ b/doc/book/en/D030-architecture.en.txt	Wed Apr 01 17:26:36 2009 +0200
@@ -1,8 +1,8 @@
 .. -*- coding: utf-8 -*-
 
 
-Architecture du serveur
------------------------
+Server Architecture
+-------------------
 
 .. image:: images/server-class-diagram.png
 
--- a/doc/book/en/D070-cookbook.en.txt	Wed Apr 01 17:25:40 2009 +0200
+++ b/doc/book/en/D070-cookbook.en.txt	Wed Apr 01 17:26:36 2009 +0200
@@ -10,7 +10,7 @@
 * How to import LDAP users in `CubicWeb`?
 
   Here is a very usefull script which enables you to import LDAP users
-  into your `CubicWeb` application by runing the following: ::
+  into your `CubicWeb` application by running the following: ::
 
 
     import os
--- a/i18n/fr.po	Wed Apr 01 17:25:40 2009 +0200
+++ b/i18n/fr.po	Wed Apr 01 17:26:36 2009 +0200
@@ -918,7 +918,7 @@
 "s #%(toeid)s"
 
 msgid "address"
-msgstr "adresse"
+msgstr "adresse électronique"
 
 msgid "alias"
 msgstr "alias"
--- a/server/checkintegrity.py	Wed Apr 01 17:25:40 2009 +0200
+++ b/server/checkintegrity.py	Wed Apr 01 17:26:36 2009 +0200
@@ -71,6 +71,7 @@
                             'before_update_entity', '')
     repo.hm.unregister_hook(uniquecstrcheck_before_modification,
                             'before_update_entity', '')
+    repo.do_fti = True  # ensure full-text indexation is activated
     etypes = set()
     for eschema in schema.entities():
         if eschema.is_final():
--- a/server/msplanner.py	Wed Apr 01 17:25:40 2009 +0200
+++ b/server/msplanner.py	Wed Apr 01 17:26:36 2009 +0200
@@ -49,7 +49,7 @@
 
 
 :organization: Logilab
-:copyright: 2003-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2003-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
--- a/server/repository.py	Wed Apr 01 17:25:40 2009 +0200
+++ b/server/repository.py	Wed Apr 01 17:26:36 2009 +0200
@@ -144,6 +144,8 @@
         self.schema = CubicWebSchema(config.appid)
         # querier helper, need to be created after sources initialization
         self.querier = QuerierHelper(self, self.schema)
+        # should we reindex in changes?
+        self.do_fti = config['delay-full-text-indexation']
         # sources
         self.sources = []
         self.sources_by_uri = {}
@@ -211,7 +213,7 @@
         self._get_pool().close(True) 
         for i in xrange(config['connections-pool-size']):
             self._available_pools.put_nowait(ConnectionsPool(self.sources))
-     
+        
     # internals ###############################################################
 
     def get_source(self, uri, source_config):
@@ -829,7 +831,8 @@
             entity.complete(entity.e_schema.indexable_attributes())
         session.add_query_data('neweids', entity.eid)
         # now we can update the full text index
-        FTIndexEntityOp(session, entity=entity)
+        if self.do_fti:
+            FTIndexEntityOp(session, entity=entity)
         CleanupEidTypeCacheOp(session)
         
     def delete_info(self, session, eid):
@@ -995,7 +998,7 @@
                                     entity)
         source.update_entity(session, entity)
         if not only_inline_rels:
-            if need_fti_update:
+            if need_fti_update and self.do_fti:
                 # reindex the entity only if this query is updating at least
                 # one indexable attribute
                 FTIndexEntityOp(session, entity=entity)
--- a/server/serverconfig.py	Wed Apr 01 17:25:40 2009 +0200
+++ b/server/serverconfig.py	Wed Apr 01 17:26:36 2009 +0200
@@ -73,6 +73,15 @@
           'help': 'size of the parsed rql cache size.',
           'group': 'main', 'inputlevel': 1,
           }),
+        ('delay-full-text-indexation',
+         {'type' : 'yn', 'default': False,
+          'help': 'When full text indexation of entity has a too important cost'
+          ' to be done when entity are added/modified by users, activate this '
+          'option and setup a job using cubicweb-ctl db-rebuild-fti on your '
+          'system (using cron for instance).',
+          'group': 'main', 'inputlevel': 1,
+          }),
+        
         # email configuration
         ('default-recipients-mode',
          {'type' : 'choice',
--- a/server/session.py	Wed Apr 01 17:25:40 2009 +0200
+++ b/server/session.py	Wed Apr 01 17:26:36 2009 +0200
@@ -444,10 +444,14 @@
                     # None value inserted by an outer join, no type
                     row_descr[index] = None
                     continue
-                if isfinal:
-                    row_descr[index] = etype_from_pyobj(value)
-                else:
-                    row_descr[index] = etype_from_eid(value)[0]
+                try:
+                    if isfinal:
+                        row_descr[index] = etype_from_pyobj(value)
+                    else:
+                        row_descr[index] = etype_from_eid(value)[0]
+                except UnknownEid:
+                    self.critical('wrong eid in repository, should check database')
+                    row_descr[index] = row[index] = None
             description.append(tuple(row_descr))
         return description
 
--- a/server/sources/extlite.py	Wed Apr 01 17:25:40 2009 +0200
+++ b/server/sources/extlite.py	Wed Apr 01 17:26:36 2009 +0200
@@ -141,7 +141,7 @@
         
     def set_schema(self, schema):
         super(SQLiteAbstractSource, self).set_schema(schema)
-        if self._need_sql_create and self._is_schema_complete():
+        if self._need_sql_create and self._is_schema_complete() and self.dbpath:
             self._create_database()
         self.rqlsqlgen = self.sqlgen_class(schema, self.sqladapter.dbhelper)
                 
--- a/server/sources/rql2sql.py	Wed Apr 01 17:25:40 2009 +0200
+++ b/server/sources/rql2sql.py	Wed Apr 01 17:26:36 2009 +0200
@@ -25,7 +25,7 @@
 
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
--- a/web/data/cubicweb.timeline-bundle.js	Wed Apr 01 17:25:40 2009 +0200
+++ b/web/data/cubicweb.timeline-bundle.js	Wed Apr 01 17:26:36 2009 +0200
@@ -1,3980 +1,10123 @@
-
-var SimileAjax = {
-    loaded:                 false,
-    loadingScriptsCount:    0,
-    error:                  null,
-    params:                 { bundle:"true" }
+var SimileAjax_urlPrefix = baseuri() + 'data/';
+var Timeline_urlPrefix = baseuri() + 'data/';
+
+/*==================================================
+ *  Simile Ajax API
+ *
+ *  Include this file in your HTML file as follows:
+ *
+ *    <script src="http://simile.mit.edu/ajax/api/simile-ajax-api.js" type="text/javascript"></script>
+ *
+ *==================================================
+ */
+
+if (typeof SimileAjax == "undefined") {
+    var SimileAjax = {
+        loaded:                 false,
+        loadingScriptsCount:    0,
+        error:                  null,
+        params:                 { bundle:"true" }
+    };
+
+    SimileAjax.Platform = new Object();
+        /*
+            HACK: We need these 2 things here because we cannot simply append
+            a <script> element containing code that accesses SimileAjax.Platform
+            to initialize it because IE executes that <script> code first
+            before it loads ajax.js and platform.js.
+        */
+
+    var getHead = function(doc) {
+        return doc.getElementsByTagName("head")[0];
+    };
+
+    SimileAjax.findScript = function(doc, substring) {
+        var heads = doc.documentElement.getElementsByTagName("head");
+        for (var h = 0; h < heads.length; h++) {
+            var node = heads[h].firstChild;
+            while (node != null) {
+                if (node.nodeType == 1 && node.tagName.toLowerCase() == "script") {
+                    var url = node.src;
+                    var i = url.indexOf(substring);
+                    if (i >= 0) {
+                        return url;
+                    }
+                }
+                node = node.nextSibling;
+            }
+        }
+        return null;
+    };
+    SimileAjax.includeJavascriptFile = function(doc, url, onerror, charset) {
+        onerror = onerror || "";
+        if (doc.body == null) {
+            try {
+                var q = "'" + onerror.replace( /'/g, '&apos' ) + "'"; // "
+                doc.write("<script src='" + url + "' onerror="+ q +
+                          (charset ? " charset='"+ charset +"'" : "") +
+                          " type='text/javascript'>"+ onerror + "</script>");
+                return;
+            } catch (e) {
+                // fall through
+            }
+        }
+
+        var script = doc.createElement("script");
+        if (onerror) {
+            try { script.innerHTML = onerror; } catch(e) {}
+            script.setAttribute("onerror", onerror);
+        }
+        if (charset) {
+            script.setAttribute("charset", charset);
+        }
+        script.type = "text/javascript";
+        script.language = "JavaScript";
+        script.src = url;
+        return getHead(doc).appendChild(script);
+    };
+    SimileAjax.includeJavascriptFiles = function(doc, urlPrefix, filenames) {
+        for (var i = 0; i < filenames.length; i++) {
+            SimileAjax.includeJavascriptFile(doc, urlPrefix + filenames[i]);
+        }
+        SimileAjax.loadingScriptsCount += filenames.length;
+        SimileAjax.includeJavascriptFile(doc, SimileAjax.urlPrefix + "scripts/signal.js?" + filenames.length);
+    };
+    SimileAjax.includeCssFile = function(doc, url) {
+        if (doc.body == null) {
+            try {
+                doc.write("<link rel='stylesheet' href='" + url + "' type='text/css'/>");
+                return;
+            } catch (e) {
+                // fall through
+            }
+        }
+
+        var link = doc.createElement("link");
+        link.setAttribute("rel", "stylesheet");
+        link.setAttribute("type", "text/css");
+        link.setAttribute("href", url);
+        getHead(doc).appendChild(link);
+    };
+    SimileAjax.includeCssFiles = function(doc, urlPrefix, filenames) {
+        for (var i = 0; i < filenames.length; i++) {
+            SimileAjax.includeCssFile(doc, urlPrefix + filenames[i]);
+        }
+    };
+
+    /**
+     * Append into urls each string in suffixes after prefixing it with urlPrefix.
+     * @param {Array} urls
+     * @param {String} urlPrefix
+     * @param {Array} suffixes
+     */
+    SimileAjax.prefixURLs = function(urls, urlPrefix, suffixes) {
+        for (var i = 0; i < suffixes.length; i++) {
+            urls.push(urlPrefix + suffixes[i]);
+        }
+    };
+
+    /**
+     * Parse out the query parameters from a URL
+     * @param {String} url    the url to parse, or location.href if undefined
+     * @param {Object} to     optional object to extend with the parameters
+     * @param {Object} types  optional object mapping keys to value types
+     *        (String, Number, Boolean or Array, String by default)
+     * @return a key/value Object whose keys are the query parameter names
+     * @type Object
+     */
+    SimileAjax.parseURLParameters = function(url, to, types) {
+        to = to || {};
+        types = types || {};
+
+        if (typeof url == "undefined") {
+            url = location.href;
+        }
+        var q = url.indexOf("?");
+        if (q < 0) {
+            return to;
+        }
+        url = (url+"#").slice(q+1, url.indexOf("#")); // toss the URL fragment
+
+        var params = url.split("&"), param, parsed = {};
+        var decode = window.decodeURIComponent || unescape;
+        for (var i = 0; param = params[i]; i++) {
+            var eq = param.indexOf("=");
+            var name = decode(param.slice(0,eq));
+            var old = parsed[name];
+            if (typeof old == "undefined") {
+                old = [];
+            } else if (!(old instanceof Array)) {
+                old = [old];
+            }
+            parsed[name] = old.concat(decode(param.slice(eq+1)));
+        }
+        for (var i in parsed) {
+            if (!parsed.hasOwnProperty(i)) continue;
+            var type = types[i] || String;
+            var data = parsed[i];
+            if (!(data instanceof Array)) {
+                data = [data];
+            }
+            if (type === Boolean && data[0] == "false") {
+                to[i] = false; // because Boolean("false") === true
+            } else {
+                to[i] = type.apply(this, data);
+            }
+        }
+        return to;
+    };
+
+    (function() {
+        var javascriptFiles = [
+            "jquery-1.2.6.js",
+            "platform.js",
+            "debug.js",
+            "xmlhttp.js",
+            "json.js",
+            "dom.js",
+            "graphics.js",
+            "date-time.js",
+            "string.js",
+            "html.js",
+            "data-structure.js",
+            "units.js",
+
+            "ajax.js",
+            "history.js",
+            "window-manager.js"
+        ];
+        var cssFiles = [
+            "graphics.css"
+        ];
+
+        if (typeof SimileAjax_urlPrefix == "string") {
+            SimileAjax.urlPrefix = SimileAjax_urlPrefix;
+        } else {
+            var url = SimileAjax.findScript(document, "simile-ajax-api.js");
+            if (url == null) {
+                SimileAjax.error = new Error("Failed to derive URL prefix for Simile Ajax API code files");
+                return;
+            }
+
+            SimileAjax.urlPrefix = url.substr(0, url.indexOf("simile-ajax-api.js"));
+        }
+
+        SimileAjax.parseURLParameters(url, SimileAjax.params, {bundle:Boolean});
+//         if (SimileAjax.params.bundle) {
+//             SimileAjax.includeJavascriptFiles(document, SimileAjax.urlPrefix, [ "simile-ajax-bundle.js" ]);
+//         } else {
+//             SimileAjax.includeJavascriptFiles(document, SimileAjax.urlPrefix + "scripts/", javascriptFiles);
+//         }
+        SimileAjax.includeCssFiles(document, SimileAjax.urlPrefix + "styles/", cssFiles);
+
+        SimileAjax.loaded = true;
+    })();
+}
+/*==================================================
+ *  Platform Utility Functions and Constants
+ *==================================================
+ */
+
+/*  This must be called after our jQuery has been loaded
+    but before control returns to user-code.
+*/
+SimileAjax.jQuery = jQuery;
+// SimileAjax.jQuery = jQuery.noConflict(true);
+if (typeof window["$"] == "undefined") {
+    window.$ = SimileAjax.jQuery;
+}
+
+SimileAjax.Platform.os = {
+    isMac:   false,
+    isWin:   false,
+    isWin32: false,
+    isUnix:  false
+};
+SimileAjax.Platform.browser = {
+    isIE:           false,
+    isNetscape:     false,
+    isMozilla:      false,
+    isFirefox:      false,
+    isOpera:        false,
+    isSafari:       false,
+
+    majorVersion:   0,
+    minorVersion:   0
+};
+
+(function() {
+    var an = navigator.appName.toLowerCase();
+	var ua = navigator.userAgent.toLowerCase();
+
+    /*
+     *  Operating system
+     */
+	SimileAjax.Platform.os.isMac = (ua.indexOf('mac') != -1);
+	SimileAjax.Platform.os.isWin = (ua.indexOf('win') != -1);
+	SimileAjax.Platform.os.isWin32 = SimileAjax.Platform.isWin && (
+        ua.indexOf('95') != -1 ||
+        ua.indexOf('98') != -1 ||
+        ua.indexOf('nt') != -1 ||
+        ua.indexOf('win32') != -1 ||
+        ua.indexOf('32bit') != -1
+    );
+	SimileAjax.Platform.os.isUnix = (ua.indexOf('x11') != -1);
+
+    /*
+     *  Browser
+     */
+    SimileAjax.Platform.browser.isIE = (an.indexOf("microsoft") != -1);
+    SimileAjax.Platform.browser.isNetscape = (an.indexOf("netscape") != -1);
+    SimileAjax.Platform.browser.isMozilla = (ua.indexOf("mozilla") != -1);
+    SimileAjax.Platform.browser.isFirefox = (ua.indexOf("firefox") != -1);
+    SimileAjax.Platform.browser.isOpera = (an.indexOf("opera") != -1);
+    SimileAjax.Platform.browser.isSafari = (an.indexOf("safari") != -1);
+
+    var parseVersionString = function(s) {
+        var a = s.split(".");
+        SimileAjax.Platform.browser.majorVersion = parseInt(a[0]);
+        SimileAjax.Platform.browser.minorVersion = parseInt(a[1]);
+    };
+    var indexOf = function(s, sub, start) {
+        var i = s.indexOf(sub, start);
+        return i >= 0 ? i : s.length;
+    };
+
+    if (SimileAjax.Platform.browser.isMozilla) {
+        var offset = ua.indexOf("mozilla/");
+        if (offset >= 0) {
+            parseVersionString(ua.substring(offset + 8, indexOf(ua, " ", offset)));
+        }
+    }
+    if (SimileAjax.Platform.browser.isIE) {
+        var offset = ua.indexOf("msie ");
+        if (offset >= 0) {
+            parseVersionString(ua.substring(offset + 5, indexOf(ua, ";", offset)));
+        }
+    }
+    if (SimileAjax.Platform.browser.isNetscape) {
+        var offset = ua.indexOf("rv:");
+        if (offset >= 0) {
+            parseVersionString(ua.substring(offset + 3, indexOf(ua, ")", offset)));
+        }
+    }
+    if (SimileAjax.Platform.browser.isFirefox) {
+        var offset = ua.indexOf("firefox/");
+        if (offset >= 0) {
+            parseVersionString(ua.substring(offset + 8, indexOf(ua, " ", offset)));
+        }
+    }
+
+    if (!("localeCompare" in String.prototype)) {
+        String.prototype.localeCompare = function (s) {
+            if (this < s) return -1;
+            else if (this > s) return 1;
+            else return 0;
+        };
+    }
+})();
+
+SimileAjax.Platform.getDefaultLocale = function() {
+    return SimileAjax.Platform.clientLocale;
+};/*==================================================
+ *  Debug Utility Functions
+ *==================================================
+ */
+
+SimileAjax.Debug = {
+    silent: false
+};
+
+SimileAjax.Debug.log = function(msg) {
+    var f;
+    if ("console" in window && "log" in window.console) { // FireBug installed
+        f = function(msg2) {
+            console.log(msg2);
+        }
+    } else {
+        f = function(msg2) {
+            if (!SimileAjax.Debug.silent) {
+                alert(msg2);
+            }
+        }
+    }
+    SimileAjax.Debug.log = f;
+    f(msg);
+};
+
+SimileAjax.Debug.warn = function(msg) {
+    var f;
+    if ("console" in window && "warn" in window.console) { // FireBug installed
+        f = function(msg2) {
+            console.warn(msg2);
+        }
+    } else {
+        f = function(msg2) {
+            if (!SimileAjax.Debug.silent) {
+                alert(msg2);
+            }
+        }
+    }
+    SimileAjax.Debug.warn = f;
+    f(msg);
+};
+
+SimileAjax.Debug.exception = function(e, msg) {
+    var f, params = SimileAjax.parseURLParameters();
+    if (params.errors == "throw" || SimileAjax.params.errors == "throw") {
+        f = function(e2, msg2) {
+            throw(e2); // do not hide from browser's native debugging features
+        };
+    } else if ("console" in window && "error" in window.console) { // FireBug installed
+        f = function(e2, msg2) {
+            if (msg2 != null) {
+                console.error(msg2 + " %o", e2);
+            } else {
+                console.error(e2);
+            }
+            throw(e2); // do not hide from browser's native debugging features
+        };
+    } else {
+        f = function(e2, msg2) {
+            if (!SimileAjax.Debug.silent) {
+                alert("Caught exception: " + msg2 + "\n\nDetails: " + ("description" in e2 ? e2.description : e2));
+            }
+            throw(e2); // do not hide from browser's native debugging features
+        };
+    }
+    SimileAjax.Debug.exception = f;
+    f(e, msg);
+};
+
+SimileAjax.Debug.objectToString = function(o) {
+    return SimileAjax.Debug._objectToString(o, "");
+};
+
+SimileAjax.Debug._objectToString = function(o, indent) {
+    var indent2 = indent + " ";
+    if (typeof o == "object") {
+        var s = "{";
+        for (n in o) {
+            s += indent2 + n + ": " + SimileAjax.Debug._objectToString(o[n], indent2) + "\n";
+        }
+        s += indent + "}";
+        return s;
+    } else if (typeof o == "array") {
+        var s = "[";
+        for (var n = 0; n < o.length; n++) {
+            s += SimileAjax.Debug._objectToString(o[n], indent2) + "\n";
+        }
+        s += indent + "]";
+        return s;
+    } else {
+        return o;
+    }
+};
+/**
+ * @fileOverview XmlHttp utility functions
+ * @name SimileAjax.XmlHttp
+ */
+
+SimileAjax.XmlHttp = new Object();
+
+/**
+ *  Callback for XMLHttp onRequestStateChange.
+ */
+SimileAjax.XmlHttp._onReadyStateChange = function(xmlhttp, fError, fDone) {
+    switch (xmlhttp.readyState) {
+    // 1: Request not yet made
+    // 2: Contact established with server but nothing downloaded yet
+    // 3: Called multiple while downloading in progress
+
+    // Download complete
+    case 4:
+        try {
+            if (xmlhttp.status == 0     // file:// urls, works on Firefox
+             || xmlhttp.status == 200   // http:// urls
+            ) {
+                if (fDone) {
+                    fDone(xmlhttp);
+                }
+            } else {
+                if (fError) {
+                    fError(
+                        xmlhttp.statusText,
+                        xmlhttp.status,
+                        xmlhttp
+                    );
+                }
+            }
+        } catch (e) {
+            SimileAjax.Debug.exception("XmlHttp: Error handling onReadyStateChange", e);
+        }
+        break;
+    }
+};
+
+/**
+ *  Creates an XMLHttpRequest object. On the first run, this
+ *  function creates a platform-specific function for
+ *  instantiating an XMLHttpRequest object and then replaces
+ *  itself with that function.
+ */
+SimileAjax.XmlHttp._createRequest = function() {
+    if (SimileAjax.Platform.browser.isIE) {
+        var programIDs = [
+        "Msxml2.XMLHTTP",
+        "Microsoft.XMLHTTP",
+        "Msxml2.XMLHTTP.4.0"
+        ];
+        for (var i = 0; i < programIDs.length; i++) {
+            try {
+                var programID = programIDs[i];
+                var f = function() {
+                    return new ActiveXObject(programID);
+                };
+                var o = f();
+
+                // We are replacing the SimileAjax._createXmlHttpRequest
+                // function with this inner function as we've
+                // found out that it works. This is so that we
+                // don't have to do all the testing over again
+                // on subsequent calls.
+                SimileAjax.XmlHttp._createRequest = f;
+
+                return o;
+            } catch (e) {
+                // silent
+            }
+        }
+        // fall through to try new XMLHttpRequest();
+    }
+
+    try {
+        var f = function() {
+            return new XMLHttpRequest();
+        };
+        var o = f();
+
+        // We are replacing the SimileAjax._createXmlHttpRequest
+        // function with this inner function as we've
+        // found out that it works. This is so that we
+        // don't have to do all the testing over again
+        // on subsequent calls.
+        SimileAjax.XmlHttp._createRequest = f;
+
+        return o;
+    } catch (e) {
+        throw new Error("Failed to create an XMLHttpRequest object");
+    }
+};
+
+/**
+ * Performs an asynchronous HTTP GET.
+ *
+ * @param {Function} fError a function of the form
+     function(statusText, statusCode, xmlhttp)
+ * @param {Function} fDone a function of the form function(xmlhttp)
+ */
+SimileAjax.XmlHttp.get = function(url, fError, fDone) {
+    var xmlhttp = SimileAjax.XmlHttp._createRequest();
+
+    xmlhttp.open("GET", url, true);
+    xmlhttp.onreadystatechange = function() {
+        SimileAjax.XmlHttp._onReadyStateChange(xmlhttp, fError, fDone);
+    };
+    xmlhttp.send(null);
+};
+
+/**
+ * Performs an asynchronous HTTP POST.
+ *
+ * @param {Function} fError a function of the form
+     function(statusText, statusCode, xmlhttp)
+ * @param {Function} fDone a function of the form function(xmlhttp)
+ */
+SimileAjax.XmlHttp.post = function(url, body, fError, fDone) {
+    var xmlhttp = SimileAjax.XmlHttp._createRequest();
+
+    xmlhttp.open("POST", url, true);
+    xmlhttp.onreadystatechange = function() {
+        SimileAjax.XmlHttp._onReadyStateChange(xmlhttp, fError, fDone);
+    };
+    xmlhttp.send(body);
+};
+
+SimileAjax.XmlHttp._forceXML = function(xmlhttp) {
+    try {
+        xmlhttp.overrideMimeType("text/xml");
+    } catch (e) {
+        xmlhttp.setrequestheader("Content-Type", "text/xml");
+    }
+};/*
+ *  Copied directly from http://www.json.org/json.js.
+ */
+
+/*
+    json.js
+    2006-04-28
+
+    This file adds these methods to JavaScript:
+
+        object.toJSONString()
+
+            This method produces a JSON text from an object. The
+            object must not contain any cyclical references.
+
+        array.toJSONString()
+
+            This method produces a JSON text from an array. The
+            array must not contain any cyclical references.
+
+        string.parseJSON()
+
+            This method parses a JSON text to produce an object or
+            array. It will return false if there is an error.
+*/
+
+SimileAjax.JSON = new Object();
+
+(function () {
+    var m = {
+        '\b': '\\b',
+        '\t': '\\t',
+        '\n': '\\n',
+        '\f': '\\f',
+        '\r': '\\r',
+        '"' : '\\"',
+        '\\': '\\\\'
+    };
+    var s = {
+        array: function (x) {
+            var a = ['['], b, f, i, l = x.length, v;
+            for (i = 0; i < l; i += 1) {
+                v = x[i];
+                f = s[typeof v];
+                if (f) {
+                    v = f(v);
+                    if (typeof v == 'string') {
+                        if (b) {
+                            a[a.length] = ',';
+                        }
+                        a[a.length] = v;
+                        b = true;
+                    }
+                }
+            }
+            a[a.length] = ']';
+            return a.join('');
+        },
+        'boolean': function (x) {
+            return String(x);
+        },
+        'null': function (x) {
+            return "null";
+        },
+        number: function (x) {
+            return isFinite(x) ? String(x) : 'null';
+        },
+        object: function (x) {
+            if (x) {
+                if (x instanceof Array) {
+                    return s.array(x);
+                }
+                var a = ['{'], b, f, i, v;
+                for (i in x) {
+                    v = x[i];
+                    f = s[typeof v];
+                    if (f) {
+                        v = f(v);
+                        if (typeof v == 'string') {
+                            if (b) {
+                                a[a.length] = ',';
+                            }
+                            a.push(s.string(i), ':', v);
+                            b = true;
+                        }
+                    }
+                }
+                a[a.length] = '}';
+                return a.join('');
+            }
+            return 'null';
+        },
+        string: function (x) {
+            if (/["\\\x00-\x1f]/.test(x)) {
+                x = x.replace(/([\x00-\x1f\\"])/g, function(a, b) {
+                    var c = m[b];
+                    if (c) {
+                        return c;
+                    }
+                    c = b.charCodeAt();
+                    return '\\u00' +
+                        Math.floor(c / 16).toString(16) +
+                        (c % 16).toString(16);
+                });
+            }
+            return '"' + x + '"';
+        }
+    };
+
+    SimileAjax.JSON.toJSONString = function(o) {
+        if (o instanceof Object) {
+            return s.object(o);
+        } else if (o instanceof Array) {
+            return s.array(o);
+        } else {
+            return o.toString();
+        }
+    };
+
+    SimileAjax.JSON.parseJSON = function () {
+        try {
+            return !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
+                    this.replace(/"(\\.|[^"\\])*"/g, ''))) &&
+                eval('(' + this + ')');
+        } catch (e) {
+            return false;
+        }
+    };
+})();
+/*==================================================
+ *  DOM Utility Functions
+ *==================================================
+ */
+
+SimileAjax.DOM = new Object();
+
+SimileAjax.DOM.registerEventWithObject = function(elmt, eventName, obj, handlerName) {
+    SimileAjax.DOM.registerEvent(elmt, eventName, function(elmt2, evt, target) {
+        return obj[handlerName].call(obj, elmt2, evt, target);
+    });
+};
+
+SimileAjax.DOM.registerEvent = function(elmt, eventName, handler) {
+    var handler2 = function(evt) {
+        evt = (evt) ? evt : ((event) ? event : null);
+        if (evt) {
+            var target = (evt.target) ?
+                evt.target : ((evt.srcElement) ? evt.srcElement : null);
+            if (target) {
+                target = (target.nodeType == 1 || target.nodeType == 9) ?
+                    target : target.parentNode;
+            }
+
+            return handler(elmt, evt, target);
+        }
+        return true;
+    }
+
+    if (SimileAjax.Platform.browser.isIE) {
+        elmt.attachEvent("on" + eventName, handler2);
+    } else {
+        elmt.addEventListener(eventName, handler2, false);
+    }
+};
+
+SimileAjax.DOM.getPageCoordinates = function(elmt) {
+    var left = 0;
+    var top = 0;
+
+    if (elmt.nodeType != 1) {
+        elmt = elmt.parentNode;
+    }
+
+    var elmt2 = elmt;
+    while (elmt2 != null) {
+        left += elmt2.offsetLeft;
+        top += elmt2.offsetTop;
+        elmt2 = elmt2.offsetParent;
+    }
+
+    var body = document.body;
+    while (elmt != null && elmt != body) {
+        if ("scrollLeft" in elmt) {
+            left -= elmt.scrollLeft;
+            top -= elmt.scrollTop;
+        }
+        elmt = elmt.parentNode;
+    }
+
+    return { left: left, top: top };
+};
+
+SimileAjax.DOM.getSize = function(elmt) {
+	var w = this.getStyle(elmt,"width");
+	var h = this.getStyle(elmt,"height");
+	if (w.indexOf("px") > -1) w = w.replace("px","");
+	if (h.indexOf("px") > -1) h = h.replace("px","");
+	return {
+		w: w,
+		h: h
+	}
+}
+
+SimileAjax.DOM.getStyle = function(elmt, styleProp) {
+    if (elmt.currentStyle) { // IE
+        var style = elmt.currentStyle[styleProp];
+    } else if (window.getComputedStyle) { // standard DOM
+        var style = document.defaultView.getComputedStyle(elmt, null).getPropertyValue(styleProp);
+    } else {
+    	var style = "";
+    }
+    return style;
+}
+
+SimileAjax.DOM.getEventRelativeCoordinates = function(evt, elmt) {
+    if (SimileAjax.Platform.browser.isIE) {
+      if (evt.type == "mousewheel") {
+        var coords = SimileAjax.DOM.getPageCoordinates(elmt);
+        return {
+          x: evt.clientX - coords.left,
+          y: evt.clientY - coords.top
+        };
+      } else {
+        return {
+          x: evt.offsetX,
+          y: evt.offsetY
+        };
+      }
+    } else {
+        var coords = SimileAjax.DOM.getPageCoordinates(elmt);
+
+        if ((evt.type == "DOMMouseScroll") &&
+          SimileAjax.Platform.browser.isFirefox &&
+          (SimileAjax.Platform.browser.majorVersion == 2)) {
+          // Due to: https://bugzilla.mozilla.org/show_bug.cgi?id=352179
+
+          return {
+            x: evt.screenX - coords.left,
+            y: evt.screenY - coords.top
+          };
+        } else {
+          return {
+              x: evt.pageX - coords.left,
+              y: evt.pageY - coords.top
+          };
+        }
+    }
+};
+
+SimileAjax.DOM.getEventPageCoordinates = function(evt) {
+    if (SimileAjax.Platform.browser.isIE) {
+        return {
+            x: evt.clientX + document.body.scrollLeft,
+            y: evt.clientY + document.body.scrollTop
+        };
+    } else {
+        return {
+            x: evt.pageX,
+            y: evt.pageY
+        };
+    }
+};
+
+SimileAjax.DOM.hittest = function(x, y, except) {
+    return SimileAjax.DOM._hittest(document.body, x, y, except);
+};
+
+SimileAjax.DOM._hittest = function(elmt, x, y, except) {
+    var childNodes = elmt.childNodes;
+    outer: for (var i = 0; i < childNodes.length; i++) {
+        var childNode = childNodes[i];
+        for (var j = 0; j < except.length; j++) {
+            if (childNode == except[j]) {
+                continue outer;
+            }
+        }
+
+        if (childNode.offsetWidth == 0 && childNode.offsetHeight == 0) {
+            /*
+             *  Sometimes SPAN elements have zero width and height but
+             *  they have children like DIVs that cover non-zero areas.
+             */
+            var hitNode = SimileAjax.DOM._hittest(childNode, x, y, except);
+            if (hitNode != childNode) {
+                return hitNode;
+            }
+        } else {
+            var top = 0;
+            var left = 0;
+
+            var node = childNode;
+            while (node) {
+                top += node.offsetTop;
+                left += node.offsetLeft;
+                node = node.offsetParent;
+            }
+
+            if (left <= x && top <= y && (x - left) < childNode.offsetWidth && (y - top) < childNode.offsetHeight) {
+                return SimileAjax.DOM._hittest(childNode, x, y, except);
+            } else if (childNode.nodeType == 1 && childNode.tagName == "TR") {
+                /*
+                 *  Table row might have cells that span several rows.
+                 */
+                var childNode2 = SimileAjax.DOM._hittest(childNode, x, y, except);
+                if (childNode2 != childNode) {
+                    return childNode2;
+                }
+            }
+        }
+    }
+    return elmt;
+};
+
+SimileAjax.DOM.cancelEvent = function(evt) {
+    evt.returnValue = false;
+    evt.cancelBubble = true;
+    if ("preventDefault" in evt) {
+        evt.preventDefault();
+    }
+};
+
+SimileAjax.DOM.appendClassName = function(elmt, className) {
+    var classes = elmt.className.split(" ");
+    for (var i = 0; i < classes.length; i++) {
+        if (classes[i] == className) {
+            return;
+        }
+    }
+    classes.push(className);
+    elmt.className = classes.join(" ");
+};
+
+SimileAjax.DOM.createInputElement = function(type) {
+    var div = document.createElement("div");
+    div.innerHTML = "<input type='" + type + "' />";
+
+    return div.firstChild;
+};
+
+SimileAjax.DOM.createDOMFromTemplate = function(template) {
+    var result = {};
+    result.elmt = SimileAjax.DOM._createDOMFromTemplate(template, result, null);
+
+    return result;
+};
+
+SimileAjax.DOM._createDOMFromTemplate = function(templateNode, result, parentElmt) {
+    if (templateNode == null) {
+        /*
+        var node = doc.createTextNode("--null--");
+        if (parentElmt != null) {
+            parentElmt.appendChild(node);
+        }
+        return node;
+        */
+        return null;
+    } else if (typeof templateNode != "object") {
+        var node = document.createTextNode(templateNode);
+        if (parentElmt != null) {
+            parentElmt.appendChild(node);
+        }
+        return node;
+    } else {
+        var elmt = null;
+        if ("tag" in templateNode) {
+            var tag = templateNode.tag;
+            if (parentElmt != null) {
+                if (tag == "tr") {
+                    elmt = parentElmt.insertRow(parentElmt.rows.length);
+                } else if (tag == "td") {
+                    elmt = parentElmt.insertCell(parentElmt.cells.length);
+                }
+            }
+            if (elmt == null) {
+                elmt = tag == "input" ?
+                    SimileAjax.DOM.createInputElement(templateNode.type) :
+                    document.createElement(tag);
+
+                if (parentElmt != null) {
+                    parentElmt.appendChild(elmt);
+                }
+            }
+        } else {
+            elmt = templateNode.elmt;
+            if (parentElmt != null) {
+                parentElmt.appendChild(elmt);
+            }
+        }
+
+        for (var attribute in templateNode) {
+            var value = templateNode[attribute];
+
+            if (attribute == "field") {
+                result[value] = elmt;
+
+            } else if (attribute == "className") {
+                elmt.className = value;
+            } else if (attribute == "id") {
+                elmt.id = value;
+            } else if (attribute == "title") {
+                elmt.title = value;
+            } else if (attribute == "type" && elmt.tagName == "input") {
+                // do nothing
+            } else if (attribute == "style") {
+                for (n in value) {
+                    var v = value[n];
+                    if (n == "float") {
+                        n = SimileAjax.Platform.browser.isIE ? "styleFloat" : "cssFloat";
+                    }
+                    elmt.style[n] = v;
+                }
+            } else if (attribute == "children") {
+                for (var i = 0; i < value.length; i++) {
+                    SimileAjax.DOM._createDOMFromTemplate(value[i], result, elmt);
+                }
+            } else if (attribute != "tag" && attribute != "elmt") {
+                elmt.setAttribute(attribute, value);
+            }
+        }
+        return elmt;
+    }
+}
+
+SimileAjax.DOM._cachedParent = null;
+SimileAjax.DOM.createElementFromString = function(s) {
+    if (SimileAjax.DOM._cachedParent == null) {
+        SimileAjax.DOM._cachedParent = document.createElement("div");
+    }
+    SimileAjax.DOM._cachedParent.innerHTML = s;
+    return SimileAjax.DOM._cachedParent.firstChild;
+};
+
+SimileAjax.DOM.createDOMFromString = function(root, s, fieldElmts) {
+    var elmt = typeof root == "string" ? document.createElement(root) : root;
+    elmt.innerHTML = s;
+
+    var dom = { elmt: elmt };
+    SimileAjax.DOM._processDOMChildrenConstructedFromString(dom, elmt, fieldElmts != null ? fieldElmts : {} );
+
+    return dom;
+};
+
+SimileAjax.DOM._processDOMConstructedFromString = function(dom, elmt, fieldElmts) {
+    var id = elmt.id;
+    if (id != null && id.length > 0) {
+        elmt.removeAttribute("id");
+        if (id in fieldElmts) {
+            var parentElmt = elmt.parentNode;
+            parentElmt.insertBefore(fieldElmts[id], elmt);
+            parentElmt.removeChild(elmt);
+
+            dom[id] = fieldElmts[id];
+            return;
+        } else {
+            dom[id] = elmt;
+        }
+    }
+
+    if (elmt.hasChildNodes()) {
+        SimileAjax.DOM._processDOMChildrenConstructedFromString(dom, elmt, fieldElmts);
+    }
+};
+
+SimileAjax.DOM._processDOMChildrenConstructedFromString = function(dom, elmt, fieldElmts) {
+    var node = elmt.firstChild;
+    while (node != null) {
+        var node2 = node.nextSibling;
+        if (node.nodeType == 1) {
+            SimileAjax.DOM._processDOMConstructedFromString(dom, node, fieldElmts);
+        }
+        node = node2;
+    }
+};
+/**
+ * @fileOverview Graphics utility functions and constants
+ * @name SimileAjax.Graphics
+ */
+
+SimileAjax.Graphics = new Object();
+
+/**
+ * A boolean value indicating whether PNG translucency is supported on the
+ * user's browser or not.
+ *
+ * @type Boolean
+ */
+SimileAjax.Graphics.pngIsTranslucent = (!SimileAjax.Platform.browser.isIE) || (SimileAjax.Platform.browser.majorVersion > 6);
+if (!SimileAjax.Graphics.pngIsTranslucent) {
+    SimileAjax.includeCssFile(document, SimileAjax.urlPrefix + "styles/graphics-ie6.css");
+}
+
+/*==================================================
+ *  Opacity, translucency
+ *==================================================
+ */
+SimileAjax.Graphics._createTranslucentImage1 = function(url, verticalAlign) {
+    var elmt = document.createElement("img");
+    elmt.setAttribute("src", url);
+    if (verticalAlign != null) {
+        elmt.style.verticalAlign = verticalAlign;
+    }
+    return elmt;
+};
+SimileAjax.Graphics._createTranslucentImage2 = function(url, verticalAlign) {
+    var elmt = document.createElement("img");
+    elmt.style.width = "1px";  // just so that IE will calculate the size property
+    elmt.style.height = "1px";
+    elmt.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + url +"', sizingMethod='image')";
+    elmt.style.verticalAlign = (verticalAlign != null) ? verticalAlign : "middle";
+    return elmt;
+};
+
+/**
+ * Creates a DOM element for an <code>img</code> tag using the URL given. This
+ * is a convenience method that automatically includes the necessary CSS to
+ * allow for translucency, even on IE.
+ *
+ * @function
+ * @param {String} url the URL to the image
+ * @param {String} verticalAlign the CSS value for the image's vertical-align
+ * @return {Element} a DOM element containing the <code>img</code> tag
+ */
+SimileAjax.Graphics.createTranslucentImage = SimileAjax.Graphics.pngIsTranslucent ?
+    SimileAjax.Graphics._createTranslucentImage1 :
+    SimileAjax.Graphics._createTranslucentImage2;
+
+SimileAjax.Graphics._createTranslucentImageHTML1 = function(url, verticalAlign) {
+    return "<img src=\"" + url + "\"" +
+        (verticalAlign != null ? " style=\"vertical-align: " + verticalAlign + ";\"" : "") +
+        " />";
+};
+SimileAjax.Graphics._createTranslucentImageHTML2 = function(url, verticalAlign) {
+    var style =
+        "width: 1px; height: 1px; " +
+        "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + url +"', sizingMethod='image');" +
+        (verticalAlign != null ? " vertical-align: " + verticalAlign + ";" : "");
+
+    return "<img src='" + url + "' style=\"" + style + "\" />";
+};
+
+/**
+ * Creates an HTML string for an <code>img</code> tag using the URL given.
+ * This is a convenience method that automatically includes the necessary CSS
+ * to allow for translucency, even on IE.
+ *
+ * @function
+ * @param {String} url the URL to the image
+ * @param {String} verticalAlign the CSS value for the image's vertical-align
+ * @return {String} a string containing the <code>img</code> tag
+ */
+SimileAjax.Graphics.createTranslucentImageHTML = SimileAjax.Graphics.pngIsTranslucent ?
+    SimileAjax.Graphics._createTranslucentImageHTML1 :
+    SimileAjax.Graphics._createTranslucentImageHTML2;
+
+/**
+ * Sets the opacity on the given DOM element.
+ *
+ * @param {Element} elmt the DOM element to set the opacity on
+ * @param {Number} opacity an integer from 0 to 100 specifying the opacity
+ */
+SimileAjax.Graphics.setOpacity = function(elmt, opacity) {
+    if (SimileAjax.Platform.browser.isIE) {
+        elmt.style.filter = "progid:DXImageTransform.Microsoft.Alpha(Style=0,Opacity=" + opacity + ")";
+    } else {
+        var o = (opacity / 100).toString();
+        elmt.style.opacity = o;
+        elmt.style.MozOpacity = o;
+    }
+};
+
+/*==================================================
+ *  Bubble
+ *==================================================
+ */
+
+SimileAjax.Graphics.bubbleConfig = {
+    containerCSSClass:              "simileAjax-bubble-container",
+    innerContainerCSSClass:         "simileAjax-bubble-innerContainer",
+    contentContainerCSSClass:       "simileAjax-bubble-contentContainer",
+
+    borderGraphicSize:              50,
+    borderGraphicCSSClassPrefix:    "simileAjax-bubble-border-",
+
+    arrowGraphicTargetOffset:       33,  // from tip of arrow to the side of the graphic that touches the content of the bubble
+    arrowGraphicLength:             100, // dimension of arrow graphic along the direction that the arrow points
+    arrowGraphicWidth:              49,  // dimension of arrow graphic perpendicular to the direction that the arrow points
+    arrowGraphicCSSClassPrefix:     "simileAjax-bubble-arrow-",
+
+    closeGraphicCSSClass:           "simileAjax-bubble-close",
+
+    extraPadding:                   20
+};
+
+/**
+ * Creates a nice, rounded bubble popup with the given content in a div,
+ * page coordinates and a suggested width. The bubble will point to the
+ * location on the page as described by pageX and pageY.  All measurements
+ * should be given in pixels.
+ *
+ * @param {Element} the content div
+ * @param {Number} pageX the x coordinate of the point to point to
+ * @param {Number} pageY the y coordinate of the point to point to
+ * @param {Number} contentWidth a suggested width of the content
+ * @param {String} orientation a string ("top", "bottom", "left", or "right")
+ *   that describes the orientation of the arrow on the bubble
+ * @param {Number} maxHeight. Add a scrollbar div if bubble would be too tall.
+ *   Default of 0 or null means no maximum
+ */
+SimileAjax.Graphics.createBubbleForContentAndPoint = function(
+       div, pageX, pageY, contentWidth, orientation, maxHeight) {
+    if (typeof contentWidth != "number") {
+        contentWidth = 300;
+    }
+    if (typeof maxHeight != "number") {
+        maxHeight = 0;
+    }
+
+    div.style.position = "absolute";
+    div.style.left = "-5000px";
+    div.style.top = "0px";
+    div.style.width = contentWidth + "px";
+    document.body.appendChild(div);
+
+    window.setTimeout(function() {
+        var width = div.scrollWidth + 10;
+        var height = div.scrollHeight + 10;
+        var scrollDivW = 0; // width of the possible inner container when we want vertical scrolling
+        if (maxHeight > 0 && height > maxHeight) {
+          height = maxHeight;
+          scrollDivW = width - 25;
+        }
+
+        var bubble = SimileAjax.Graphics.createBubbleForPoint(pageX, pageY, width, height, orientation);
+
+        document.body.removeChild(div);
+        div.style.position = "static";
+        div.style.left = "";
+        div.style.top = "";
+
+        // create a scroll div if needed
+        if (scrollDivW > 0) {
+          var scrollDiv = document.createElement("div");
+          div.style.width = "";
+          scrollDiv.style.width = scrollDivW + "px";
+          scrollDiv.appendChild(div);
+          bubble.content.appendChild(scrollDiv);
+        } else {
+          div.style.width = width + "px";
+          bubble.content.appendChild(div);
+        }
+    }, 200);
+};
+
+/**
+ * Creates a nice, rounded bubble popup with the given page coordinates and
+ * content dimensions.  The bubble will point to the location on the page
+ * as described by pageX and pageY.  All measurements should be given in
+ * pixels.
+ *
+ * @param {Number} pageX the x coordinate of the point to point to
+ * @param {Number} pageY the y coordinate of the point to point to
+ * @param {Number} contentWidth the width of the content box in the bubble
+ * @param {Number} contentHeight the height of the content box in the bubble
+ * @param {String} orientation a string ("top", "bottom", "left", or "right")
+ *   that describes the orientation of the arrow on the bubble
+ * @return {Element} a DOM element for the newly created bubble
+ */
+SimileAjax.Graphics.createBubbleForPoint = function(pageX, pageY, contentWidth, contentHeight, orientation) {
+    contentWidth = parseInt(contentWidth, 10); // harden against bad input bugs
+    contentHeight = parseInt(contentHeight, 10); // getting numbers-as-strings
+
+    var bubbleConfig = SimileAjax.Graphics.bubbleConfig;
+    var pngTransparencyClassSuffix =
+        SimileAjax.Graphics.pngIsTranslucent ? "pngTranslucent" : "pngNotTranslucent";
+
+    var bubbleWidth = contentWidth + 2 * bubbleConfig.borderGraphicSize;
+    var bubbleHeight = contentHeight + 2 * bubbleConfig.borderGraphicSize;
+
+    var generatePngSensitiveClass = function(className) {
+        return className + " " + className + "-" + pngTransparencyClassSuffix;
+    };
+
+    /*
+     *  Render container divs
+     */
+    var div = document.createElement("div");
+    div.className = generatePngSensitiveClass(bubbleConfig.containerCSSClass);
+    div.style.width = contentWidth + "px";
+    div.style.height = contentHeight + "px";
+
+    var divInnerContainer = document.createElement("div");
+    divInnerContainer.className = generatePngSensitiveClass(bubbleConfig.innerContainerCSSClass);
+    div.appendChild(divInnerContainer);
+
+    /*
+     *  Create layer for bubble
+     */
+    var close = function() {
+        if (!bubble._closed) {
+            document.body.removeChild(bubble._div);
+            bubble._doc = null;
+            bubble._div = null;
+            bubble._content = null;
+            bubble._closed = true;
+        }
+    }
+    var bubble = { _closed: false };
+    var layer = SimileAjax.WindowManager.pushLayer(close, true, div);
+    bubble._div = div;
+    bubble.close = function() { SimileAjax.WindowManager.popLayer(layer); }
+
+    /*
+     *  Render border graphics
+     */
+    var createBorder = function(classNameSuffix) {
+        var divBorderGraphic = document.createElement("div");
+        divBorderGraphic.className = generatePngSensitiveClass(bubbleConfig.borderGraphicCSSClassPrefix + classNameSuffix);
+        divInnerContainer.appendChild(divBorderGraphic);
+    };
+    createBorder("top-left");
+    createBorder("top-right");
+    createBorder("bottom-left");
+    createBorder("bottom-right");
+    createBorder("left");
+    createBorder("right");
+    createBorder("top");
+    createBorder("bottom");
+
+    /*
+     *  Render content
+     */
+    var divContentContainer = document.createElement("div");
+    divContentContainer.className = generatePngSensitiveClass(bubbleConfig.contentContainerCSSClass);
+    divInnerContainer.appendChild(divContentContainer);
+    bubble.content = divContentContainer;
+
+    /*
+     *  Render close button
+     */
+    var divClose = document.createElement("div");
+    divClose.className = generatePngSensitiveClass(bubbleConfig.closeGraphicCSSClass);
+    divInnerContainer.appendChild(divClose);
+    SimileAjax.WindowManager.registerEventWithObject(divClose, "click", bubble, "close");
+
+    (function() {
+        var dims = SimileAjax.Graphics.getWindowDimensions();
+        var docWidth = dims.w;
+        var docHeight = dims.h;
+
+        var halfArrowGraphicWidth = Math.ceil(bubbleConfig.arrowGraphicWidth / 2);
+
+        var createArrow = function(classNameSuffix) {
+            var divArrowGraphic = document.createElement("div");
+            divArrowGraphic.className = generatePngSensitiveClass(bubbleConfig.arrowGraphicCSSClassPrefix + "point-" + classNameSuffix);
+            divInnerContainer.appendChild(divArrowGraphic);
+            return divArrowGraphic;
+        };
+
+        if (pageX - halfArrowGraphicWidth - bubbleConfig.borderGraphicSize - bubbleConfig.extraPadding > 0 &&
+            pageX + halfArrowGraphicWidth + bubbleConfig.borderGraphicSize + bubbleConfig.extraPadding < docWidth) {
+
+            /*
+             *  Bubble can be positioned above or below the target point.
+             */
+
+            var left = pageX - Math.round(contentWidth / 2);
+            left = pageX < (docWidth / 2) ?
+                Math.max(left, bubbleConfig.extraPadding + bubbleConfig.borderGraphicSize) :
+                Math.min(left, docWidth - bubbleConfig.extraPadding - bubbleConfig.borderGraphicSize - contentWidth);
+
+            if ((orientation && orientation == "top") ||
+                (!orientation &&
+                    (pageY
+                        - bubbleConfig.arrowGraphicTargetOffset
+                        - contentHeight
+                        - bubbleConfig.borderGraphicSize
+                        - bubbleConfig.extraPadding > 0))) {
+
+                /*
+                 *  Position bubble above the target point.
+                 */
+
+                var divArrow = createArrow("down");
+                divArrow.style.left = (pageX - halfArrowGraphicWidth - left) + "px";
+
+                div.style.left = left + "px";
+                div.style.top = (pageY - bubbleConfig.arrowGraphicTargetOffset - contentHeight) + "px";
+
+                return;
+            } else if ((orientation && orientation == "bottom") ||
+                (!orientation &&
+                    (pageY
+                        + bubbleConfig.arrowGraphicTargetOffset
+                        + contentHeight
+                        + bubbleConfig.borderGraphicSize
+                        + bubbleConfig.extraPadding < docHeight))) {
+
+                /*
+                 *  Position bubble below the target point.
+                 */
+
+                var divArrow = createArrow("up");
+                divArrow.style.left = (pageX - halfArrowGraphicWidth - left) + "px";
+
+                div.style.left = left + "px";
+                div.style.top = (pageY + bubbleConfig.arrowGraphicTargetOffset) + "px";
+
+                return;
+            }
+        }
+
+        var top = pageY - Math.round(contentHeight / 2);
+        top = pageY < (docHeight / 2) ?
+            Math.max(top, bubbleConfig.extraPadding + bubbleConfig.borderGraphicSize) :
+            Math.min(top, docHeight - bubbleConfig.extraPadding - bubbleConfig.borderGraphicSize - contentHeight);
+
+        if ((orientation && orientation == "left") ||
+            (!orientation &&
+                (pageX
+                    - bubbleConfig.arrowGraphicTargetOffset
+                    - contentWidth
+                    - bubbleConfig.borderGraphicSize
+                    - bubbleConfig.extraPadding > 0))) {
+
+            /*
+             *  Position bubble left of the target point.
+             */
+
+            var divArrow = createArrow("right");
+            divArrow.style.top = (pageY - halfArrowGraphicWidth - top) + "px";
+
+            div.style.top = top + "px";
+            div.style.left = (pageX - bubbleConfig.arrowGraphicTargetOffset - contentWidth) + "px";
+        } else {
+
+            /*
+             *  Position bubble right of the target point, as the last resort.
+             */
+
+            var divArrow = createArrow("left");
+            divArrow.style.top = (pageY - halfArrowGraphicWidth - top) + "px";
+
+            div.style.top = top + "px";
+            div.style.left = (pageX + bubbleConfig.arrowGraphicTargetOffset) + "px";
+        }
+    })();
+
+    document.body.appendChild(div);
+
+    return bubble;
+};
+
+SimileAjax.Graphics.getWindowDimensions = function() {
+    if (typeof window.innerHeight == 'number') {
+        return { w:window.innerWidth, h:window.innerHeight }; // Non-IE
+    } else if (document.documentElement && document.documentElement.clientHeight) {
+        return { // IE6+, in "standards compliant mode"
+            w:document.documentElement.clientWidth,
+            h:document.documentElement.clientHeight
+        };
+    } else if (document.body && document.body.clientHeight) {
+        return { // IE 4 compatible
+            w:document.body.clientWidth,
+            h:document.body.clientHeight
+        };
+    }
 };
 
 
 /**
- * Parse out the query parameters from a URL
- * @param {String} url    the url to parse, or location.href if undefined
- * @param {Object} to     optional object to extend with the parameters
- * @param {Object} types  optional object mapping keys to value types
- *        (String, Number, Boolean or Array, String by default)
- * @return a key/value Object whose keys are the query parameter names
- * @type Object
+ * Creates a floating, rounded message bubble in the center of the window for
+ * displaying modal information, e.g. "Loading..."
+ *
+ * @param {Document} doc the root document for the page to render on
+ * @param {Object} an object with two properties, contentDiv and containerDiv,
+ *   consisting of the newly created DOM elements
+ */
+SimileAjax.Graphics.createMessageBubble = function(doc) {
+    var containerDiv = doc.createElement("div");
+    if (SimileAjax.Graphics.pngIsTranslucent) {
+        var topDiv = doc.createElement("div");
+        topDiv.style.height = "33px";
+        topDiv.style.background = "url(" + SimileAjax.urlPrefix + "images/message-top-left.png) top left no-repeat";
+        topDiv.style.paddingLeft = "44px";
+        containerDiv.appendChild(topDiv);
+
+        var topRightDiv = doc.createElement("div");
+        topRightDiv.style.height = "33px";
+        topRightDiv.style.background = "url(" + SimileAjax.urlPrefix + "images/message-top-right.png) top right no-repeat";
+        topDiv.appendChild(topRightDiv);
+
+        var middleDiv = doc.createElement("div");
+        middleDiv.style.background = "url(" + SimileAjax.urlPrefix + "images/message-left.png) top left repeat-y";
+        middleDiv.style.paddingLeft = "44px";
+        containerDiv.appendChild(middleDiv);
+
+        var middleRightDiv = doc.createElement("div");
+        middleRightDiv.style.background = "url(" + SimileAjax.urlPrefix + "images/message-right.png) top right repeat-y";
+        middleRightDiv.style.paddingRight = "44px";
+        middleDiv.appendChild(middleRightDiv);
+
+        var contentDiv = doc.createElement("div");
+        middleRightDiv.appendChild(contentDiv);
+
+        var bottomDiv = doc.createElement("div");
+        bottomDiv.style.height = "55px";
+        bottomDiv.style.background = "url(" + SimileAjax.urlPrefix + "images/message-bottom-left.png) bottom left no-repeat";
+        bottomDiv.style.paddingLeft = "44px";
+        containerDiv.appendChild(bottomDiv);
+
+        var bottomRightDiv = doc.createElement("div");
+        bottomRightDiv.style.height = "55px";
+        bottomRightDiv.style.background = "url(" + SimileAjax.urlPrefix + "images/message-bottom-right.png) bottom right no-repeat";
+        bottomDiv.appendChild(bottomRightDiv);
+    } else {
+        containerDiv.style.border = "2px solid #7777AA";
+        containerDiv.style.padding = "20px";
+        containerDiv.style.background = "white";
+        SimileAjax.Graphics.setOpacity(containerDiv, 90);
+
+        var contentDiv = doc.createElement("div");
+        containerDiv.appendChild(contentDiv);
+    }
+
+    return {
+        containerDiv:   containerDiv,
+        contentDiv:     contentDiv
+    };
+};
+
+/*==================================================
+ *  Animation
+ *==================================================
+ */
+
+/**
+ * Creates an animation for a function, and an interval of values.  The word
+ * "animation" here is used in the sense of repeatedly calling a function with
+ * a current value from within an interval, and a delta value.
+ *
+ * @param {Function} f a function to be called every 50 milliseconds throughout
+ *   the animation duration, of the form f(current, delta), where current is
+ *   the current value within the range and delta is the current change.
+ * @param {Number} from a starting value
+ * @param {Number} to an ending value
+ * @param {Number} duration the duration of the animation in milliseconds
+ * @param {Function} [cont] an optional function that is called at the end of
+ *   the animation, i.e. a continuation.
+ * @return {SimileAjax.Graphics._Animation} a new animation object
+ */
+SimileAjax.Graphics.createAnimation = function(f, from, to, duration, cont) {
+    return new SimileAjax.Graphics._Animation(f, from, to, duration, cont);
+};
+
+SimileAjax.Graphics._Animation = function(f, from, to, duration, cont) {
+    this.f = f;
+    this.cont = (typeof cont == "function") ? cont : function() {};
+
+    this.from = from;
+    this.to = to;
+    this.current = from;
+
+    this.duration = duration;
+    this.start = new Date().getTime();
+    this.timePassed = 0;
+};
+
+/**
+ * Runs this animation.
+ */
+SimileAjax.Graphics._Animation.prototype.run = function() {
+    var a = this;
+    window.setTimeout(function() { a.step(); }, 50);
+};
+
+/**
+ * Increments this animation by one step, and then continues the animation with
+ * <code>run()</code>.
+ */
+SimileAjax.Graphics._Animation.prototype.step = function() {
+    this.timePassed += 50;
+
+    var timePassedFraction = this.timePassed / this.duration;
+    var parameterFraction = -Math.cos(timePassedFraction * Math.PI) / 2 + 0.5;
+    var current = parameterFraction * (this.to - this.from) + this.from;
+
+    try {
+        this.f(current, current - this.current);
+    } catch (e) {
+    }
+    this.current = current;
+
+    if (this.timePassed < this.duration) {
+        this.run();
+    } else {
+        this.f(this.to, 0);
+        this["cont"]();
+    }
+};
+
+/*==================================================
+ *  CopyPasteButton
+ *
+ *  Adapted from http://spaces.live.com/editorial/rayozzie/demo/liveclip/liveclipsample/techPreview.html.
+ *==================================================
+ */
+
+/**
+ * Creates a button and textarea for displaying structured data and copying it
+ * to the clipboard.  The data is dynamically generated by the given
+ * createDataFunction parameter.
+ *
+ * @param {String} image an image URL to use as the background for the
+ *   generated box
+ * @param {Number} width the width in pixels of the generated box
+ * @param {Number} height the height in pixels of the generated box
+ * @param {Function} createDataFunction a function that is called with no
+ *   arguments to generate the structured data
+ * @return a new DOM element
+ */
+SimileAjax.Graphics.createStructuredDataCopyButton = function(image, width, height, createDataFunction) {
+    var div = document.createElement("div");
+    div.style.position = "relative";
+    div.style.display = "inline";
+    div.style.width = width + "px";
+    div.style.height = height + "px";
+    div.style.overflow = "hidden";
+    div.style.margin = "2px";
+
+    if (SimileAjax.Graphics.pngIsTranslucent) {
+        div.style.background = "url(" + image + ") no-repeat";
+    } else {
+        div.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + image +"', sizingMethod='image')";
+    }
+
+    var style;
+    if (SimileAjax.Platform.browser.isIE) {
+        style = "filter:alpha(opacity=0)";
+    } else {
+        style = "opacity: 0";
+    }
+    div.innerHTML = "<textarea rows='1' autocomplete='off' value='none' style='" + style + "' />";
+
+    var textarea = div.firstChild;
+    textarea.style.width = width + "px";
+    textarea.style.height = height + "px";
+    textarea.onmousedown = function(evt) {
+        evt = (evt) ? evt : ((event) ? event : null);
+        if (evt.button == 2) {
+            textarea.value = createDataFunction();
+            textarea.select();
+        }
+    };
+
+    return div;
+};
+
+/*==================================================
+ *  getWidthHeight
+ *==================================================
+ */
+SimileAjax.Graphics.getWidthHeight = function(el) {
+    // RETURNS hash {width:  w, height: h} in pixels
+
+    var w, h;
+    // offsetWidth rounds on FF, so doesn't work for us.
+    // See https://bugzilla.mozilla.org/show_bug.cgi?id=458617
+    if (el.getBoundingClientRect == null) {
+    	// use offsetWidth
+      w = el.offsetWidth;
+      h = el.offsetHeight;
+    } else {
+    	// use getBoundingClientRect
+      var rect = el.getBoundingClientRect();
+      w = Math.ceil(rect.right - rect.left);
+    	h = Math.ceil(rect.bottom - rect.top);
+    }
+    return {
+        width:  w,
+        height: h
+    };
+};
+
+
+/*==================================================
+ *  FontRenderingContext
+ *==================================================
  */
-SimileAjax.parseURLParameters = function(url, to, types) {
-    to = to || {};
-    types = types || {};
-
-    if (typeof url == "undefined") {
-        url = location.href;
-    }
-    var q = url.indexOf("?");
-    if (q < 0) {
-        return to;
-    }
-    url = (url+"#").slice(q+1, url.indexOf("#")); // toss the URL fragment
-
-    var params = url.split("&"), param, parsed = {};
-    var decode = window.decodeURIComponent || unescape;
-    for (var i = 0; param = params[i]; i++) {
-        var eq = param.indexOf("=");
-        var name = decode(param.slice(0,eq));
-        var old = parsed[name];
-        if (typeof old == "undefined") {
-            old = [];
-        } else if (!(old instanceof Array)) {
-            old = [old];
-        }
-        parsed[name] = old.concat(decode(param.slice(eq+1)));
-    }
-    for (var i in parsed) {
-        if (!parsed.hasOwnProperty(i)) continue;
-        var type = types[i] || String;
-        var data = parsed[i];
-        if (!(data instanceof Array)) {
-            data = [data];
-        }
-        if (type === Boolean && data[0] == "false") {
-            to[i] = false; // because Boolean("false") === true
+SimileAjax.Graphics.getFontRenderingContext = function(elmt, width) {
+    return new SimileAjax.Graphics._FontRenderingContext(elmt, width);
+};
+
+SimileAjax.Graphics._FontRenderingContext = function(elmt, width) {
+    this._elmt = elmt;
+    this._elmt.style.visibility = "hidden";
+    if (typeof width == "string") {
+        this._elmt.style.width = width;
+    } else if (typeof width == "number") {
+        this._elmt.style.width = width + "px";
+    }
+};
+
+SimileAjax.Graphics._FontRenderingContext.prototype.dispose = function() {
+    this._elmt = null;
+};
+
+SimileAjax.Graphics._FontRenderingContext.prototype.update = function() {
+    this._elmt.innerHTML = "A";
+    this._lineHeight = this._elmt.offsetHeight;
+};
+
+SimileAjax.Graphics._FontRenderingContext.prototype.computeSize = function(text, className) {
+    // className arg is optional
+    var el = this._elmt;
+    el.innerHTML = text;
+    el.className = className === undefined ? '' : className;
+    var wh = SimileAjax.Graphics.getWidthHeight(el);
+    el.className = ''; // reset for the next guy
+
+    return wh;
+};
+
+SimileAjax.Graphics._FontRenderingContext.prototype.getLineHeight = function() {
+    return this._lineHeight;
+};
+
+/**
+ * @fileOverview A collection of date/time utility functions
+ * @name SimileAjax.DateTime
+ */
+
+SimileAjax.DateTime = new Object();
+
+SimileAjax.DateTime.MILLISECOND    = 0;
+SimileAjax.DateTime.SECOND         = 1;
+SimileAjax.DateTime.MINUTE         = 2;
+SimileAjax.DateTime.HOUR           = 3;
+SimileAjax.DateTime.DAY            = 4;
+SimileAjax.DateTime.WEEK           = 5;
+SimileAjax.DateTime.MONTH          = 6;
+SimileAjax.DateTime.YEAR           = 7;
+SimileAjax.DateTime.DECADE         = 8;
+SimileAjax.DateTime.CENTURY        = 9;
+SimileAjax.DateTime.MILLENNIUM     = 10;
+
+SimileAjax.DateTime.EPOCH          = -1;
+SimileAjax.DateTime.ERA            = -2;
+
+/**
+ * An array of unit lengths, expressed in milliseconds, of various lengths of
+ * time.  The array indices are predefined and stored as properties of the
+ * SimileAjax.DateTime object, e.g. SimileAjax.DateTime.YEAR.
+ * @type Array
+ */
+SimileAjax.DateTime.gregorianUnitLengths = [];
+    (function() {
+        var d = SimileAjax.DateTime;
+        var a = d.gregorianUnitLengths;
+
+        a[d.MILLISECOND] = 1;
+        a[d.SECOND]      = 1000;
+        a[d.MINUTE]      = a[d.SECOND] * 60;
+        a[d.HOUR]        = a[d.MINUTE] * 60;
+        a[d.DAY]         = a[d.HOUR] * 24;
+        a[d.WEEK]        = a[d.DAY] * 7;
+        a[d.MONTH]       = a[d.DAY] * 31;
+        a[d.YEAR]        = a[d.DAY] * 365;
+        a[d.DECADE]      = a[d.YEAR] * 10;
+        a[d.CENTURY]     = a[d.YEAR] * 100;
+        a[d.MILLENNIUM]  = a[d.YEAR] * 1000;
+    })();
+
+SimileAjax.DateTime._dateRegexp = new RegExp(
+    "^(-?)([0-9]{4})(" + [
+        "(-?([0-9]{2})(-?([0-9]{2}))?)", // -month-dayOfMonth
+        "(-?([0-9]{3}))",                // -dayOfYear
+        "(-?W([0-9]{2})(-?([1-7]))?)"    // -Wweek-dayOfWeek
+    ].join("|") + ")?$"
+);
+SimileAjax.DateTime._timezoneRegexp = new RegExp(
+    "Z|(([-+])([0-9]{2})(:?([0-9]{2}))?)$"
+);
+SimileAjax.DateTime._timeRegexp = new RegExp(
+    "^([0-9]{2})(:?([0-9]{2})(:?([0-9]{2})(\.([0-9]+))?)?)?$"
+);
+
+/**
+ * Takes a date object and a string containing an ISO 8601 date and sets the
+ * the date using information parsed from the string.  Note that this method
+ * does not parse any time information.
+ *
+ * @param {Date} dateObject the date object to modify
+ * @param {String} string an ISO 8601 string to parse
+ * @return {Date} the modified date object
+ */
+SimileAjax.DateTime.setIso8601Date = function(dateObject, string) {
+    /*
+     *  This function has been adapted from dojo.date, v.0.3.0
+     *  http://dojotoolkit.org/.
+     */
+
+    var d = string.match(SimileAjax.DateTime._dateRegexp);
+    if(!d) {
+        throw new Error("Invalid date string: " + string);
+    }
+
+    var sign = (d[1] == "-") ? -1 : 1; // BC or AD
+    var year = sign * d[2];
+    var month = d[5];
+    var date = d[7];
+    var dayofyear = d[9];
+    var week = d[11];
+    var dayofweek = (d[13]) ? d[13] : 1;
+
+    dateObject.setUTCFullYear(year);
+    if (dayofyear) {
+        dateObject.setUTCMonth(0);
+        dateObject.setUTCDate(Number(dayofyear));
+    } else if (week) {
+        dateObject.setUTCMonth(0);
+        dateObject.setUTCDate(1);
+        var gd = dateObject.getUTCDay();
+        var day =  (gd) ? gd : 7;
+        var offset = Number(dayofweek) + (7 * Number(week));
+
+        if (day <= 4) {
+            dateObject.setUTCDate(offset + 1 - day);
+        } else {
+            dateObject.setUTCDate(offset + 8 - day);
+        }
+    } else {
+        if (month) {
+            dateObject.setUTCDate(1);
+            dateObject.setUTCMonth(month - 1);
+        }
+        if (date) {
+            dateObject.setUTCDate(date);
+        }
+    }
+
+    return dateObject;
+};
+
+/**
+ * Takes a date object and a string containing an ISO 8601 time and sets the
+ * the time using information parsed from the string.  Note that this method
+ * does not parse any date information.
+ *
+ * @param {Date} dateObject the date object to modify
+ * @param {String} string an ISO 8601 string to parse
+ * @return {Date} the modified date object
+ */
+SimileAjax.DateTime.setIso8601Time = function (dateObject, string) {
+    /*
+     *  This function has been adapted from dojo.date, v.0.3.0
+     *  http://dojotoolkit.org/.
+     */
+
+    var d = string.match(SimileAjax.DateTime._timeRegexp);
+    if(!d) {
+        SimileAjax.Debug.warn("Invalid time string: " + string);
+        return false;
+    }
+    var hours = d[1];
+    var mins = Number((d[3]) ? d[3] : 0);
+    var secs = (d[5]) ? d[5] : 0;
+    var ms = d[7] ? (Number("0." + d[7]) * 1000) : 0;
+
+    dateObject.setUTCHours(hours);
+    dateObject.setUTCMinutes(mins);
+    dateObject.setUTCSeconds(secs);
+    dateObject.setUTCMilliseconds(ms);
+
+    return dateObject;
+};
+
+/**
+ * The timezone offset in minutes in the user's browser.
+ * @type Number
+ */
+SimileAjax.DateTime.timezoneOffset = new Date().getTimezoneOffset();
+
+/**
+ * Takes a date object and a string containing an ISO 8601 date and time and
+ * sets the date object using information parsed from the string.
+ *
+ * @param {Date} dateObject the date object to modify
+ * @param {String} string an ISO 8601 string to parse
+ * @return {Date} the modified date object
+ */
+SimileAjax.DateTime.setIso8601 = function (dateObject, string){
+    /*
+     *  This function has been adapted from dojo.date, v.0.3.0
+     *  http://dojotoolkit.org/.
+     */
+
+    var offset = null;
+    var comps = (string.indexOf("T") == -1) ? string.split(" ") : string.split("T");
+
+    SimileAjax.DateTime.setIso8601Date(dateObject, comps[0]);
+    if (comps.length == 2) {
+        // first strip timezone info from the end
+        var d = comps[1].match(SimileAjax.DateTime._timezoneRegexp);
+        if (d) {
+            if (d[0] == 'Z') {
+                offset = 0;
+            } else {
+                offset = (Number(d[3]) * 60) + Number(d[5]);
+                offset *= ((d[2] == '-') ? 1 : -1);
+            }
+            comps[1] = comps[1].substr(0, comps[1].length - d[0].length);
+        }
+
+        SimileAjax.DateTime.setIso8601Time(dateObject, comps[1]);
+    }
+    if (offset == null) {
+        offset = dateObject.getTimezoneOffset(); // local time zone if no tz info
+    }
+    dateObject.setTime(dateObject.getTime() + offset * 60000);
+
+    return dateObject;
+};
+
+/**
+ * Takes a string containing an ISO 8601 date and returns a newly instantiated
+ * date object with the parsed date and time information from the string.
+ *
+ * @param {String} string an ISO 8601 string to parse
+ * @return {Date} a new date object created from the string
+ */
+SimileAjax.DateTime.parseIso8601DateTime = function (string) {
+    try {
+        return SimileAjax.DateTime.setIso8601(new Date(0), string);
+    } catch (e) {
+        return null;
+    }
+};
+
+/**
+ * Takes a string containing a Gregorian date and time and returns a newly
+ * instantiated date object with the parsed date and time information from the
+ * string.  If the param is actually an instance of Date instead of a string,
+ * simply returns the given date instead.
+ *
+ * @param {Object} o an object, to either return or parse as a string
+ * @return {Date} the date object
+ */
+SimileAjax.DateTime.parseGregorianDateTime = function(o) {
+    if (o == null) {
+        return null;
+    } else if (o instanceof Date) {
+        return o;
+    }
+
+    var s = o.toString();
+    if (s.length > 0 && s.length < 8) {
+        var space = s.indexOf(" ");
+        if (space > 0) {
+            var year = parseInt(s.substr(0, space));
+            var suffix = s.substr(space + 1);
+            if (suffix.toLowerCase() == "bc") {
+                year = 1 - year;
+            }
         } else {
-            to[i] = type.apply(this, data);
-        }
-    }
-    return to;
-};
-
-
-SimileAjax.Platform = new Object();
-SimileAjax.urlPrefix = baseuri();
-
-window.Timeline = new Object();
-Timeline.urlPrefix = baseuri();
-window.Timeline.DateTime = window.SimileAjax.DateTime; // for backward compatibility
-
-/* platform.js */
-SimileAjax.jQuery = jQuery;
-// SimileAjax.jQuery=jQuery.noConflict(true);
-if(typeof window["$"]=="undefined"){window.$=SimileAjax.jQuery;
-}SimileAjax.Platform.os={isMac:false,isWin:false,isWin32:false,isUnix:false};
-SimileAjax.Platform.browser={isIE:false,isNetscape:false,isMozilla:false,isFirefox:false,isOpera:false,isSafari:false,majorVersion:0,minorVersion:0};
-(function(){var C=navigator.appName.toLowerCase();
-var A=navigator.userAgent.toLowerCase();
-SimileAjax.Platform.os.isMac=(A.indexOf("mac")!=-1);
-SimileAjax.Platform.os.isWin=(A.indexOf("win")!=-1);
-SimileAjax.Platform.os.isWin32=SimileAjax.Platform.isWin&&(A.indexOf("95")!=-1||A.indexOf("98")!=-1||A.indexOf("nt")!=-1||A.indexOf("win32")!=-1||A.indexOf("32bit")!=-1);
-SimileAjax.Platform.os.isUnix=(A.indexOf("x11")!=-1);
-SimileAjax.Platform.browser.isIE=(C.indexOf("microsoft")!=-1);
-SimileAjax.Platform.browser.isNetscape=(C.indexOf("netscape")!=-1);
-SimileAjax.Platform.browser.isMozilla=(A.indexOf("mozilla")!=-1);
-SimileAjax.Platform.browser.isFirefox=(A.indexOf("firefox")!=-1);
-SimileAjax.Platform.browser.isOpera=(C.indexOf("opera")!=-1);
-SimileAjax.Platform.browser.isSafari=(C.indexOf("safari")!=-1);
-var E=function(G){var F=G.split(".");
-SimileAjax.Platform.browser.majorVersion=parseInt(F[0]);
-SimileAjax.Platform.browser.minorVersion=parseInt(F[1]);
-};
-var B=function(H,G,I){var F=H.indexOf(G,I);
-return F>=0?F:H.length;
-};
-if(SimileAjax.Platform.browser.isMozilla){var D=A.indexOf("mozilla/");
-if(D>=0){E(A.substring(D+8,B(A," ",D)));
-}}if(SimileAjax.Platform.browser.isIE){var D=A.indexOf("msie ");
-if(D>=0){E(A.substring(D+5,B(A,";",D)));
-}}if(SimileAjax.Platform.browser.isNetscape){var D=A.indexOf("rv:");
-if(D>=0){E(A.substring(D+3,B(A,")",D)));
-}}if(SimileAjax.Platform.browser.isFirefox){var D=A.indexOf("firefox/");
-if(D>=0){E(A.substring(D+8,B(A," ",D)));
-}}if(!("localeCompare" in String.prototype)){String.prototype.localeCompare=function(F){if(this<F){return -1;
-}else{if(this>F){return 1;
-}else{return 0;
-}}};
-}})();
-SimileAjax.Platform.getDefaultLocale=function(){return SimileAjax.Platform.clientLocale;
-};
-
-
-/* ajax.js */
-SimileAjax.ListenerQueue=function(A){this._listeners=[];
-this._wildcardHandlerName=A;
-};
-SimileAjax.ListenerQueue.prototype.add=function(A){this._listeners.push(A);
-};
-SimileAjax.ListenerQueue.prototype.remove=function(C){var B=this._listeners;
-for(var A=0;
-A<B.length;
-A++){if(B[A]==C){B.splice(A,1);
-break;
-}}};
-SimileAjax.ListenerQueue.prototype.fire=function(B,A){var D=[].concat(this._listeners);
-for(var C=0;
-C<D.length;
-C++){var E=D[C];
-if(B in E){try{E[B].apply(E,A);
-}catch(F){SimileAjax.Debug.exception("Error firing event of name "+B,F);
-}}else{if(this._wildcardHandlerName!=null&&this._wildcardHandlerName in E){try{E[this._wildcardHandlerName].apply(E,[B]);
-}catch(F){SimileAjax.Debug.exception("Error firing event of name "+B+" to wildcard handler",F);
-}}}}};
-
-
-/* data-structure.js */
-SimileAjax.Set=function(A){this._hash={};
-this._count=0;
-if(A instanceof Array){for(var B=0;
-B<A.length;
-B++){this.add(A[B]);
-}}else{if(A instanceof SimileAjax.Set){this.addSet(A);
-}}};
-SimileAjax.Set.prototype.add=function(A){if(!(A in this._hash)){this._hash[A]=true;
-this._count++;
-return true;
-}return false;
-};
-SimileAjax.Set.prototype.addSet=function(B){for(var A in B._hash){this.add(A);
-}};
-SimileAjax.Set.prototype.remove=function(A){if(A in this._hash){delete this._hash[A];
-this._count--;
-return true;
-}return false;
-};
-SimileAjax.Set.prototype.removeSet=function(B){for(var A in B._hash){this.remove(A);
-}};
-SimileAjax.Set.prototype.retainSet=function(B){for(var A in this._hash){if(!B.contains(A)){delete this._hash[A];
-this._count--;
-}}};
-SimileAjax.Set.prototype.contains=function(A){return(A in this._hash);
-};
-SimileAjax.Set.prototype.size=function(){return this._count;
-};
-SimileAjax.Set.prototype.toArray=function(){var A=[];
-for(var B in this._hash){A.push(B);
-}return A;
-};
-SimileAjax.Set.prototype.visit=function(A){for(var B in this._hash){if(A(B)==true){break;
-}}};
-SimileAjax.SortedArray=function(B,A){this._a=(A instanceof Array)?A:[];
-this._compare=B;
-};
-SimileAjax.SortedArray.prototype.add=function(C){var A=this;
-var B=this.find(function(D){return A._compare(D,C);
-});
-if(B<this._a.length){this._a.splice(B,0,C);
-}else{this._a.push(C);
-}};
-SimileAjax.SortedArray.prototype.remove=function(C){var A=this;
-var B=this.find(function(D){return A._compare(D,C);
-});
-while(B<this._a.length&&this._compare(this._a[B],C)==0){if(this._a[B]==C){this._a.splice(B,1);
-return true;
-}else{B++;
-}}return false;
-};
-SimileAjax.SortedArray.prototype.removeAll=function(){this._a=[];
-};
-SimileAjax.SortedArray.prototype.elementAt=function(A){return this._a[A];
-};
-SimileAjax.SortedArray.prototype.length=function(){return this._a.length;
-};
-SimileAjax.SortedArray.prototype.find=function(D){var B=0;
-var A=this._a.length;
-while(B<A){var C=Math.floor((B+A)/2);
-var E=D(this._a[C]);
-if(C==B){return E<0?B+1:B;
-}else{if(E<0){B=C;
-}else{A=C;
-}}}return B;
-};
-SimileAjax.SortedArray.prototype.getFirst=function(){return(this._a.length>0)?this._a[0]:null;
-};
-SimileAjax.SortedArray.prototype.getLast=function(){return(this._a.length>0)?this._a[this._a.length-1]:null;
-};
-SimileAjax.EventIndex=function(B){var A=this;
-this._unit=(B!=null)?B:SimileAjax.NativeDateUnit;
-this._events=new SimileAjax.SortedArray(function(D,C){return A._unit.compare(D.getStart(),C.getStart());
-});
-this._idToEvent={};
-this._indexed=true;
-};
-SimileAjax.EventIndex.prototype.getUnit=function(){return this._unit;
-};
-SimileAjax.EventIndex.prototype.getEvent=function(A){return this._idToEvent[A];
-};
-SimileAjax.EventIndex.prototype.add=function(A){this._events.add(A);
-this._idToEvent[A.getID()]=A;
-this._indexed=false;
-};
-SimileAjax.EventIndex.prototype.removeAll=function(){this._events.removeAll();
-this._idToEvent={};
-this._indexed=false;
-};
-SimileAjax.EventIndex.prototype.getCount=function(){return this._events.length();
-};
-SimileAjax.EventIndex.prototype.getIterator=function(A,B){if(!this._indexed){this._index();
-}return new SimileAjax.EventIndex._Iterator(this._events,A,B,this._unit);
-};
-SimileAjax.EventIndex.prototype.getReverseIterator=function(A,B){if(!this._indexed){this._index();
-}return new SimileAjax.EventIndex._ReverseIterator(this._events,A,B,this._unit);
-};
-SimileAjax.EventIndex.prototype.getAllIterator=function(){return new SimileAjax.EventIndex._AllIterator(this._events);
-};
-SimileAjax.EventIndex.prototype.getEarliestDate=function(){var A=this._events.getFirst();
-return(A==null)?null:A.getStart();
-};
-SimileAjax.EventIndex.prototype.getLatestDate=function(){var A=this._events.getLast();
-if(A==null){return null;
-}if(!this._indexed){this._index();
-}var C=A._earliestOverlapIndex;
-var B=this._events.elementAt(C).getEnd();
-for(var D=C+1;
-D<this._events.length();
-D++){B=this._unit.later(B,this._events.elementAt(D).getEnd());
-}return B;
-};
-SimileAjax.EventIndex.prototype._index=function(){var D=this._events.length();
-for(var E=0;
-E<D;
-E++){var C=this._events.elementAt(E);
-C._earliestOverlapIndex=E;
-}var G=1;
-for(var E=0;
-E<D;
-E++){var C=this._events.elementAt(E);
-var B=C.getEnd();
-G=Math.max(G,E+1);
-while(G<D){var A=this._events.elementAt(G);
-var F=A.getStart();
-if(this._unit.compare(F,B)<0){A._earliestOverlapIndex=E;
-G++;
-}else{break;
-}}}this._indexed=true;
-};
-SimileAjax.EventIndex._Iterator=function(B,A,D,C){this._events=B;
-this._startDate=A;
-this._endDate=D;
-this._unit=C;
-this._currentIndex=B.find(function(E){return C.compare(E.getStart(),A);
-});
-if(this._currentIndex-1>=0){this._currentIndex=this._events.elementAt(this._currentIndex-1)._earliestOverlapIndex;
-}this._currentIndex--;
-this._maxIndex=B.find(function(E){return C.compare(E.getStart(),D);
-});
-this._hasNext=false;
-this._next=null;
-this._findNext();
-};
-SimileAjax.EventIndex._Iterator.prototype={hasNext:function(){return this._hasNext;
-},next:function(){if(this._hasNext){var A=this._next;
-this._findNext();
-return A;
-}else{return null;
-}},_findNext:function(){var B=this._unit;
-while((++this._currentIndex)<this._maxIndex){var A=this._events.elementAt(this._currentIndex);
-if(B.compare(A.getStart(),this._endDate)<0&&B.compare(A.getEnd(),this._startDate)>0){this._next=A;
-this._hasNext=true;
-return ;
-}}this._next=null;
-this._hasNext=false;
-}};
-SimileAjax.EventIndex._ReverseIterator=function(B,A,D,C){this._events=B;
-this._startDate=A;
-this._endDate=D;
-this._unit=C;
-this._minIndex=B.find(function(E){return C.compare(E.getStart(),A);
-});
-if(this._minIndex-1>=0){this._minIndex=this._events.elementAt(this._minIndex-1)._earliestOverlapIndex;
-}this._maxIndex=B.find(function(E){return C.compare(E.getStart(),D);
-});
-this._currentIndex=this._maxIndex;
-this._hasNext=false;
-this._next=null;
-this._findNext();
-};
-SimileAjax.EventIndex._ReverseIterator.prototype={hasNext:function(){return this._hasNext;
-},next:function(){if(this._hasNext){var A=this._next;
-this._findNext();
-return A;
-}else{return null;
-}},_findNext:function(){var B=this._unit;
-while((--this._currentIndex)>=this._minIndex){var A=this._events.elementAt(this._currentIndex);
-if(B.compare(A.getStart(),this._endDate)<0&&B.compare(A.getEnd(),this._startDate)>0){this._next=A;
-this._hasNext=true;
-return ;
-}}this._next=null;
-this._hasNext=false;
-}};
-SimileAjax.EventIndex._AllIterator=function(A){this._events=A;
-this._index=0;
-};
-SimileAjax.EventIndex._AllIterator.prototype={hasNext:function(){return this._index<this._events.length();
-},next:function(){return this._index<this._events.length()?this._events.elementAt(this._index++):null;
-}};
-
-
-/* date-time.js */
-SimileAjax.DateTime=new Object();
-SimileAjax.DateTime.MILLISECOND=0;
-SimileAjax.DateTime.SECOND=1;
-SimileAjax.DateTime.MINUTE=2;
-SimileAjax.DateTime.HOUR=3;
-SimileAjax.DateTime.DAY=4;
-SimileAjax.DateTime.WEEK=5;
-SimileAjax.DateTime.MONTH=6;
-SimileAjax.DateTime.YEAR=7;
-SimileAjax.DateTime.DECADE=8;
-SimileAjax.DateTime.CENTURY=9;
-SimileAjax.DateTime.MILLENNIUM=10;
-SimileAjax.DateTime.EPOCH=-1;
-SimileAjax.DateTime.ERA=-2;
-SimileAjax.DateTime.gregorianUnitLengths=[];
-(function(){var B=SimileAjax.DateTime;
-var A=B.gregorianUnitLengths;
-A[B.MILLISECOND]=1;
-A[B.SECOND]=1000;
-A[B.MINUTE]=A[B.SECOND]*60;
-A[B.HOUR]=A[B.MINUTE]*60;
-A[B.DAY]=A[B.HOUR]*24;
-A[B.WEEK]=A[B.DAY]*7;
-A[B.MONTH]=A[B.DAY]*31;
-A[B.YEAR]=A[B.DAY]*365;
-A[B.DECADE]=A[B.YEAR]*10;
-A[B.CENTURY]=A[B.YEAR]*100;
-A[B.MILLENNIUM]=A[B.YEAR]*1000;
+            var year = parseInt(s);
+        }
+
+        var d = new Date(0);
+        d.setUTCFullYear(year);
+
+        return d;
+    }
+
+    try {
+        return new Date(Date.parse(s));
+    } catch (e) {
+        return null;
+    }
+};
+
+/**
+ * Rounds date objects down to the nearest interval or multiple of an interval.
+ * This method modifies the given date object, converting it to the given
+ * timezone if specified.
+ *
+ * @param {Date} date the date object to round
+ * @param {Number} intervalUnit a constant, integer index specifying an
+ *   interval, e.g. SimileAjax.DateTime.HOUR
+ * @param {Number} timeZone a timezone shift, given in hours
+ * @param {Number} multiple a multiple of the interval to round by
+ * @param {Number} firstDayOfWeek an integer specifying the first day of the
+ *   week, 0 corresponds to Sunday, 1 to Monday, etc.
+ */
+SimileAjax.DateTime.roundDownToInterval = function(date, intervalUnit, timeZone, multiple, firstDayOfWeek) {
+    var timeShift = timeZone *
+        SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.HOUR];
+
+    var date2 = new Date(date.getTime() + timeShift);
+    var clearInDay = function(d) {
+        d.setUTCMilliseconds(0);
+        d.setUTCSeconds(0);
+        d.setUTCMinutes(0);
+        d.setUTCHours(0);
+    };
+    var clearInYear = function(d) {
+        clearInDay(d);
+        d.setUTCDate(1);
+        d.setUTCMonth(0);
+    };
+
+    switch(intervalUnit) {
+    case SimileAjax.DateTime.MILLISECOND:
+        var x = date2.getUTCMilliseconds();
+        date2.setUTCMilliseconds(x - (x % multiple));
+        break;
+    case SimileAjax.DateTime.SECOND:
+        date2.setUTCMilliseconds(0);
+
+        var x = date2.getUTCSeconds();
+        date2.setUTCSeconds(x - (x % multiple));
+        break;
+    case SimileAjax.DateTime.MINUTE:
+        date2.setUTCMilliseconds(0);
+        date2.setUTCSeconds(0);
+
+        var x = date2.getUTCMinutes();
+        date2.setTime(date2.getTime() -
+            (x % multiple) * SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.MINUTE]);
+        break;
+    case SimileAjax.DateTime.HOUR:
+        date2.setUTCMilliseconds(0);
+        date2.setUTCSeconds(0);
+        date2.setUTCMinutes(0);
+
+        var x = date2.getUTCHours();
+        date2.setUTCHours(x - (x % multiple));
+        break;
+    case SimileAjax.DateTime.DAY:
+        clearInDay(date2);
+        break;
+    case SimileAjax.DateTime.WEEK:
+        clearInDay(date2);
+        var d = (date2.getUTCDay() + 7 - firstDayOfWeek) % 7;
+        date2.setTime(date2.getTime() -
+            d * SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.DAY]);
+        break;
+    case SimileAjax.DateTime.MONTH:
+        clearInDay(date2);
+        date2.setUTCDate(1);
+
+        var x = date2.getUTCMonth();
+        date2.setUTCMonth(x - (x % multiple));
+        break;
+    case SimileAjax.DateTime.YEAR:
+        clearInYear(date2);
+
+        var x = date2.getUTCFullYear();
+        date2.setUTCFullYear(x - (x % multiple));
+        break;
+    case SimileAjax.DateTime.DECADE:
+        clearInYear(date2);
+        date2.setUTCFullYear(Math.floor(date2.getUTCFullYear() / 10) * 10);
+        break;
+    case SimileAjax.DateTime.CENTURY:
+        clearInYear(date2);
+        date2.setUTCFullYear(Math.floor(date2.getUTCFullYear() / 100) * 100);
+        break;
+    case SimileAjax.DateTime.MILLENNIUM:
+        clearInYear(date2);
+        date2.setUTCFullYear(Math.floor(date2.getUTCFullYear() / 1000) * 1000);
+        break;
+    }
+
+    date.setTime(date2.getTime() - timeShift);
+};
+
+/**
+ * Rounds date objects up to the nearest interval or multiple of an interval.
+ * This method modifies the given date object, converting it to the given
+ * timezone if specified.
+ *
+ * @param {Date} date the date object to round
+ * @param {Number} intervalUnit a constant, integer index specifying an
+ *   interval, e.g. SimileAjax.DateTime.HOUR
+ * @param {Number} timeZone a timezone shift, given in hours
+ * @param {Number} multiple a multiple of the interval to round by
+ * @param {Number} firstDayOfWeek an integer specifying the first day of the
+ *   week, 0 corresponds to Sunday, 1 to Monday, etc.
+ * @see SimileAjax.DateTime.roundDownToInterval
+ */
+SimileAjax.DateTime.roundUpToInterval = function(date, intervalUnit, timeZone, multiple, firstDayOfWeek) {
+    var originalTime = date.getTime();
+    SimileAjax.DateTime.roundDownToInterval(date, intervalUnit, timeZone, multiple, firstDayOfWeek);
+    if (date.getTime() < originalTime) {
+        date.setTime(date.getTime() +
+            SimileAjax.DateTime.gregorianUnitLengths[intervalUnit] * multiple);
+    }
+};
+
+/**
+ * Increments a date object by a specified interval, taking into
+ * consideration the timezone.
+ *
+ * @param {Date} date the date object to increment
+ * @param {Number} intervalUnit a constant, integer index specifying an
+ *   interval, e.g. SimileAjax.DateTime.HOUR
+ * @param {Number} timeZone the timezone offset in hours
+ */
+SimileAjax.DateTime.incrementByInterval = function(date, intervalUnit, timeZone) {
+    timeZone = (typeof timeZone == 'undefined') ? 0 : timeZone;
+
+    var timeShift = timeZone *
+        SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.HOUR];
+
+    var date2 = new Date(date.getTime() + timeShift);
+
+    switch(intervalUnit) {
+    case SimileAjax.DateTime.MILLISECOND:
+        date2.setTime(date2.getTime() + 1)
+        break;
+    case SimileAjax.DateTime.SECOND:
+        date2.setTime(date2.getTime() + 1000);
+        break;
+    case SimileAjax.DateTime.MINUTE:
+        date2.setTime(date2.getTime() +
+            SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.MINUTE]);
+        break;
+    case SimileAjax.DateTime.HOUR:
+        date2.setTime(date2.getTime() +
+            SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.HOUR]);
+        break;
+    case SimileAjax.DateTime.DAY:
+        date2.setUTCDate(date2.getUTCDate() + 1);
+        break;
+    case SimileAjax.DateTime.WEEK:
+        date2.setUTCDate(date2.getUTCDate() + 7);
+        break;
+    case SimileAjax.DateTime.MONTH:
+        date2.setUTCMonth(date2.getUTCMonth() + 1);
+        break;
+    case SimileAjax.DateTime.YEAR:
+        date2.setUTCFullYear(date2.getUTCFullYear() + 1);
+        break;
+    case SimileAjax.DateTime.DECADE:
+        date2.setUTCFullYear(date2.getUTCFullYear() + 10);
+        break;
+    case SimileAjax.DateTime.CENTURY:
+        date2.setUTCFullYear(date2.getUTCFullYear() + 100);
+        break;
+    case SimileAjax.DateTime.MILLENNIUM:
+        date2.setUTCFullYear(date2.getUTCFullYear() + 1000);
+        break;
+    }
+
+    date.setTime(date2.getTime() - timeShift);
+};
+
+/**
+ * Returns a new date object with the given time offset removed.
+ *
+ * @param {Date} date the starting date
+ * @param {Number} timeZone a timezone specified in an hour offset to remove
+ * @return {Date} a new date object with the offset removed
+ */
+SimileAjax.DateTime.removeTimeZoneOffset = function(date, timeZone) {
+    return new Date(date.getTime() +
+        timeZone * SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.HOUR]);
+};
+
+/**
+ * Returns the timezone of the user's browser.
+ *
+ * @return {Number} the timezone in the user's locale in hours
+ */
+SimileAjax.DateTime.getTimezone = function() {
+    var d = new Date().getTimezoneOffset();
+    return d / -60;
+};
+/*==================================================
+ *  String Utility Functions and Constants
+ *==================================================
+ */
+
+String.prototype.trim = function() {
+    return this.replace(/^\s+|\s+$/g, '');
+};
+
+String.prototype.startsWith = function(prefix) {
+    return this.length >= prefix.length && this.substr(0, prefix.length) == prefix;
+};
+
+String.prototype.endsWith = function(suffix) {
+    return this.length >= suffix.length && this.substr(this.length - suffix.length) == suffix;
+};
+
+String.substitute = function(s, objects) {
+    var result = "";
+    var start = 0;
+    while (start < s.length - 1) {
+        var percent = s.indexOf("%", start);
+        if (percent < 0 || percent == s.length - 1) {
+            break;
+        } else if (percent > start && s.charAt(percent - 1) == "\\") {
+            result += s.substring(start, percent - 1) + "%";
+            start = percent + 1;
+        } else {
+            var n = parseInt(s.charAt(percent + 1));
+            if (isNaN(n) || n >= objects.length) {
+                result += s.substring(start, percent + 2);
+            } else {
+                result += s.substring(start, percent) + objects[n].toString();
+            }
+            start = percent + 2;
+        }
+    }
+
+    if (start < s.length) {
+        result += s.substring(start);
+    }
+    return result;
+};
+/*==================================================
+ *  HTML Utility Functions
+ *==================================================
+ */
+
+SimileAjax.HTML = new Object();
+
+SimileAjax.HTML._e2uHash = {};
+(function() {
+    var e2uHash = SimileAjax.HTML._e2uHash;
+    e2uHash['nbsp']= '\u00A0[space]';
+    e2uHash['iexcl']= '\u00A1';
+    e2uHash['cent']= '\u00A2';
+    e2uHash['pound']= '\u00A3';
+    e2uHash['curren']= '\u00A4';
+    e2uHash['yen']= '\u00A5';
+    e2uHash['brvbar']= '\u00A6';
+    e2uHash['sect']= '\u00A7';
+    e2uHash['uml']= '\u00A8';
+    e2uHash['copy']= '\u00A9';
+    e2uHash['ordf']= '\u00AA';
+    e2uHash['laquo']= '\u00AB';
+    e2uHash['not']= '\u00AC';
+    e2uHash['shy']= '\u00AD';
+    e2uHash['reg']= '\u00AE';
+    e2uHash['macr']= '\u00AF';
+    e2uHash['deg']= '\u00B0';
+    e2uHash['plusmn']= '\u00B1';
+    e2uHash['sup2']= '\u00B2';
+    e2uHash['sup3']= '\u00B3';
+    e2uHash['acute']= '\u00B4';
+    e2uHash['micro']= '\u00B5';
+    e2uHash['para']= '\u00B6';
+    e2uHash['middot']= '\u00B7';
+    e2uHash['cedil']= '\u00B8';
+    e2uHash['sup1']= '\u00B9';
+    e2uHash['ordm']= '\u00BA';
+    e2uHash['raquo']= '\u00BB';
+    e2uHash['frac14']= '\u00BC';
+    e2uHash['frac12']= '\u00BD';
+    e2uHash['frac34']= '\u00BE';
+    e2uHash['iquest']= '\u00BF';
+    e2uHash['Agrave']= '\u00C0';
+    e2uHash['Aacute']= '\u00C1';
+    e2uHash['Acirc']= '\u00C2';
+    e2uHash['Atilde']= '\u00C3';
+    e2uHash['Auml']= '\u00C4';
+    e2uHash['Aring']= '\u00C5';
+    e2uHash['AElig']= '\u00C6';
+    e2uHash['Ccedil']= '\u00C7';
+    e2uHash['Egrave']= '\u00C8';
+    e2uHash['Eacute']= '\u00C9';
+    e2uHash['Ecirc']= '\u00CA';
+    e2uHash['Euml']= '\u00CB';
+    e2uHash['Igrave']= '\u00CC';
+    e2uHash['Iacute']= '\u00CD';
+    e2uHash['Icirc']= '\u00CE';
+    e2uHash['Iuml']= '\u00CF';
+    e2uHash['ETH']= '\u00D0';
+    e2uHash['Ntilde']= '\u00D1';
+    e2uHash['Ograve']= '\u00D2';
+    e2uHash['Oacute']= '\u00D3';
+    e2uHash['Ocirc']= '\u00D4';
+    e2uHash['Otilde']= '\u00D5';
+    e2uHash['Ouml']= '\u00D6';
+    e2uHash['times']= '\u00D7';
+    e2uHash['Oslash']= '\u00D8';
+    e2uHash['Ugrave']= '\u00D9';
+    e2uHash['Uacute']= '\u00DA';
+    e2uHash['Ucirc']= '\u00DB';
+    e2uHash['Uuml']= '\u00DC';
+    e2uHash['Yacute']= '\u00DD';
+    e2uHash['THORN']= '\u00DE';
+    e2uHash['szlig']= '\u00DF';
+    e2uHash['agrave']= '\u00E0';
+    e2uHash['aacute']= '\u00E1';
+    e2uHash['acirc']= '\u00E2';
+    e2uHash['atilde']= '\u00E3';
+    e2uHash['auml']= '\u00E4';
+    e2uHash['aring']= '\u00E5';
+    e2uHash['aelig']= '\u00E6';
+    e2uHash['ccedil']= '\u00E7';
+    e2uHash['egrave']= '\u00E8';
+    e2uHash['eacute']= '\u00E9';
+    e2uHash['ecirc']= '\u00EA';
+    e2uHash['euml']= '\u00EB';
+    e2uHash['igrave']= '\u00EC';
+    e2uHash['iacute']= '\u00ED';
+    e2uHash['icirc']= '\u00EE';
+    e2uHash['iuml']= '\u00EF';
+    e2uHash['eth']= '\u00F0';
+    e2uHash['ntilde']= '\u00F1';
+    e2uHash['ograve']= '\u00F2';
+    e2uHash['oacute']= '\u00F3';
+    e2uHash['ocirc']= '\u00F4';
+    e2uHash['otilde']= '\u00F5';
+    e2uHash['ouml']= '\u00F6';
+    e2uHash['divide']= '\u00F7';
+    e2uHash['oslash']= '\u00F8';
+    e2uHash['ugrave']= '\u00F9';
+    e2uHash['uacute']= '\u00FA';
+    e2uHash['ucirc']= '\u00FB';
+    e2uHash['uuml']= '\u00FC';
+    e2uHash['yacute']= '\u00FD';
+    e2uHash['thorn']= '\u00FE';
+    e2uHash['yuml']= '\u00FF';
+    e2uHash['quot']= '\u0022';
+    e2uHash['amp']= '\u0026';
+    e2uHash['lt']= '\u003C';
+    e2uHash['gt']= '\u003E';
+    e2uHash['OElig']= '';
+    e2uHash['oelig']= '\u0153';
+    e2uHash['Scaron']= '\u0160';
+    e2uHash['scaron']= '\u0161';
+    e2uHash['Yuml']= '\u0178';
+    e2uHash['circ']= '\u02C6';
+    e2uHash['tilde']= '\u02DC';
+    e2uHash['ensp']= '\u2002';
+    e2uHash['emsp']= '\u2003';
+    e2uHash['thinsp']= '\u2009';
+    e2uHash['zwnj']= '\u200C';
+    e2uHash['zwj']= '\u200D';
+    e2uHash['lrm']= '\u200E';
+    e2uHash['rlm']= '\u200F';
+    e2uHash['ndash']= '\u2013';
+    e2uHash['mdash']= '\u2014';
+    e2uHash['lsquo']= '\u2018';
+    e2uHash['rsquo']= '\u2019';
+    e2uHash['sbquo']= '\u201A';
+    e2uHash['ldquo']= '\u201C';
+    e2uHash['rdquo']= '\u201D';
+    e2uHash['bdquo']= '\u201E';
+    e2uHash['dagger']= '\u2020';
+    e2uHash['Dagger']= '\u2021';
+    e2uHash['permil']= '\u2030';
+    e2uHash['lsaquo']= '\u2039';
+    e2uHash['rsaquo']= '\u203A';
+    e2uHash['euro']= '\u20AC';
+    e2uHash['fnof']= '\u0192';
+    e2uHash['Alpha']= '\u0391';
+    e2uHash['Beta']= '\u0392';
+    e2uHash['Gamma']= '\u0393';
+    e2uHash['Delta']= '\u0394';
+    e2uHash['Epsilon']= '\u0395';
+    e2uHash['Zeta']= '\u0396';
+    e2uHash['Eta']= '\u0397';
+    e2uHash['Theta']= '\u0398';
+    e2uHash['Iota']= '\u0399';
+    e2uHash['Kappa']= '\u039A';
+    e2uHash['Lambda']= '\u039B';
+    e2uHash['Mu']= '\u039C';
+    e2uHash['Nu']= '\u039D';
+    e2uHash['Xi']= '\u039E';
+    e2uHash['Omicron']= '\u039F';
+    e2uHash['Pi']= '\u03A0';
+    e2uHash['Rho']= '\u03A1';
+    e2uHash['Sigma']= '\u03A3';
+    e2uHash['Tau']= '\u03A4';
+    e2uHash['Upsilon']= '\u03A5';
+    e2uHash['Phi']= '\u03A6';
+    e2uHash['Chi']= '\u03A7';
+    e2uHash['Psi']= '\u03A8';
+    e2uHash['Omega']= '\u03A9';
+    e2uHash['alpha']= '\u03B1';
+    e2uHash['beta']= '\u03B2';
+    e2uHash['gamma']= '\u03B3';
+    e2uHash['delta']= '\u03B4';
+    e2uHash['epsilon']= '\u03B5';
+    e2uHash['zeta']= '\u03B6';
+    e2uHash['eta']= '\u03B7';
+    e2uHash['theta']= '\u03B8';
+    e2uHash['iota']= '\u03B9';
+    e2uHash['kappa']= '\u03BA';
+    e2uHash['lambda']= '\u03BB';
+    e2uHash['mu']= '\u03BC';
+    e2uHash['nu']= '\u03BD';
+    e2uHash['xi']= '\u03BE';
+    e2uHash['omicron']= '\u03BF';
+    e2uHash['pi']= '\u03C0';
+    e2uHash['rho']= '\u03C1';
+    e2uHash['sigmaf']= '\u03C2';
+    e2uHash['sigma']= '\u03C3';
+    e2uHash['tau']= '\u03C4';
+    e2uHash['upsilon']= '\u03C5';
+    e2uHash['phi']= '\u03C6';
+    e2uHash['chi']= '\u03C7';
+    e2uHash['psi']= '\u03C8';
+    e2uHash['omega']= '\u03C9';
+    e2uHash['thetasym']= '\u03D1';
+    e2uHash['upsih']= '\u03D2';
+    e2uHash['piv']= '\u03D6';
+    e2uHash['bull']= '\u2022';
+    e2uHash['hellip']= '\u2026';
+    e2uHash['prime']= '\u2032';
+    e2uHash['Prime']= '\u2033';
+    e2uHash['oline']= '\u203E';
+    e2uHash['frasl']= '\u2044';
+    e2uHash['weierp']= '\u2118';
+    e2uHash['image']= '\u2111';
+    e2uHash['real']= '\u211C';
+    e2uHash['trade']= '\u2122';
+    e2uHash['alefsym']= '\u2135';
+    e2uHash['larr']= '\u2190';
+    e2uHash['uarr']= '\u2191';
+    e2uHash['rarr']= '\u2192';
+    e2uHash['darr']= '\u2193';
+    e2uHash['harr']= '\u2194';
+    e2uHash['crarr']= '\u21B5';
+    e2uHash['lArr']= '\u21D0';
+    e2uHash['uArr']= '\u21D1';
+    e2uHash['rArr']= '\u21D2';
+    e2uHash['dArr']= '\u21D3';
+    e2uHash['hArr']= '\u21D4';
+    e2uHash['forall']= '\u2200';
+    e2uHash['part']= '\u2202';
+    e2uHash['exist']= '\u2203';
+    e2uHash['empty']= '\u2205';
+    e2uHash['nabla']= '\u2207';
+    e2uHash['isin']= '\u2208';
+    e2uHash['notin']= '\u2209';
+    e2uHash['ni']= '\u220B';
+    e2uHash['prod']= '\u220F';
+    e2uHash['sum']= '\u2211';
+    e2uHash['minus']= '\u2212';
+    e2uHash['lowast']= '\u2217';
+    e2uHash['radic']= '\u221A';
+    e2uHash['prop']= '\u221D';
+    e2uHash['infin']= '\u221E';
+    e2uHash['ang']= '\u2220';
+    e2uHash['and']= '\u2227';
+    e2uHash['or']= '\u2228';
+    e2uHash['cap']= '\u2229';
+    e2uHash['cup']= '\u222A';
+    e2uHash['int']= '\u222B';
+    e2uHash['there4']= '\u2234';
+    e2uHash['sim']= '\u223C';
+    e2uHash['cong']= '\u2245';
+    e2uHash['asymp']= '\u2248';
+    e2uHash['ne']= '\u2260';
+    e2uHash['equiv']= '\u2261';
+    e2uHash['le']= '\u2264';
+    e2uHash['ge']= '\u2265';
+    e2uHash['sub']= '\u2282';
+    e2uHash['sup']= '\u2283';
+    e2uHash['nsub']= '\u2284';
+    e2uHash['sube']= '\u2286';
+    e2uHash['supe']= '\u2287';
+    e2uHash['oplus']= '\u2295';
+    e2uHash['otimes']= '\u2297';
+    e2uHash['perp']= '\u22A5';
+    e2uHash['sdot']= '\u22C5';
+    e2uHash['lceil']= '\u2308';
+    e2uHash['rceil']= '\u2309';
+    e2uHash['lfloor']= '\u230A';
+    e2uHash['rfloor']= '\u230B';
+    e2uHash['lang']= '\u2329';
+    e2uHash['rang']= '\u232A';
+    e2uHash['loz']= '\u25CA';
+    e2uHash['spades']= '\u2660';
+    e2uHash['clubs']= '\u2663';
+    e2uHash['hearts']= '\u2665';
+    e2uHash['diams']= '\u2666';
 })();
-SimileAjax.DateTime._dateRegexp=new RegExp("^(-?)([0-9]{4})("+["(-?([0-9]{2})(-?([0-9]{2}))?)","(-?([0-9]{3}))","(-?W([0-9]{2})(-?([1-7]))?)"].join("|")+")?$");
-SimileAjax.DateTime._timezoneRegexp=new RegExp("Z|(([-+])([0-9]{2})(:?([0-9]{2}))?)$");
-SimileAjax.DateTime._timeRegexp=new RegExp("^([0-9]{2})(:?([0-9]{2})(:?([0-9]{2})(.([0-9]+))?)?)?$");
-SimileAjax.DateTime.setIso8601Date=function(H,F){var I=F.match(SimileAjax.DateTime._dateRegexp);
-if(!I){throw new Error("Invalid date string: "+F);
-}var B=(I[1]=="-")?-1:1;
-var J=B*I[2];
-var G=I[5];
-var C=I[7];
-var E=I[9];
-var A=I[11];
-var M=(I[13])?I[13]:1;
-H.setUTCFullYear(J);
-if(E){H.setUTCMonth(0);
-H.setUTCDate(Number(E));
-}else{if(A){H.setUTCMonth(0);
-H.setUTCDate(1);
-var L=H.getUTCDay();
-var K=(L)?L:7;
-var D=Number(M)+(7*Number(A));
-if(K<=4){H.setUTCDate(D+1-K);
-}else{H.setUTCDate(D+8-K);
-}}else{if(G){H.setUTCDate(1);
-H.setUTCMonth(G-1);
-}if(C){H.setUTCDate(C);
-}}}return H;
-};
-SimileAjax.DateTime.setIso8601Time=function(F,C){var G=C.match(SimileAjax.DateTime._timeRegexp);
-if(!G){SimileAjax.Debug.warn("Invalid time string: "+C);
-return false;
-}var A=G[1];
-var E=Number((G[3])?G[3]:0);
-var D=(G[5])?G[5]:0;
-var B=G[7]?(Number("0."+G[7])*1000):0;
-F.setUTCHours(A);
-F.setUTCMinutes(E);
-F.setUTCSeconds(D);
-F.setUTCMilliseconds(B);
-return F;
-};
-SimileAjax.DateTime.timezoneOffset=new Date().getTimezoneOffset();
-SimileAjax.DateTime.setIso8601=function(B,A){var D=null;
-var E=(A.indexOf("T")==-1)?A.split(" "):A.split("T");
-SimileAjax.DateTime.setIso8601Date(B,E[0]);
-if(E.length==2){var C=E[1].match(SimileAjax.DateTime._timezoneRegexp);
-if(C){if(C[0]=="Z"){D=0;
-}else{D=(Number(C[3])*60)+Number(C[5]);
-D*=((C[2]=="-")?1:-1);
-}E[1]=E[1].substr(0,E[1].length-C[0].length);
-}SimileAjax.DateTime.setIso8601Time(B,E[1]);
-}if(D==null){D=B.getTimezoneOffset();
-}B.setTime(B.getTime()+D*60000);
-return B;
-};
-SimileAjax.DateTime.parseIso8601DateTime=function(A){try{return SimileAjax.DateTime.setIso8601(new Date(0),A);
-}catch(B){return null;
-}};
-SimileAjax.DateTime.parseGregorianDateTime=function(G){if(G==null){return null;
-}else{if(G instanceof Date){return G;
-}}var B=G.toString();
-if(B.length>0&&B.length<8){var C=B.indexOf(" ");
-if(C>0){var A=parseInt(B.substr(0,C));
-var E=B.substr(C+1);
-if(E.toLowerCase()=="bc"){A=1-A;
-}}else{var A=parseInt(B);
-}var F=new Date(0);
-F.setUTCFullYear(A);
-return F;
-}try{return new Date(Date.parse(B));
-}catch(D){return null;
-}};
-SimileAjax.DateTime.roundDownToInterval=function(B,G,J,K,A){var D=J*SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.HOUR];
-var I=new Date(B.getTime()+D);
-var E=function(L){L.setUTCMilliseconds(0);
-L.setUTCSeconds(0);
-L.setUTCMinutes(0);
-L.setUTCHours(0);
-};
-var C=function(L){E(L);
-L.setUTCDate(1);
-L.setUTCMonth(0);
-};
-switch(G){case SimileAjax.DateTime.MILLISECOND:var H=I.getUTCMilliseconds();
-I.setUTCMilliseconds(H-(H%K));
-break;
-case SimileAjax.DateTime.SECOND:I.setUTCMilliseconds(0);
-var H=I.getUTCSeconds();
-I.setUTCSeconds(H-(H%K));
-break;
-case SimileAjax.DateTime.MINUTE:I.setUTCMilliseconds(0);
-I.setUTCSeconds(0);
-var H=I.getUTCMinutes();
-I.setTime(I.getTime()-(H%K)*SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.MINUTE]);
-break;
-case SimileAjax.DateTime.HOUR:I.setUTCMilliseconds(0);
-I.setUTCSeconds(0);
-I.setUTCMinutes(0);
-var H=I.getUTCHours();
-I.setUTCHours(H-(H%K));
-break;
-case SimileAjax.DateTime.DAY:E(I);
-break;
-case SimileAjax.DateTime.WEEK:E(I);
-var F=(I.getUTCDay()+7-A)%7;
-I.setTime(I.getTime()-F*SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.DAY]);
-break;
-case SimileAjax.DateTime.MONTH:E(I);
-I.setUTCDate(1);
-var H=I.getUTCMonth();
-I.setUTCMonth(H-(H%K));
-break;
-case SimileAjax.DateTime.YEAR:C(I);
-var H=I.getUTCFullYear();
-I.setUTCFullYear(H-(H%K));
-break;
-case SimileAjax.DateTime.DECADE:C(I);
-I.setUTCFullYear(Math.floor(I.getUTCFullYear()/10)*10);
-break;
-case SimileAjax.DateTime.CENTURY:C(I);
-I.setUTCFullYear(Math.floor(I.getUTCFullYear()/100)*100);
-break;
-case SimileAjax.DateTime.MILLENNIUM:C(I);
-I.setUTCFullYear(Math.floor(I.getUTCFullYear()/1000)*1000);
-break;
-}B.setTime(I.getTime()-D);
-};
-SimileAjax.DateTime.roundUpToInterval=function(D,F,C,A,B){var E=D.getTime();
-SimileAjax.DateTime.roundDownToInterval(D,F,C,A,B);
-if(D.getTime()<E){D.setTime(D.getTime()+SimileAjax.DateTime.gregorianUnitLengths[F]*A);
-}};
-SimileAjax.DateTime.incrementByInterval=function(B,E,A){A=(typeof A=="undefined")?0:A;
-var D=A*SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.HOUR];
-var C=new Date(B.getTime()+D);
-switch(E){case SimileAjax.DateTime.MILLISECOND:C.setTime(C.getTime()+1);
-break;
-case SimileAjax.DateTime.SECOND:C.setTime(C.getTime()+1000);
-break;
-case SimileAjax.DateTime.MINUTE:C.setTime(C.getTime()+SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.MINUTE]);
-break;
-case SimileAjax.DateTime.HOUR:C.setTime(C.getTime()+SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.HOUR]);
-break;
-case SimileAjax.DateTime.DAY:C.setUTCDate(C.getUTCDate()+1);
-break;
-case SimileAjax.DateTime.WEEK:C.setUTCDate(C.getUTCDate()+7);
-break;
-case SimileAjax.DateTime.MONTH:C.setUTCMonth(C.getUTCMonth()+1);
-break;
-case SimileAjax.DateTime.YEAR:C.setUTCFullYear(C.getUTCFullYear()+1);
-break;
-case SimileAjax.DateTime.DECADE:C.setUTCFullYear(C.getUTCFullYear()+10);
-break;
-case SimileAjax.DateTime.CENTURY:C.setUTCFullYear(C.getUTCFullYear()+100);
-break;
-case SimileAjax.DateTime.MILLENNIUM:C.setUTCFullYear(C.getUTCFullYear()+1000);
-break;
-}B.setTime(C.getTime()-D);
-};
-SimileAjax.DateTime.removeTimeZoneOffset=function(B,A){return new Date(B.getTime()+A*SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.HOUR]);
-};
-SimileAjax.DateTime.getTimezone=function(){var A=new Date().getTimezoneOffset();
-return A/-60;
-};
-
-
-/* debug.js */
-SimileAjax.Debug={silent:false};
-SimileAjax.Debug.log=function(B){var A;
-if("console" in window&&"log" in window.console){A=function(C){console.log(C);
-};
-}else{A=function(C){if(!SimileAjax.Debug.silent){alert(C);
-}};
-}SimileAjax.Debug.log=A;
-A(B);
-};
-SimileAjax.Debug.warn=function(B){var A;
-if("console" in window&&"warn" in window.console){A=function(C){console.warn(C);
-};
-}else{A=function(C){if(!SimileAjax.Debug.silent){alert(C);
-}};
-}SimileAjax.Debug.warn=A;
-A(B);
-};
-SimileAjax.Debug.exception=function(B,D){var A,C=SimileAjax.parseURLParameters();
-if(C.errors=="throw"||SimileAjax.params.errors=="throw"){A=function(F,E){throw (F);
-};
-}else{if("console" in window&&"error" in window.console){A=function(F,E){if(E!=null){console.error(E+" %o",F);
-}else{console.error(F);
-}throw (F);
-};
-}else{A=function(F,E){if(!SimileAjax.Debug.silent){alert("Caught exception: "+E+"\n\nDetails: "+("description" in F?F.description:F));
-}throw (F);
-};
-}}SimileAjax.Debug.exception=A;
-A(B,D);
-};
-SimileAjax.Debug.objectToString=function(A){return SimileAjax.Debug._objectToString(A,"");
-};
-SimileAjax.Debug._objectToString=function(D,A){var C=A+" ";
-if(typeof D=="object"){var B="{";
-for(E in D){B+=C+E+": "+SimileAjax.Debug._objectToString(D[E],C)+"\n";
-}B+=A+"}";
-return B;
-}else{if(typeof D=="array"){var B="[";
-for(var E=0;
-E<D.length;
-E++){B+=SimileAjax.Debug._objectToString(D[E],C)+"\n";
-}B+=A+"]";
-return B;
-}else{return D;
-}}};
-
-
-/* dom.js */
-SimileAjax.DOM=new Object();
-SimileAjax.DOM.registerEventWithObject=function(C,A,D,B){SimileAjax.DOM.registerEvent(C,A,function(F,E,G){return D[B].call(D,F,E,G);
-});
-};
-SimileAjax.DOM.registerEvent=function(C,B,D){var A=function(E){E=(E)?E:((event)?event:null);
-if(E){var F=(E.target)?E.target:((E.srcElement)?E.srcElement:null);
-if(F){F=(F.nodeType==1||F.nodeType==9)?F:F.parentNode;
-}return D(C,E,F);
-}return true;
-};
-if(SimileAjax.Platform.browser.isIE){C.attachEvent("on"+B,A);
-}else{C.addEventListener(B,A,false);
-}};
-SimileAjax.DOM.getPageCoordinates=function(B){var E=0;
-var D=0;
-if(B.nodeType!=1){B=B.parentNode;
-}var C=B;
-while(C!=null){E+=C.offsetLeft;
-D+=C.offsetTop;
-C=C.offsetParent;
-}var A=document.body;
-while(B!=null&&B!=A){if("scrollLeft" in B){E-=B.scrollLeft;
-D-=B.scrollTop;
-}B=B.parentNode;
-}return{left:E,top:D};
-};
-SimileAjax.DOM.getSize=function(B){var A=this.getStyle(B,"width");
-var C=this.getStyle(B,"height");
-if(A.indexOf("px")>-1){A=A.replace("px","");
-}if(C.indexOf("px")>-1){C=C.replace("px","");
-}return{w:A,h:C};
-};
-SimileAjax.DOM.getStyle=function(B,A){if(B.currentStyle){var C=B.currentStyle[A];
-}else{if(window.getComputedStyle){var C=document.defaultView.getComputedStyle(B,null).getPropertyValue(A);
-}else{var C="";
-}}return C;
-};
-SimileAjax.DOM.getEventRelativeCoordinates=function(A,B){if(SimileAjax.Platform.browser.isIE){if(A.type=="mousewheel"){var C=SimileAjax.DOM.getPageCoordinates(B);
-return{x:A.clientX-C.left,y:A.clientY-C.top};
-}else{return{x:A.offsetX,y:A.offsetY};
-}}else{var C=SimileAjax.DOM.getPageCoordinates(B);
-if((A.type=="DOMMouseScroll")&&SimileAjax.Platform.browser.isFirefox&&(SimileAjax.Platform.browser.majorVersion==2)){return{x:A.screenX-C.left,y:A.screenY-C.top};
-}else{return{x:A.pageX-C.left,y:A.pageY-C.top};
-}}};
-SimileAjax.DOM.getEventPageCoordinates=function(A){if(SimileAjax.Platform.browser.isIE){return{x:A.clientX+document.body.scrollLeft,y:A.clientY+document.body.scrollTop};
-}else{return{x:A.pageX,y:A.pageY};
-}};
-SimileAjax.DOM.hittest=function(A,C,B){return SimileAjax.DOM._hittest(document.body,A,C,B);
-};
-SimileAjax.DOM._hittest=function(C,L,K,H){var M=C.childNodes;
-outer:for(var G=0;
-G<M.length;
-G++){var A=M[G];
-for(var F=0;
-F<H.length;
-F++){if(A==H[F]){continue outer;
-}}if(A.offsetWidth==0&&A.offsetHeight==0){var B=SimileAjax.DOM._hittest(A,L,K,H);
-if(B!=A){return B;
-}}else{var J=0;
-var E=0;
-var D=A;
-while(D){J+=D.offsetTop;
-E+=D.offsetLeft;
-D=D.offsetParent;
-}if(E<=L&&J<=K&&(L-E)<A.offsetWidth&&(K-J)<A.offsetHeight){return SimileAjax.DOM._hittest(A,L,K,H);
-}else{if(A.nodeType==1&&A.tagName=="TR"){var I=SimileAjax.DOM._hittest(A,L,K,H);
-if(I!=A){return I;
-}}}}}return C;
-};
-SimileAjax.DOM.cancelEvent=function(A){A.returnValue=false;
-A.cancelBubble=true;
-if("preventDefault" in A){A.preventDefault();
-}};
-SimileAjax.DOM.appendClassName=function(C,D){var B=C.className.split(" ");
-for(var A=0;
-A<B.length;
-A++){if(B[A]==D){return ;
-}}B.push(D);
-C.className=B.join(" ");
-};
-SimileAjax.DOM.createInputElement=function(A){var B=document.createElement("div");
-B.innerHTML="<input type='"+A+"' />";
-return B.firstChild;
-};
-SimileAjax.DOM.createDOMFromTemplate=function(B){var A={};
-A.elmt=SimileAjax.DOM._createDOMFromTemplate(B,A,null);
-return A;
-};
-SimileAjax.DOM._createDOMFromTemplate=function(A,I,E){if(A==null){return null;
-}else{if(typeof A!="object"){var D=document.createTextNode(A);
-if(E!=null){E.appendChild(D);
-}return D;
-}else{var C=null;
-if("tag" in A){var J=A.tag;
-if(E!=null){if(J=="tr"){C=E.insertRow(E.rows.length);
-}else{if(J=="td"){C=E.insertCell(E.cells.length);
-}}}if(C==null){C=J=="input"?SimileAjax.DOM.createInputElement(A.type):document.createElement(J);
-if(E!=null){E.appendChild(C);
-}}}else{C=A.elmt;
-if(E!=null){E.appendChild(C);
-}}for(var B in A){var G=A[B];
-if(B=="field"){I[G]=C;
-}else{if(B=="className"){C.className=G;
-}else{if(B=="id"){C.id=G;
-}else{if(B=="title"){C.title=G;
-}else{if(B=="type"&&C.tagName=="input"){}else{if(B=="style"){for(n in G){var H=G[n];
-if(n=="float"){n=SimileAjax.Platform.browser.isIE?"styleFloat":"cssFloat";
-}C.style[n]=H;
-}}else{if(B=="children"){for(var F=0;
-F<G.length;
-F++){SimileAjax.DOM._createDOMFromTemplate(G[F],I,C);
-}}else{if(B!="tag"&&B!="elmt"){C.setAttribute(B,G);
-}}}}}}}}}return C;
-}}};
-SimileAjax.DOM._cachedParent=null;
-SimileAjax.DOM.createElementFromString=function(A){if(SimileAjax.DOM._cachedParent==null){SimileAjax.DOM._cachedParent=document.createElement("div");
-}SimileAjax.DOM._cachedParent.innerHTML=A;
-return SimileAjax.DOM._cachedParent.firstChild;
-};
-SimileAjax.DOM.createDOMFromString=function(A,C,D){var B=typeof A=="string"?document.createElement(A):A;
-B.innerHTML=C;
-var E={elmt:B};
-SimileAjax.DOM._processDOMChildrenConstructedFromString(E,B,D!=null?D:{});
-return E;
-};
-SimileAjax.DOM._processDOMConstructedFromString=function(D,A,B){var E=A.id;
-if(E!=null&&E.length>0){A.removeAttribute("id");
-if(E in B){var C=A.parentNode;
-C.insertBefore(B[E],A);
-C.removeChild(A);
-D[E]=B[E];
-return ;
-}else{D[E]=A;
-}}if(A.hasChildNodes()){SimileAjax.DOM._processDOMChildrenConstructedFromString(D,A,B);
-}};
-SimileAjax.DOM._processDOMChildrenConstructedFromString=function(E,B,D){var C=B.firstChild;
-while(C!=null){var A=C.nextSibling;
-if(C.nodeType==1){SimileAjax.DOM._processDOMConstructedFromString(E,C,D);
-}C=A;
-}};
-
-
-/* graphics.js */
-SimileAjax.Graphics=new Object();
-SimileAjax.Graphics.pngIsTranslucent=(!SimileAjax.Platform.browser.isIE)||(SimileAjax.Platform.browser.majorVersion>6);
-SimileAjax.Graphics._createTranslucentImage1=function(A,C){var B=document.createElement("img");
-B.setAttribute("src",A);
-if(C!=null){B.style.verticalAlign=C;
-}return B;
-};
-SimileAjax.Graphics._createTranslucentImage2=function(A,C){var B=document.createElement("img");
-B.style.width="1px";
-B.style.height="1px";
-B.style.filter="progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+A+"', sizingMethod='image')";
-B.style.verticalAlign=(C!=null)?C:"middle";
-return B;
-};
-SimileAjax.Graphics.createTranslucentImage=SimileAjax.Graphics.pngIsTranslucent?SimileAjax.Graphics._createTranslucentImage1:SimileAjax.Graphics._createTranslucentImage2;
-SimileAjax.Graphics._createTranslucentImageHTML1=function(A,B){return'<img src="'+A+'"'+(B!=null?' style="vertical-align: '+B+';"':"")+" />";
-};
-SimileAjax.Graphics._createTranslucentImageHTML2=function(A,C){var B="width: 1px; height: 1px; filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+A+"', sizingMethod='image');"+(C!=null?" vertical-align: "+C+";":"");
-return"<img src='"+A+"' style=\""+B+'" />';
-};
-SimileAjax.Graphics.createTranslucentImageHTML=SimileAjax.Graphics.pngIsTranslucent?SimileAjax.Graphics._createTranslucentImageHTML1:SimileAjax.Graphics._createTranslucentImageHTML2;
-SimileAjax.Graphics.setOpacity=function(B,A){if(SimileAjax.Platform.browser.isIE){B.style.filter="progid:DXImageTransform.Microsoft.Alpha(Style=0,Opacity="+A+")";
-}else{var C=(A/100).toString();
-B.style.opacity=C;
-B.style.MozOpacity=C;
-}};
-SimileAjax.Graphics._bubbleMargins={top:33,bottom:42,left:33,right:40};
-SimileAjax.Graphics._arrowOffsets={top:0,bottom:9,left:1,right:8};
-SimileAjax.Graphics._bubblePadding=15;
-SimileAjax.Graphics._bubblePointOffset=6;
-SimileAjax.Graphics._halfArrowWidth=18;
-SimileAjax.Graphics.createBubbleForContentAndPoint=function(F,D,C,A,B,E){if(typeof A!="number"){A=300;
-}if(typeof E!="number"){E=0;
-}F.style.position="absolute";
-F.style.left="-5000px";
-F.style.top="0px";
-F.style.width=A+"px";
-document.body.appendChild(F);
-window.setTimeout(function(){var J=F.scrollWidth+10;
-var G=F.scrollHeight+10;
-var I=0;
-if(E>0&&G>E){G=E;
-I=J-25;
-}var H=SimileAjax.Graphics.createBubbleForPoint(D,C,J,G,B);
-document.body.removeChild(F);
-F.style.position="static";
-F.style.left="";
-F.style.top="";
-if(I>0){var K=document.createElement("div");
-F.style.width="";
-K.style.width=I+"px";
-K.appendChild(F);
-H.content.appendChild(K);
-}else{F.style.width=J+"px";
-H.content.appendChild(F);
-}},200);
-};
-SimileAjax.Graphics.createBubbleForPoint=function(C,B,N,R,F){function T(){if(typeof window.innerHeight=="number"){return{w:window.innerWidth,h:window.innerHeight};
-}else{if(document.documentElement&&document.documentElement.clientHeight){return{w:document.documentElement.clientWidth,h:document.documentElement.clientHeight};
-}else{if(document.body&&document.body.clientHeight){return{w:document.body.clientWidth,h:document.body.clientHeight};
-}}}}var L=function(){if(!M._closed){document.body.removeChild(M._div);
-M._doc=null;
-M._div=null;
-M._content=null;
-M._closed=true;
-}};
-var M={_closed:false};
-var O=T();
-var H=O.w;
-var G=O.h;
-var D=SimileAjax.Graphics._bubbleMargins;
-N=parseInt(N,10);
-R=parseInt(R,10);
-var P=D.left+N+D.right;
-var U=D.top+R+D.bottom;
-var Q=SimileAjax.Graphics.pngIsTranslucent;
-var J=SimileAjax.urlPrefix;
-var A=function(Z,Y,a,X){Z.style.position="absolute";
-Z.style.width=a+"px";
-Z.style.height=X+"px";
-if(Q){Z.style.background="url("+Y+")";
-}else{Z.style.filter="progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+Y+"', sizingMethod='crop')";
-}};
-var K=document.createElement("div");
-K.style.width=P+"px";
-K.style.height=U+"px";
-K.style.position="absolute";
-K.style.zIndex=1000;
-var W=SimileAjax.WindowManager.pushLayer(L,true,K);
-M._div=K;
-M.close=function(){SimileAjax.WindowManager.popLayer(W);
-};
-var I=document.createElement("div");
-I.style.width="100%";
-I.style.height="100%";
-I.style.position="relative";
-K.appendChild(I);
-var S=function(Z,c,b,a,Y){var X=document.createElement("div");
-X.style.left=c+"px";
-X.style.top=b+"px";
-A(X,Z,a,Y);
-I.appendChild(X);
-};
-S(J+"data/timeline/bubble-top-left.png",0,0,D.left,D.top);
-S(J+"data/timeline/bubble-top.png",D.left,0,N,D.top);
-S(J+"data/timeline/bubble-top-right.png",D.left+N,0,D.right,D.top);
-S(J+"data/timeline/bubble-left.png",0,D.top,D.left,R);
-S(J+"data/timeline/bubble-right.png",D.left+N,D.top,D.right,R);
-S(J+"data/timeline/bubble-bottom-left.png",0,D.top+R,D.left,D.bottom);
-S(J+"data/timeline/bubble-bottom.png",D.left,D.top+R,N,D.bottom);
-S(J+"data/timeline/bubble-bottom-right.png",D.left+N,D.top+R,D.right,D.bottom);
-var V=document.createElement("div");
-V.style.left=(P-D.right+SimileAjax.Graphics._bubblePadding-16-2)+"px";
-V.style.top=(D.top-SimileAjax.Graphics._bubblePadding+1)+"px";
-V.style.cursor="pointer";
-A(V,J+"data/timeline/close-button.png",16,16);
-SimileAjax.WindowManager.registerEventWithObject(V,"click",M,"close");
-I.appendChild(V);
-var E=document.createElement("div");
-E.style.position="absolute";
-E.style.left=D.left+"px";
-E.style.top=D.top+"px";
-E.style.width=N+"px";
-E.style.height=R+"px";
-E.style.overflow="auto";
-E.style.background="white";
-I.appendChild(E);
-M.content=E;
-(function(){if(C-SimileAjax.Graphics._halfArrowWidth-SimileAjax.Graphics._bubblePadding>0&&C+SimileAjax.Graphics._halfArrowWidth+SimileAjax.Graphics._bubblePadding<H){var Z=C-Math.round(N/2)-D.left;
-Z=C<(H/2)?Math.max(Z,-(D.left-SimileAjax.Graphics._bubblePadding)):Math.min(Z,H+(D.right-SimileAjax.Graphics._bubblePadding)-P);
-if((F&&F=="top")||(!F&&(B-SimileAjax.Graphics._bubblePointOffset-U>0))){var X=document.createElement("div");
-X.style.left=(C-SimileAjax.Graphics._halfArrowWidth-Z)+"px";
-X.style.top=(D.top+R)+"px";
-A(X,J+"data/timeline/bubble-bottom-arrow.png",37,D.bottom);
-I.appendChild(X);
-K.style.left=Z+"px";
-K.style.top=(B-SimileAjax.Graphics._bubblePointOffset-U+SimileAjax.Graphics._arrowOffsets.bottom)+"px";
-return ;
-}else{if((F&&F=="bottom")||(!F&&(B+SimileAjax.Graphics._bubblePointOffset+U<G))){var X=document.createElement("div");
-X.style.left=(C-SimileAjax.Graphics._halfArrowWidth-Z)+"px";
-X.style.top="0px";
-A(X,J+"data/timeline/bubble-top-arrow.png",37,D.top);
-I.appendChild(X);
-K.style.left=Z+"px";
-K.style.top=(B+SimileAjax.Graphics._bubblePointOffset-SimileAjax.Graphics._arrowOffsets.top)+"px";
-return ;
-}}}var Y=B-Math.round(R/2)-D.top;
-Y=B<(G/2)?Math.max(Y,-(D.top-SimileAjax.Graphics._bubblePadding)):Math.min(Y,G+(D.bottom-SimileAjax.Graphics._bubblePadding)-U);
-if((F&&F=="left")||(!F&&(C-SimileAjax.Graphics._bubblePointOffset-P>0))){var X=document.createElement("div");
-X.style.left=(D.left+N)+"px";
-X.style.top=(B-SimileAjax.Graphics._halfArrowWidth-Y)+"px";
-A(X,J+"data/timeline/bubble-right-arrow.png",D.right,37);
-I.appendChild(X);
-K.style.left=(C-SimileAjax.Graphics._bubblePointOffset-P+SimileAjax.Graphics._arrowOffsets.right)+"px";
-K.style.top=Y+"px";
-}else{if((F&&F=="right")||(!F&&(C-SimileAjax.Graphics._bubblePointOffset-P<H))){var X=document.createElement("div");
-X.style.left="0px";
-X.style.top=(B-SimileAjax.Graphics._halfArrowWidth-Y)+"px";
-A(X,J+"data/timeline/bubble-left-arrow.png",D.left,37);
-I.appendChild(X);
-K.style.left=(C+SimileAjax.Graphics._bubblePointOffset-SimileAjax.Graphics._arrowOffsets.left)+"px";
-K.style.top=Y+"px";
-}}})();
-document.body.appendChild(K);
-return M;
-};
-SimileAjax.Graphics.createMessageBubble=function(H){var G=H.createElement("div");
-if(SimileAjax.Graphics.pngIsTranslucent){var I=H.createElement("div");
-I.style.height="33px";
-I.style.background="url("+SimileAjax.urlPrefix+"data/timeline/message-top-left.png) top left no-repeat";
-I.style.paddingLeft="44px";
-G.appendChild(I);
-var C=H.createElement("div");
-C.style.height="33px";
-C.style.background="url("+SimileAjax.urlPrefix+"data/timeline/message-top-right.png) top right no-repeat";
-I.appendChild(C);
-var F=H.createElement("div");
-F.style.background="url("+SimileAjax.urlPrefix+"data/timeline/message-left.png) top left repeat-y";
-F.style.paddingLeft="44px";
-G.appendChild(F);
-var A=H.createElement("div");
-A.style.background="url("+SimileAjax.urlPrefix+"data/timeline/message-right.png) top right repeat-y";
-A.style.paddingRight="44px";
-F.appendChild(A);
-var D=H.createElement("div");
-A.appendChild(D);
-var B=H.createElement("div");
-B.style.height="55px";
-B.style.background="url("+SimileAjax.urlPrefix+"data/timeline/message-bottom-left.png) bottom left no-repeat";
-B.style.paddingLeft="44px";
-G.appendChild(B);
-var E=H.createElement("div");
-E.style.height="55px";
-E.style.background="url("+SimileAjax.urlPrefix+"data/timeline/message-bottom-right.png) bottom right no-repeat";
-B.appendChild(E);
-}else{G.style.border="2px solid #7777AA";
-G.style.padding="20px";
-G.style.background="white";
-SimileAjax.Graphics.setOpacity(G,90);
-var D=H.createElement("div");
-G.appendChild(D);
-}return{containerDiv:G,contentDiv:D};
-};
-SimileAjax.Graphics.createAnimation=function(B,E,D,C,A){return new SimileAjax.Graphics._Animation(B,E,D,C,A);
-};
-SimileAjax.Graphics._Animation=function(B,E,D,C,A){this.f=B;
-this.cont=(typeof A=="function")?A:function(){};
-this.from=E;
-this.to=D;
-this.current=E;
-this.duration=C;
-this.start=new Date().getTime();
-this.timePassed=0;
-};
-SimileAjax.Graphics._Animation.prototype.run=function(){var A=this;
-window.setTimeout(function(){A.step();
-},50);
-};
-SimileAjax.Graphics._Animation.prototype.step=function(){this.timePassed+=50;
-var B=this.timePassed/this.duration;
-var A=-Math.cos(B*Math.PI)/2+0.5;
-var D=A*(this.to-this.from)+this.from;
-try{this.f(D,D-this.current);
-}catch(C){}this.current=D;
-if(this.timePassed<this.duration){this.run();
-}else{this.f(this.to,0);
-this["cont"]();
-}};
-SimileAjax.Graphics.createStructuredDataCopyButton=function(F,D,A,E){var G=document.createElement("div");
-G.style.position="relative";
-G.style.display="inline";
-G.style.width=D+"px";
-G.style.height=A+"px";
-G.style.overflow="hidden";
-G.style.margin="2px";
-if(SimileAjax.Graphics.pngIsTranslucent){G.style.background="url("+F+") no-repeat";
-}else{G.style.filter="progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+F+"', sizingMethod='image')";
-}var C;
-if(SimileAjax.Platform.browser.isIE){C="filter:alpha(opacity=0)";
-}else{C="opacity: 0";
-}G.innerHTML="<textarea rows='1' autocomplete='off' value='none' style='"+C+"' />";
-var B=G.firstChild;
-B.style.width=D+"px";
-B.style.height=A+"px";
-B.onmousedown=function(H){H=(H)?H:((event)?event:null);
-if(H.button==2){B.value=E();
-B.select();
-}};
-return G;
-};
-SimileAjax.Graphics.getWidthHeight=function(C){var A,B;
-if(C.getBoundingClientRect==null){A=C.offsetWidth;
-B=C.offsetHeight;
-}else{var D=C.getBoundingClientRect();
-A=Math.ceil(D.right-D.left);
-B=Math.ceil(D.bottom-D.top);
-}return{width:A,height:B};
-};
-SimileAjax.Graphics.getFontRenderingContext=function(A,B){return new SimileAjax.Graphics._FontRenderingContext(A,B);
-};
-SimileAjax.Graphics._FontRenderingContext=function(A,B){this._elmt=A;
-this._elmt.style.visibility="hidden";
-if(typeof B=="string"){this._elmt.style.width=B;
-}else{if(typeof B=="number"){this._elmt.style.width=B+"px";
-}}};
-SimileAjax.Graphics._FontRenderingContext.prototype.dispose=function(){this._elmt=null;
-};
-SimileAjax.Graphics._FontRenderingContext.prototype.update=function(){this._elmt.innerHTML="A";
-this._lineHeight=this._elmt.offsetHeight;
-};
-SimileAjax.Graphics._FontRenderingContext.prototype.computeSize=function(D,C){var B=this._elmt;
-B.innerHTML=D;
-B.className=C===undefined?"":C;
-var A=SimileAjax.Graphics.getWidthHeight(B);
-B.className="";
-return A;
-};
-SimileAjax.Graphics._FontRenderingContext.prototype.getLineHeight=function(){return this._lineHeight;
-};
-
-
-/* history.js */
-SimileAjax.History={maxHistoryLength:10,historyFile:"__history__.html",enabled:true,_initialized:false,_listeners:new SimileAjax.ListenerQueue(),_actions:[],_baseIndex:0,_currentIndex:0,_plainDocumentTitle:document.title};
-SimileAjax.History.formatHistoryEntryTitle=function(A){return SimileAjax.History._plainDocumentTitle+" {"+A+"}";
-};
-SimileAjax.History.initialize=function(){if(SimileAjax.History._initialized){return ;
-}if(SimileAjax.History.enabled){var A=document.createElement("iframe");
-A.id="simile-ajax-history";
-A.style.position="absolute";
-A.style.width="10px";
-A.style.height="10px";
-A.style.top="0px";
-A.style.left="0px";
-A.style.visibility="hidden";
-A.src=SimileAjax.History.historyFile+"?0";
-document.body.appendChild(A);
-SimileAjax.DOM.registerEvent(A,"load",SimileAjax.History._handleIFrameOnLoad);
-SimileAjax.History._iframe=A;
-}SimileAjax.History._initialized=true;
-};
-SimileAjax.History.addListener=function(A){SimileAjax.History.initialize();
-SimileAjax.History._listeners.add(A);
-};
-SimileAjax.History.removeListener=function(A){SimileAjax.History.initialize();
-SimileAjax.History._listeners.remove(A);
-};
-SimileAjax.History.addAction=function(A){SimileAjax.History.initialize();
-SimileAjax.History._listeners.fire("onBeforePerform",[A]);
-window.setTimeout(function(){try{A.perform();
-SimileAjax.History._listeners.fire("onAfterPerform",[A]);
-if(SimileAjax.History.enabled){SimileAjax.History._actions=SimileAjax.History._actions.slice(0,SimileAjax.History._currentIndex-SimileAjax.History._baseIndex);
-SimileAjax.History._actions.push(A);
-SimileAjax.History._currentIndex++;
-var C=SimileAjax.History._actions.length-SimileAjax.History.maxHistoryLength;
-if(C>0){SimileAjax.History._actions=SimileAjax.History._actions.slice(C);
-SimileAjax.History._baseIndex+=C;
-}try{SimileAjax.History._iframe.contentWindow.location.search="?"+SimileAjax.History._currentIndex;
-}catch(B){var D=SimileAjax.History.formatHistoryEntryTitle(A.label);
-document.title=D;
-}}}catch(B){SimileAjax.Debug.exception(B,"Error adding action {"+A.label+"} to history");
-}},0);
-};
-SimileAjax.History.addLengthyAction=function(C,A,B){SimileAjax.History.addAction({perform:C,undo:A,label:B,uiLayer:SimileAjax.WindowManager.getBaseLayer(),lengthy:true});
-};
-SimileAjax.History._handleIFrameOnLoad=function(){try{var B=SimileAjax.History._iframe.contentWindow.location.search;
-var F=(B.length==0)?0:Math.max(0,parseInt(B.substr(1)));
-var E=function(){var G=F-SimileAjax.History._currentIndex;
-SimileAjax.History._currentIndex+=G;
-SimileAjax.History._baseIndex+=G;
-SimileAjax.History._iframe.contentWindow.location.search="?"+F;
-};
-if(F<SimileAjax.History._currentIndex){SimileAjax.History._listeners.fire("onBeforeUndoSeveral",[]);
-window.setTimeout(function(){while(SimileAjax.History._currentIndex>F&&SimileAjax.History._currentIndex>SimileAjax.History._baseIndex){SimileAjax.History._currentIndex--;
-var G=SimileAjax.History._actions[SimileAjax.History._currentIndex-SimileAjax.History._baseIndex];
-try{G.undo();
-}catch(H){SimileAjax.Debug.exception(H,"History: Failed to undo action {"+G.label+"}");
-}}SimileAjax.History._listeners.fire("onAfterUndoSeveral",[]);
-E();
-},0);
-}else{if(F>SimileAjax.History._currentIndex){SimileAjax.History._listeners.fire("onBeforeRedoSeveral",[]);
-window.setTimeout(function(){while(SimileAjax.History._currentIndex<F&&SimileAjax.History._currentIndex-SimileAjax.History._baseIndex<SimileAjax.History._actions.length){var G=SimileAjax.History._actions[SimileAjax.History._currentIndex-SimileAjax.History._baseIndex];
-try{G.perform();
-}catch(H){SimileAjax.Debug.exception(H,"History: Failed to redo action {"+G.label+"}");
-}SimileAjax.History._currentIndex++;
-}SimileAjax.History._listeners.fire("onAfterRedoSeveral",[]);
-E();
-},0);
-}else{var A=SimileAjax.History._currentIndex-SimileAjax.History._baseIndex-1;
-var D=(A>=0&&A<SimileAjax.History._actions.length)?SimileAjax.History.formatHistoryEntryTitle(SimileAjax.History._actions[A].label):SimileAjax.History._plainDocumentTitle;
-SimileAjax.History._iframe.contentWindow.document.title=D;
-document.title=D;
-}}}catch(C){}};
-SimileAjax.History.getNextUndoAction=function(){try{var A=SimileAjax.History._currentIndex-SimileAjax.History._baseIndex-1;
-return SimileAjax.History._actions[A];
-}catch(B){return null;
-}};
-SimileAjax.History.getNextRedoAction=function(){try{var A=SimileAjax.History._currentIndex-SimileAjax.History._baseIndex;
-return SimileAjax.History._actions[A];
-}catch(B){return null;
-}};
-
-
-/* html.js */
-SimileAjax.HTML=new Object();
-SimileAjax.HTML._e2uHash={};
-(function(){var A=SimileAjax.HTML._e2uHash;
-A["nbsp"]="\u00A0[space]";
-A["iexcl"]="\u00A1";
-A["cent"]="\u00A2";
-A["pound"]="\u00A3";
-A["curren"]="\u00A4";
-A["yen"]="\u00A5";
-A["brvbar"]="\u00A6";
-A["sect"]="\u00A7";
-A["uml"]="\u00A8";
-A["copy"]="\u00A9";
-A["ordf"]="\u00AA";
-A["laquo"]="\u00AB";
-A["not"]="\u00AC";
-A["shy"]="\u00AD";
-A["reg"]="\u00AE";
-A["macr"]="\u00AF";
-A["deg"]="\u00B0";
-A["plusmn"]="\u00B1";
-A["sup2"]="\u00B2";
-A["sup3"]="\u00B3";
-A["acute"]="\u00B4";
-A["micro"]="\u00B5";
-A["para"]="\u00B6";
-A["middot"]="\u00B7";
-A["cedil"]="\u00B8";
-A["sup1"]="\u00B9";
-A["ordm"]="\u00BA";
-A["raquo"]="\u00BB";
-A["frac14"]="\u00BC";
-A["frac12"]="\u00BD";
-A["frac34"]="\u00BE";
-A["iquest"]="\u00BF";
-A["Agrave"]="\u00C0";
-A["Aacute"]="\u00C1";
-A["Acirc"]="\u00C2";
-A["Atilde"]="\u00C3";
-A["Auml"]="\u00C4";
-A["Aring"]="\u00C5";
-A["AElig"]="\u00C6";
-A["Ccedil"]="\u00C7";
-A["Egrave"]="\u00C8";
-A["Eacute"]="\u00C9";
-A["Ecirc"]="\u00CA";
-A["Euml"]="\u00CB";
-A["Igrave"]="\u00CC";
-A["Iacute"]="\u00CD";
-A["Icirc"]="\u00CE";
-A["Iuml"]="\u00CF";
-A["ETH"]="\u00D0";
-A["Ntilde"]="\u00D1";
-A["Ograve"]="\u00D2";
-A["Oacute"]="\u00D3";
-A["Ocirc"]="\u00D4";
-A["Otilde"]="\u00D5";
-A["Ouml"]="\u00D6";
-A["times"]="\u00D7";
-A["Oslash"]="\u00D8";
-A["Ugrave"]="\u00D9";
-A["Uacute"]="\u00DA";
-A["Ucirc"]="\u00DB";
-A["Uuml"]="\u00DC";
-A["Yacute"]="\u00DD";
-A["THORN"]="\u00DE";
-A["szlig"]="\u00DF";
-A["agrave"]="\u00E0";
-A["aacute"]="\u00E1";
-A["acirc"]="\u00E2";
-A["atilde"]="\u00E3";
-A["auml"]="\u00E4";
-A["aring"]="\u00E5";
-A["aelig"]="\u00E6";
-A["ccedil"]="\u00E7";
-A["egrave"]="\u00E8";
-A["eacute"]="\u00E9";
-A["ecirc"]="\u00EA";
-A["euml"]="\u00EB";
-A["igrave"]="\u00EC";
-A["iacute"]="\u00ED";
-A["icirc"]="\u00EE";
-A["iuml"]="\u00EF";
-A["eth"]="\u00F0";
-A["ntilde"]="\u00F1";
-A["ograve"]="\u00F2";
-A["oacute"]="\u00F3";
-A["ocirc"]="\u00F4";
-A["otilde"]="\u00F5";
-A["ouml"]="\u00F6";
-A["divide"]="\u00F7";
-A["oslash"]="\u00F8";
-A["ugrave"]="\u00F9";
-A["uacute"]="\u00FA";
-A["ucirc"]="\u00FB";
-A["uuml"]="\u00FC";
-A["yacute"]="\u00FD";
-A["thorn"]="\u00FE";
-A["yuml"]="\u00FF";
-A["quot"]="\u0022";
-A["amp"]="\u0026";
-A["lt"]="\u003C";
-A["gt"]="\u003E";
-A["OElig"]="";
-A["oelig"]="\u0153";
-A["Scaron"]="\u0160";
-A["scaron"]="\u0161";
-A["Yuml"]="\u0178";
-A["circ"]="\u02C6";
-A["tilde"]="\u02DC";
-A["ensp"]="\u2002";
-A["emsp"]="\u2003";
-A["thinsp"]="\u2009";
-A["zwnj"]="\u200C";
-A["zwj"]="\u200D";
-A["lrm"]="\u200E";
-A["rlm"]="\u200F";
-A["ndash"]="\u2013";
-A["mdash"]="\u2014";
-A["lsquo"]="\u2018";
-A["rsquo"]="\u2019";
-A["sbquo"]="\u201A";
-A["ldquo"]="\u201C";
-A["rdquo"]="\u201D";
-A["bdquo"]="\u201E";
-A["dagger"]="\u2020";
-A["Dagger"]="\u2021";
-A["permil"]="\u2030";
-A["lsaquo"]="\u2039";
-A["rsaquo"]="\u203A";
-A["euro"]="\u20AC";
-A["fnof"]="\u0192";
-A["Alpha"]="\u0391";
-A["Beta"]="\u0392";
-A["Gamma"]="\u0393";
-A["Delta"]="\u0394";
-A["Epsilon"]="\u0395";
-A["Zeta"]="\u0396";
-A["Eta"]="\u0397";
-A["Theta"]="\u0398";
-A["Iota"]="\u0399";
-A["Kappa"]="\u039A";
-A["Lambda"]="\u039B";
-A["Mu"]="\u039C";
-A["Nu"]="\u039D";
-A["Xi"]="\u039E";
-A["Omicron"]="\u039F";
-A["Pi"]="\u03A0";
-A["Rho"]="\u03A1";
-A["Sigma"]="\u03A3";
-A["Tau"]="\u03A4";
-A["Upsilon"]="\u03A5";
-A["Phi"]="\u03A6";
-A["Chi"]="\u03A7";
-A["Psi"]="\u03A8";
-A["Omega"]="\u03A9";
-A["alpha"]="\u03B1";
-A["beta"]="\u03B2";
-A["gamma"]="\u03B3";
-A["delta"]="\u03B4";
-A["epsilon"]="\u03B5";
-A["zeta"]="\u03B6";
-A["eta"]="\u03B7";
-A["theta"]="\u03B8";
-A["iota"]="\u03B9";
-A["kappa"]="\u03BA";
-A["lambda"]="\u03BB";
-A["mu"]="\u03BC";
-A["nu"]="\u03BD";
-A["xi"]="\u03BE";
-A["omicron"]="\u03BF";
-A["pi"]="\u03C0";
-A["rho"]="\u03C1";
-A["sigmaf"]="\u03C2";
-A["sigma"]="\u03C3";
-A["tau"]="\u03C4";
-A["upsilon"]="\u03C5";
-A["phi"]="\u03C6";
-A["chi"]="\u03C7";
-A["psi"]="\u03C8";
-A["omega"]="\u03C9";
-A["thetasym"]="\u03D1";
-A["upsih"]="\u03D2";
-A["piv"]="\u03D6";
-A["bull"]="\u2022";
-A["hellip"]="\u2026";
-A["prime"]="\u2032";
-A["Prime"]="\u2033";
-A["oline"]="\u203E";
-A["frasl"]="\u2044";
-A["weierp"]="\u2118";
-A["image"]="\u2111";
-A["real"]="\u211C";
-A["trade"]="\u2122";
-A["alefsym"]="\u2135";
-A["larr"]="\u2190";
-A["uarr"]="\u2191";
-A["rarr"]="\u2192";
-A["darr"]="\u2193";
-A["harr"]="\u2194";
-A["crarr"]="\u21B5";
-A["lArr"]="\u21D0";
-A["uArr"]="\u21D1";
-A["rArr"]="\u21D2";
-A["dArr"]="\u21D3";
-A["hArr"]="\u21D4";
-A["forall"]="\u2200";
-A["part"]="\u2202";
-A["exist"]="\u2203";
-A["empty"]="\u2205";
-A["nabla"]="\u2207";
-A["isin"]="\u2208";
-A["notin"]="\u2209";
-A["ni"]="\u220B";
-A["prod"]="\u220F";
-A["sum"]="\u2211";
-A["minus"]="\u2212";
-A["lowast"]="\u2217";
-A["radic"]="\u221A";
-A["prop"]="\u221D";
-A["infin"]="\u221E";
-A["ang"]="\u2220";
-A["and"]="\u2227";
-A["or"]="\u2228";
-A["cap"]="\u2229";
-A["cup"]="\u222A";
-A["int"]="\u222B";
-A["there4"]="\u2234";
-A["sim"]="\u223C";
-A["cong"]="\u2245";
-A["asymp"]="\u2248";
-A["ne"]="\u2260";
-A["equiv"]="\u2261";
-A["le"]="\u2264";
-A["ge"]="\u2265";
-A["sub"]="\u2282";
-A["sup"]="\u2283";
-A["nsub"]="\u2284";
-A["sube"]="\u2286";
-A["supe"]="\u2287";
-A["oplus"]="\u2295";
-A["otimes"]="\u2297";
-A["perp"]="\u22A5";
-A["sdot"]="\u22C5";
-A["lceil"]="\u2308";
-A["rceil"]="\u2309";
-A["lfloor"]="\u230A";
-A["rfloor"]="\u230B";
-A["lang"]="\u2329";
-A["rang"]="\u232A";
-A["loz"]="\u25CA";
-A["spades"]="\u2660";
-A["clubs"]="\u2663";
-A["hearts"]="\u2665";
-A["diams"]="\u2666";
-})();
-SimileAjax.HTML.deEntify=function(C){var D=SimileAjax.HTML._e2uHash;
-var B=/&(\w+?);/;
-while(B.test(C)){var A=C.match(B);
-C=C.replace(B,D[A[1]]);
-}return C;
-};
-
-
-/* json.js */
-SimileAjax.JSON=new Object();
-(function(){var m={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"};
-var s={array:function(x){var a=["["],b,f,i,l=x.length,v;
-for(i=0;
-i<l;
-i+=1){v=x[i];
-f=s[typeof v];
-if(f){v=f(v);
-if(typeof v=="string"){if(b){a[a.length]=",";
-}a[a.length]=v;
-b=true;
-}}}a[a.length]="]";
-return a.join("");
-},"boolean":function(x){return String(x);
-},"null":function(x){return"null";
-},number:function(x){return isFinite(x)?String(x):"null";
-},object:function(x){if(x){if(x instanceof Array){return s.array(x);
-}var a=["{"],b,f,i,v;
-for(i in x){v=x[i];
-f=s[typeof v];
-if(f){v=f(v);
-if(typeof v=="string"){if(b){a[a.length]=",";
-}a.push(s.string(i),":",v);
-b=true;
-}}}a[a.length]="}";
-return a.join("");
-}return"null";
-},string:function(x){if(/["\\\x00-\x1f]/.test(x)){x=x.replace(/([\x00-\x1f\\"])/g,function(a,b){var c=m[b];
-if(c){return c;
-}c=b.charCodeAt();
-return"\\u00"+Math.floor(c/16).toString(16)+(c%16).toString(16);
-});
-}return'"'+x+'"';
-}};
-SimileAjax.JSON.toJSONString=function(o){if(o instanceof Object){return s.object(o);
-}else{if(o instanceof Array){return s.array(o);
-}else{return o.toString();
-}}};
-SimileAjax.JSON.parseJSON=function(){try{return !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(this.replace(/"(\\.|[^"\\])*"/g,"")))&&eval("("+this+")");
-}catch(e){return false;
-}};
+
+SimileAjax.HTML.deEntify = function(s) {
+    var e2uHash = SimileAjax.HTML._e2uHash;
+
+    var re = /&(\w+?);/;
+    while (re.test(s)) {
+        var m = s.match(re);
+        s = s.replace(re, e2uHash[m[1]]);
+    }
+    return s;
+};/**
+ * A basic set (in the mathematical sense) data structure
+ *
+ * @constructor
+ * @param {Array or SimileAjax.Set} [a] an initial collection
+ */
+SimileAjax.Set = function(a) {
+    this._hash = {};
+    this._count = 0;
+
+    if (a instanceof Array) {
+        for (var i = 0; i < a.length; i++) {
+            this.add(a[i]);
+        }
+    } else if (a instanceof SimileAjax.Set) {
+        this.addSet(a);
+    }
+}
+
+/**
+ * Adds the given object to this set, assuming there it does not already exist
+ *
+ * @param {Object} o the object to add
+ * @return {Boolean} true if the object was added, false if not
+ */
+SimileAjax.Set.prototype.add = function(o) {
+    if (!(o in this._hash)) {
+        this._hash[o] = true;
+        this._count++;
+        return true;
+    }
+    return false;
+}
+
+/**
+ * Adds each element in the given set to this set
+ *
+ * @param {SimileAjax.Set} set the set of elements to add
+ */
+SimileAjax.Set.prototype.addSet = function(set) {
+    for (var o in set._hash) {
+        this.add(o);
+    }
+}
+
+/**
+ * Removes the given element from this set
+ *
+ * @param {Object} o the object to remove
+ * @return {Boolean} true if the object was successfully removed,
+ *   false otherwise
+ */
+SimileAjax.Set.prototype.remove = function(o) {
+    if (o in this._hash) {
+        delete this._hash[o];
+        this._count--;
+        return true;
+    }
+    return false;
+}
+
+/**
+ * Removes the elements in this set that correspond to the elements in the
+ * given set
+ *
+ * @param {SimileAjax.Set} set the set of elements to remove
+ */
+SimileAjax.Set.prototype.removeSet = function(set) {
+    for (var o in set._hash) {
+        this.remove(o);
+    }
+}
+
+/**
+ * Removes all elements in this set that are not present in the given set, i.e.
+ * modifies this set to the intersection of the two sets
+ *
+ * @param {SimileAjax.Set} set the set to intersect
+ */
+SimileAjax.Set.prototype.retainSet = function(set) {
+    for (var o in this._hash) {
+        if (!set.contains(o)) {
+            delete this._hash[o];
+            this._count--;
+        }
+    }
+}
+
+/**
+ * Returns whether or not the given element exists in this set
+ *
+ * @param {SimileAjax.Set} o the object to test for
+ * @return {Boolean} true if the object is present, false otherwise
+ */
+SimileAjax.Set.prototype.contains = function(o) {
+    return (o in this._hash);
+}
+
+/**
+ * Returns the number of elements in this set
+ *
+ * @return {Number} the number of elements in this set
+ */
+SimileAjax.Set.prototype.size = function() {
+    return this._count;
+}
+
+/**
+ * Returns the elements of this set as an array
+ *
+ * @return {Array} a new array containing the elements of this set
+ */
+SimileAjax.Set.prototype.toArray = function() {
+    var a = [];
+    for (var o in this._hash) {
+        a.push(o);
+    }
+    return a;
+}
+
+/**
+ * Iterates through the elements of this set, order unspecified, executing the
+ * given function on each element until the function returns true
+ *
+ * @param {Function} f a function of form f(element)
+ */
+SimileAjax.Set.prototype.visit = function(f) {
+    for (var o in this._hash) {
+        if (f(o) == true) {
+            break;
+        }
+    }
+}
+
+/**
+ * A sorted array data structure
+ *
+ * @constructor
+ */
+SimileAjax.SortedArray = function(compare, initialArray) {
+    this._a = (initialArray instanceof Array) ? initialArray : [];
+    this._compare = compare;
+};
+
+SimileAjax.SortedArray.prototype.add = function(elmt) {
+    var sa = this;
+    var index = this.find(function(elmt2) {
+        return sa._compare(elmt2, elmt);
+    });
+
+    if (index < this._a.length) {
+        this._a.splice(index, 0, elmt);
+    } else {
+        this._a.push(elmt);
+    }
+};
+
+SimileAjax.SortedArray.prototype.remove = function(elmt) {
+    var sa = this;
+    var index = this.find(function(elmt2) {
+        return sa._compare(elmt2, elmt);
+    });
+
+    while (index < this._a.length && this._compare(this._a[index], elmt) == 0) {
+        if (this._a[index] == elmt) {
+            this._a.splice(index, 1);
+            return true;
+        } else {
+            index++;
+        }
+    }
+    return false;
+};
+
+SimileAjax.SortedArray.prototype.removeAll = function() {
+    this._a = [];
+};
+
+SimileAjax.SortedArray.prototype.elementAt = function(index) {
+    return this._a[index];
+};
+
+SimileAjax.SortedArray.prototype.length = function() {
+    return this._a.length;
+};
+
+SimileAjax.SortedArray.prototype.find = function(compare) {
+    var a = 0;
+    var b = this._a.length;
+
+    while (a < b) {
+        var mid = Math.floor((a + b) / 2);
+        var c = compare(this._a[mid]);
+        if (mid == a) {
+            return c < 0 ? a+1 : a;
+        } else if (c < 0) {
+            a = mid;
+        } else {
+            b = mid;
+        }
+    }
+    return a;
+};
+
+SimileAjax.SortedArray.prototype.getFirst = function() {
+    return (this._a.length > 0) ? this._a[0] : null;
+};
+
+SimileAjax.SortedArray.prototype.getLast = function() {
+    return (this._a.length > 0) ? this._a[this._a.length - 1] : null;
+};
+
+/*==================================================
+ *  Event Index
+ *==================================================
+ */
+
+SimileAjax.EventIndex = function(unit) {
+    var eventIndex = this;
+
+    this._unit = (unit != null) ? unit : SimileAjax.NativeDateUnit;
+    this._events = new SimileAjax.SortedArray(
+        function(event1, event2) {
+            return eventIndex._unit.compare(event1.getStart(), event2.getStart());
+        }
+    );
+    this._idToEvent = {};
+    this._indexed = true;
+};
+
+SimileAjax.EventIndex.prototype.getUnit = function() {
+    return this._unit;
+};
+
+SimileAjax.EventIndex.prototype.getEvent = function(id) {
+    return this._idToEvent[id];
+};
+
+SimileAjax.EventIndex.prototype.add = function(evt) {
+    this._events.add(evt);
+    this._idToEvent[evt.getID()] = evt;
+    this._indexed = false;
+};
+
+SimileAjax.EventIndex.prototype.removeAll = function() {
+    this._events.removeAll();
+    this._idToEvent = {};
+    this._indexed = false;
+};
+
+SimileAjax.EventIndex.prototype.getCount = function() {
+    return this._events.length();
+};
+
+SimileAjax.EventIndex.prototype.getIterator = function(startDate, endDate) {
+    if (!this._indexed) {
+        this._index();
+    }
+    return new SimileAjax.EventIndex._Iterator(this._events, startDate, endDate, this._unit);
+};
+
+SimileAjax.EventIndex.prototype.getReverseIterator = function(startDate, endDate) {
+    if (!this._indexed) {
+        this._index();
+    }
+    return new SimileAjax.EventIndex._ReverseIterator(this._events, startDate, endDate, this._unit);
+};
+
+SimileAjax.EventIndex.prototype.getAllIterator = function() {
+    return new SimileAjax.EventIndex._AllIterator(this._events);
+};
+
+SimileAjax.EventIndex.prototype.getEarliestDate = function() {
+    var evt = this._events.getFirst();
+    return (evt == null) ? null : evt.getStart();
+};
+
+SimileAjax.EventIndex.prototype.getLatestDate = function() {
+    var evt = this._events.getLast();
+    if (evt == null) {
+        return null;
+    }
+
+    if (!this._indexed) {
+        this._index();
+    }
+
+    var index = evt._earliestOverlapIndex;
+    var date = this._events.elementAt(index).getEnd();
+    for (var i = index + 1; i < this._events.length(); i++) {
+        date = this._unit.later(date, this._events.elementAt(i).getEnd());
+    }
+
+    return date;
+};
+
+SimileAjax.EventIndex.prototype._index = function() {
+    /*
+     *  For each event, we want to find the earliest preceding
+     *  event that overlaps with it, if any.
+     */
+
+    var l = this._events.length();
+    for (var i = 0; i < l; i++) {
+        var evt = this._events.elementAt(i);
+        evt._earliestOverlapIndex = i;
+    }
+
+    var toIndex = 1;
+    for (var i = 0; i < l; i++) {
+        var evt = this._events.elementAt(i);
+        var end = evt.getEnd();
+
+        toIndex = Math.max(toIndex, i + 1);
+        while (toIndex < l) {
+            var evt2 = this._events.elementAt(toIndex);
+            var start2 = evt2.getStart();
+
+            if (this._unit.compare(start2, end) < 0) {
+                evt2._earliestOverlapIndex = i;
+                toIndex++;
+            } else {
+                break;
+            }
+        }
+    }
+    this._indexed = true;
+};
+
+SimileAjax.EventIndex._Iterator = function(events, startDate, endDate, unit) {
+    this._events = events;
+    this._startDate = startDate;
+    this._endDate = endDate;
+    this._unit = unit;
+
+    this._currentIndex = events.find(function(evt) {
+        return unit.compare(evt.getStart(), startDate);
+    });
+    if (this._currentIndex - 1 >= 0) {
+        this._currentIndex = this._events.elementAt(this._currentIndex - 1)._earliestOverlapIndex;
+    }
+    this._currentIndex--;
+
+    this._maxIndex = events.find(function(evt) {
+        return unit.compare(evt.getStart(), endDate);
+    });
+
+    this._hasNext = false;
+    this._next = null;
+    this._findNext();
+};
+
+SimileAjax.EventIndex._Iterator.prototype = {
+    hasNext: function() { return this._hasNext; },
+    next: function() {
+        if (this._hasNext) {
+            var next = this._next;
+            this._findNext();
+
+            return next;
+        } else {
+            return null;
+        }
+    },
+    _findNext: function() {
+        var unit = this._unit;
+        while ((++this._currentIndex) < this._maxIndex) {
+            var evt = this._events.elementAt(this._currentIndex);
+            if (unit.compare(evt.getStart(), this._endDate) < 0 &&
+                unit.compare(evt.getEnd(), this._startDate) > 0) {
+
+                this._next = evt;
+                this._hasNext = true;
+                return;
+            }
+        }
+        this._next = null;
+        this._hasNext = false;
+    }
+};
+
+SimileAjax.EventIndex._ReverseIterator = function(events, startDate, endDate, unit) {
+    this._events = events;
+    this._startDate = startDate;
+    this._endDate = endDate;
+    this._unit = unit;
+
+    this._minIndex = events.find(function(evt) {
+        return unit.compare(evt.getStart(), startDate);
+    });
+    if (this._minIndex - 1 >= 0) {
+        this._minIndex = this._events.elementAt(this._minIndex - 1)._earliestOverlapIndex;
+    }
+
+    this._maxIndex = events.find(function(evt) {
+        return unit.compare(evt.getStart(), endDate);
+    });
+
+    this._currentIndex = this._maxIndex;
+    this._hasNext = false;
+    this._next = null;
+    this._findNext();
+};
+
+SimileAjax.EventIndex._ReverseIterator.prototype = {
+    hasNext: function() { return this._hasNext; },
+    next: function() {
+        if (this._hasNext) {
+            var next = this._next;
+            this._findNext();
+
+            return next;
+        } else {
+            return null;
+        }
+    },
+    _findNext: function() {
+        var unit = this._unit;
+        while ((--this._currentIndex) >= this._minIndex) {
+            var evt = this._events.elementAt(this._currentIndex);
+            if (unit.compare(evt.getStart(), this._endDate) < 0 &&
+                unit.compare(evt.getEnd(), this._startDate) > 0) {
+
+                this._next = evt;
+                this._hasNext = true;
+                return;
+            }
+        }
+        this._next = null;
+        this._hasNext = false;
+    }
+};
+
+SimileAjax.EventIndex._AllIterator = function(events) {
+    this._events = events;
+    this._index = 0;
+};
+
+SimileAjax.EventIndex._AllIterator.prototype = {
+    hasNext: function() {
+        return this._index < this._events.length();
+    },
+    next: function() {
+        return this._index < this._events.length() ?
+            this._events.elementAt(this._index++) : null;
+    }
+};/*==================================================
+ *  Default Unit
+ *==================================================
+ */
+
+SimileAjax.NativeDateUnit = new Object();
+
+SimileAjax.NativeDateUnit.makeDefaultValue = function() {
+    return new Date();
+};
+
+SimileAjax.NativeDateUnit.cloneValue = function(v) {
+    return new Date(v.getTime());
+};
+
+SimileAjax.NativeDateUnit.getParser = function(format) {
+    if (typeof format == "string") {
+        format = format.toLowerCase();
+    }
+    return (format == "iso8601" || format == "iso 8601") ?
+        SimileAjax.DateTime.parseIso8601DateTime : 
+        SimileAjax.DateTime.parseGregorianDateTime;
+};
+
+SimileAjax.NativeDateUnit.parseFromObject = function(o) {
+    return SimileAjax.DateTime.parseGregorianDateTime(o);
+};
+
+SimileAjax.NativeDateUnit.toNumber = function(v) {
+    return v.getTime();
+};
+
+SimileAjax.NativeDateUnit.fromNumber = function(n) {
+    return new Date(n);
+};
+
+SimileAjax.NativeDateUnit.compare = function(v1, v2) {
+    var n1, n2;
+    if (typeof v1 == "object") {
+        n1 = v1.getTime();
+    } else {
+        n1 = Number(v1);
+    }
+    if (typeof v2 == "object") {
+        n2 = v2.getTime();
+    } else {
+        n2 = Number(v2);
+    }
+    
+    return n1 - n2;
+};
+
+SimileAjax.NativeDateUnit.earlier = function(v1, v2) {
+    return SimileAjax.NativeDateUnit.compare(v1, v2) < 0 ? v1 : v2;
+};
+
+SimileAjax.NativeDateUnit.later = function(v1, v2) {
+    return SimileAjax.NativeDateUnit.compare(v1, v2) > 0 ? v1 : v2;
+};
+
+SimileAjax.NativeDateUnit.change = function(v, n) {
+    return new Date(v.getTime() + n);
+};
+
+/*==================================================
+ *  General, miscellaneous SimileAjax stuff
+ *==================================================
+ */
+
+SimileAjax.ListenerQueue = function(wildcardHandlerName) {
+    this._listeners = [];
+    this._wildcardHandlerName = wildcardHandlerName;
+};
+
+SimileAjax.ListenerQueue.prototype.add = function(listener) {
+    this._listeners.push(listener);
+};
+
+SimileAjax.ListenerQueue.prototype.remove = function(listener) {
+    var listeners = this._listeners;
+    for (var i = 0; i < listeners.length; i++) {
+        if (listeners[i] == listener) {
+            listeners.splice(i, 1);
+            break;
+        }
+    }
+};
+
+SimileAjax.ListenerQueue.prototype.fire = function(handlerName, args) {
+    var listeners = [].concat(this._listeners);
+    for (var i = 0; i < listeners.length; i++) {
+        var listener = listeners[i];
+        if (handlerName in listener) {
+            try {
+                listener[handlerName].apply(listener, args);
+            } catch (e) {
+                SimileAjax.Debug.exception("Error firing event of name " + handlerName, e);
+            }
+        } else if (this._wildcardHandlerName != null &&
+            this._wildcardHandlerName in listener) {
+            try {
+                listener[this._wildcardHandlerName].apply(listener, [ handlerName ]);
+            } catch (e) {
+                SimileAjax.Debug.exception("Error firing event of name " + handlerName + " to wildcard handler", e);
+            }
+        }
+    }
+};
+
+/*======================================================================
+ *  History
+ *
+ *  This is a singleton that keeps track of undoable user actions and
+ *  performs undos and redos in response to the browser's Back and
+ *  Forward buttons.
+ *
+ *  Call addAction(action) to register an undoable user action. action
+ *  must have 4 fields:
+ *
+ *      perform: an argument-less function that carries out the action
+ *      undo:    an argument-less function that undos the action
+ *      label:   a short, user-friendly string describing the action
+ *      uiLayer: the UI layer on which the action takes place
+ *
+ *  By default, the history keeps track of upto 10 actions. You can
+ *  configure this behavior by setting
+ *      SimileAjax.History.maxHistoryLength
+ *  to a different number.
+ *
+ *  An iframe is inserted into the document's body element to track
+ *  onload events.
+ *======================================================================
+ */
+
+SimileAjax.History = {
+    maxHistoryLength:       10,
+    historyFile:            "__history__.html",
+    enabled:               true,
+
+    _initialized:           false,
+    _listeners:             new SimileAjax.ListenerQueue(),
+
+    _actions:               [],
+    _baseIndex:             0,
+    _currentIndex:          0,
+
+    _plainDocumentTitle:    document.title
+};
+
+SimileAjax.History.formatHistoryEntryTitle = function(actionLabel) {
+    return SimileAjax.History._plainDocumentTitle + " {" + actionLabel + "}";
+};
+
+SimileAjax.History.initialize = function() {
+    if (SimileAjax.History._initialized) {
+        return;
+    }
+
+    if (SimileAjax.History.enabled) {
+        var iframe = document.createElement("iframe");
+        iframe.id = "simile-ajax-history";
+        iframe.style.position = "absolute";
+        iframe.style.width = "10px";
+        iframe.style.height = "10px";
+        iframe.style.top = "0px";
+        iframe.style.left = "0px";
+        iframe.style.visibility = "hidden";
+        iframe.src = SimileAjax.History.historyFile + "?0";
+
+        document.body.appendChild(iframe);
+        SimileAjax.DOM.registerEvent(iframe, "load", SimileAjax.History._handleIFrameOnLoad);
+
+        SimileAjax.History._iframe = iframe;
+    }
+    SimileAjax.History._initialized = true;
+};
+
+SimileAjax.History.addListener = function(listener) {
+    SimileAjax.History.initialize();
+
+    SimileAjax.History._listeners.add(listener);
+};
+
+SimileAjax.History.removeListener = function(listener) {
+    SimileAjax.History.initialize();
+
+    SimileAjax.History._listeners.remove(listener);
+};
+
+SimileAjax.History.addAction = function(action) {
+    SimileAjax.History.initialize();
+
+    SimileAjax.History._listeners.fire("onBeforePerform", [ action ]);
+    window.setTimeout(function() {
+        try {
+            action.perform();
+            SimileAjax.History._listeners.fire("onAfterPerform", [ action ]);
+
+            if (SimileAjax.History.enabled) {
+                SimileAjax.History._actions = SimileAjax.History._actions.slice(
+                    0, SimileAjax.History._currentIndex - SimileAjax.History._baseIndex);
+
+                SimileAjax.History._actions.push(action);
+                SimileAjax.History._currentIndex++;
+
+                var diff = SimileAjax.History._actions.length - SimileAjax.History.maxHistoryLength;
+                if (diff > 0) {
+                    SimileAjax.History._actions = SimileAjax.History._actions.slice(diff);
+                    SimileAjax.History._baseIndex += diff;
+                }
+
+                try {
+                    SimileAjax.History._iframe.contentWindow.location.search =
+                        "?" + SimileAjax.History._currentIndex;
+                } catch (e) {
+                    /*
+                     *  We can't modify location.search most probably because it's a file:// url.
+                     *  We'll just going to modify the document's title.
+                     */
+                    var title = SimileAjax.History.formatHistoryEntryTitle(action.label);
+                    document.title = title;
+                }
+            }
+        } catch (e) {
+            SimileAjax.Debug.exception(e, "Error adding action {" + action.label + "} to history");
+        }
+    }, 0);
+};
+
+SimileAjax.History.addLengthyAction = function(perform, undo, label) {
+    SimileAjax.History.addAction({
+        perform:    perform,
+        undo:       undo,
+        label:      label,
+        uiLayer:    SimileAjax.WindowManager.getBaseLayer(),
+        lengthy:    true
+    });
+};
+
+SimileAjax.History._handleIFrameOnLoad = function() {
+    /*
+     *  This function is invoked when the user herself
+     *  navigates backward or forward. We need to adjust
+     *  the application's state accordingly.
+     */
+
+    try {
+        var q = SimileAjax.History._iframe.contentWindow.location.search;
+        var c = (q.length == 0) ? 0 : Math.max(0, parseInt(q.substr(1)));
+
+        var finishUp = function() {
+            var diff = c - SimileAjax.History._currentIndex;
+            SimileAjax.History._currentIndex += diff;
+            SimileAjax.History._baseIndex += diff;
+
+            SimileAjax.History._iframe.contentWindow.location.search = "?" + c;
+        };
+
+        if (c < SimileAjax.History._currentIndex) { // need to undo
+            SimileAjax.History._listeners.fire("onBeforeUndoSeveral", []);
+            window.setTimeout(function() {
+                while (SimileAjax.History._currentIndex > c &&
+                       SimileAjax.History._currentIndex > SimileAjax.History._baseIndex) {
+
+                    SimileAjax.History._currentIndex--;
+
+                    var action = SimileAjax.History._actions[SimileAjax.History._currentIndex - SimileAjax.History._baseIndex];
+
+                    try {
+                        action.undo();
+                    } catch (e) {
+                        SimileAjax.Debug.exception(e, "History: Failed to undo action {" + action.label + "}");
+                    }
+                }
+
+                SimileAjax.History._listeners.fire("onAfterUndoSeveral", []);
+                finishUp();
+            }, 0);
+        } else if (c > SimileAjax.History._currentIndex) { // need to redo
+            SimileAjax.History._listeners.fire("onBeforeRedoSeveral", []);
+            window.setTimeout(function() {
+                while (SimileAjax.History._currentIndex < c &&
+                       SimileAjax.History._currentIndex - SimileAjax.History._baseIndex < SimileAjax.History._actions.length) {
+
+                    var action = SimileAjax.History._actions[SimileAjax.History._currentIndex - SimileAjax.History._baseIndex];
+
+                    try {
+                        action.perform();
+                    } catch (e) {
+                        SimileAjax.Debug.exception(e, "History: Failed to redo action {" + action.label + "}");
+                    }
+
+                    SimileAjax.History._currentIndex++;
+                }
+
+                SimileAjax.History._listeners.fire("onAfterRedoSeveral", []);
+                finishUp();
+            }, 0);
+        } else {
+            var index = SimileAjax.History._currentIndex - SimileAjax.History._baseIndex - 1;
+            var title = (index >= 0 && index < SimileAjax.History._actions.length) ?
+                SimileAjax.History.formatHistoryEntryTitle(SimileAjax.History._actions[index].label) :
+                SimileAjax.History._plainDocumentTitle;
+
+            SimileAjax.History._iframe.contentWindow.document.title = title;
+            document.title = title;
+        }
+    } catch (e) {
+        // silent
+    }
+};
+
+SimileAjax.History.getNextUndoAction = function() {
+    try {
+        var index = SimileAjax.History._currentIndex - SimileAjax.History._baseIndex - 1;
+        return SimileAjax.History._actions[index];
+    } catch (e) {
+        return null;
+    }
+};
+
+SimileAjax.History.getNextRedoAction = function() {
+    try {
+        var index = SimileAjax.History._currentIndex - SimileAjax.History._baseIndex;
+        return SimileAjax.History._actions[index];
+    } catch (e) {
+        return null;
+    }
+};
+/**
+ * @fileOverview UI layers and window-wide dragging
+ * @name SimileAjax.WindowManager
+ */
+
+/**
+ *  This is a singleton that keeps track of UI layers (modal and
+ *  modeless) and enables/disables UI elements based on which layers
+ *  they belong to. It also provides window-wide dragging
+ *  implementation.
+ */
+SimileAjax.WindowManager = {
+    _initialized:       false,
+    _listeners:         [],
+
+    _draggedElement:                null,
+    _draggedElementCallback:        null,
+    _dropTargetHighlightElement:    null,
+    _lastCoords:                    null,
+    _ghostCoords:                   null,
+    _draggingMode:                  "",
+    _dragging:                      false,
+
+    _layers:            []
+};
+
+SimileAjax.WindowManager.initialize = function() {
+    if (SimileAjax.WindowManager._initialized) {
+        return;
+    }
+
+    SimileAjax.DOM.registerEvent(document.body, "mousedown", SimileAjax.WindowManager._onBodyMouseDown);
+    SimileAjax.DOM.registerEvent(document.body, "mousemove", SimileAjax.WindowManager._onBodyMouseMove);
+    SimileAjax.DOM.registerEvent(document.body, "mouseup",   SimileAjax.WindowManager._onBodyMouseUp);
+    SimileAjax.DOM.registerEvent(document, "keydown",       SimileAjax.WindowManager._onBodyKeyDown);
+    SimileAjax.DOM.registerEvent(document, "keyup",         SimileAjax.WindowManager._onBodyKeyUp);
+
+    SimileAjax.WindowManager._layers.push({index: 0});
+
+    SimileAjax.WindowManager._historyListener = {
+        onBeforeUndoSeveral:    function() {},
+        onAfterUndoSeveral:     function() {},
+        onBeforeUndo:           function() {},
+        onAfterUndo:            function() {},
+
+        onBeforeRedoSeveral:    function() {},
+        onAfterRedoSeveral:     function() {},
+        onBeforeRedo:           function() {},
+        onAfterRedo:            function() {}
+    };
+    SimileAjax.History.addListener(SimileAjax.WindowManager._historyListener);
+
+    SimileAjax.WindowManager._initialized = true;
+};
+
+SimileAjax.WindowManager.getBaseLayer = function() {
+    SimileAjax.WindowManager.initialize();
+    return SimileAjax.WindowManager._layers[0];
+};
+
+SimileAjax.WindowManager.getHighestLayer = function() {
+    SimileAjax.WindowManager.initialize();
+    return SimileAjax.WindowManager._layers[SimileAjax.WindowManager._layers.length - 1];
+};
+
+SimileAjax.WindowManager.registerEventWithObject = function(elmt, eventName, obj, handlerName, layer) {
+    SimileAjax.WindowManager.registerEvent(
+        elmt,
+        eventName,
+        function(elmt2, evt, target) {
+            return obj[handlerName].call(obj, elmt2, evt, target);
+        },
+        layer
+    );
+};
+
+SimileAjax.WindowManager.registerEvent = function(elmt, eventName, handler, layer) {
+    if (layer == null) {
+        layer = SimileAjax.WindowManager.getHighestLayer();
+    }
+
+    var handler2 = function(elmt, evt, target) {
+        if (SimileAjax.WindowManager._canProcessEventAtLayer(layer)) {
+            SimileAjax.WindowManager._popToLayer(layer.index);
+            try {
+                handler(elmt, evt, target);
+            } catch (e) {
+                SimileAjax.Debug.exception(e);
+            }
+        }
+        SimileAjax.DOM.cancelEvent(evt);
+        return false;
+    }
+
+    SimileAjax.DOM.registerEvent(elmt, eventName, handler2);
+};
+
+SimileAjax.WindowManager.pushLayer = function(f, ephemeral, elmt) {
+    var layer = { onPop: f, index: SimileAjax.WindowManager._layers.length, ephemeral: (ephemeral), elmt: elmt };
+    SimileAjax.WindowManager._layers.push(layer);
+
+    return layer;
+};
+
+SimileAjax.WindowManager.popLayer = function(layer) {
+    for (var i = 1; i < SimileAjax.WindowManager._layers.length; i++) {
+        if (SimileAjax.WindowManager._layers[i] == layer) {
+            SimileAjax.WindowManager._popToLayer(i - 1);
+            break;
+        }
+    }
+};
+
+SimileAjax.WindowManager.popAllLayers = function() {
+    SimileAjax.WindowManager._popToLayer(0);
+};
+
+SimileAjax.WindowManager.registerForDragging = function(elmt, callback, layer) {
+    SimileAjax.WindowManager.registerEvent(
+        elmt,
+        "mousedown",
+        function(elmt, evt, target) {
+            SimileAjax.WindowManager._handleMouseDown(elmt, evt, callback);
+        },
+        layer
+    );
+};
+
+SimileAjax.WindowManager._popToLayer = function(level) {
+    while (level+1 < SimileAjax.WindowManager._layers.length) {
+        try {
+            var layer = SimileAjax.WindowManager._layers.pop();
+            if (layer.onPop != null) {
+                layer.onPop();
+            }
+        } catch (e) {
+        }
+    }
+};
+
+SimileAjax.WindowManager._canProcessEventAtLayer = function(layer) {
+    if (layer.index == (SimileAjax.WindowManager._layers.length - 1)) {
+        return true;
+    }
+    for (var i = layer.index + 1; i < SimileAjax.WindowManager._layers.length; i++) {
+        if (!SimileAjax.WindowManager._layers[i].ephemeral) {
+            return false;
+        }
+    }
+    return true;
+};
+
+SimileAjax.WindowManager.cancelPopups = function(evt) {
+    var evtCoords = (evt) ? SimileAjax.DOM.getEventPageCoordinates(evt) : { x: -1, y: -1 };
+
+    var i = SimileAjax.WindowManager._layers.length - 1;
+    while (i > 0 && SimileAjax.WindowManager._layers[i].ephemeral) {
+        var layer = SimileAjax.WindowManager._layers[i];
+        if (layer.elmt != null) { // if event falls within main element of layer then don't cancel
+            var elmt = layer.elmt;
+            var elmtCoords = SimileAjax.DOM.getPageCoordinates(elmt);
+            if (evtCoords.x >= elmtCoords.left && evtCoords.x < (elmtCoords.left + elmt.offsetWidth) &&
+                evtCoords.y >= elmtCoords.top && evtCoords.y < (elmtCoords.top + elmt.offsetHeight)) {
+                break;
+            }
+        }
+        i--;
+    }
+    SimileAjax.WindowManager._popToLayer(i);
+};
+
+SimileAjax.WindowManager._onBodyMouseDown = function(elmt, evt, target) {
+    if (!("eventPhase" in evt) || evt.eventPhase == evt.BUBBLING_PHASE) {
+        SimileAjax.WindowManager.cancelPopups(evt);
+    }
+};
+
+SimileAjax.WindowManager._handleMouseDown = function(elmt, evt, callback) {
+    SimileAjax.WindowManager._draggedElement = elmt;
+    SimileAjax.WindowManager._draggedElementCallback = callback;
+    SimileAjax.WindowManager._lastCoords = { x: evt.clientX, y: evt.clientY };
+
+    SimileAjax.DOM.cancelEvent(evt);
+    return false;
+};
+
+SimileAjax.WindowManager._onBodyKeyDown = function(elmt, evt, target) {
+    if (SimileAjax.WindowManager._dragging) {
+        if (evt.keyCode == 27) { // esc
+            SimileAjax.WindowManager._cancelDragging();
+        } else if ((evt.keyCode == 17 || evt.keyCode == 16) && SimileAjax.WindowManager._draggingMode != "copy") {
+            SimileAjax.WindowManager._draggingMode = "copy";
+
+            var img = SimileAjax.Graphics.createTranslucentImage(SimileAjax.urlPrefix + "images/copy.png");
+            img.style.position = "absolute";
+            img.style.left = (SimileAjax.WindowManager._ghostCoords.left - 16) + "px";
+            img.style.top = (SimileAjax.WindowManager._ghostCoords.top) + "px";
+            document.body.appendChild(img);
+
+            SimileAjax.WindowManager._draggingModeIndicatorElmt = img;
+        }
+    }
+};
+
+SimileAjax.WindowManager._onBodyKeyUp = function(elmt, evt, target) {
+    if (SimileAjax.WindowManager._dragging) {
+        if (evt.keyCode == 17 || evt.keyCode == 16) {
+            SimileAjax.WindowManager._draggingMode = "";
+            if (SimileAjax.WindowManager._draggingModeIndicatorElmt != null) {
+                document.body.removeChild(SimileAjax.WindowManager._draggingModeIndicatorElmt);
+                SimileAjax.WindowManager._draggingModeIndicatorElmt = null;
+            }
+        }
+    }
+};
+
+SimileAjax.WindowManager._onBodyMouseMove = function(elmt, evt, target) {
+    if (SimileAjax.WindowManager._draggedElement != null) {
+        var callback = SimileAjax.WindowManager._draggedElementCallback;
+
+        var lastCoords = SimileAjax.WindowManager._lastCoords;
+        var diffX = evt.clientX - lastCoords.x;
+        var diffY = evt.clientY - lastCoords.y;
+
+        if (!SimileAjax.WindowManager._dragging) {
+            if (Math.abs(diffX) > 5 || Math.abs(diffY) > 5) {
+                try {
+                    if ("onDragStart" in callback) {
+                        callback.onDragStart();
+                    }
+
+                    if ("ghost" in callback && callback.ghost) {
+                        var draggedElmt = SimileAjax.WindowManager._draggedElement;
+
+                        SimileAjax.WindowManager._ghostCoords = SimileAjax.DOM.getPageCoordinates(draggedElmt);
+                        SimileAjax.WindowManager._ghostCoords.left += diffX;
+                        SimileAjax.WindowManager._ghostCoords.top += diffY;
+
+                        var ghostElmt = draggedElmt.cloneNode(true);
+                        ghostElmt.style.position = "absolute";
+                        ghostElmt.style.left = SimileAjax.WindowManager._ghostCoords.left + "px";
+                        ghostElmt.style.top = SimileAjax.WindowManager._ghostCoords.top + "px";
+                        ghostElmt.style.zIndex = 1000;
+                        SimileAjax.Graphics.setOpacity(ghostElmt, 50);
+
+                        document.body.appendChild(ghostElmt);
+                        callback._ghostElmt = ghostElmt;
+                    }
+
+                    SimileAjax.WindowManager._dragging = true;
+                    SimileAjax.WindowManager._lastCoords = { x: evt.clientX, y: evt.clientY };
+
+                    document.body.focus();
+                } catch (e) {
+                    SimileAjax.Debug.exception("WindowManager: Error handling mouse down", e);
+                    SimileAjax.WindowManager._cancelDragging();
+                }
+            }
+        } else {
+            try {
+                SimileAjax.WindowManager._lastCoords = { x: evt.clientX, y: evt.clientY };
+
+                if ("onDragBy" in callback) {
+                    callback.onDragBy(diffX, diffY);
+                }
+
+                if ("_ghostElmt" in callback) {
+                    var ghostElmt = callback._ghostElmt;
+
+                    SimileAjax.WindowManager._ghostCoords.left += diffX;
+                    SimileAjax.WindowManager._ghostCoords.top += diffY;
+
+                    ghostElmt.style.left = SimileAjax.WindowManager._ghostCoords.left + "px";
+                    ghostElmt.style.top = SimileAjax.WindowManager._ghostCoords.top + "px";
+                    if (SimileAjax.WindowManager._draggingModeIndicatorElmt != null) {
+                        var indicatorElmt = SimileAjax.WindowManager._draggingModeIndicatorElmt;
+
+                        indicatorElmt.style.left = (SimileAjax.WindowManager._ghostCoords.left - 16) + "px";
+                        indicatorElmt.style.top = SimileAjax.WindowManager._ghostCoords.top + "px";
+                    }
+
+                    if ("droppable" in callback && callback.droppable) {
+                        var coords = SimileAjax.DOM.getEventPageCoordinates(evt);
+                        var target = SimileAjax.DOM.hittest(
+                            coords.x, coords.y,
+                            [   SimileAjax.WindowManager._ghostElmt,
+                                SimileAjax.WindowManager._dropTargetHighlightElement
+                            ]
+                        );
+                        target = SimileAjax.WindowManager._findDropTarget(target);
+
+                        if (target != SimileAjax.WindowManager._potentialDropTarget) {
+                            if (SimileAjax.WindowManager._dropTargetHighlightElement != null) {
+                                document.body.removeChild(SimileAjax.WindowManager._dropTargetHighlightElement);
+
+                                SimileAjax.WindowManager._dropTargetHighlightElement = null;
+                                SimileAjax.WindowManager._potentialDropTarget = null;
+                            }
+
+                            var droppable = false;
+                            if (target != null) {
+                                if ((!("canDropOn" in callback) || callback.canDropOn(target)) &&
+                                    (!("canDrop" in target) || target.canDrop(SimileAjax.WindowManager._draggedElement))) {
+
+                                    droppable = true;
+                                }
+                            }
+
+                            if (droppable) {
+                                var border = 4;
+                                var targetCoords = SimileAjax.DOM.getPageCoordinates(target);
+                                var highlight = document.createElement("div");
+                                highlight.style.border = border + "px solid yellow";
+                                highlight.style.backgroundColor = "yellow";
+                                highlight.style.position = "absolute";
+                                highlight.style.left = targetCoords.left + "px";
+                                highlight.style.top = targetCoords.top + "px";
+                                highlight.style.width = (target.offsetWidth - border * 2) + "px";
+                                highlight.style.height = (target.offsetHeight - border * 2) + "px";
+                                SimileAjax.Graphics.setOpacity(highlight, 30);
+                                document.body.appendChild(highlight);
+
+                                SimileAjax.WindowManager._potentialDropTarget = target;
+                                SimileAjax.WindowManager._dropTargetHighlightElement = highlight;
+                            }
+                        }
+                    }
+                }
+            } catch (e) {
+                SimileAjax.Debug.exception("WindowManager: Error handling mouse move", e);
+                SimileAjax.WindowManager._cancelDragging();
+            }
+        }
+
+        SimileAjax.DOM.cancelEvent(evt);
+        return false;
+    }
+};
+
+SimileAjax.WindowManager._onBodyMouseUp = function(elmt, evt, target) {
+    if (SimileAjax.WindowManager._draggedElement != null) {
+        try {
+            if (SimileAjax.WindowManager._dragging) {
+                var callback = SimileAjax.WindowManager._draggedElementCallback;
+                if ("onDragEnd" in callback) {
+                    callback.onDragEnd();
+                }
+                if ("droppable" in callback && callback.droppable) {
+                    var dropped = false;
+
+                    var target = SimileAjax.WindowManager._potentialDropTarget;
+                    if (target != null) {
+                        if ((!("canDropOn" in callback) || callback.canDropOn(target)) &&
+                            (!("canDrop" in target) || target.canDrop(SimileAjax.WindowManager._draggedElement))) {
+
+                            if ("onDropOn" in callback) {
+                                callback.onDropOn(target);
+                            }
+                            target.ondrop(SimileAjax.WindowManager._draggedElement, SimileAjax.WindowManager._draggingMode);
+
+                            dropped = true;
+                        }
+                    }
+
+                    if (!dropped) {
+                        // TODO: do holywood explosion here
+                    }
+                }
+            }
+        } finally {
+            SimileAjax.WindowManager._cancelDragging();
+        }
+
+        SimileAjax.DOM.cancelEvent(evt);
+        return false;
+    }
+};
+
+SimileAjax.WindowManager._cancelDragging = function() {
+    var callback = SimileAjax.WindowManager._draggedElementCallback;
+    if ("_ghostElmt" in callback) {
+        var ghostElmt = callback._ghostElmt;
+        document.body.removeChild(ghostElmt);
+
+        delete callback._ghostElmt;
+    }
+    if (SimileAjax.WindowManager._dropTargetHighlightElement != null) {
+        document.body.removeChild(SimileAjax.WindowManager._dropTargetHighlightElement);
+        SimileAjax.WindowManager._dropTargetHighlightElement = null;
+    }
+    if (SimileAjax.WindowManager._draggingModeIndicatorElmt != null) {
+        document.body.removeChild(SimileAjax.WindowManager._draggingModeIndicatorElmt);
+        SimileAjax.WindowManager._draggingModeIndicatorElmt = null;
+    }
+
+    SimileAjax.WindowManager._draggedElement = null;
+    SimileAjax.WindowManager._draggedElementCallback = null;
+    SimileAjax.WindowManager._potentialDropTarget = null;
+    SimileAjax.WindowManager._dropTargetHighlightElement = null;
+    SimileAjax.WindowManager._lastCoords = null;
+    SimileAjax.WindowManager._ghostCoords = null;
+    SimileAjax.WindowManager._draggingMode = "";
+    SimileAjax.WindowManager._dragging = false;
+};
+
+SimileAjax.WindowManager._findDropTarget = function(elmt) {
+    while (elmt != null) {
+        if ("ondrop" in elmt && (typeof elmt.ondrop) == "function") {
+            break;
+        }
+        elmt = elmt.parentNode;
+    }
+    return elmt;
+};
+/*==================================================
+ *  Timeline API
+ *
+ *  This file will load all the Javascript files
+ *  necessary to make the standard timeline work.
+ *  It also detects the default locale.
+ *
+ *  To run from the MIT copy of Timeline:
+ *  Include this file in your HTML file as follows:
+ *
+ *    <script src="http://api.simile-widgets.org/timeline/2.3.1/timeline-api.js"
+ *     type="text/javascript"></script>
+ *
+ *
+ * To host the Timeline files on your own server:
+ *   1) Install the Timeline and Simile-Ajax files onto your webserver using
+ *      timeline_libraries.zip or timeline_source.zip
+ *
+ *   2) Set global js variables used to send parameters to this script:
+ *        var Timeline_ajax_url -- url for simile-ajax-api.js
+ *        var Timeline_urlPrefix -- url for the *directory* that contains timeline-api.js
+ *            Include trailing slash
+ *        var Timeline_parameters='bundle=true'; // you must set bundle to true if you are using
+ *                                               // timeline_libraries.zip since only the
+ *                                               // bundled libraries are included
+ *
+ * eg your html page would include
+ *
+ *   <script>
+ *     var Timeline_ajax_url="http://YOUR_SERVER/javascripts/timeline/timeline_ajax/simile-ajax-api.js";
+ *     var Timeline_urlPrefix='http://YOUR_SERVER/javascripts/timeline/timeline_js/';
+ *     var Timeline_parameters='bundle=true';
+ *   </script>
+ *   <script src="http://YOUR_SERVER/javascripts/timeline/timeline_js/timeline-api.js"
+ *     type="text/javascript">
+ *   </script>
+ *
+ * SCRIPT PARAMETERS
+ * This script auto-magically figures out locale and has defaults for other parameters
+ * To set parameters explicity, set js global variable Timeline_parameters or include as
+ * parameters on the url using GET style. Eg the two next lines pass the same parameters:
+ *     Timeline_parameters='bundle=true';                    // pass parameter via js variable
+ *     <script src="http://....timeline-api.js?bundle=true"  // pass parameter via url
+ *
+ * Parameters
+ *   timeline-use-local-resources --
+ *   bundle -- true: use the single js bundle file; false: load individual files (for debugging)
+ *   locales --
+ *   defaultLocale --
+ *   forceLocale -- force locale to be a particular value--used for debugging. Normally locale is determined
+ *                  by browser's and server's locale settings.
+ *
+ * DEBUGGING
+ * If you have a problem with Timeline, the first step is to use the unbundled Javascript files. To do so:
+ * To use the unbundled Timeline and Ajax libraries
+ * Change
+ *   <script src="http://api.simile-widgets.org/timeline/2.3.1/api/timeline-api.js?bundle=true" type="text/javascript"></script>
+ * To
+ *   <script>var Timeline_ajax_url = "http://api.simile-widgets.org/ajax/2.2.1/simile-ajax-api.js?bundle=false"</script>
+ *   <script src="http://api.simile-widgets.org/timeline/2.3.1/api/timeline-api.js?bundle=false" type="text/javascript"></script>
+ *
+ * Note that the Ajax version is usually NOT the same as the Timeline version.
+ * See variable simile_ajax_ver below for the current version
+ *
+ *==================================================
+ */
+
+(function() {
+
+    var simile_ajax_ver = "2.2.1"; // ===========>>>  current Simile-Ajax version
+
+    var useLocalResources = false;
+    if (document.location.search.length > 0) {
+        var params = document.location.search.substr(1).split("&");
+        for (var i = 0; i < params.length; i++) {
+            if (params[i] == "timeline-use-local-resources") {
+                useLocalResources = true;
+            }
+        }
+    };
+
+    var loadMe = function() {
+        if ("Timeline" in window) {
+            return;
+        }
+
+        window.Timeline = new Object();
+        window.Timeline.DateTime = window.SimileAjax.DateTime; // for backward compatibility
+
+        var bundle = false;
+        var javascriptFiles = [
+            "timeline.js",
+            "band.js",
+            "themes.js",
+            "ethers.js",
+            "ether-painters.js",
+            "event-utils.js",
+            "labellers.js",
+            "sources.js",
+            "original-painter.js",
+            "detailed-painter.js",
+            "overview-painter.js",
+            "compact-painter.js",
+            "decorators.js",
+            "units.js"
+        ];
+        var cssFiles = [
+            "timeline.css",
+            "ethers.css",
+            "events.css"
+        ];
+
+        var localizedJavascriptFiles = [
+            "timeline.js",
+            "labellers.js"
+        ];
+        var localizedCssFiles = [
+        ];
+
+        // ISO-639 language codes, ISO-3166 country codes (2 characters)
+        var supportedLocales = [
+            "cs",       // Czech
+            "de",       // German
+            "en",       // English
+            "es",       // Spanish
+            "fr",       // French
+            "it",       // Italian
+            "nl",       // Dutch (The Netherlands)
+            "ru",       // Russian
+            "se",       // Swedish
+            "tr",       // Turkish
+            "vi",       // Vietnamese
+            "zh"        // Chinese
+        ];
+
+        try {
+            var desiredLocales = [ "en" ],
+                defaultServerLocale = "en",
+                forceLocale = null;
+
+            var parseURLParameters = function(parameters) {
+                var params = parameters.split("&");
+                for (var p = 0; p < params.length; p++) {
+                    var pair = params[p].split("=");
+                    if (pair[0] == "locales") {
+                        desiredLocales = desiredLocales.concat(pair[1].split(","));
+                    } else if (pair[0] == "defaultLocale") {
+                        defaultServerLocale = pair[1];
+                    } else if (pair[0] == "forceLocale") {
+                        forceLocale = pair[1];
+                        desiredLocales = desiredLocales.concat(pair[1].split(","));
+                    } else if (pair[0] == "bundle") {
+                        bundle = pair[1] != "false";
+                    }
+                }
+            };
+
+            (function() {
+                if (typeof Timeline_urlPrefix == "string") {
+                    Timeline.urlPrefix = Timeline_urlPrefix;
+                    if (typeof Timeline_parameters == "string") {
+                        parseURLParameters(Timeline_parameters);
+                    }
+                } else {
+                    var heads = document.documentElement.getElementsByTagName("head");
+                    for (var h = 0; h < heads.length; h++) {
+                        var scripts = heads[h].getElementsByTagName("script");
+                        for (var s = 0; s < scripts.length; s++) {
+                            var url = scripts[s].src;
+                            var i = url.indexOf("timeline-api.js");
+                            if (i >= 0) {
+                                Timeline.urlPrefix = url.substr(0, i);
+                                var q = url.indexOf("?");
+                                if (q > 0) {
+                                    parseURLParameters(url.substr(q + 1));
+                                }
+                                return;
+                            }
+                        }
+                    }
+                    throw new Error("Failed to derive URL prefix for Timeline API code files");
+                }
+            })();
+
+            var includeJavascriptFiles = function(urlPrefix, filenames) {
+                SimileAjax.includeJavascriptFiles(document, urlPrefix, filenames);
+            }
+            var includeCssFiles = function(urlPrefix, filenames) {
+                SimileAjax.includeCssFiles(document, urlPrefix, filenames);
+            }
+
+            /*
+             *  Include non-localized files
+             */
+            if (bundle) {
+                includeJavascriptFiles(Timeline.urlPrefix, [ "timeline-bundle.js" ]);
+                includeCssFiles(Timeline.urlPrefix, [ "timeline-bundle.css" ]);
+            } else {
+                includeJavascriptFiles(Timeline.urlPrefix + "scripts/", javascriptFiles);
+                includeCssFiles(Timeline.urlPrefix + "styles/", cssFiles);
+            }
+
+            /*
+             *  Include localized files
+             */
+            var loadLocale = [];
+            loadLocale[defaultServerLocale] = true;
+
+            var tryExactLocale = function(locale) {
+                for (var l = 0; l < supportedLocales.length; l++) {
+                    if (locale == supportedLocales[l]) {
+                        loadLocale[locale] = true;
+                        return true;
+                    }
+                }
+                return false;
+            }
+            var tryLocale = function(locale) {
+                if (tryExactLocale(locale)) {
+                    return locale;
+                }
+
+                var dash = locale.indexOf("-");
+                if (dash > 0 && tryExactLocale(locale.substr(0, dash))) {
+                    return locale.substr(0, dash);
+                }
+
+                return null;
+            }
+
+            for (var l = 0; l < desiredLocales.length; l++) {
+                tryLocale(desiredLocales[l]);
+            }
+
+            var defaultClientLocale = defaultServerLocale;
+            var defaultClientLocales = ("language" in navigator ? navigator.language : navigator.browserLanguage).split(";");
+            for (var l = 0; l < defaultClientLocales.length; l++) {
+                var locale = tryLocale(defaultClientLocales[l]);
+                if (locale != null) {
+                    defaultClientLocale = locale;
+                    break;
+                }
+            }
+
+            for (var l = 0; l < supportedLocales.length; l++) {
+                var locale = supportedLocales[l];
+                if (loadLocale[locale]) {
+                    includeJavascriptFiles(Timeline.urlPrefix + "scripts/l10n/" + locale + "/", localizedJavascriptFiles);
+                    includeCssFiles(Timeline.urlPrefix + "styles/l10n/" + locale + "/", localizedCssFiles);
+                }
+            }
+
+            if (forceLocale == null) {
+              Timeline.serverLocale = defaultServerLocale;
+              Timeline.clientLocale = defaultClientLocale;
+            } else {
+              Timeline.serverLocale = forceLocale;
+              Timeline.clientLocale = forceLocale;
+            }
+        } catch (e) {
+            alert(e);
+        }
+    };
+
+    /*
+     *  Load SimileAjax if it's not already loaded
+     */
+    if (typeof SimileAjax == "undefined") {
+        window.SimileAjax_onLoad = loadMe;
+
+        var url = useLocalResources ?
+            "http://127.0.0.1:9999/ajax/api/simile-ajax-api.js?bundle=false" :
+            "http://api.simile-widgets.org/ajax/" + simile_ajax_ver + "/simile-ajax-api.js";
+        if (typeof Timeline_ajax_url == "string") {
+           url = Timeline_ajax_url;
+        }
+        var createScriptElement = function() {
+            var script = document.createElement("script");
+            script.type = "text/javascript";
+            script.language = "JavaScript";
+            script.src = url;
+            document.getElementsByTagName("head")[0].appendChild(script);
+        }
+        if (document.body == null) {
+            try {
+                document.write("<script src='" + url + "' type='text/javascript'></script>");
+            } catch (e) {
+                createScriptElement();
+            }
+        } else {
+            createScriptElement();
+        }
+    } else {
+        loadMe();
+    }
 })();
-
-
-/* string.js */
-String.prototype.trim=function(){return this.replace(/^\s+|\s+$/g,"");
-};
-String.prototype.startsWith=function(A){return this.length>=A.length&&this.substr(0,A.length)==A;
-};
-String.prototype.endsWith=function(A){return this.length>=A.length&&this.substr(this.length-A.length)==A;
-};
-String.substitute=function(B,D){var A="";
-var F=0;
-while(F<B.length-1){var C=B.indexOf("%",F);
-if(C<0||C==B.length-1){break;
-}else{if(C>F&&B.charAt(C-1)=="\\"){A+=B.substring(F,C-1)+"%";
-F=C+1;
-}else{var E=parseInt(B.charAt(C+1));
-if(isNaN(E)||E>=D.length){A+=B.substring(F,C+2);
-}else{A+=B.substring(F,C)+D[E].toString();
-}F=C+2;
-}}}if(F<B.length){A+=B.substring(F);
-}return A;
-};
-
-
-/* units.js */
-SimileAjax.NativeDateUnit=new Object();
-SimileAjax.NativeDateUnit.makeDefaultValue=function(){return new Date();
-};
-SimileAjax.NativeDateUnit.cloneValue=function(A){return new Date(A.getTime());
-};
-SimileAjax.NativeDateUnit.getParser=function(A){if(typeof A=="string"){A=A.toLowerCase();
-}return(A=="iso8601"||A=="iso 8601")?SimileAjax.DateTime.parseIso8601DateTime:SimileAjax.DateTime.parseGregorianDateTime;
-};
-SimileAjax.NativeDateUnit.parseFromObject=function(A){return SimileAjax.DateTime.parseGregorianDateTime(A);
-};
-SimileAjax.NativeDateUnit.toNumber=function(A){return A.getTime();
-};
-SimileAjax.NativeDateUnit.fromNumber=function(A){return new Date(A);
-};
-SimileAjax.NativeDateUnit.compare=function(D,C){var B,A;
-if(typeof D=="object"){B=D.getTime();
-}else{B=Number(D);
-}if(typeof C=="object"){A=C.getTime();
-}else{A=Number(C);
-}return B-A;
-};
-SimileAjax.NativeDateUnit.earlier=function(B,A){return SimileAjax.NativeDateUnit.compare(B,A)<0?B:A;
-};
-SimileAjax.NativeDateUnit.later=function(B,A){return SimileAjax.NativeDateUnit.compare(B,A)>0?B:A;
-};
-SimileAjax.NativeDateUnit.change=function(A,B){return new Date(A.getTime()+B);
-};
-
-
-/* window-manager.js */
-SimileAjax.WindowManager={_initialized:false,_listeners:[],_draggedElement:null,_draggedElementCallback:null,_dropTargetHighlightElement:null,_lastCoords:null,_ghostCoords:null,_draggingMode:"",_dragging:false,_layers:[]};
-SimileAjax.WindowManager.initialize=function(){if(SimileAjax.WindowManager._initialized){return ;
-}SimileAjax.DOM.registerEvent(document.body,"mousedown",SimileAjax.WindowManager._onBodyMouseDown);
-SimileAjax.DOM.registerEvent(document.body,"mousemove",SimileAjax.WindowManager._onBodyMouseMove);
-SimileAjax.DOM.registerEvent(document.body,"mouseup",SimileAjax.WindowManager._onBodyMouseUp);
-SimileAjax.DOM.registerEvent(document,"keydown",SimileAjax.WindowManager._onBodyKeyDown);
-SimileAjax.DOM.registerEvent(document,"keyup",SimileAjax.WindowManager._onBodyKeyUp);
-SimileAjax.WindowManager._layers.push({index:0});
-SimileAjax.WindowManager._historyListener={onBeforeUndoSeveral:function(){},onAfterUndoSeveral:function(){},onBeforeUndo:function(){},onAfterUndo:function(){},onBeforeRedoSeveral:function(){},onAfterRedoSeveral:function(){},onBeforeRedo:function(){},onAfterRedo:function(){}};
-SimileAjax.History.addListener(SimileAjax.WindowManager._historyListener);
-SimileAjax.WindowManager._initialized=true;
-};
-SimileAjax.WindowManager.getBaseLayer=function(){SimileAjax.WindowManager.initialize();
-return SimileAjax.WindowManager._layers[0];
-};
-SimileAjax.WindowManager.getHighestLayer=function(){SimileAjax.WindowManager.initialize();
-return SimileAjax.WindowManager._layers[SimileAjax.WindowManager._layers.length-1];
-};
-SimileAjax.WindowManager.registerEventWithObject=function(D,A,E,B,C){SimileAjax.WindowManager.registerEvent(D,A,function(G,F,H){return E[B].call(E,G,F,H);
-},C);
-};
-SimileAjax.WindowManager.registerEvent=function(D,B,E,C){if(C==null){C=SimileAjax.WindowManager.getHighestLayer();
-}var A=function(G,F,I){if(SimileAjax.WindowManager._canProcessEventAtLayer(C)){SimileAjax.WindowManager._popToLayer(C.index);
-try{E(G,F,I);
-}catch(H){SimileAjax.Debug.exception(H);
-}}SimileAjax.DOM.cancelEvent(F);
-return false;
-};
-SimileAjax.DOM.registerEvent(D,B,A);
-};
-SimileAjax.WindowManager.pushLayer=function(C,D,B){var A={onPop:C,index:SimileAjax.WindowManager._layers.length,ephemeral:(D),elmt:B};
-SimileAjax.WindowManager._layers.push(A);
-return A;
-};
-SimileAjax.WindowManager.popLayer=function(B){for(var A=1;
-A<SimileAjax.WindowManager._layers.length;
-A++){if(SimileAjax.WindowManager._layers[A]==B){SimileAjax.WindowManager._popToLayer(A-1);
-break;
-}}};
-SimileAjax.WindowManager.popAllLayers=function(){SimileAjax.WindowManager._popToLayer(0);
-};
-SimileAjax.WindowManager.registerForDragging=function(B,C,A){SimileAjax.WindowManager.registerEvent(B,"mousedown",function(E,D,F){SimileAjax.WindowManager._handleMouseDown(E,D,C);
-},A);
-};
-SimileAjax.WindowManager._popToLayer=function(C){while(C+1<SimileAjax.WindowManager._layers.length){try{var A=SimileAjax.WindowManager._layers.pop();
-if(A.onPop!=null){A.onPop();
-}}catch(B){}}};
-SimileAjax.WindowManager._canProcessEventAtLayer=function(B){if(B.index==(SimileAjax.WindowManager._layers.length-1)){return true;
-}for(var A=B.index+1;
-A<SimileAjax.WindowManager._layers.length;
-A++){if(!SimileAjax.WindowManager._layers[A].ephemeral){return false;
-}}return true;
-};
-SimileAjax.WindowManager.cancelPopups=function(A){var F=(A)?SimileAjax.DOM.getEventPageCoordinates(A):{x:-1,y:-1};
-var E=SimileAjax.WindowManager._layers.length-1;
-while(E>0&&SimileAjax.WindowManager._layers[E].ephemeral){var D=SimileAjax.WindowManager._layers[E];
-if(D.elmt!=null){var C=D.elmt;
-var B=SimileAjax.DOM.getPageCoordinates(C);
-if(F.x>=B.left&&F.x<(B.left+C.offsetWidth)&&F.y>=B.top&&F.y<(B.top+C.offsetHeight)){break;
-}}E--;
-}SimileAjax.WindowManager._popToLayer(E);
-};
-SimileAjax.WindowManager._onBodyMouseDown=function(B,A,C){if(!("eventPhase" in A)||A.eventPhase==A.BUBBLING_PHASE){SimileAjax.WindowManager.cancelPopups(A);
-}};
-SimileAjax.WindowManager._handleMouseDown=function(B,A,C){SimileAjax.WindowManager._draggedElement=B;
-SimileAjax.WindowManager._draggedElementCallback=C;
-SimileAjax.WindowManager._lastCoords={x:A.clientX,y:A.clientY};
-SimileAjax.DOM.cancelEvent(A);
-return false;
-};
-SimileAjax.WindowManager._onBodyKeyDown=function(C,A,D){if(SimileAjax.WindowManager._dragging){if(A.keyCode==27){SimileAjax.WindowManager._cancelDragging();
-}else{if((A.keyCode==17||A.keyCode==16)&&SimileAjax.WindowManager._draggingMode!="copy"){SimileAjax.WindowManager._draggingMode="copy";
-var B=SimileAjax.Graphics.createTranslucentImage(SimileAjax.urlPrefix+"data/timeline/copy.png");
-B.style.position="absolute";
-B.style.left=(SimileAjax.WindowManager._ghostCoords.left-16)+"px";
-B.style.top=(SimileAjax.WindowManager._ghostCoords.top)+"px";
-document.body.appendChild(B);
-SimileAjax.WindowManager._draggingModeIndicatorElmt=B;
-}}}};
-SimileAjax.WindowManager._onBodyKeyUp=function(B,A,C){if(SimileAjax.WindowManager._dragging){if(A.keyCode==17||A.keyCode==16){SimileAjax.WindowManager._draggingMode="";
-if(SimileAjax.WindowManager._draggingModeIndicatorElmt!=null){document.body.removeChild(SimileAjax.WindowManager._draggingModeIndicatorElmt);
-SimileAjax.WindowManager._draggingModeIndicatorElmt=null;
-}}}};
-SimileAjax.WindowManager._onBodyMouseMove=function(A,N,H){if(SimileAjax.WindowManager._draggedElement!=null){var P=SimileAjax.WindowManager._draggedElementCallback;
-var E=SimileAjax.WindowManager._lastCoords;
-var M=N.clientX-E.x;
-var J=N.clientY-E.y;
-if(!SimileAjax.WindowManager._dragging){if(Math.abs(M)>5||Math.abs(J)>5){try{if("onDragStart" in P){P.onDragStart();
-}if("ghost" in P&&P.ghost){var K=SimileAjax.WindowManager._draggedElement;
-SimileAjax.WindowManager._ghostCoords=SimileAjax.DOM.getPageCoordinates(K);
-SimileAjax.WindowManager._ghostCoords.left+=M;
-SimileAjax.WindowManager._ghostCoords.top+=J;
-var O=K.cloneNode(true);
-O.style.position="absolute";
-O.style.left=SimileAjax.WindowManager._ghostCoords.left+"px";
-O.style.top=SimileAjax.WindowManager._ghostCoords.top+"px";
-O.style.zIndex=1000;
-SimileAjax.Graphics.setOpacity(O,50);
-document.body.appendChild(O);
-P._ghostElmt=O;
-}SimileAjax.WindowManager._dragging=true;
-SimileAjax.WindowManager._lastCoords={x:N.clientX,y:N.clientY};
-document.body.focus();
-}catch(G){SimileAjax.Debug.exception("WindowManager: Error handling mouse down",G);
-SimileAjax.WindowManager._cancelDragging();
-}}}else{try{SimileAjax.WindowManager._lastCoords={x:N.clientX,y:N.clientY};
-if("onDragBy" in P){P.onDragBy(M,J);
-}if("_ghostElmt" in P){var O=P._ghostElmt;
-SimileAjax.WindowManager._ghostCoords.left+=M;
-SimileAjax.WindowManager._ghostCoords.top+=J;
-O.style.left=SimileAjax.WindowManager._ghostCoords.left+"px";
-O.style.top=SimileAjax.WindowManager._ghostCoords.top+"px";
-if(SimileAjax.WindowManager._draggingModeIndicatorElmt!=null){var I=SimileAjax.WindowManager._draggingModeIndicatorElmt;
-I.style.left=(SimileAjax.WindowManager._ghostCoords.left-16)+"px";
-I.style.top=SimileAjax.WindowManager._ghostCoords.top+"px";
-}if("droppable" in P&&P.droppable){var L=SimileAjax.DOM.getEventPageCoordinates(N);
-var H=SimileAjax.DOM.hittest(L.x,L.y,[SimileAjax.WindowManager._ghostElmt,SimileAjax.WindowManager._dropTargetHighlightElement]);
-H=SimileAjax.WindowManager._findDropTarget(H);
-if(H!=SimileAjax.WindowManager._potentialDropTarget){if(SimileAjax.WindowManager._dropTargetHighlightElement!=null){document.body.removeChild(SimileAjax.WindowManager._dropTargetHighlightElement);
-SimileAjax.WindowManager._dropTargetHighlightElement=null;
-SimileAjax.WindowManager._potentialDropTarget=null;
-}var F=false;
-if(H!=null){if((!("canDropOn" in P)||P.canDropOn(H))&&(!("canDrop" in H)||H.canDrop(SimileAjax.WindowManager._draggedElement))){F=true;
-}}if(F){var C=4;
-var D=SimileAjax.DOM.getPageCoordinates(H);
-var B=document.createElement("div");
-B.style.border=C+"px solid yellow";
-B.style.backgroundColor="yellow";
-B.style.position="absolute";
-B.style.left=D.left+"px";
-B.style.top=D.top+"px";
-B.style.width=(H.offsetWidth-C*2)+"px";
-B.style.height=(H.offsetHeight-C*2)+"px";
-SimileAjax.Graphics.setOpacity(B,30);
-document.body.appendChild(B);
-SimileAjax.WindowManager._potentialDropTarget=H;
-SimileAjax.WindowManager._dropTargetHighlightElement=B;
-}}}}}catch(G){SimileAjax.Debug.exception("WindowManager: Error handling mouse move",G);
-SimileAjax.WindowManager._cancelDragging();
-}}SimileAjax.DOM.cancelEvent(N);
-return false;
-}};
-SimileAjax.WindowManager._onBodyMouseUp=function(B,A,C){if(SimileAjax.WindowManager._draggedElement!=null){try{if(SimileAjax.WindowManager._dragging){var E=SimileAjax.WindowManager._draggedElementCallback;
-if("onDragEnd" in E){E.onDragEnd();
-}if("droppable" in E&&E.droppable){var D=false;
-var C=SimileAjax.WindowManager._potentialDropTarget;
-if(C!=null){if((!("canDropOn" in E)||E.canDropOn(C))&&(!("canDrop" in C)||C.canDrop(SimileAjax.WindowManager._draggedElement))){if("onDropOn" in E){E.onDropOn(C);
-}C.ondrop(SimileAjax.WindowManager._draggedElement,SimileAjax.WindowManager._draggingMode);
-D=true;
-}}if(!D){}}}}finally{SimileAjax.WindowManager._cancelDragging();
-}SimileAjax.DOM.cancelEvent(A);
-return false;
-}};
-SimileAjax.WindowManager._cancelDragging=function(){var B=SimileAjax.WindowManager._draggedElementCallback;
-if("_ghostElmt" in B){var A=B._ghostElmt;
-document.body.removeChild(A);
-delete B._ghostElmt;
-}if(SimileAjax.WindowManager._dropTargetHighlightElement!=null){document.body.removeChild(SimileAjax.WindowManager._dropTargetHighlightElement);
-SimileAjax.WindowManager._dropTargetHighlightElement=null;
-}if(SimileAjax.WindowManager._draggingModeIndicatorElmt!=null){document.body.removeChild(SimileAjax.WindowManager._draggingModeIndicatorElmt);
-SimileAjax.WindowManager._draggingModeIndicatorElmt=null;
-}SimileAjax.WindowManager._draggedElement=null;
-SimileAjax.WindowManager._draggedElementCallback=null;
-SimileAjax.WindowManager._potentialDropTarget=null;
-SimileAjax.WindowManager._dropTargetHighlightElement=null;
-SimileAjax.WindowManager._lastCoords=null;
-SimileAjax.WindowManager._ghostCoords=null;
-SimileAjax.WindowManager._draggingMode="";
-SimileAjax.WindowManager._dragging=false;
-};
-SimileAjax.WindowManager._findDropTarget=function(A){while(A!=null){if("ondrop" in A&&(typeof A.ondrop)=="function"){break;
-}A=A.parentNode;
-}return A;
-};
-
-
-/* xmlhttp.js */
-SimileAjax.XmlHttp=new Object();
-SimileAjax.XmlHttp._onReadyStateChange=function(A,D,B){switch(A.readyState){case 4:try{if(A.status==0||A.status==200){if(B){B(A);
-}}else{if(D){D(A.statusText,A.status,A);
-}}}catch(C){SimileAjax.Debug.exception("XmlHttp: Error handling onReadyStateChange",C);
-}break;
-}};
-SimileAjax.XmlHttp._createRequest=function(){if(SimileAjax.Platform.browser.isIE){var A=["Msxml2.XMLHTTP","Microsoft.XMLHTTP","Msxml2.XMLHTTP.4.0"];
-for(var B=0;
-B<A.length;
-B++){try{var C=A[B];
-var D=function(){return new ActiveXObject(C);
-};
-var F=D();
-SimileAjax.XmlHttp._createRequest=D;
-return F;
-}catch(E){}}}try{var D=function(){return new XMLHttpRequest();
-};
-var F=D();
-SimileAjax.XmlHttp._createRequest=D;
-return F;
-}catch(E){throw new Error("Failed to create an XMLHttpRequest object");
-}};
-SimileAjax.XmlHttp.get=function(A,D,C){var B=SimileAjax.XmlHttp._createRequest();
-B.open("GET",A,true);
-B.onreadystatechange=function(){SimileAjax.XmlHttp._onReadyStateChange(B,D,C);
-};
-B.send(null);
-};
-SimileAjax.XmlHttp.post=function(B,A,E,D){var C=SimileAjax.XmlHttp._createRequest();
-C.open("POST",B,true);
-C.onreadystatechange=function(){SimileAjax.XmlHttp._onReadyStateChange(C,E,D);
-};
-C.send(A);
-};
-SimileAjax.XmlHttp._forceXML=function(A){try{A.overrideMimeType("text/xml");
-}catch(B){A.setrequestheader("Content-Type","text/xml");
-}};
-
-/******** end of simile-ajax-bundle.js ********/
-
-/******** start of simile-timeline-bundle.js ********/
-
-/* band.js */
-Timeline._Band=function(B,C,A){if(B!==undefined){this.initialize(B,C,A);
-}};
-Timeline._Band.prototype.initialize=function(F,G,B){if(F.autoWidth&&typeof G.width=="string"){G.width=G.width.indexOf("%")>-1?0:parseInt(G.width);
-}this._timeline=F;
-this._bandInfo=G;
-this._index=B;
-this._locale=("locale" in G)?G.locale:Timeline.getDefaultLocale();
-this._timeZone=("timeZone" in G)?G.timeZone:0;
-this._labeller=("labeller" in G)?G.labeller:(("createLabeller" in F.getUnit())?F.getUnit().createLabeller(this._locale,this._timeZone):new Timeline.GregorianDateLabeller(this._locale,this._timeZone));
-this._theme=G.theme;
-this._zoomIndex=("zoomIndex" in G)?G.zoomIndex:0;
-this._zoomSteps=("zoomSteps" in G)?G.zoomSteps:null;
-this._dragging=false;
-this._changing=false;
-this._originalScrollSpeed=5;
-this._scrollSpeed=this._originalScrollSpeed;
-this._onScrollListeners=[];
-var A=this;
-this._syncWithBand=null;
-this._syncWithBandHandler=function(H){A._onHighlightBandScroll();
-};
-this._selectorListener=function(H){A._onHighlightBandScroll();
-};
-var D=this._timeline.getDocument().createElement("div");
-D.className="timeline-band-input";
-this._timeline.addDiv(D);
-this._keyboardInput=document.createElement("input");
-this._keyboardInput.type="text";
-D.appendChild(this._keyboardInput);
-SimileAjax.DOM.registerEventWithObject(this._keyboardInput,"keydown",this,"_onKeyDown");
-SimileAjax.DOM.registerEventWithObject(this._keyboardInput,"keyup",this,"_onKeyUp");
-this._div=this._timeline.getDocument().createElement("div");
-this._div.id="timeline-band-"+B;
-this._div.className="timeline-band timeline-band-"+B;
-this._timeline.addDiv(this._div);
-SimileAjax.DOM.registerEventWithObject(this._div,"mousedown",this,"_onMouseDown");
-SimileAjax.DOM.registerEventWithObject(this._div,"mousemove",this,"_onMouseMove");
-SimileAjax.DOM.registerEventWithObject(this._div,"mouseup",this,"_onMouseUp");
-SimileAjax.DOM.registerEventWithObject(this._div,"mouseout",this,"_onMouseOut");
-SimileAjax.DOM.registerEventWithObject(this._div,"dblclick",this,"_onDblClick");
-var E=this._theme!=null?this._theme.mouseWheel:"scroll";
-if(E==="zoom"||E==="scroll"||this._zoomSteps){if(SimileAjax.Platform.browser.isFirefox){SimileAjax.DOM.registerEventWithObject(this._div,"DOMMouseScroll",this,"_onMouseScroll");
-}else{SimileAjax.DOM.registerEventWithObject(this._div,"mousewheel",this,"_onMouseScroll");
-}}this._innerDiv=this._timeline.getDocument().createElement("div");
-this._innerDiv.className="timeline-band-inner";
-this._div.appendChild(this._innerDiv);
-this._ether=G.ether;
-G.ether.initialize(this,F);
-this._etherPainter=G.etherPainter;
-G.etherPainter.initialize(this,F);
-this._eventSource=G.eventSource;
-if(this._eventSource){this._eventListener={onAddMany:function(){A._onAddMany();
-},onClear:function(){A._onClear();
-}};
-this._eventSource.addListener(this._eventListener);
-}this._eventPainter=G.eventPainter;
-this._eventTracksNeeded=0;
-this._eventTrackIncrement=0;
-G.eventPainter.initialize(this,F);
-this._decorators=("decorators" in G)?G.decorators:[];
-for(var C=0;
-C<this._decorators.length;
-C++){this._decorators[C].initialize(this,F);
-}};
-Timeline._Band.SCROLL_MULTIPLES=5;
-Timeline._Band.prototype.dispose=function(){this.closeBubble();
-if(this._eventSource){this._eventSource.removeListener(this._eventListener);
-this._eventListener=null;
-this._eventSource=null;
-}this._timeline=null;
-this._bandInfo=null;
-this._labeller=null;
-this._ether=null;
-this._etherPainter=null;
-this._eventPainter=null;
-this._decorators=null;
-this._onScrollListeners=null;
-this._syncWithBandHandler=null;
-this._selectorListener=null;
-this._div=null;
-this._innerDiv=null;
-this._keyboardInput=null;
-};
-Timeline._Band.prototype.addOnScrollListener=function(A){this._onScrollListeners.push(A);
-};
-Timeline._Band.prototype.removeOnScrollListener=function(B){for(var A=0;
-A<this._onScrollListeners.length;
-A++){if(this._onScrollListeners[A]==B){this._onScrollListeners.splice(A,1);
-break;
-}}};
-Timeline._Band.prototype.setSyncWithBand=function(B,A){if(this._syncWithBand){this._syncWithBand.removeOnScrollListener(this._syncWithBandHandler);
-}this._syncWithBand=B;
-this._syncWithBand.addOnScrollListener(this._syncWithBandHandler);
-this._highlight=A;
-this._positionHighlight();
-};
-Timeline._Band.prototype.getLocale=function(){return this._locale;
-};
-Timeline._Band.prototype.getTimeZone=function(){return this._timeZone;
-};
-Timeline._Band.prototype.getLabeller=function(){return this._labeller;
-};
-Timeline._Band.prototype.getIndex=function(){return this._index;
-};
-Timeline._Band.prototype.getEther=function(){return this._ether;
-};
-Timeline._Band.prototype.getEtherPainter=function(){return this._etherPainter;
-};
-Timeline._Band.prototype.getEventSource=function(){return this._eventSource;
-};
-Timeline._Band.prototype.getEventPainter=function(){return this._eventPainter;
-};
-Timeline._Band.prototype.getTimeline=function(){return this._timeline;
-};
-Timeline._Band.prototype.updateEventTrackInfo=function(B,A){this._eventTrackIncrement=A;
-if(B>this._eventTracksNeeded){this._eventTracksNeeded=B;
-}};
-Timeline._Band.prototype.checkAutoWidth=function(){if(!this._timeline.autoWidth){return ;
-}var A=this._eventPainter.getType()=="overview";
-var C=A?this._theme.event.overviewTrack.autoWidthMargin:this._theme.event.track.autoWidthMargin;
-var B=Math.ceil((this._eventTracksNeeded+C)*this._eventTrackIncrement);
-B+=A?this._theme.event.overviewTrack.offset:this._theme.event.track.offset;
-var D=this._bandInfo;
-if(B!=D.width){D.width=B;
-}};
-Timeline._Band.prototype.layout=function(){this.paint();
-};
-Timeline._Band.prototype.paint=function(){this._etherPainter.paint();
-this._paintDecorators();
-this._paintEvents();
-};
-Timeline._Band.prototype.softLayout=function(){this.softPaint();
-};
-Timeline._Band.prototype.softPaint=function(){this._etherPainter.softPaint();
-this._softPaintDecorators();
-this._softPaintEvents();
-};
-Timeline._Band.prototype.setBandShiftAndWidth=function(A,D){var C=this._keyboardInput.parentNode;
-var B=A+Math.floor(D/2);
-if(this._timeline.isHorizontal()){this._div.style.top=A+"px";
-this._div.style.height=D+"px";
-C.style.top=B+"px";
-C.style.left="-1em";
-}else{this._div.style.left=A+"px";
-this._div.style.width=D+"px";
-C.style.left=B+"px";
-C.style.top="-1em";
-}};
-Timeline._Band.prototype.getViewWidth=function(){if(this._timeline.isHorizontal()){return this._div.offsetHeight;
-}else{return this._div.offsetWidth;
-}};
-Timeline._Band.prototype.setViewLength=function(A){this._viewLength=A;
-this._recenterDiv();
-this._onChanging();
-};
-Timeline._Band.prototype.getViewLength=function(){return this._viewLength;
-};
-Timeline._Band.prototype.getTotalViewLength=function(){return Timeline._Band.SCROLL_MULTIPLES*this._viewLength;
-};
-Timeline._Band.prototype.getViewOffset=function(){return this._viewOffset;
-};
-Timeline._Band.prototype.getMinDate=function(){return this._ether.pixelOffsetToDate(this._viewOffset);
-};
-Timeline._Band.prototype.getMaxDate=function(){return this._ether.pixelOffsetToDate(this._viewOffset+Timeline._Band.SCROLL_MULTIPLES*this._viewLength);
-};
-Timeline._Band.prototype.getMinVisibleDate=function(){return this._ether.pixelOffsetToDate(0);
-};
-Timeline._Band.prototype.getMaxVisibleDate=function(){return this._ether.pixelOffsetToDate(this._viewLength);
-};
-Timeline._Band.prototype.getCenterVisibleDate=function(){return this._ether.pixelOffsetToDate(this._viewLength/2);
-};
-Timeline._Band.prototype.setMinVisibleDate=function(A){if(!this._changing){this._moveEther(Math.round(-this._ether.dateToPixelOffset(A)));
-}};
-Timeline._Band.prototype.setMaxVisibleDate=function(A){if(!this._changing){this._moveEther(Math.round(this._viewLength-this._ether.dateToPixelOffset(A)));
-}};
-Timeline._Band.prototype.setCenterVisibleDate=function(A){if(!this._changing){this._moveEther(Math.round(this._viewLength/2-this._ether.dateToPixelOffset(A)));
-}};
-Timeline._Band.prototype.dateToPixelOffset=function(A){return this._ether.dateToPixelOffset(A)-this._viewOffset;
-};
-Timeline._Band.prototype.pixelOffsetToDate=function(A){return this._ether.pixelOffsetToDate(A+this._viewOffset);
-};
-Timeline._Band.prototype.createLayerDiv=function(D,B){var C=this._timeline.getDocument().createElement("div");
-C.className="timeline-band-layer"+(typeof B=="string"?(" "+B):"");
-C.style.zIndex=D;
-this._innerDiv.appendChild(C);
-var A=this._timeline.getDocument().createElement("div");
-A.className="timeline-band-layer-inner";
-if(SimileAjax.Platform.browser.isIE){A.style.cursor="move";
-}else{A.style.cursor="-moz-grab";
-}C.appendChild(A);
-return A;
-};
-Timeline._Band.prototype.removeLayerDiv=function(A){this._innerDiv.removeChild(A.parentNode);
-};
-Timeline._Band.prototype.scrollToCenter=function(B,C){var A=this._ether.dateToPixelOffset(B);
-if(A<-this._viewLength/2){this.setCenterVisibleDate(this.pixelOffsetToDate(A+this._viewLength));
-}else{if(A>3*this._viewLength/2){this.setCenterVisibleDate(this.pixelOffsetToDate(A-this._viewLength));
-}}this._autoScroll(Math.round(this._viewLength/2-this._ether.dateToPixelOffset(B)),C);
-};
-Timeline._Band.prototype.showBubbleForEvent=function(C){var A=this.getEventSource().getEvent(C);
-if(A){var B=this;
-this.scrollToCenter(A.getStart(),function(){B._eventPainter.showBubble(A);
-});
-}};
-Timeline._Band.prototype.zoom=function(F,A,E,C){if(!this._zoomSteps){return ;
-}A+=this._viewOffset;
-var D=this._ether.pixelOffsetToDate(A);
-var B=this._ether.zoom(F);
-this._etherPainter.zoom(B);
-this._moveEther(Math.round(-this._ether.dateToPixelOffset(D)));
-this._moveEther(A);
-};
-Timeline._Band.prototype._onMouseDown=function(B,A,C){this.closeBubble();
-this._dragging=true;
-this._dragX=A.clientX;
-this._dragY=A.clientY;
-};
-Timeline._Band.prototype._onMouseMove=function(D,A,E){if(this._dragging){var C=A.clientX-this._dragX;
-var B=A.clientY-this._dragY;
-this._dragX=A.clientX;
-this._dragY=A.clientY;
-this._moveEther(this._timeline.isHorizontal()?C:B);
-this._positionHighlight();
-}};
-Timeline._Band.prototype._onMouseUp=function(B,A,C){this._dragging=false;
-this._keyboardInput.focus();
-};
-Timeline._Band.prototype._onMouseOut=function(B,A,D){var C=SimileAjax.DOM.getEventRelativeCoordinates(A,B);
-C.x+=this._viewOffset;
-if(C.x<0||C.x>B.offsetWidth||C.y<0||C.y>B.offsetHeight){this._dragging=false;
-}};
-Timeline._Band.prototype._onMouseScroll=function(G,I,E){var A=new Date();
-A=A.getTime();
-if(!this._lastScrollTime||((A-this._lastScrollTime)>50)){this._lastScrollTime=A;
-var H=0;
-if(I.wheelDelta){H=I.wheelDelta/120;
-}else{if(I.detail){H=-I.detail/3;
-}}var F=this._theme.mouseWheel;
-if(this._zoomSteps||F==="zoom"){var D=SimileAjax.DOM.getEventRelativeCoordinates(I,G);
-if(H!=0){var C;
-if(H>0){C=true;
-}if(H<0){C=false;
-}this._timeline.zoom(C,D.x,D.y,G);
-}}else{if(F==="scroll"){var B=50*(H<0?-1:1);
-this._moveEther(B);
-}}}if(I.stopPropagation){I.stopPropagation();
-}I.cancelBubble=true;
-if(I.preventDefault){I.preventDefault();
-}I.returnValue=false;
-};
-Timeline._Band.prototype._onDblClick=function(B,A,D){var C=SimileAjax.DOM.getEventRelativeCoordinates(A,B);
-var E=C.x-(this._viewLength/2-this._viewOffset);
-this._autoScroll(-E);
-};
-Timeline._Band.prototype._onKeyDown=function(B,A,C){if(!this._dragging){switch(A.keyCode){case 27:break;
-case 37:case 38:this._scrollSpeed=Math.min(50,Math.abs(this._scrollSpeed*1.05));
-this._moveEther(this._scrollSpeed);
-break;
-case 39:case 40:this._scrollSpeed=-Math.min(50,Math.abs(this._scrollSpeed*1.05));
-this._moveEther(this._scrollSpeed);
-break;
-default:return true;
-}this.closeBubble();
-SimileAjax.DOM.cancelEvent(A);
-return false;
-}return true;
-};
-Timeline._Band.prototype._onKeyUp=function(B,A,C){if(!this._dragging){this._scrollSpeed=this._originalScrollSpeed;
-switch(A.keyCode){case 35:this.setCenterVisibleDate(this._eventSource.getLatestDate());
-break;
-case 36:this.setCenterVisibleDate(this._eventSource.getEarliestDate());
-break;
-case 33:this._autoScroll(this._timeline.getPixelLength());
-break;
-case 34:this._autoScroll(-this._timeline.getPixelLength());
-break;
-default:return true;
-}this.closeBubble();
-SimileAjax.DOM.cancelEvent(A);
-return false;
-}return true;
-};
-Timeline._Band.prototype._autoScroll=function(D,C){var A=this;
-var B=SimileAjax.Graphics.createAnimation(function(E,F){A._moveEther(F);
-},0,D,1000,C);
-B.run();
-};
-Timeline._Band.prototype._moveEther=function(A){this.closeBubble();
-this._viewOffset+=A;
-this._ether.shiftPixels(-A);
-if(this._timeline.isHorizontal()){this._div.style.left=this._viewOffset+"px";
-}else{this._div.style.top=this._viewOffset+"px";
-}if(this._viewOffset>-this._viewLength*0.5||this._viewOffset<-this._viewLength*(Timeline._Band.SCROLL_MULTIPLES-1.5)){this._recenterDiv();
-}else{this.softLayout();
-}this._onChanging();
-};
-Timeline._Band.prototype._onChanging=function(){this._changing=true;
-this._fireOnScroll();
-this._setSyncWithBandDate();
-this._changing=false;
-};
-Timeline._Band.prototype._fireOnScroll=function(){for(var A=0;
-A<this._onScrollListeners.length;
-A++){this._onScrollListeners[A](this);
-}};
-Timeline._Band.prototype._setSyncWithBandDate=function(){if(this._syncWithBand){var A=this._ether.pixelOffsetToDate(this.getViewLength()/2);
-this._syncWithBand.setCenterVisibleDate(A);
-}};
-Timeline._Band.prototype._onHighlightBandScroll=function(){if(this._syncWithBand){var A=this._syncWithBand.getCenterVisibleDate();
-var B=this._ether.dateToPixelOffset(A);
-this._moveEther(Math.round(this._viewLength/2-B));
-if(this._highlight){this._etherPainter.setHighlight(this._syncWithBand.getMinVisibleDate(),this._syncWithBand.getMaxVisibleDate());
-}}};
-Timeline._Band.prototype._onAddMany=function(){this._paintEvents();
-};
-Timeline._Band.prototype._onClear=function(){this._paintEvents();
-};
-Timeline._Band.prototype._positionHighlight=function(){if(this._syncWithBand){var A=this._syncWithBand.getMinVisibleDate();
-var B=this._syncWithBand.getMaxVisibleDate();
-if(this._highlight){this._etherPainter.setHighlight(A,B);
-}}};
-Timeline._Band.prototype._recenterDiv=function(){this._viewOffset=-this._viewLength*(Timeline._Band.SCROLL_MULTIPLES-1)/2;
-if(this._timeline.isHorizontal()){this._div.style.left=this._viewOffset+"px";
-this._div.style.width=(Timeline._Band.SCROLL_MULTIPLES*this._viewLength)+"px";
-}else{this._div.style.top=this._viewOffset+"px";
-this._div.style.height=(Timeline._Band.SCROLL_MULTIPLES*this._viewLength)+"px";
-}this.layout();
-};
-Timeline._Band.prototype._paintEvents=function(){this._eventPainter.paint();
-};
-Timeline._Band.prototype._softPaintEvents=function(){this._eventPainter.softPaint();
-};
-Timeline._Band.prototype._paintDecorators=function(){for(var A=0;
-A<this._decorators.length;
-A++){this._decorators[A].paint();
-}};
-Timeline._Band.prototype._softPaintDecorators=function(){for(var A=0;
-A<this._decorators.length;
-A++){this._decorators[A].softPaint();
-}};
-Timeline._Band.prototype.closeBubble=function(){SimileAjax.WindowManager.cancelPopups();
-};
-
-
-/* decorators.js */
-Timeline.SpanHighlightDecorator=function(A){this._unit=("unit" in A)?A.unit:SimileAjax.NativeDateUnit;
-this._startDate=(typeof A.startDate=="string")?this._unit.parseFromObject(A.startDate):A.startDate;
-this._endDate=(typeof A.endDate=="string")?this._unit.parseFromObject(A.endDate):A.endDate;
-this._startLabel=A.startLabel;
-this._endLabel=A.endLabel;
-this._color=A.color;
-this._cssClass=("cssClass" in A)?A.cssClass:null;
-this._opacity=("opacity" in A)?A.opacity:100;
-};
-Timeline.SpanHighlightDecorator.prototype.initialize=function(B,A){this._band=B;
-this._timeline=A;
-this._layerDiv=null;
-};
-Timeline.SpanHighlightDecorator.prototype.paint=function(){if(this._layerDiv!=null){this._band.removeLayerDiv(this._layerDiv);
-}this._layerDiv=this._band.createLayerDiv(10);
-this._layerDiv.setAttribute("name","span-highlight-decorator");
-this._layerDiv.style.display="none";
-var F=this._band.getMinDate();
-var C=this._band.getMaxDate();
-if(this._unit.compare(this._startDate,C)<0&&this._unit.compare(this._endDate,F)>0){F=this._unit.later(F,this._startDate);
-C=this._unit.earlier(C,this._endDate);
-var D=this._band.dateToPixelOffset(F);
-var K=this._band.dateToPixelOffset(C);
-var I=this._timeline.getDocument();
-var H=function(){var L=I.createElement("table");
-L.insertRow(0).insertCell(0);
-return L;
-};
-var B=I.createElement("div");
-B.className="timeline-highlight-decorator";
-if(this._cssClass){B.className+=" "+this._cssClass;
-}if(this._opacity<100){SimileAjax.Graphics.setOpacity(B,this._opacity);
-}this._layerDiv.appendChild(B);
-var J=H();
-J.className="timeline-highlight-label timeline-highlight-label-start";
-var G=J.rows[0].cells[0];
-G.innerHTML=this._startLabel;
-if(this._cssClass){G.className="label_"+this._cssClass;
-}this._layerDiv.appendChild(J);
-var A=H();
-A.className="timeline-highlight-label timeline-highlight-label-end";
-var E=A.rows[0].cells[0];
-E.innerHTML=this._endLabel;
-if(this._cssClass){E.className="label_"+this._cssClass;
-}this._layerDiv.appendChild(A);
-if(this._timeline.isHorizontal()){B.style.left=D+"px";
-B.style.width=(K-D)+"px";
-J.style.right=(this._band.getTotalViewLength()-D)+"px";
-J.style.width=(this._startLabel.length)+"em";
-A.style.left=K+"px";
-A.style.width=(this._endLabel.length)+"em";
-}else{B.style.top=D+"px";
-B.style.height=(K-D)+"px";
-J.style.bottom=D+"px";
-J.style.height="1.5px";
-A.style.top=K+"px";
-A.style.height="1.5px";
-}}this._layerDiv.style.display="block";
-};
-Timeline.SpanHighlightDecorator.prototype.softPaint=function(){};
-Timeline.PointHighlightDecorator=function(A){this._unit=("unit" in A)?A.unit:SimileAjax.NativeDateUnit;
-this._date=(typeof A.date=="string")?this._unit.parseFromObject(A.date):A.date;
-this._width=("width" in A)?A.width:10;
-this._color=A.color;
-this._cssClass=("cssClass" in A)?A.cssClass:"";
-this._opacity=("opacity" in A)?A.opacity:100;
-};
-Timeline.PointHighlightDecorator.prototype.initialize=function(B,A){this._band=B;
-this._timeline=A;
-this._layerDiv=null;
-};
-Timeline.PointHighlightDecorator.prototype.paint=function(){if(this._layerDiv!=null){this._band.removeLayerDiv(this._layerDiv);
-}this._layerDiv=this._band.createLayerDiv(10);
-this._layerDiv.setAttribute("name","span-highlight-decorator");
-this._layerDiv.style.display="none";
-var C=this._band.getMinDate();
-var E=this._band.getMaxDate();
-if(this._unit.compare(this._date,E)<0&&this._unit.compare(this._date,C)>0){var B=this._band.dateToPixelOffset(this._date);
-var A=B-Math.round(this._width/2);
-var D=this._timeline.getDocument();
-var F=D.createElement("div");
-F.className="timeline-highlight-point-decorator";
-F.className+=" "+this._cssClass;
-if(this._opacity<100){SimileAjax.Graphics.setOpacity(F,this._opacity);
-}this._layerDiv.appendChild(F);
-if(this._timeline.isHorizontal()){F.style.left=A+"px";
-}else{F.style.top=A+"px";
-}}this._layerDiv.style.display="block";
-};
-Timeline.PointHighlightDecorator.prototype.softPaint=function(){};
-
-
-/* detailed-painter.js */
-Timeline.DetailedEventPainter=function(A){this._params=A;
-this._onSelectListeners=[];
-this._filterMatcher=null;
-this._highlightMatcher=null;
-this._frc=null;
-this._eventIdToElmt={};
-};
-Timeline.DetailedEventPainter.prototype.initialize=function(B,A){this._band=B;
-this._timeline=A;
-this._backLayer=null;
-this._eventLayer=null;
-this._lineLayer=null;
-this._highlightLayer=null;
-this._eventIdToElmt=null;
-};
-Timeline.DetailedEventPainter.prototype.getType=function(){return"detailed";
-};
-Timeline.DetailedEventPainter.prototype.addOnSelectListener=function(A){this._onSelectListeners.push(A);
-};
-Timeline.DetailedEventPainter.prototype.removeOnSelectListener=function(B){for(var A=0;
-A<this._onSelectListeners.length;
-A++){if(this._onSelectListeners[A]==B){this._onSelectListeners.splice(A,1);
-break;
-}}};
-Timeline.DetailedEventPainter.prototype.getFilterMatcher=function(){return this._filterMatcher;
-};
-Timeline.DetailedEventPainter.prototype.setFilterMatcher=function(A){this._filterMatcher=A;
-};
-Timeline.DetailedEventPainter.prototype.getHighlightMatcher=function(){return this._highlightMatcher;
-};
-Timeline.DetailedEventPainter.prototype.setHighlightMatcher=function(A){this._highlightMatcher=A;
-};
-Timeline.DetailedEventPainter.prototype.paint=function(){var B=this._band.getEventSource();
-if(B==null){return ;
-}this._eventIdToElmt={};
-this._prepareForPainting();
-var I=this._params.theme.event;
-var G=Math.max(I.track.height,this._frc.getLineHeight());
-var F={trackOffset:Math.round(this._band.getViewWidth()/2-G/2),trackHeight:G,trackGap:I.track.gap,trackIncrement:G+I.track.gap,icon:I.instant.icon,iconWidth:I.instant.iconWidth,iconHeight:I.instant.iconHeight,labelWidth:I.label.width};
-var C=this._band.getMinDate();
-var A=this._band.getMaxDate();
-var J=(this._filterMatcher!=null)?this._filterMatcher:function(K){return true;
-};
-var E=(this._highlightMatcher!=null)?this._highlightMatcher:function(K){return -1;
-};
-var D=B.getEventReverseIterator(C,A);
-while(D.hasNext()){var H=D.next();
-if(J(H)){this.paintEvent(H,F,this._params.theme,E(H));
-}}this._highlightLayer.style.display="block";
-this._lineLayer.style.display="block";
-this._eventLayer.style.display="block";
-this._band.updateEventTrackInfo(this._lowerTracks.length+this._upperTracks.length,F.trackIncrement);
-};
-Timeline.DetailedEventPainter.prototype.softPaint=function(){};
-Timeline.DetailedEventPainter.prototype._prepareForPainting=function(){var B=this._band;
-if(this._backLayer==null){this._backLayer=this._band.createLayerDiv(0,"timeline-band-events");
-this._backLayer.style.visibility="hidden";
-var A=document.createElement("span");
-A.className="timeline-event-label";
-this._backLayer.appendChild(A);
-this._frc=SimileAjax.Graphics.getFontRenderingContext(A);
-}this._frc.update();
-this._lowerTracks=[];
-this._upperTracks=[];
-if(this._highlightLayer!=null){B.removeLayerDiv(this._highlightLayer);
-}this._highlightLayer=B.createLayerDiv(105,"timeline-band-highlights");
-this._highlightLayer.style.display="none";
-if(this._lineLayer!=null){B.removeLayerDiv(this._lineLayer);
-}this._lineLayer=B.createLayerDiv(110,"timeline-band-lines");
-this._lineLayer.style.display="none";
-if(this._eventLayer!=null){B.removeLayerDiv(this._eventLayer);
-}this._eventLayer=B.createLayerDiv(110,"timeline-band-events");
-this._eventLayer.style.display="none";
-};
-Timeline.DetailedEventPainter.prototype.paintEvent=function(B,C,D,A){if(B.isInstant()){this.paintInstantEvent(B,C,D,A);
-}else{this.paintDurationEvent(B,C,D,A);
-}};
-Timeline.DetailedEventPainter.prototype.paintInstantEvent=function(B,C,D,A){if(B.isImprecise()){this.paintImpreciseInstantEvent(B,C,D,A);
-}else{this.paintPreciseInstantEvent(B,C,D,A);
-}};
-Timeline.DetailedEventPainter.prototype.paintDurationEvent=function(B,C,D,A){if(B.isImprecise()){this.paintImpreciseDurationEvent(B,C,D,A);
-}else{this.paintPreciseDurationEvent(B,C,D,A);
-}};
-Timeline.DetailedEventPainter.prototype.paintPreciseInstantEvent=function(K,N,Q,O){var S=this._timeline.getDocument();
-var J=K.getText();
-var E=K.getStart();
-var C=Math.round(this._band.dateToPixelOffset(E));
-var A=Math.round(C+N.iconWidth/2);
-var I=Math.round(C-N.iconWidth/2);
-var G=this._frc.computeSize(J);
-var D=this._findFreeTrackForSolid(A,C);
-var B=this._paintEventIcon(K,D,I,N,Q);
-var T=A+Q.event.label.offsetFromLine;
-var P=D;
-var F=this._getTrackData(D);
-if(Math.min(F.solid,F.text)>=T+G.width){F.solid=I;
-F.text=T;
-}else{F.solid=I;
-T=C+Q.event.label.offsetFromLine;
-P=this._findFreeTrackForText(D,T+G.width,function(U){U.line=C-2;
-});
-this._getTrackData(P).text=I;
-this._paintEventLine(K,C,D,P,N,Q);
-}var R=Math.round(N.trackOffset+P*N.trackIncrement+N.trackHeight/2-G.height/2);
-var M=this._paintEventLabel(K,J,T,R,G.width,G.height,Q);
-var L=this;
-var H=function(U,V,W){return L._onClickInstantEvent(B.elmt,V,K);
-};
-SimileAjax.DOM.registerEvent(B.elmt,"mousedown",H);
-SimileAjax.DOM.registerEvent(M.elmt,"mousedown",H);
-this._createHighlightDiv(O,B,Q);
-this._eventIdToElmt[K.getID()]=B.elmt;
-};
-Timeline.DetailedEventPainter.prototype.paintImpreciseInstantEvent=function(N,Q,V,R){var X=this._timeline.getDocument();
-var M=N.getText();
-var H=N.getStart();
-var S=N.getEnd();
-var E=Math.round(this._band.dateToPixelOffset(H));
-var B=Math.round(this._band.dateToPixelOffset(S));
-var A=Math.round(E+Q.iconWidth/2);
-var L=Math.round(E-Q.iconWidth/2);
-var J=this._frc.computeSize(M);
-var F=this._findFreeTrackForSolid(B,E);
-var G=this._paintEventTape(N,F,E,B,V.event.instant.impreciseColor,V.event.instant.impreciseOpacity,Q,V);
-var C=this._paintEventIcon(N,F,L,Q,V);
-var I=this._getTrackData(F);
-I.solid=L;
-var W=A+V.event.label.offsetFromLine;
-var D=W+J.width;
-var T;
-if(D<B){T=F;
-}else{W=E+V.event.label.offsetFromLine;
-D=W+J.width;
-T=this._findFreeTrackForText(F,D,function(Y){Y.line=E-2;
-});
-this._getTrackData(T).text=L;
-this._paintEventLine(N,E,F,T,Q,V);
-}var U=Math.round(Q.trackOffset+T*Q.trackIncrement+Q.trackHeight/2-J.height/2);
-var P=this._paintEventLabel(N,M,W,U,J.width,J.height,V);
-var O=this;
-var K=function(Y,Z,a){return O._onClickInstantEvent(C.elmt,Z,N);
-};
-SimileAjax.DOM.registerEvent(C.elmt,"mousedown",K);
-SimileAjax.DOM.registerEvent(G.elmt,"mousedown",K);
-SimileAjax.DOM.registerEvent(P.elmt,"mousedown",K);
-this._createHighlightDiv(R,C,V);
-this._eventIdToElmt[N.getID()]=C.elmt;
-};
-Timeline.DetailedEventPainter.prototype.paintPreciseDurationEvent=function(J,M,S,O){var T=this._timeline.getDocument();
-var I=J.getText();
-var D=J.getStart();
-var P=J.getEnd();
-var B=Math.round(this._band.dateToPixelOffset(D));
-var A=Math.round(this._band.dateToPixelOffset(P));
-var F=this._frc.computeSize(I);
-var E=this._findFreeTrackForSolid(A);
-var N=J.getColor();
-N=N!=null?N:S.event.duration.color;
-var C=this._paintEventTape(J,E,B,A,N,100,M,S);
-var H=this._getTrackData(E);
-H.solid=B;
-var U=B+S.event.label.offsetFromLine;
-var Q=this._findFreeTrackForText(E,U+F.width,function(V){V.line=B-2;
-});
-this._getTrackData(Q).text=B-2;
-this._paintEventLine(J,B,E,Q,M,S);
-var R=Math.round(M.trackOffset+Q*M.trackIncrement+M.trackHeight/2-F.height/2);
-var L=this._paintEventLabel(J,I,U,R,F.width,F.height,S);
-var K=this;
-var G=function(V,W,X){return K._onClickDurationEvent(C.elmt,W,J);
-};
-SimileAjax.DOM.registerEvent(C.elmt,"mousedown",G);
-SimileAjax.DOM.registerEvent(L.elmt,"mousedown",G);
-this._createHighlightDiv(O,C,S);
-this._eventIdToElmt[J.getID()]=C.elmt;
-};
-Timeline.DetailedEventPainter.prototype.paintImpreciseDurationEvent=function(L,P,W,S){var Z=this._timeline.getDocument();
-var K=L.getText();
-var D=L.getStart();
-var Q=L.getLatestStart();
-var T=L.getEnd();
-var X=L.getEarliestEnd();
-var B=Math.round(this._band.dateToPixelOffset(D));
-var F=Math.round(this._band.dateToPixelOffset(Q));
-var A=Math.round(this._band.dateToPixelOffset(T));
-var G=Math.round(this._band.dateToPixelOffset(X));
-var H=this._frc.computeSize(K);
-var E=this._findFreeTrackForSolid(A);
-var R=L.getColor();
-R=R!=null?R:W.event.duration.color;
-var O=this._paintEventTape(L,E,B,A,W.event.duration.impreciseColor,W.event.duration.impreciseOpacity,P,W);
-var C=this._paintEventTape(L,E,F,G,R,100,P,W);
-var J=this._getTrackData(E);
-J.solid=B;
-var Y=F+W.event.label.offsetFromLine;
-var U=this._findFreeTrackForText(E,Y+H.width,function(a){a.line=F-2;
-});
-this._getTrackData(U).text=F-2;
-this._paintEventLine(L,F,E,U,P,W);
-var V=Math.round(P.trackOffset+U*P.trackIncrement+P.trackHeight/2-H.height/2);
-var N=this._paintEventLabel(L,K,Y,V,H.width,H.height,W);
-var M=this;
-var I=function(a,b,c){return M._onClickDurationEvent(C.elmt,b,L);
-};
-SimileAjax.DOM.registerEvent(C.elmt,"mousedown",I);
-SimileAjax.DOM.registerEvent(N.elmt,"mousedown",I);
-this._createHighlightDiv(S,C,W);
-this._eventIdToElmt[L.getID()]=C.elmt;
-};
-Timeline.DetailedEventPainter.prototype._findFreeTrackForSolid=function(B,A){for(var D=0;
-true;
-D++){if(D<this._lowerTracks.length){var C=this._lowerTracks[D];
-if(Math.min(C.solid,C.text)>B&&(!(A)||C.line>A)){return D;
-}}else{this._lowerTracks.push({solid:Number.POSITIVE_INFINITY,text:Number.POSITIVE_INFINITY,line:Number.POSITIVE_INFINITY});
-return D;
-}if(D<this._upperTracks.length){var C=this._upperTracks[D];
-if(Math.min(C.solid,C.text)>B&&(!(A)||C.line>A)){return -1-D;
-}}else{this._upperTracks.push({solid:Number.POSITIVE_INFINITY,text:Number.POSITIVE_INFINITY,line:Number.POSITIVE_INFINITY});
-return -1-D;
-}}};
-Timeline.DetailedEventPainter.prototype._findFreeTrackForText=function(D,C,H){var F;
-var G;
-var B;
-var J;
-if(D<0){F=true;
-B=-D;
-G=this._findFreeUpperTrackForText(B,C);
-J=-1-G;
-}else{if(D>0){F=false;
-B=D+1;
-G=this._findFreeLowerTrackForText(B,C);
-J=G;
-}else{var A=this._findFreeUpperTrackForText(0,C);
-var I=this._findFreeLowerTrackForText(1,C);
-if(I-1<=A){F=false;
-B=1;
-G=I;
-J=G;
-}else{F=true;
-B=0;
-G=A;
-J=-1-G;
-}}}if(F){if(G==this._upperTracks.length){this._upperTracks.push({solid:Number.POSITIVE_INFINITY,text:Number.POSITIVE_INFINITY,line:Number.POSITIVE_INFINITY});
-}for(var E=B;
-E<G;
-E++){H(this._upperTracks[E]);
-}}else{if(G==this._lowerTracks.length){this._lowerTracks.push({solid:Number.POSITIVE_INFINITY,text:Number.POSITIVE_INFINITY,line:Number.POSITIVE_INFINITY});
-}for(var E=B;
-E<G;
-E++){H(this._lowerTracks[E]);
-}}return J;
-};
-Timeline.DetailedEventPainter.prototype._findFreeLowerTrackForText=function(A,C){for(;
-A<this._lowerTracks.length;
-A++){var B=this._lowerTracks[A];
-if(Math.min(B.solid,B.text)>=C){break;
-}}return A;
-};
-Timeline.DetailedEventPainter.prototype._findFreeUpperTrackForText=function(A,C){for(;
-A<this._upperTracks.length;
-A++){var B=this._upperTracks[A];
-if(Math.min(B.solid,B.text)>=C){break;
-}}return A;
-};
-Timeline.DetailedEventPainter.prototype._getTrackData=function(A){return(A<0)?this._upperTracks[-A-1]:this._lowerTracks[A];
-};
-Timeline.DetailedEventPainter.prototype._paintEventLine=function(I,C,F,A,G,D){var H=Math.round(G.trackOffset+F*G.trackIncrement+G.trackHeight/2);
-var J=Math.round(Math.abs(A-F)*G.trackIncrement);
-var E="1px solid "+D.event.label.lineColor;
-var B=this._timeline.getDocument().createElement("div");
-B.style.position="absolute";
-B.style.left=C+"px";
-B.style.width=D.event.label.offsetFromLine+"px";
-B.style.height=J+"px";
-if(F>A){B.style.top=(H-J)+"px";
-B.style.borderTop=E;
-}else{B.style.top=H+"px";
-B.style.borderBottom=E;
-}B.style.borderLeft=E;
-this._lineLayer.appendChild(B);
-};
-Timeline.DetailedEventPainter.prototype._paintEventIcon=function(I,E,B,F,D){var H=I.getIcon();
-H=H!=null?H:F.icon;
-var J=F.trackOffset+E*F.trackIncrement+F.trackHeight/2;
-var G=Math.round(J-F.iconHeight/2);
-var C=SimileAjax.Graphics.createTranslucentImage(H);
-var A=this._timeline.getDocument().createElement("div");
-A.style.position="absolute";
-A.style.left=B+"px";
-A.style.top=G+"px";
-A.appendChild(C);
-A.style.cursor="pointer";
-if(I._title!=null){A.title=I._title;
-}this._eventLayer.appendChild(A);
-return{left:B,top:G,width:F.iconWidth,height:F.iconHeight,elmt:A};
-};
-Timeline.DetailedEventPainter.prototype._paintEventLabel=function(H,I,B,F,A,J,D){var G=this._timeline.getDocument();
-var K=G.createElement("div");
-K.style.position="absolute";
-K.style.left=B+"px";
-K.style.width=A+"px";
-K.style.top=F+"px";
-K.style.height=J+"px";
-K.style.backgroundColor=D.event.label.backgroundColor;
-SimileAjax.Graphics.setOpacity(K,D.event.label.backgroundOpacity);
-this._eventLayer.appendChild(K);
-var E=G.createElement("div");
-E.style.position="absolute";
-E.style.left=B+"px";
-E.style.width=A+"px";
-E.style.top=F+"px";
-E.innerHTML=I;
-E.style.cursor="pointer";
-if(H._title!=null){E.title=H._title;
-}var C=H.getTextColor();
-if(C==null){C=H.getColor();
-}if(C!=null){E.style.color=C;
-}this._eventLayer.appendChild(E);
-return{left:B,top:F,width:A,height:J,elmt:E};
-};
-Timeline.DetailedEventPainter.prototype._paintEventTape=function(L,H,E,A,C,G,I,F){var B=A-E;
-var D=F.event.tape.height;
-var M=I.trackOffset+H*I.trackIncrement+I.trackHeight/2;
-var J=Math.round(M-D/2);
-var K=this._timeline.getDocument().createElement("div");
-K.style.position="absolute";
-K.style.left=E+"px";
-K.style.width=B+"px";
-K.style.top=J+"px";
-K.style.height=D+"px";
-K.style.backgroundColor=C;
-K.style.overflow="hidden";
-K.style.cursor="pointer";
-if(L._title!=null){K.title=L._title;
-}SimileAjax.Graphics.setOpacity(K,G);
-this._eventLayer.appendChild(K);
-return{left:E,top:J,width:B,height:D,elmt:K};
-};
-Timeline.DetailedEventPainter.prototype._createHighlightDiv=function(A,C,E){if(A>=0){var D=this._timeline.getDocument();
-var G=E.event;
-var B=G.highlightColors[Math.min(A,G.highlightColors.length-1)];
-var F=D.createElement("div");
-F.style.position="absolute";
-F.style.overflow="hidden";
-F.style.left=(C.left-2)+"px";
-F.style.width=(C.width+4)+"px";
-F.style.top=(C.top-2)+"px";
-F.style.height=(C.height+4)+"px";
-F.style.background=B;
-this._highlightLayer.appendChild(F);
-}};
-Timeline.DetailedEventPainter.prototype._onClickInstantEvent=function(B,C,A){var D=SimileAjax.DOM.getPageCoordinates(B);
-this._showBubble(D.left+Math.ceil(B.offsetWidth/2),D.top+Math.ceil(B.offsetHeight/2),A);
-this._fireOnSelect(A.getID());
-C.cancelBubble=true;
-SimileAjax.DOM.cancelEvent(C);
-return false;
-};
-Timeline.DetailedEventPainter.prototype._onClickDurationEvent=function(D,C,B){if("pageX" in C){var A=C.pageX;
-var F=C.pageY;
-}else{var E=SimileAjax.DOM.getPageCoordinates(D);
-var A=C.offsetX+E.left;
-var F=C.offsetY+E.top;
-}this._showBubble(A,F,B);
-this._fireOnSelect(B.getID());
-C.cancelBubble=true;
-SimileAjax.DOM.cancelEvent(C);
-return false;
-};
-Timeline.DetailedEventPainter.prototype.showBubble=function(A){var B=this._eventIdToElmt[A.getID()];
-if(B){var C=SimileAjax.DOM.getPageCoordinates(B);
-this._showBubble(C.left+B.offsetWidth/2,C.top+B.offsetHeight/2,A);
-}};
-Timeline.DetailedEventPainter.prototype._showBubble=function(A,E,B){var D=document.createElement("div");
-var C=this._params.theme.event.bubble;
-B.fillInfoBubble(D,this._params.theme,this._band.getLabeller());
-SimileAjax.WindowManager.cancelPopups();
-SimileAjax.Graphics.createBubbleForContentAndPoint(D,A,E,C.width,null,C.maxHeight);
-};
-Timeline.DetailedEventPainter.prototype._fireOnSelect=function(B){for(var A=0;
-A<this._onSelectListeners.length;
-A++){this._onSelectListeners[A](B);
-}};
-
-
-/* ether-painters.js */
-Timeline.GregorianEtherPainter=function(A){this._params=A;
-this._theme=A.theme;
-this._unit=A.unit;
-this._multiple=("multiple" in A)?A.multiple:1;
-};
-Timeline.GregorianEtherPainter.prototype.initialize=function(C,B){this._band=C;
-this._timeline=B;
-this._backgroundLayer=C.createLayerDiv(0);
-this._backgroundLayer.setAttribute("name","ether-background");
-this._backgroundLayer.className="timeline-ether-bg";
-this._markerLayer=null;
-this._lineLayer=null;
-var D=("align" in this._params&&this._params.align!=undefined)?this._params.align:this._theme.ether.interval.marker[B.isHorizontal()?"hAlign":"vAlign"];
-var A=("showLine" in this._params)?this._params.showLine:this._theme.ether.interval.line.show;
-this._intervalMarkerLayout=new Timeline.EtherIntervalMarkerLayout(this._timeline,this._band,this._theme,D,A);
-this._highlight=new Timeline.EtherHighlight(this._timeline,this._band,this._theme,this._backgroundLayer);
-};
-Timeline.GregorianEtherPainter.prototype.setHighlight=function(A,B){this._highlight.position(A,B);
-};
-Timeline.GregorianEtherPainter.prototype.paint=function(){if(this._markerLayer){this._band.removeLayerDiv(this._markerLayer);
-}this._markerLayer=this._band.createLayerDiv(100);
-this._markerLayer.setAttribute("name","ether-markers");
-this._markerLayer.style.display="none";
-if(this._lineLayer){this._band.removeLayerDiv(this._lineLayer);
-}this._lineLayer=this._band.createLayerDiv(1);
-this._lineLayer.setAttribute("name","ether-lines");
-this._lineLayer.style.display="none";
-var C=this._band.getMinDate();
-var F=this._band.getMaxDate();
-var B=this._band.getTimeZone();
-var E=this._band.getLabeller();
-SimileAjax.DateTime.roundDownToInterval(C,this._unit,B,this._multiple,this._theme.firstDayOfWeek);
-var D=this;
-var A=function(G){for(var H=0;
-H<D._multiple;
-H++){SimileAjax.DateTime.incrementByInterval(G,D._unit);
-}};
-while(C.getTime()<F.getTime()){this._intervalMarkerLayout.createIntervalMarker(C,E,this._unit,this._markerLayer,this._lineLayer);
-A(C);
-}this._markerLayer.style.display="block";
-this._lineLayer.style.display="block";
-};
-Timeline.GregorianEtherPainter.prototype.softPaint=function(){};
-Timeline.GregorianEtherPainter.prototype.zoom=function(A){if(A!=0){this._unit+=A;
-}};
-Timeline.HotZoneGregorianEtherPainter=function(G){this._params=G;
-this._theme=G.theme;
-this._zones=[{startTime:Number.NEGATIVE_INFINITY,endTime:Number.POSITIVE_INFINITY,unit:G.unit,multiple:1}];
-for(var E=0;
-E<G.zones.length;
-E++){var B=G.zones[E];
-var D=SimileAjax.DateTime.parseGregorianDateTime(B.start).getTime();
-var F=SimileAjax.DateTime.parseGregorianDateTime(B.end).getTime();
-for(var C=0;
-C<this._zones.length&&F>D;
-C++){var A=this._zones[C];
-if(D<A.endTime){if(D>A.startTime){this._zones.splice(C,0,{startTime:A.startTime,endTime:D,unit:A.unit,multiple:A.multiple});
-C++;
-A.startTime=D;
-}if(F<A.endTime){this._zones.splice(C,0,{startTime:D,endTime:F,unit:B.unit,multiple:(B.multiple)?B.multiple:1});
-C++;
-A.startTime=F;
-D=F;
-}else{A.multiple=B.multiple;
-A.unit=B.unit;
-D=A.endTime;
-}}}}};
-Timeline.HotZoneGregorianEtherPainter.prototype.initialize=function(C,B){this._band=C;
-this._timeline=B;
-this._backgroundLayer=C.createLayerDiv(0);
-this._backgroundLayer.setAttribute("name","ether-background");
-this._backgroundLayer.className="timeline-ether-bg";
-this._markerLayer=null;
-this._lineLayer=null;
-var D=("align" in this._params&&this._params.align!=undefined)?this._params.align:this._theme.ether.interval.marker[B.isHorizontal()?"hAlign":"vAlign"];
-var A=("showLine" in this._params)?this._params.showLine:this._theme.ether.interval.line.show;
-this._intervalMarkerLayout=new Timeline.EtherIntervalMarkerLayout(this._timeline,this._band,this._theme,D,A);
-this._highlight=new Timeline.EtherHighlight(this._timeline,this._band,this._theme,this._backgroundLayer);
-};
-Timeline.HotZoneGregorianEtherPainter.prototype.setHighlight=function(A,B){this._highlight.position(A,B);
-};
-Timeline.HotZoneGregorianEtherPainter.prototype.paint=function(){if(this._markerLayer){this._band.removeLayerDiv(this._markerLayer);
-}this._markerLayer=this._band.createLayerDiv(100);
-this._markerLayer.setAttribute("name","ether-markers");
-this._markerLayer.style.display="none";
-if(this._lineLayer){this._band.removeLayerDiv(this._lineLayer);
-}this._lineLayer=this._band.createLayerDiv(1);
-this._lineLayer.setAttribute("name","ether-lines");
-this._lineLayer.style.display="none";
-var D=this._band.getMinDate();
-var A=this._band.getMaxDate();
-var K=this._band.getTimeZone();
-var I=this._band.getLabeller();
-var B=this;
-var L=function(N,M){for(var O=0;
-O<M.multiple;
-O++){SimileAjax.DateTime.incrementByInterval(N,M.unit);
-}};
-var C=0;
-while(C<this._zones.length){if(D.getTime()<this._zones[C].endTime){break;
-}C++;
-}var E=this._zones.length-1;
-while(E>=0){if(A.getTime()>this._zones[E].startTime){break;
-}E--;
-}for(var H=C;
-H<=E;
-H++){var G=this._zones[H];
-var J=new Date(Math.max(D.getTime(),G.startTime));
-var F=new Date(Math.min(A.getTime(),G.endTime));
-SimileAjax.DateTime.roundDownToInterval(J,G.unit,K,G.multiple,this._theme.firstDayOfWeek);
-SimileAjax.DateTime.roundUpToInterval(F,G.unit,K,G.multiple,this._theme.firstDayOfWeek);
-while(J.getTime()<F.getTime()){this._intervalMarkerLayout.createIntervalMarker(J,I,G.unit,this._markerLayer,this._lineLayer);
-L(J,G);
-}}this._markerLayer.style.display="block";
-this._lineLayer.style.display="block";
-};
-Timeline.HotZoneGregorianEtherPainter.prototype.softPaint=function(){};
-Timeline.HotZoneGregorianEtherPainter.prototype.zoom=function(B){if(B!=0){for(var A=0;
-A<this._zones.length;
-++A){if(this._zones[A]){this._zones[A].unit+=B;
-}}}};
-Timeline.YearCountEtherPainter=function(A){this._params=A;
-this._theme=A.theme;
-this._startDate=SimileAjax.DateTime.parseGregorianDateTime(A.startDate);
-this._multiple=("multiple" in A)?A.multiple:1;
-};
-Timeline.YearCountEtherPainter.prototype.initialize=function(C,B){this._band=C;
-this._timeline=B;
-this._backgroundLayer=C.createLayerDiv(0);
-this._backgroundLayer.setAttribute("name","ether-background");
-this._backgroundLayer.className="timeline-ether-bg";
-this._markerLayer=null;
-this._lineLayer=null;
-var D=("align" in this._params)?this._params.align:this._theme.ether.interval.marker[B.isHorizontal()?"hAlign":"vAlign"];
-var A=("showLine" in this._params)?this._params.showLine:this._theme.ether.interval.line.show;
-this._intervalMarkerLayout=new Timeline.EtherIntervalMarkerLayout(this._timeline,this._band,this._theme,D,A);
-this._highlight=new Timeline.EtherHighlight(this._timeline,this._band,this._theme,this._backgroundLayer);
-};
-Timeline.YearCountEtherPainter.prototype.setHighlight=function(A,B){this._highlight.position(A,B);
-};
-Timeline.YearCountEtherPainter.prototype.paint=function(){if(this._markerLayer){this._band.removeLayerDiv(this._markerLayer);
-}this._markerLayer=this._band.createLayerDiv(100);
-this._markerLayer.setAttribute("name","ether-markers");
-this._markerLayer.style.display="none";
-if(this._lineLayer){this._band.removeLayerDiv(this._lineLayer);
-}this._lineLayer=this._band.createLayerDiv(1);
-this._lineLayer.setAttribute("name","ether-lines");
-this._lineLayer.style.display="none";
-var B=new Date(this._startDate.getTime());
-var F=this._band.getMaxDate();
-var E=this._band.getMinDate().getUTCFullYear()-this._startDate.getUTCFullYear();
-B.setUTCFullYear(this._band.getMinDate().getUTCFullYear()-E%this._multiple);
-var C=this;
-var A=function(G){for(var H=0;
-H<C._multiple;
-H++){SimileAjax.DateTime.incrementByInterval(G,SimileAjax.DateTime.YEAR);
-}};
-var D={labelInterval:function(G,I){var H=G.getUTCFullYear()-C._startDate.getUTCFullYear();
-return{text:H,emphasized:H==0};
-}};
-while(B.getTime()<F.getTime()){this._intervalMarkerLayout.createIntervalMarker(B,D,SimileAjax.DateTime.YEAR,this._markerLayer,this._lineLayer);
-A(B);
-}this._markerLayer.style.display="block";
-this._lineLayer.style.display="block";
-};
-Timeline.YearCountEtherPainter.prototype.softPaint=function(){};
-Timeline.QuarterlyEtherPainter=function(A){this._params=A;
-this._theme=A.theme;
-this._startDate=SimileAjax.DateTime.parseGregorianDateTime(A.startDate);
-};
-Timeline.QuarterlyEtherPainter.prototype.initialize=function(C,B){this._band=C;
-this._timeline=B;
-this._backgroundLayer=C.createLayerDiv(0);
-this._backgroundLayer.setAttribute("name","ether-background");
-this._backgroundLayer.className="timeline-ether-bg";
-this._markerLayer=null;
-this._lineLayer=null;
-var D=("align" in this._params)?this._params.align:this._theme.ether.interval.marker[B.isHorizontal()?"hAlign":"vAlign"];
-var A=("showLine" in this._params)?this._params.showLine:this._theme.ether.interval.line.show;
-this._intervalMarkerLayout=new Timeline.EtherIntervalMarkerLayout(this._timeline,this._band,this._theme,D,A);
-this._highlight=new Timeline.EtherHighlight(this._timeline,this._band,this._theme,this._backgroundLayer);
-};
-Timeline.QuarterlyEtherPainter.prototype.setHighlight=function(A,B){this._highlight.position(A,B);
-};
-Timeline.QuarterlyEtherPainter.prototype.paint=function(){if(this._markerLayer){this._band.removeLayerDiv(this._markerLayer);
-}this._markerLayer=this._band.createLayerDiv(100);
-this._markerLayer.setAttribute("name","ether-markers");
-this._markerLayer.style.display="none";
-if(this._lineLayer){this._band.removeLayerDiv(this._lineLayer);
-}this._lineLayer=this._band.createLayerDiv(1);
-this._lineLayer.setAttribute("name","ether-lines");
-this._lineLayer.style.display="none";
-var B=new Date(0);
-var E=this._band.getMaxDate();
-B.setUTCFullYear(Math.max(this._startDate.getUTCFullYear(),this._band.getMinDate().getUTCFullYear()));
-B.setUTCMonth(this._startDate.getUTCMonth());
-var C=this;
-var A=function(F){F.setUTCMonth(F.getUTCMonth()+3);
-};
-var D={labelInterval:function(F,H){var G=(4+(F.getUTCMonth()-C._startDate.getUTCMonth())/3)%4;
-if(G!=0){return{text:"Q"+(G+1),emphasized:false};
-}else{return{text:"Y"+(F.getUTCFullYear()-C._startDate.getUTCFullYear()+1),emphasized:true};
-}}};
-while(B.getTime()<E.getTime()){this._intervalMarkerLayout.createIntervalMarker(B,D,SimileAjax.DateTime.YEAR,this._markerLayer,this._lineLayer);
-A(B);
-}this._markerLayer.style.display="block";
-this._lineLayer.style.display="block";
-};
-Timeline.QuarterlyEtherPainter.prototype.softPaint=function(){};
-Timeline.EtherIntervalMarkerLayout=function(M,L,C,E,H){var A=M.isHorizontal();
-if(A){if(E=="Top"){this.positionDiv=function(O,N){O.style.left=N+"px";
-O.style.top="0px";
-};
-}else{this.positionDiv=function(O,N){O.style.left=N+"px";
-O.style.bottom="0px";
-};
-}}else{if(E=="Left"){this.positionDiv=function(O,N){O.style.top=N+"px";
-O.style.left="0px";
-};
-}else{this.positionDiv=function(O,N){O.style.top=N+"px";
-O.style.right="0px";
-};
-}}var D=C.ether.interval.marker;
-var I=C.ether.interval.line;
-var B=C.ether.interval.weekend;
-var K=(A?"h":"v")+E;
-var G=D[K+"Styler"];
-var J=D[K+"EmphasizedStyler"];
-var F=SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.DAY];
-this.createIntervalMarker=function(T,a,b,c,Q){var U=Math.round(L.dateToPixelOffset(T));
-if(H&&b!=SimileAjax.DateTime.WEEK){var V=M.getDocument().createElement("div");
-V.className="timeline-ether-lines";
-if(I.opacity<100){SimileAjax.Graphics.setOpacity(V,I.opacity);
-}if(A){V.style.left=U+"px";
-}else{V.style.top=U+"px";
-}Q.appendChild(V);
-}if(b==SimileAjax.DateTime.WEEK){var N=C.firstDayOfWeek;
-var W=new Date(T.getTime()+(6-N-7)*F);
-var Z=new Date(W.getTime()+2*F);
-var X=Math.round(L.dateToPixelOffset(W));
-var S=Math.round(L.dateToPixelOffset(Z));
-var R=Math.max(1,S-X);
-var P=M.getDocument().createElement("div");
-P.className="timeline-ether-weekends";
-if(B.opacity<100){SimileAjax.Graphics.setOpacity(P,B.opacity);
-}if(A){P.style.left=X+"px";
-P.style.width=R+"px";
-}else{P.style.top=X+"px";
-P.style.height=R+"px";
-}Q.appendChild(P);
-}var Y=a.labelInterval(T,b);
-var O=M.getDocument().createElement("div");
-O.innerHTML=Y.text;
-O.className="timeline-date-label";
-if(Y.emphasized){O.className+=" timeline-date-label-em";
-}this.positionDiv(O,U);
-c.appendChild(O);
-return O;
-};
-};
-Timeline.EtherHighlight=function(C,E,D,B){var A=C.isHorizontal();
-this._highlightDiv=null;
-this._createHighlightDiv=function(){if(this._highlightDiv==null){this._highlightDiv=C.getDocument().createElement("div");
-this._highlightDiv.setAttribute("name","ether-highlight");
-this._highlightDiv.className="timeline-ether-highlight";
-var F=D.ether.highlightOpacity;
-if(F<100){SimileAjax.Graphics.setOpacity(this._highlightDiv,F);
-}B.appendChild(this._highlightDiv);
-}};
-this.position=function(F,I){this._createHighlightDiv();
-var J=Math.round(E.dateToPixelOffset(F));
-var H=Math.round(E.dateToPixelOffset(I));
-var G=Math.max(H-J,3);
-if(A){this._highlightDiv.style.left=J+"px";
-this._highlightDiv.style.width=G+"px";
-this._highlightDiv.style.height=(E.getViewWidth()-4)+"px";
-}else{this._highlightDiv.style.top=J+"px";
-this._highlightDiv.style.height=G+"px";
-this._highlightDiv.style.width=(E.getViewWidth()-4)+"px";
-}};
-};
-
-
-/* ethers.js */
-Timeline.LinearEther=function(A){this._params=A;
-this._interval=A.interval;
-this._pixelsPerInterval=A.pixelsPerInterval;
-};
-Timeline.LinearEther.prototype.initialize=function(B,A){this._band=B;
-this._timeline=A;
-this._unit=A.getUnit();
-if("startsOn" in this._params){this._start=this._unit.parseFromObject(this._params.startsOn);
-}else{if("endsOn" in this._params){this._start=this._unit.parseFromObject(this._params.endsOn);
-this.shiftPixels(-this._timeline.getPixelLength());
-}else{if("centersOn" in this._params){this._start=this._unit.parseFromObject(this._params.centersOn);
-this.shiftPixels(-this._timeline.getPixelLength()/2);
-}else{this._start=this._unit.makeDefaultValue();
-this.shiftPixels(-this._timeline.getPixelLength()/2);
-}}}};
-Timeline.LinearEther.prototype.setDate=function(A){this._start=this._unit.cloneValue(A);
-};
-Timeline.LinearEther.prototype.shiftPixels=function(B){var A=this._interval*B/this._pixelsPerInterval;
-this._start=this._unit.change(this._start,A);
-};
-Timeline.LinearEther.prototype.dateToPixelOffset=function(A){var B=this._unit.compare(A,this._start);
-return this._pixelsPerInterval*B/this._interval;
-};
-Timeline.LinearEther.prototype.pixelOffsetToDate=function(B){var A=B*this._interval/this._pixelsPerInterval;
-return this._unit.change(this._start,A);
-};
-Timeline.LinearEther.prototype.zoom=function(D){var B=0;
-var A=this._band._zoomIndex;
-var C=A;
-if(D&&(A>0)){C=A-1;
-}if(!D&&(A<(this._band._zoomSteps.length-1))){C=A+1;
-}this._band._zoomIndex=C;
-this._interval=SimileAjax.DateTime.gregorianUnitLengths[this._band._zoomSteps[C].unit];
-this._pixelsPerInterval=this._band._zoomSteps[C].pixelsPerInterval;
-B=this._band._zoomSteps[C].unit-this._band._zoomSteps[A].unit;
-return B;
-};
-Timeline.HotZoneEther=function(A){this._params=A;
-this._interval=A.interval;
-this._pixelsPerInterval=A.pixelsPerInterval;
-this._theme=A.theme;
-};
-Timeline.HotZoneEther.prototype.initialize=function(H,I){this._band=H;
-this._timeline=I;
-this._unit=I.getUnit();
-this._zones=[{startTime:Number.NEGATIVE_INFINITY,endTime:Number.POSITIVE_INFINITY,magnify:1}];
-var B=this._params;
-for(var D=0;
-D<B.zones.length;
-D++){var G=B.zones[D];
-var E=this._unit.parseFromObject(G.start);
-var F=this._unit.parseFromObject(G.end);
-for(var C=0;
-C<this._zones.length&&this._unit.compare(F,E)>0;
-C++){var A=this._zones[C];
-if(this._unit.compare(E,A.endTime)<0){if(this._unit.compare(E,A.startTime)>0){this._zones.splice(C,0,{startTime:A.startTime,endTime:E,magnify:A.magnify});
-C++;
-A.startTime=E;
-}if(this._unit.compare(F,A.endTime)<0){this._zones.splice(C,0,{startTime:E,endTime:F,magnify:G.magnify*A.magnify});
-C++;
-A.startTime=F;
-E=F;
-}else{A.magnify*=G.magnify;
-E=A.endTime;
-}}}}if("startsOn" in this._params){this._start=this._unit.parseFromObject(this._params.startsOn);
-}else{if("endsOn" in this._params){this._start=this._unit.parseFromObject(this._params.endsOn);
-this.shiftPixels(-this._timeline.getPixelLength());
-}else{if("centersOn" in this._params){this._start=this._unit.parseFromObject(this._params.centersOn);
-this.shiftPixels(-this._timeline.getPixelLength()/2);
-}else{this._start=this._unit.makeDefaultValue();
-this.shiftPixels(-this._timeline.getPixelLength()/2);
-}}}};
-Timeline.HotZoneEther.prototype.setDate=function(A){this._start=this._unit.cloneValue(A);
-};
-Timeline.HotZoneEther.prototype.shiftPixels=function(A){this._start=this.pixelOffsetToDate(A);
-};
-Timeline.HotZoneEther.prototype.dateToPixelOffset=function(A){return this._dateDiffToPixelOffset(this._start,A);
-};
-Timeline.HotZoneEther.prototype.pixelOffsetToDate=function(A){return this._pixelOffsetToDate(A,this._start);
-};
-Timeline.HotZoneEther.prototype.zoom=function(D){var B=0;
-var A=this._band._zoomIndex;
-var C=A;
-if(D&&(A>0)){C=A-1;
-}if(!D&&(A<(this._band._zoomSteps.length-1))){C=A+1;
-}this._band._zoomIndex=C;
-this._interval=SimileAjax.DateTime.gregorianUnitLengths[this._band._zoomSteps[C].unit];
-this._pixelsPerInterval=this._band._zoomSteps[C].pixelsPerInterval;
-B=this._band._zoomSteps[C].unit-this._band._zoomSteps[A].unit;
-return B;
-};
-Timeline.HotZoneEther.prototype._dateDiffToPixelOffset=function(I,D){var B=this._getScale();
-var H=I;
-var C=D;
-var A=0;
-if(this._unit.compare(H,C)<0){var G=0;
-while(G<this._zones.length){if(this._unit.compare(H,this._zones[G].endTime)<0){break;
-}G++;
-}while(this._unit.compare(H,C)<0){var E=this._zones[G];
-var F=this._unit.earlier(C,E.endTime);
-A+=(this._unit.compare(F,H)/(B/E.magnify));
-H=F;
-G++;
-}}else{var G=this._zones.length-1;
-while(G>=0){if(this._unit.compare(H,this._zones[G].startTime)>0){break;
-}G--;
-}while(this._unit.compare(H,C)>0){var E=this._zones[G];
-var F=this._unit.later(C,E.startTime);
-A+=(this._unit.compare(F,H)/(B/E.magnify));
-H=F;
-G--;
-}}return A;
-};
-Timeline.HotZoneEther.prototype._pixelOffsetToDate=function(H,C){var G=this._getScale();
-var E=C;
-if(H>0){var F=0;
-while(F<this._zones.length){if(this._unit.compare(E,this._zones[F].endTime)<0){break;
-}F++;
-}while(H>0){var A=this._zones[F];
-var D=G/A.magnify;
-if(A.endTime==Number.POSITIVE_INFINITY){E=this._unit.change(E,H*D);
-H=0;
-}else{var B=this._unit.compare(A.endTime,E)/D;
-if(B>H){E=this._unit.change(E,H*D);
-H=0;
-}else{E=A.endTime;
-H-=B;
-}}F++;
-}}else{var F=this._zones.length-1;
-while(F>=0){if(this._unit.compare(E,this._zones[F].startTime)>0){break;
-}F--;
-}H=-H;
-while(H>0){var A=this._zones[F];
-var D=G/A.magnify;
-if(A.startTime==Number.NEGATIVE_INFINITY){E=this._unit.change(E,-H*D);
-H=0;
-}else{var B=this._unit.compare(E,A.startTime)/D;
-if(B>H){E=this._unit.change(E,-H*D);
-H=0;
-}else{E=A.startTime;
-H-=B;
-}}F--;
-}}return E;
-};
-Timeline.HotZoneEther.prototype._getScale=function(){return this._interval/this._pixelsPerInterval;
-};
-
-
-/* event-utils.js */
-Timeline.EventUtils={};
-Timeline.EventUtils.getNewEventID=function(){if(this._lastEventID==null){this._lastEventID=0;
-}this._lastEventID+=1;
-return"e"+this._lastEventID;
-};
-Timeline.EventUtils.decodeEventElID=function(B){var D=B.split("-");
-if(D[1]!="tl"){alert("Internal Timeline problem 101, please consult support");
-return{band:null,evt:null};
-}var C=Timeline.getTimelineFromID(D[2]);
-var E=C.getBand(D[3]);
-var A=E.getEventSource.getEvent(D[4]);
-return{band:E,evt:A};
-};
-Timeline.EventUtils.encodeEventElID=function(C,D,B,A){return B+"-tl-"+C.timelineID+"-"+D.getIndex()+"-"+A.getID();
-};
-
-
-/* labellers.js */
-Timeline.GregorianDateLabeller=function(A,B){this._locale=A;
-this._timeZone=B;
-};
-Timeline.GregorianDateLabeller.monthNames=[];
-Timeline.GregorianDateLabeller.dayNames=[];
-Timeline.GregorianDateLabeller.labelIntervalFunctions=[];
-Timeline.GregorianDateLabeller.getMonthName=function(B,A){return Timeline.GregorianDateLabeller.monthNames[A][B];
-};
-Timeline.GregorianDateLabeller.prototype.labelInterval=function(A,C){var B=Timeline.GregorianDateLabeller.labelIntervalFunctions[this._locale];
-if(B==null){B=Timeline.GregorianDateLabeller.prototype.defaultLabelInterval;
-}return B.call(this,A,C);
-};
-Timeline.GregorianDateLabeller.prototype.labelPrecise=function(A){return SimileAjax.DateTime.removeTimeZoneOffset(A,this._timeZone).toUTCString();
-};
-Timeline.GregorianDateLabeller.prototype.defaultLabelInterval=function(B,F){var C;
-var E=false;
-B=SimileAjax.DateTime.removeTimeZoneOffset(B,this._timeZone);
-switch(F){case SimileAjax.DateTime.MILLISECOND:C=B.getUTCMilliseconds();
-break;
-case SimileAjax.DateTime.SECOND:C=B.getUTCSeconds();
-break;
-case SimileAjax.DateTime.MINUTE:var A=B.getUTCMinutes();
-if(A==0){C=B.getUTCHours()+":00";
-E=true;
-}else{C=A;
-}break;
-case SimileAjax.DateTime.HOUR:C=B.getUTCHours()+"hr";
-break;
-case SimileAjax.DateTime.DAY:C=Timeline.GregorianDateLabeller.getMonthName(B.getUTCMonth(),this._locale)+" "+B.getUTCDate();
-break;
-case SimileAjax.DateTime.WEEK:C=Timeline.GregorianDateLabeller.getMonthName(B.getUTCMonth(),this._locale)+" "+B.getUTCDate();
-break;
-case SimileAjax.DateTime.MONTH:var A=B.getUTCMonth();
-if(A!=0){C=Timeline.GregorianDateLabeller.getMonthName(A,this._locale);
-break;
-}case SimileAjax.DateTime.YEAR:case SimileAjax.DateTime.DECADE:case SimileAjax.DateTime.CENTURY:case SimileAjax.DateTime.MILLENNIUM:var D=B.getUTCFullYear();
-if(D>0){C=B.getUTCFullYear();
-}else{C=(1-D)+"BC";
-}E=(F==SimileAjax.DateTime.MONTH)||(F==SimileAjax.DateTime.DECADE&&D%100==0)||(F==SimileAjax.DateTime.CENTURY&&D%1000==0);
-break;
-default:C=B.toUTCString();
-}return{text:C,emphasized:E};
-};
-
-
-/* original-painter.js */
-Timeline.OriginalEventPainter=function(A){this._params=A;
-this._onSelectListeners=[];
-this._eventPaintListeners=[];
-this._filterMatcher=null;
-this._highlightMatcher=null;
-this._frc=null;
-this._eventIdToElmt={};
-};
-Timeline.OriginalEventPainter.prototype.initialize=function(B,A){this._band=B;
-this._timeline=A;
-this._backLayer=null;
-this._eventLayer=null;
-this._lineLayer=null;
-this._highlightLayer=null;
-this._eventIdToElmt=null;
-};
-Timeline.OriginalEventPainter.prototype.getType=function(){return"original";
-};
-Timeline.OriginalEventPainter.prototype.addOnSelectListener=function(A){this._onSelectListeners.push(A);
-};
-Timeline.OriginalEventPainter.prototype.removeOnSelectListener=function(B){for(var A=0;
-A<this._onSelectListeners.length;
-A++){if(this._onSelectListeners[A]==B){this._onSelectListeners.splice(A,1);
-break;
-}}};
-Timeline.OriginalEventPainter.prototype.addEventPaintListener=function(A){this._eventPaintListeners.push(A);
-};
-Timeline.OriginalEventPainter.prototype.removeEventPaintListener=function(B){for(var A=0;
-A<this._eventPaintListeners.length;
-A++){if(this._eventPaintListeners[A]==B){this._eventPaintListeners.splice(A,1);
-break;
-}}};
-Timeline.OriginalEventPainter.prototype.getFilterMatcher=function(){return this._filterMatcher;
-};
-Timeline.OriginalEventPainter.prototype.setFilterMatcher=function(A){this._filterMatcher=A;
-};
-Timeline.OriginalEventPainter.prototype.getHighlightMatcher=function(){return this._highlightMatcher;
-};
-Timeline.OriginalEventPainter.prototype.setHighlightMatcher=function(A){this._highlightMatcher=A;
-};
-Timeline.OriginalEventPainter.prototype.paint=function(){var B=this._band.getEventSource();
-if(B==null){return ;
-}this._eventIdToElmt={};
-this._fireEventPaintListeners("paintStarting",null,null);
-this._prepareForPainting();
-var I=this._params.theme.event;
-var G=Math.max(I.track.height,I.tape.height+this._frc.getLineHeight());
-var F={trackOffset:I.track.offset,trackHeight:G,trackGap:I.track.gap,trackIncrement:G+I.track.gap,icon:I.instant.icon,iconWidth:I.instant.iconWidth,iconHeight:I.instant.iconHeight,labelWidth:I.label.width,maxLabelChar:I.label.maxLabelChar,impreciseIconMargin:I.instant.impreciseIconMargin};
-var C=this._band.getMinDate();
-var A=this._band.getMaxDate();
-var J=(this._filterMatcher!=null)?this._filterMatcher:function(K){return true;
-};
-var E=(this._highlightMatcher!=null)?this._highlightMatcher:function(K){return -1;
-};
-var D=B.getEventReverseIterator(C,A);
-while(D.hasNext()){var H=D.next();
-if(J(H)){this.paintEvent(H,F,this._params.theme,E(H));
-}}this._highlightLayer.style.display="block";
-this._lineLayer.style.display="block";
-this._eventLayer.style.display="block";
-this._band.updateEventTrackInfo(this._tracks.length,F.trackIncrement);
-this._fireEventPaintListeners("paintEnded",null,null);
-};
-Timeline.OriginalEventPainter.prototype.softPaint=function(){};
-Timeline.OriginalEventPainter.prototype._prepareForPainting=function(){var B=this._band;
-if(this._backLayer==null){this._backLayer=this._band.createLayerDiv(0,"timeline-band-events");
-this._backLayer.style.visibility="hidden";
-var A=document.createElement("span");
-A.className="timeline-event-label";
-this._backLayer.appendChild(A);
-this._frc=SimileAjax.Graphics.getFontRenderingContext(A);
-}this._frc.update();
-this._tracks=[];
-if(this._highlightLayer!=null){B.removeLayerDiv(this._highlightLayer);
-}this._highlightLayer=B.createLayerDiv(105,"timeline-band-highlights");
-this._highlightLayer.style.display="none";
-if(this._lineLayer!=null){B.removeLayerDiv(this._lineLayer);
-}this._lineLayer=B.createLayerDiv(110,"timeline-band-lines");
-this._lineLayer.style.display="none";
-if(this._eventLayer!=null){B.removeLayerDiv(this._eventLayer);
-}this._eventLayer=B.createLayerDiv(115,"timeline-band-events");
-this._eventLayer.style.display="none";
-};
-Timeline.OriginalEventPainter.prototype.paintEvent=function(B,C,D,A){if(B.isInstant()){this.paintInstantEvent(B,C,D,A);
-}else{this.paintDurationEvent(B,C,D,A);
-}};
-Timeline.OriginalEventPainter.prototype.paintInstantEvent=function(B,C,D,A){if(B.isImprecise()){this.paintImpreciseInstantEvent(B,C,D,A);
-}else{this.paintPreciseInstantEvent(B,C,D,A);
-}};
-Timeline.OriginalEventPainter.prototype.paintDurationEvent=function(B,C,D,A){if(B.isImprecise()){this.paintImpreciseDurationEvent(B,C,D,A);
-}else{this.paintPreciseDurationEvent(B,C,D,A);
-}};
-Timeline.OriginalEventPainter.prototype.paintPreciseInstantEvent=function(M,Q,S,R){var V=this._timeline.getDocument();
-var L=M.getText();
-var E=M.getStart();
-var C=Math.round(this._band.dateToPixelOffset(E));
-var A=Math.round(C+Q.iconWidth/2);
-var K=Math.round(C-Q.iconWidth/2);
-var H=this._getLabelDivClassName(M);
-var I=this._frc.computeSize(L,H);
-var W=A+S.event.label.offsetFromLine;
-var D=W+I.width;
-var U=D;
-var O=this._findFreeTrack(M,U);
-var T=Math.round(Q.trackOffset+O*Q.trackIncrement+Q.trackHeight/2-I.height/2);
-var B=this._paintEventIcon(M,O,K,Q,S,0);
-var P=this._paintEventLabel(M,L,W,T,I.width,I.height,S,H,R);
-var F=[B.elmt,P.elmt];
-var N=this;
-var J=function(X,Y,Z){return N._onClickInstantEvent(B.elmt,Y,M);
-};
-SimileAjax.DOM.registerEvent(B.elmt,"mousedown",J);
-SimileAjax.DOM.registerEvent(P.elmt,"mousedown",J);
-var G=this._createHighlightDiv(R,B,S,M);
-if(G!=null){F.push(G);
-}this._fireEventPaintListeners("paintedEvent",M,F);
-this._eventIdToElmt[M.getID()]=B.elmt;
-this._tracks[O]=K;
-};
-Timeline.OriginalEventPainter.prototype.paintImpreciseInstantEvent=function(P,T,Y,V){var a=this._timeline.getDocument();
-var N=P.getText();
-var G=P.getStart();
-var W=P.getEnd();
-var D=Math.round(this._band.dateToPixelOffset(G));
-var B=Math.round(this._band.dateToPixelOffset(W));
-var A=Math.round(D+T.iconWidth/2);
-var M=Math.round(D-T.iconWidth/2);
-var J=this._getLabelDivClassName(P);
-var K=this._frc.computeSize(N,J);
-var b=A+Y.event.label.offsetFromLine;
-var E=b+K.width;
-var Z=Math.max(E,B);
-var R=this._findFreeTrack(P,Z);
-var O=Y.event.tape.height;
-var X=Math.round(T.trackOffset+R*T.trackIncrement+O);
-var C=this._paintEventIcon(P,R,M,T,Y,O);
-var S=this._paintEventLabel(P,N,b,X,K.width,K.height,Y,J,V);
-var U=P.getColor();
-U=U!=null?U:Y.event.instant.impreciseColor;
-var F=this._paintEventTape(P,R,D,B,U,Y.event.instant.impreciseOpacity,T,Y,0);
-var H=[C.elmt,S.elmt,F.elmt];
-var Q=this;
-var L=function(c,d,e){return Q._onClickInstantEvent(C.elmt,d,P);
-};
-SimileAjax.DOM.registerEvent(C.elmt,"mousedown",L);
-SimileAjax.DOM.registerEvent(F.elmt,"mousedown",L);
-SimileAjax.DOM.registerEvent(S.elmt,"mousedown",L);
-var I=this._createHighlightDiv(V,C,Y,P);
-if(I!=null){H.push(I);
-}this._fireEventPaintListeners("paintedEvent",P,H);
-this._eventIdToElmt[P.getID()]=C.elmt;
-this._tracks[R]=M;
-};
-Timeline.OriginalEventPainter.prototype.paintPreciseDurationEvent=function(L,P,T,R){var W=this._timeline.getDocument();
-var K=L.getText();
-var E=L.getStart();
-var S=L.getEnd();
-var B=Math.round(this._band.dateToPixelOffset(E));
-var A=Math.round(this._band.dateToPixelOffset(S));
-var H=this._getLabelDivClassName(L);
-var I=this._frc.computeSize(K,H);
-var X=B;
-var C=X+I.width;
-var V=Math.max(C,A);
-var N=this._findFreeTrack(L,V);
-var U=Math.round(P.trackOffset+N*P.trackIncrement+T.event.tape.height);
-var Q=L.getColor();
-Q=Q!=null?Q:T.event.duration.color;
-var D=this._paintEventTape(L,N,B,A,Q,100,P,T,0);
-var O=this._paintEventLabel(L,K,X,U,I.width,I.height,T,H,R);
-var F=[D.elmt,O.elmt];
-var M=this;
-var J=function(Y,Z,a){return M._onClickDurationEvent(D.elmt,Z,L);
-};
-SimileAjax.DOM.registerEvent(D.elmt,"mousedown",J);
-SimileAjax.DOM.registerEvent(O.elmt,"mousedown",J);
-var G=this._createHighlightDiv(R,D,T,L);
-if(G!=null){F.push(G);
-}this._fireEventPaintListeners("paintedEvent",L,F);
-this._eventIdToElmt[L.getID()]=D.elmt;
-this._tracks[N]=B;
-};
-Timeline.OriginalEventPainter.prototype.paintImpreciseDurationEvent=function(N,S,Y,V){var b=this._timeline.getDocument();
-var M=N.getText();
-var E=N.getStart();
-var T=N.getLatestStart();
-var W=N.getEnd();
-var a=N.getEarliestEnd();
-var C=Math.round(this._band.dateToPixelOffset(E));
-var G=Math.round(this._band.dateToPixelOffset(T));
-var A=Math.round(this._band.dateToPixelOffset(W));
-var H=Math.round(this._band.dateToPixelOffset(a));
-var J=this._getLabelDivClassName(N);
-var K=this._frc.computeSize(M,J);
-var c=G;
-var B=c+K.width;
-var Z=Math.max(B,A);
-var P=this._findFreeTrack(N,Z);
-var X=Math.round(S.trackOffset+P*S.trackIncrement+Y.event.tape.height);
-var U=N.getColor();
-U=U!=null?U:Y.event.duration.color;
-var R=this._paintEventTape(N,P,C,A,Y.event.duration.impreciseColor,Y.event.duration.impreciseOpacity,S,Y,0);
-var D=this._paintEventTape(N,P,G,H,U,100,S,Y,1);
-var Q=this._paintEventLabel(N,M,c,X,K.width,K.height,Y,J,V);
-var F=[R.elmt,D.elmt,Q.elmt];
-var O=this;
-var L=function(d,e,f){return O._onClickDurationEvent(D.elmt,e,N);
-};
-SimileAjax.DOM.registerEvent(D.elmt,"mousedown",L);
-SimileAjax.DOM.registerEvent(Q.elmt,"mousedown",L);
-var I=this._createHighlightDiv(V,D,Y,N);
-if(I!=null){F.push(I);
-}this._fireEventPaintListeners("paintedEvent",N,F);
-this._eventIdToElmt[N.getID()]=D.elmt;
-this._tracks[P]=C;
-};
-Timeline.OriginalEventPainter.prototype._encodeEventElID=function(B,A){return Timeline.EventUtils.encodeEventElID(this._timeline,this._band,B,A);
-};
-Timeline.OriginalEventPainter.prototype._findFreeTrack=function(E,A){var D=E.getTrackNum();
-if(D!=null){return D;
-}for(var C=0;
-C<this._tracks.length;
-C++){var B=this._tracks[C];
-if(B>A){break;
-}}return C;
-};
-Timeline.OriginalEventPainter.prototype._paintEventIcon=function(J,F,B,G,E,C){var I=J.getIcon();
-I=I!=null?I:G.icon;
-var H;
-if(C>0){H=G.trackOffset+F*G.trackIncrement+C+G.impreciseIconMargin;
-}else{var K=G.trackOffset+F*G.trackIncrement+G.trackHeight/2;
-H=Math.round(K-G.iconHeight/2);
-}var D=SimileAjax.Graphics.createTranslucentImage(I);
-var A=this._timeline.getDocument().createElement("div");
-A.className=this._getElClassName("timeline-event-icon",J);
-A.id=this._encodeEventElID("icon",J);
-A.style.left=B+"px";
-A.style.top=H+"px";
-A.appendChild(D);
-if(J._title!=null){A.title=J._title;
-}this._eventLayer.appendChild(A);
-return{left:B,top:H,width:G.iconWidth,height:G.iconHeight,elmt:A};
-};
-Timeline.OriginalEventPainter.prototype._paintEventLabel=function(J,K,C,H,A,L,E,F,B){var I=this._timeline.getDocument();
-var G=I.createElement("div");
-G.className=F;
-G.id=this._encodeEventElID("label",J);
-G.style.left=C+"px";
-G.style.width=A+"px";
-G.style.top=H+"px";
-G.innerHTML=K;
-if(J._title!=null){G.title=J._title;
-}var D=J.getTextColor();
-if(D==null){D=J.getColor();
-}if(D!=null){G.style.color=D;
-}if(E.event.highlightLabelBackground&&B>=0){G.style.background=this._getHighlightColor(B,E);
-}this._eventLayer.appendChild(G);
-return{left:C,top:H,width:A,height:L,elmt:G};
-};
-Timeline.OriginalEventPainter.prototype._paintEventTape=function(N,I,F,A,C,H,J,G,O){var B=A-F;
-var E=G.event.tape.height;
-var K=J.trackOffset+I*J.trackIncrement;
-var M=this._timeline.getDocument().createElement("div");
-M.className=this._getElClassName("timeline-event-tape",N);
-M.id=this._encodeEventElID("tape"+O,N);
-M.style.left=F+"px";
-M.style.width=B+"px";
-M.style.height=E+"px";
-M.style.top=K+"px";
-if(N._title!=null){M.title=N._title;
-}if(C!=null){M.style.backgroundColor=C;
-}var L=N.getTapeImage();
-var D=N.getTapeRepeat();
-D=D!=null?D:"repeat";
-if(L!=null){M.style.backgroundImage="url("+L+")";
-M.style.backgroundRepeat=D;
-}SimileAjax.Graphics.setOpacity(M,H);
-this._eventLayer.appendChild(M);
-return{left:F,top:K,width:B,height:E,elmt:M};
-};
-Timeline.OriginalEventPainter.prototype._getLabelDivClassName=function(A){return this._getElClassName("timeline-event-label",A);
-};
-Timeline.OriginalEventPainter.prototype._getElClassName=function(B,A){var C=A.getClassName();
-return B+(C!=null?(" "+C):"");
-};
-Timeline.OriginalEventPainter.prototype._getHighlightColor=function(A,C){var B=C.event.highlightColors;
-return B[Math.min(A,B.length-1)];
-};
-Timeline.OriginalEventPainter.prototype._createHighlightDiv=function(A,D,F,B){var G=null;
-if(A>=0){var E=this._timeline.getDocument();
-var C=this._getHighlightColor(A,F);
-G=E.createElement("div");
-G.className=this._getElClassName("timeline-event-highlight",B);
-G.id=this._encodeEventElID("highlight0",B);
-G.style.position="absolute";
-G.style.overflow="hidden";
-G.style.left=(D.left-2)+"px";
-G.style.width=(D.width+4)+"px";
-G.style.top=(D.top-2)+"px";
-G.style.height=(D.height+4)+"px";
-G.style.background=C;
-this._highlightLayer.appendChild(G);
-}return G;
-};
-Timeline.OriginalEventPainter.prototype._onClickInstantEvent=function(B,C,A){var D=SimileAjax.DOM.getPageCoordinates(B);
-this._showBubble(D.left+Math.ceil(B.offsetWidth/2),D.top+Math.ceil(B.offsetHeight/2),A);
-this._fireOnSelect(A.getID());
-C.cancelBubble=true;
-SimileAjax.DOM.cancelEvent(C);
-return false;
-};
-Timeline.OriginalEventPainter.prototype._onClickDurationEvent=function(D,C,B){if("pageX" in C){var A=C.pageX;
-var F=C.pageY;
-}else{var E=SimileAjax.DOM.getPageCoordinates(D);
-var A=C.offsetX+E.left;
-var F=C.offsetY+E.top;
-}this._showBubble(A,F,B);
-this._fireOnSelect(B.getID());
-C.cancelBubble=true;
-SimileAjax.DOM.cancelEvent(C);
-return false;
-};
-Timeline.OriginalEventPainter.prototype.showBubble=function(A){var B=this._eventIdToElmt[A.getID()];
-if(B){var C=SimileAjax.DOM.getPageCoordinates(B);
-this._showBubble(C.left+B.offsetWidth/2,C.top+B.offsetHeight/2,A);
-}};
-Timeline.OriginalEventPainter.prototype._showBubble=function(A,E,B){var D=document.createElement("div");
-var C=this._params.theme.event.bubble;
-B.fillInfoBubble(D,this._params.theme,this._band.getLabeller());
-SimileAjax.WindowManager.cancelPopups();
-SimileAjax.Graphics.createBubbleForContentAndPoint(D,A,E,C.width,null,C.maxHeight);
-};
-Timeline.OriginalEventPainter.prototype._fireOnSelect=function(B){for(var A=0;
-A<this._onSelectListeners.length;
-A++){this._onSelectListeners[A](B);
-}};
-Timeline.OriginalEventPainter.prototype._fireEventPaintListeners=function(D,A,C){for(var B=0;
-B<this._eventPaintListeners.length;
-B++){this._eventPaintListeners[B](this._band,D,A,C);
-}};
-
-
-/* overview-painter.js */
-Timeline.OverviewEventPainter=function(A){this._params=A;
-this._onSelectListeners=[];
-this._filterMatcher=null;
-this._highlightMatcher=null;
-};
-Timeline.OverviewEventPainter.prototype.initialize=function(B,A){this._band=B;
-this._timeline=A;
-this._eventLayer=null;
-this._highlightLayer=null;
-};
-Timeline.OverviewEventPainter.prototype.getType=function(){return"overview";
-};
-Timeline.OverviewEventPainter.prototype.addOnSelectListener=function(A){this._onSelectListeners.push(A);
-};
-Timeline.OverviewEventPainter.prototype.removeOnSelectListener=function(B){for(var A=0;
-A<this._onSelectListeners.length;
-A++){if(this._onSelectListeners[A]==B){this._onSelectListeners.splice(A,1);
-break;
-}}};
-Timeline.OverviewEventPainter.prototype.getFilterMatcher=function(){return this._filterMatcher;
-};
-Timeline.OverviewEventPainter.prototype.setFilterMatcher=function(A){this._filterMatcher=A;
-};
-Timeline.OverviewEventPainter.prototype.getHighlightMatcher=function(){return this._highlightMatcher;
-};
-Timeline.OverviewEventPainter.prototype.setHighlightMatcher=function(A){this._highlightMatcher=A;
-};
-Timeline.OverviewEventPainter.prototype.paint=function(){var B=this._band.getEventSource();
-if(B==null){return ;
-}this._prepareForPainting();
-var H=this._params.theme.event;
-var F={trackOffset:H.overviewTrack.offset,trackHeight:H.overviewTrack.height,trackGap:H.overviewTrack.gap,trackIncrement:H.overviewTrack.height+H.overviewTrack.gap};
-var C=this._band.getMinDate();
-var A=this._band.getMaxDate();
-var I=(this._filterMatcher!=null)?this._filterMatcher:function(J){return true;
-};
-var E=(this._highlightMatcher!=null)?this._highlightMatcher:function(J){return -1;
-};
-var D=B.getEventReverseIterator(C,A);
-while(D.hasNext()){var G=D.next();
-if(I(G)){this.paintEvent(G,F,this._params.theme,E(G));
-}}this._highlightLayer.style.display="block";
-this._eventLayer.style.display="block";
-this._band.updateEventTrackInfo(this._tracks.length,F.trackIncrement);
-};
-Timeline.OverviewEventPainter.prototype.softPaint=function(){};
-Timeline.OverviewEventPainter.prototype._prepareForPainting=function(){var A=this._band;
-this._tracks=[];
-if(this._highlightLayer!=null){A.removeLayerDiv(this._highlightLayer);
-}this._highlightLayer=A.createLayerDiv(105,"timeline-band-highlights");
-this._highlightLayer.style.display="none";
-if(this._eventLayer!=null){A.removeLayerDiv(this._eventLayer);
-}this._eventLayer=A.createLayerDiv(110,"timeline-band-events");
-this._eventLayer.style.display="none";
-};
-Timeline.OverviewEventPainter.prototype.paintEvent=function(B,C,D,A){if(B.isInstant()){this.paintInstantEvent(B,C,D,A);
-}else{this.paintDurationEvent(B,C,D,A);
-}};
-Timeline.OverviewEventPainter.prototype.paintInstantEvent=function(C,F,G,B){var A=C.getStart();
-var H=Math.round(this._band.dateToPixelOffset(A));
-var D=C.getColor();
-D=D!=null?D:G.event.duration.color;
-var E=this._paintEventTick(C,H,D,100,F,G);
-this._createHighlightDiv(B,E,G);
-};
-Timeline.OverviewEventPainter.prototype.paintDurationEvent=function(K,J,I,D){var A=K.getLatestStart();
-var C=K.getEarliestEnd();
-var B=Math.round(this._band.dateToPixelOffset(A));
-var E=Math.round(this._band.dateToPixelOffset(C));
-var H=0;
-for(;
-H<this._tracks.length;
-H++){if(E<this._tracks[H]){break;
-}}this._tracks[H]=E;
-var G=K.getColor();
-G=G!=null?G:I.event.duration.color;
-var F=this._paintEventTape(K,H,B,E,G,100,J,I);
-this._createHighlightDiv(D,F,I);
-};
-Timeline.OverviewEventPainter.prototype._paintEventTape=function(K,B,C,J,D,F,G,E){var H=G.trackOffset+B*G.trackIncrement;
-var A=J-C;
-var L=G.trackHeight;
-var I=this._timeline.getDocument().createElement("div");
-I.className="timeline-small-event-tape";
-I.style.left=C+"px";
-I.style.width=A+"px";
-I.style.top=H+"px";
-I.style.height=L+"px";
-if(D!=null){I.style.backgroundColor=D;
-}if(F<100){SimileAjax.Graphics.setOpacity(I,F);
-}this._eventLayer.appendChild(I);
-return{left:C,top:H,width:A,height:L,elmt:I};
-};
-Timeline.OverviewEventPainter.prototype._paintEventTick=function(J,B,D,F,G,E){var K=E.event.overviewTrack.tickHeight;
-var H=G.trackOffset-K;
-var A=1;
-var I=this._timeline.getDocument().createElement("div");
-I.className="timeline-small-event-icon";
-I.style.left=B+"px";
-I.style.top=H+"px";
-var C=J.getClassName();
-if(C){I.className+=" small-"+C;
-}if(F<100){SimileAjax.Graphics.setOpacity(I,F);
-}this._eventLayer.appendChild(I);
-return{left:B,top:H,width:A,height:K,elmt:I};
-};
-Timeline.OverviewEventPainter.prototype._createHighlightDiv=function(A,C,E){if(A>=0){var D=this._timeline.getDocument();
-var G=E.event;
-var B=G.highlightColors[Math.min(A,G.highlightColors.length-1)];
-var F=D.createElement("div");
-F.style.position="absolute";
-F.style.overflow="hidden";
-F.style.left=(C.left-1)+"px";
-F.style.width=(C.width+2)+"px";
-F.style.top=(C.top-1)+"px";
-F.style.height=(C.height+2)+"px";
-F.style.background=B;
-this._highlightLayer.appendChild(F);
-}};
-Timeline.OverviewEventPainter.prototype.showBubble=function(A){};
-
-
-/* sources.js */
-Timeline.DefaultEventSource=function(A){this._events=(A instanceof Object)?A:new SimileAjax.EventIndex();
-this._listeners=[];
-};
-Timeline.DefaultEventSource.prototype.addListener=function(A){this._listeners.push(A);
-};
-Timeline.DefaultEventSource.prototype.removeListener=function(B){for(var A=0;
-A<this._listeners.length;
-A++){if(this._listeners[A]==B){this._listeners.splice(A,1);
-break;
-}}};
-Timeline.DefaultEventSource.prototype.loadXML=function(G,A){var C=this._getBaseURL(A);
-var H=G.documentElement.getAttribute("wiki-url");
-var L=G.documentElement.getAttribute("wiki-section");
-var E=G.documentElement.getAttribute("date-time-format");
-var F=this._events.getUnit().getParser(E);
-var D=G.documentElement.firstChild;
-var I=false;
-while(D!=null){if(D.nodeType==1){var K="";
-if(D.firstChild!=null&&D.firstChild.nodeType==3){K=D.firstChild.nodeValue;
-}var B=(D.getAttribute("isDuration")===null&&D.getAttribute("durationEvent")===null)||D.getAttribute("isDuration")=="false"||D.getAttribute("durationEvent")=="false";
-var J=new Timeline.DefaultEventSource.Event({id:D.getAttribute("id"),start:F(D.getAttribute("start")),end:F(D.getAttribute("end")),latestStart:F(D.getAttribute("latestStart")),earliestEnd:F(D.getAttribute("earliestEnd")),instant:B,text:D.getAttribute("title"),description:K,image:this._resolveRelativeURL(D.getAttribute("image"),C),link:this._resolveRelativeURL(D.getAttribute("link"),C),icon:this._resolveRelativeURL(D.getAttribute("icon"),C),color:D.getAttribute("color"),textColor:D.getAttribute("textColor"),hoverText:D.getAttribute("hoverText"),classname:D.getAttribute("classname"),tapeImage:D.getAttribute("tapeImage"),tapeRepeat:D.getAttribute("tapeRepeat"),caption:D.getAttribute("caption"),eventID:D.getAttribute("eventID"),trackNum:D.getAttribute("trackNum")});
-J._node=D;
-J.getProperty=function(M){return this._node.getAttribute(M);
-};
-J.setWikiInfo(H,L);
-this._events.add(J);
-I=true;
-}D=D.nextSibling;
-}if(I){this._fire("onAddMany",[]);
-}};
-Timeline.DefaultEventSource.prototype.loadJSON=function(G,B){var D=this._getBaseURL(B);
-var J=false;
-if(G&&G.events){var I=("wikiURL" in G)?G.wikiURL:null;
-var L=("wikiSection" in G)?G.wikiSection:null;
-var E=("dateTimeFormat" in G)?G.dateTimeFormat:null;
-var H=this._events.getUnit().getParser(E);
-for(var F=0;
-F<G.events.length;
-F++){var A=G.events[F];
-var C=A.isDuration||(A.durationEvent!=null&&!A.durationEvent);
-var K=new Timeline.DefaultEventSource.Event({id:("id" in A)?A.id:undefined,start:H(A.start),end:H(A.end),latestStart:H(A.latestStart),earliestEnd:H(A.earliestEnd),instant:C,text:A.title,description:A.description,image:this._resolveRelativeURL(A.image,D),link:this._resolveRelativeURL(A.link,D),icon:this._resolveRelativeURL(A.icon,D),color:A.color,textColor:A.textColor,hoverText:A.hoverText,classname:A.classname,tapeImage:A.tapeImage,tapeRepeat:A.tapeRepeat,caption:A.caption,eventID:A.eventID,trackNum:A.trackNum});
-K._obj=A;
-K.getProperty=function(M){return this._obj[M];
-};
-K.setWikiInfo(I,L);
-this._events.add(K);
-J=true;
-}}if(J){this._fire("onAddMany",[]);
-}};
-Timeline.DefaultEventSource.prototype.loadSPARQL=function(H,A){var D=this._getBaseURL(A);
-var F="iso8601";
-var G=this._events.getUnit().getParser(F);
-if(H==null){return ;
-}var E=H.documentElement.firstChild;
-while(E!=null&&(E.nodeType!=1||E.nodeName!="results")){E=E.nextSibling;
-}var J=null;
-var M=null;
-if(E!=null){J=E.getAttribute("wiki-url");
-M=E.getAttribute("wiki-section");
-E=E.firstChild;
-}var K=false;
-while(E!=null){if(E.nodeType==1){var C={};
-var I=E.firstChild;
-while(I!=null){if(I.nodeType==1&&I.firstChild!=null&&I.firstChild.nodeType==1&&I.firstChild.firstChild!=null&&I.firstChild.firstChild.nodeType==3){C[I.getAttribute("name")]=I.firstChild.firstChild.nodeValue;
-}I=I.nextSibling;
-}if(C["start"]==null&&C["date"]!=null){C["start"]=C["date"];
-}var B=(C["isDuration"]===null&&C["durationEvent"]===null)||C["isDuration"]=="false"||C["durationEvent"]=="false";
-var L=new Timeline.DefaultEventSource.Event({id:C["id"],start:G(C["start"]),end:G(C["end"]),latestStart:G(C["latestStart"]),earliestEnd:G(C["earliestEnd"]),instant:B,text:C["title"],description:C["description"],image:this._resolveRelativeURL(C["image"],D),link:this._resolveRelativeURL(C["link"],D),icon:this._resolveRelativeURL(C["icon"],D),color:C["color"],textColor:C["textColor"],hoverText:C["hoverText"],caption:C["caption"],classname:C["classname"],tapeImage:C["tapeImage"],tapeRepeat:C["tapeRepeat"],eventID:C["eventID"],trackNum:C["trackNum"]});
-L._bindings=C;
-L.getProperty=function(N){return this._bindings[N];
-};
-L.setWikiInfo(J,M);
-this._events.add(L);
-K=true;
-}E=E.nextSibling;
-}if(K){this._fire("onAddMany",[]);
-}};
-Timeline.DefaultEventSource.prototype.add=function(A){this._events.add(A);
-this._fire("onAddOne",[A]);
-};
-Timeline.DefaultEventSource.prototype.addMany=function(B){for(var A=0;
-A<B.length;
-A++){this._events.add(B[A]);
-}this._fire("onAddMany",[]);
-};
-Timeline.DefaultEventSource.prototype.clear=function(){this._events.removeAll();
-this._fire("onClear",[]);
-};
-Timeline.DefaultEventSource.prototype.getEvent=function(A){return this._events.getEvent(A);
-};
-Timeline.DefaultEventSource.prototype.getEventIterator=function(A,B){return this._events.getIterator(A,B);
-};
-Timeline.DefaultEventSource.prototype.getEventReverseIterator=function(A,B){return this._events.getReverseIterator(A,B);
-};
-Timeline.DefaultEventSource.prototype.getAllEventIterator=function(){return this._events.getAllIterator();
-};
-Timeline.DefaultEventSource.prototype.getCount=function(){return this._events.getCount();
-};
-Timeline.DefaultEventSource.prototype.getEarliestDate=function(){return this._events.getEarliestDate();
-};
-Timeline.DefaultEventSource.prototype.getLatestDate=function(){return this._events.getLatestDate();
-};
-Timeline.DefaultEventSource.prototype._fire=function(B,A){for(var C=0;
-C<this._listeners.length;
-C++){var D=this._listeners[C];
-if(B in D){try{D[B].apply(D,A);
-}catch(E){SimileAjax.Debug.exception(E);
-}}}};
-Timeline.DefaultEventSource.prototype._getBaseURL=function(A){if(A.indexOf("://")<0){var C=this._getBaseURL(document.location.href);
-if(A.substr(0,1)=="/"){A=C.substr(0,C.indexOf("/",C.indexOf("://")+3))+A;
-}else{A=C+A;
-}}var B=A.lastIndexOf("/");
-if(B<0){return"";
-}else{return A.substr(0,B+1);
-}};
-Timeline.DefaultEventSource.prototype._resolveRelativeURL=function(A,B){if(A==null||A==""){return A;
-}else{if(A.indexOf("://")>0){return A;
-}else{if(A.substr(0,1)=="/"){return B.substr(0,B.indexOf("/",B.indexOf("://")+3))+A;
-}else{return B+A;
-}}}};
-Timeline.DefaultEventSource.Event=function(A){function D(E){return(A[E]!=null&&A[E]!="")?A[E]:null;
-}var C=A.id?A.id.trim():"";
-this._id=C.length>0?C:Timeline.EventUtils.getNewEventID();
-this._instant=A.instant||(A.end==null);
-this._start=A.start;
-this._end=(A.end!=null)?A.end:A.start;
-this._latestStart=(A.latestStart!=null)?A.latestStart:(A.instant?this._end:this._start);
-this._earliestEnd=(A.earliestEnd!=null)?A.earliestEnd:this._end;
-var B=[];
-if(this._start>this._latestStart){this._latestStart=this._start;
-B.push("start is > latestStart");
-}if(this._start>this._earliestEnd){this._earliestEnd=this._latestStart;
-B.push("start is > earliestEnd");
-}if(this._start>this._end){this._end=this._earliestEnd;
-B.push("start is > end");
-}if(this._latestStart>this._earliestEnd){this._earliestEnd=this._latestStart;
-B.push("latestStart is > earliestEnd");
-}if(this._latestStart>this._end){this._end=this._earliestEnd;
-B.push("latestStart is > end");
-}if(this._earliestEnd>this._end){this._end=this._earliestEnd;
-B.push("earliestEnd is > end");
-}this._eventID=D("eventID");
-this._text=(A.text!=null)?SimileAjax.HTML.deEntify(A.text):"";
-if(B.length>0){this._text+=" PROBLEM: "+B.join(", ");
-}this._description=SimileAjax.HTML.deEntify(A.description);
-this._image=D("image");
-this._link=D("link");
-this._title=D("hoverText");
-this._title=D("caption");
-this._icon=D("icon");
-this._color=D("color");
-this._textColor=D("textColor");
-this._classname=D("classname");
-this._tapeImage=D("tapeImage");
-this._tapeRepeat=D("tapeRepeat");
-this._trackNum=D("trackNum");
-if(this._trackNum!=null){this._trackNum=parseInt(this._trackNum);
-}this._wikiURL=null;
-this._wikiSection=null;
-};
-Timeline.DefaultEventSource.Event.prototype={getID:function(){return this._id;
-},isInstant:function(){return this._instant;
-},isImprecise:function(){return this._start!=this._latestStart||this._end!=this._earliestEnd;
-},getStart:function(){return this._start;
-},getEnd:function(){return this._end;
-},getLatestStart:function(){return this._latestStart;
-},getEarliestEnd:function(){return this._earliestEnd;
-},getEventID:function(){return this._eventID;
-},getText:function(){return this._text;
-},getDescription:function(){return this._description;
-},getImage:function(){return this._image;
-},getLink:function(){return this._link;
-},getIcon:function(){return this._icon;
-},getColor:function(){return this._color;
-},getTextColor:function(){return this._textColor;
-},getClassName:function(){return this._classname;
-},getTapeImage:function(){return this._tapeImage;
-},getTapeRepeat:function(){return this._tapeRepeat;
-},getTrackNum:function(){return this._trackNum;
-},getProperty:function(A){return null;
-},getWikiURL:function(){return this._wikiURL;
-},getWikiSection:function(){return this._wikiSection;
-},setWikiInfo:function(B,A){this._wikiURL=B;
-this._wikiSection=A;
-},fillDescription:function(A){A.innerHTML=this._description;
-},fillWikiInfo:function(D){D.style.display="none";
-if(this._wikiURL==null||this._wikiSection==null){return ;
-}var C=this.getProperty("wikiID");
-if(C==null||C.length==0){C=this.getText();
-}if(C==null||C.length==0){return ;
-}D.style.display="inline";
-C=C.replace(/\s/g,"_");
-var B=this._wikiURL+this._wikiSection.replace(/\s/g,"_")+"/"+C;
-var A=document.createElement("a");
-A.href=B;
-A.target="new";
-A.innerHTML=Timeline.strings[Timeline.clientLocale].wikiLinkLabel;
-D.appendChild(document.createTextNode("["));
-D.appendChild(A);
-D.appendChild(document.createTextNode("]"));
-},fillTime:function(A,B){if(this._instant){if(this.isImprecise()){A.appendChild(A.ownerDocument.createTextNode(B.labelPrecise(this._start)));
-A.appendChild(A.ownerDocument.createElement("br"));
-A.appendChild(A.ownerDocument.createTextNode(B.labelPrecise(this._end)));
-}else{A.appendChild(A.ownerDocument.createTextNode(B.labelPrecise(this._start)));
-}}else{if(this.isImprecise()){A.appendChild(A.ownerDocument.createTextNode(B.labelPrecise(this._start)+" ~ "+B.labelPrecise(this._latestStart)));
-A.appendChild(A.ownerDocument.createElement("br"));
-A.appendChild(A.ownerDocument.createTextNode(B.labelPrecise(this._earliestEnd)+" ~ "+B.labelPrecise(this._end)));
-}else{A.appendChild(A.ownerDocument.createTextNode(B.labelPrecise(this._start)));
-A.appendChild(A.ownerDocument.createElement("br"));
-A.appendChild(A.ownerDocument.createTextNode(B.labelPrecise(this._end)));
-}}},fillInfoBubble:function(A,D,K){var L=A.ownerDocument;
-var J=this.getText();
-var H=this.getLink();
-var C=this.getImage();
-if(C!=null){var E=L.createElement("img");
-E.src=C;
-D.event.bubble.imageStyler(E);
-A.appendChild(E);
-}var M=L.createElement("div");
-var B=L.createTextNode(J);
-if(H!=null){var I=L.createElement("a");
-I.href=H;
-I.appendChild(B);
-M.appendChild(I);
-}else{M.appendChild(B);
-}D.event.bubble.titleStyler(M);
-A.appendChild(M);
-var N=L.createElement("div");
-this.fillDescription(N);
-D.event.bubble.bodyStyler(N);
-A.appendChild(N);
-var G=L.createElement("div");
-this.fillTime(G,K);
-D.event.bubble.timeStyler(G);
-A.appendChild(G);
-var F=L.createElement("div");
-this.fillWikiInfo(F);
-D.event.bubble.wikiStyler(F);
-A.appendChild(F);
-}};
-
-
-/* themes.js */
-Timeline.ClassicTheme=new Object();
-Timeline.ClassicTheme.implementations=[];
-Timeline.ClassicTheme.create=function(A){if(A==null){A=Timeline.getDefaultLocale();
-}var B=Timeline.ClassicTheme.implementations[A];
-if(B==null){B=Timeline.ClassicTheme._Impl;
-}return new B();
-};
-Timeline.ClassicTheme._Impl=function(){this.firstDayOfWeek=0;
-this.autoWidth=false;
-this.autoWidthAnimationTime=1000;
-this.ether={backgroundColors:[],highlightOpacity:50,interval:{line:{show:true,opacity:25},weekend:{opacity:30},marker:{hAlign:"Bottom",vAlign:"Right"}}};
-this.event={track:{height:10,gap:2,offset:2,autoWidthMargin:1.5},overviewTrack:{offset:20,tickHeight:6,height:2,gap:1,autoWidthMargin:5},tape:{height:4},instant:{icon:Timeline.urlPrefix+"data/timeline/dull-blue-circle.png",iconWidth:10,iconHeight:10,impreciseOpacity:20,impreciseIconMargin:3},duration:{impreciseOpacity:20},label:{backgroundOpacity:50,offsetFromLine:3},highlightColors:["#FFFF00","#FFC000","#FF0000","#0000FF"],highlightLabelBackground:false,bubble:{width:250,maxHeight:0,titleStyler:function(A){A.className="timeline-event-bubble-title";
-},bodyStyler:function(A){A.className="timeline-event-bubble-body";
-},imageStyler:function(A){A.className="timeline-event-bubble-image";
-},wikiStyler:function(A){A.className="timeline-event-bubble-wiki";
-},timeStyler:function(A){A.className="timeline-event-bubble-time";
-}}};
-this.mouseWheel="scroll";
-};
-
-
-/* timeline.js */
-Timeline.strings={};
-Timeline.HORIZONTAL=0;
-Timeline.VERTICAL=1;
-Timeline._defaultTheme=null;
-Timeline.getDefaultLocale=function(){return Timeline.clientLocale;
-};
-Timeline.create=function(D,C,B,F){if(Timeline.timelines==null){Timeline.timelines=[];
-}var A=Timeline.timelines.length;
-Timeline.timelines[A]=null;
-var E=new Timeline._Impl(D,C,B,F,A);
-Timeline.timelines[A]=E;
-return E;
-};
-Timeline.createBandInfo=function(D){var E=("theme" in D)?D.theme:Timeline.getDefaultTheme();
-var B=("eventSource" in D)?D.eventSource:null;
-var F={interval:SimileAjax.DateTime.gregorianUnitLengths[D.intervalUnit],pixelsPerInterval:D.intervalPixels,theme:E};
-if("startsOn" in D||"endsOn" in D){if("startsOn" in D){F.startsOn=D.startsOn;
-}if("endsOn" in D){F.endsOn=D.endsOn;
-}}else{F.centersOn=("date" in D)?D.date:new Date();
-}var G=new Timeline.LinearEther(F);
-var H=new Timeline.GregorianEtherPainter({unit:D.intervalUnit,multiple:("multiple" in D)?D.multiple:1,theme:E,align:("align" in D)?D.align:undefined});
-var J={showText:("showEventText" in D)?D.showEventText:true,theme:E};
-if("eventPainterParams" in D){for(var A in D.eventPainterParams){J[A]=D.eventPainterParams[A];
-}}if("trackHeight" in D){J.trackHeight=D.trackHeight;
-}if("trackGap" in D){J.trackGap=D.trackGap;
-}var I=("overview" in D&&D.overview)?"overview":("layout" in D?D.layout:"original");
-var C;
-if("eventPainter" in D){C=new D.eventPainter(J);
-}else{switch(I){case"overview":C=new Timeline.OverviewEventPainter(J);
-break;
-case"detailed":C=new Timeline.DetailedEventPainter(J);
-break;
-default:C=new Timeline.OriginalEventPainter(J);
-}}return{width:D.width,eventSource:B,timeZone:("timeZone" in D)?D.timeZone:0,ether:G,etherPainter:H,eventPainter:C,theme:E,zoomIndex:("zoomIndex" in D)?D.zoomIndex:0,zoomSteps:("zoomSteps" in D)?D.zoomSteps:null};
-};
-Timeline.createHotZoneBandInfo=function(D){var E=("theme" in D)?D.theme:Timeline.getDefaultTheme();
-var B=("eventSource" in D)?D.eventSource:null;
-var F=new Timeline.HotZoneEther({centersOn:("date" in D)?D.date:new Date(),interval:SimileAjax.DateTime.gregorianUnitLengths[D.intervalUnit],pixelsPerInterval:D.intervalPixels,zones:D.zones,theme:E});
-var G=new Timeline.HotZoneGregorianEtherPainter({unit:D.intervalUnit,zones:D.zones,theme:E,align:("align" in D)?D.align:undefined});
-var I={showText:("showEventText" in D)?D.showEventText:true,theme:E};
-if("eventPainterParams" in D){for(var A in D.eventPainterParams){I[A]=D.eventPainterParams[A];
-}}if("trackHeight" in D){I.trackHeight=D.trackHeight;
-}if("trackGap" in D){I.trackGap=D.trackGap;
-}var H=("overview" in D&&D.overview)?"overview":("layout" in D?D.layout:"original");
-var C;
-if("eventPainter" in D){C=new D.eventPainter(I);
-}else{switch(H){case"overview":C=new Timeline.OverviewEventPainter(I);
-break;
-case"detailed":C=new Timeline.DetailedEventPainter(I);
-break;
-default:C=new Timeline.OriginalEventPainter(I);
-}}return{width:D.width,eventSource:B,timeZone:("timeZone" in D)?D.timeZone:0,ether:F,etherPainter:G,eventPainter:C,theme:E,zoomIndex:("zoomIndex" in D)?D.zoomIndex:0,zoomSteps:("zoomSteps" in D)?D.zoomSteps:null};
-};
-Timeline.getDefaultTheme=function(){if(Timeline._defaultTheme==null){Timeline._defaultTheme=Timeline.ClassicTheme.create(Timeline.getDefaultLocale());
-}return Timeline._defaultTheme;
-};
-Timeline.setDefaultTheme=function(A){Timeline._defaultTheme=A;
-};
-Timeline.loadXML=function(A,C){var D=function(G,E,F){alert("Failed to load data xml from "+A+"\n"+G);
-};
-var B=function(F){var E=F.responseXML;
-if(!E.documentElement&&F.responseStream){E.load(F.responseStream);
-}C(E,A);
-};
-SimileAjax.XmlHttp.get(A,D,B);
-};
-Timeline.loadJSON=function(url,f){var fError=function(statusText,status,xmlhttp){alert("Failed to load json data from "+url+"\n"+statusText);
-};
-var fDone=function(xmlhttp){f(eval("("+xmlhttp.responseText+")"),url);
-};
-SimileAjax.XmlHttp.get(url,fError,fDone);
-};
-Timeline.getTimelineFromID=function(A){return Timeline.timelines[A];
-};
-Timeline._Impl=function(D,C,B,E,A){SimileAjax.WindowManager.initialize();
-this._containerDiv=D;
-this._bandInfos=C;
-this._orientation=B==null?Timeline.HORIZONTAL:B;
-this._unit=(E!=null)?E:SimileAjax.NativeDateUnit;
-this._starting=true;
-this._autoResizing=false;
-this.autoWidth=C&&C[0]&&C[0].theme&&C[0].theme.autoWidth;
-this.autoWidthAnimationTime=C&&C[0]&&C[0].theme&&C[0].theme.autoWidthAnimationTime;
-this.timelineID=A;
-this._initialize();
-};
-Timeline._Impl.prototype.dispose=function(){for(var A=0;
-A<this._bands.length;
-A++){this._bands[A].dispose();
-}this._bands=null;
-this._bandInfos=null;
-this._containerDiv.innerHTML="";
-Timeline.timelines[this.timelineID]=null;
-};
-Timeline._Impl.prototype.getBandCount=function(){return this._bands.length;
-};
-Timeline._Impl.prototype.getBand=function(A){return this._bands[A];
-};
-Timeline._Impl.prototype.finishedEventLoading=function(){this._autoWidthCheck(true);
-this._starting=false;
-};
-Timeline._Impl.prototype.layout=function(){this._autoWidthCheck(true);
-this._distributeWidths();
-};
-Timeline._Impl.prototype.paint=function(){for(var A=0;
-A<this._bands.length;
-A++){this._bands[A].paint();
-}};
-Timeline._Impl.prototype.getDocument=function(){return this._containerDiv.ownerDocument;
-};
-Timeline._Impl.prototype.addDiv=function(A){this._containerDiv.appendChild(A);
-};
-Timeline._Impl.prototype.removeDiv=function(A){this._containerDiv.removeChild(A);
-};
-Timeline._Impl.prototype.isHorizontal=function(){return this._orientation==Timeline.HORIZONTAL;
-};
-Timeline._Impl.prototype.isVertical=function(){return this._orientation==Timeline.VERTICAL;
-};
-Timeline._Impl.prototype.getPixelLength=function(){return this._orientation==Timeline.HORIZONTAL?this._containerDiv.offsetWidth:this._containerDiv.offsetHeight;
-};
-Timeline._Impl.prototype.getPixelWidth=function(){return this._orientation==Timeline.VERTICAL?this._containerDiv.offsetWidth:this._containerDiv.offsetHeight;
-};
-Timeline._Impl.prototype.getUnit=function(){return this._unit;
-};
-Timeline._Impl.prototype.getWidthStyle=function(){return this._orientation==Timeline.HORIZONTAL?"height":"width";
-};
-Timeline._Impl.prototype.loadXML=function(B,D){var A=this;
-var E=function(H,F,G){alert("Failed to load data xml from "+B+"\n"+H);
-A.hideLoadingMessage();
-};
-var C=function(G){try{var F=G.responseXML;
-if(!F.documentElement&&G.responseStream){F.load(G.responseStream);
-}D(F,B);
-}finally{A.hideLoadingMessage();
-}};
-this.showLoadingMessage();
-window.setTimeout(function(){SimileAjax.XmlHttp.get(B,E,C);
-},0);
-};
-Timeline._Impl.prototype.loadJSON=function(url,f){var tl=this;
-var fError=function(statusText,status,xmlhttp){alert("Failed to load json data from "+url+"\n"+statusText);
-tl.hideLoadingMessage();
-};
-var fDone=function(xmlhttp){try{f(eval("("+xmlhttp.responseText+")"),url);
-}finally{tl.hideLoadingMessage();
-}};
-this.showLoadingMessage();
-window.setTimeout(function(){SimileAjax.XmlHttp.get(url,fError,fDone);
-},0);
-};
-Timeline._Impl.prototype._autoWidthScrollListener=function(A){A.getTimeline()._autoWidthCheck(false);
-};
-Timeline._Impl.prototype._autoWidthCheck=function(C){var E=this;
-var B=E._starting;
-var D=0;
-function A(){var H=E.getWidthStyle();
-if(B){E._containerDiv.style[H]=D+"px";
-}else{E._autoResizing=true;
-var G={};
-G[H]=D+"px";
-SimileAjax.jQuery(E._containerDiv).animate(G,E.autoWidthAnimationTime,"linear",function(){E._autoResizing=false;
-});
-}}function F(){var I=0;
-var G=E.getPixelWidth();
-if(E._autoResizing){return ;
-}for(var H=0;
-H<E._bands.length;
-H++){E._bands[H].checkAutoWidth();
-I+=E._bandInfos[H].width;
-}if(I>G||C){D=I;
-A();
-E._distributeWidths();
-}}if(!E.autoWidth){return ;
-}F();
-};
-Timeline._Impl.prototype._initialize=function(){var E=this._containerDiv;
-var G=E.ownerDocument;
-E.className=E.className.split(" ").concat("timeline-container").join(" ");
-var A=(this.isHorizontal())?"horizontal":"vertical";
-E.className+=" timeline-"+A;
-while(E.firstChild){E.removeChild(E.firstChild);
-}var B=SimileAjax.Graphics.createTranslucentImage(Timeline.urlPrefix+(this.isHorizontal()?"data/timeline/copyright-vertical.png":"data/timeline/copyright.png"));
-B.className="timeline-copyright";
-B.title="Timeline &copy; SIMILE - http://simile.mit.edu/timeline/";
-SimileAjax.DOM.registerEvent(B,"click",function(){window.location="http://simile.mit.edu/timeline/";
-});
-E.appendChild(B);
-this._bands=[];
-for(var C=0;
-C<this._bandInfos.length;
-C++){var F=this._bandInfos[C];
-var D=F.bandClass||Timeline._Band;
-var H=new D(this,this._bandInfos[C],C);
-this._bands.push(H);
-}this._distributeWidths();
-for(var C=0;
-C<this._bandInfos.length;
-C++){var F=this._bandInfos[C];
-if("syncWith" in F){this._bands[C].setSyncWithBand(this._bands[F.syncWith],("highlight" in F)?F.highlight:false);
-}}if(this.autoWidth){for(var C=0;
-C<this._bands.length;
-C++){this._bands[C].addOnScrollListener(this._autoWidthScrollListener);
-}}var I=SimileAjax.Graphics.createMessageBubble(G);
-I.containerDiv.className="timeline-message-container";
-E.appendChild(I.containerDiv);
-I.contentDiv.className="timeline-message";
-I.contentDiv.innerHTML="<img src='"+Timeline.urlPrefix+"data/timeline/progress-running.gif' /> Loading...";
-this.showLoadingMessage=function(){I.containerDiv.style.display="block";
-};
-this.hideLoadingMessage=function(){I.containerDiv.style.display="none";
-};
-};
-Timeline._Impl.prototype._distributeWidths=function(){var B=this.getPixelLength();
-var A=this.getPixelWidth();
-var C=0;
-for(var E=0;
-E<this._bands.length;
-E++){var I=this._bands[E];
-var J=this._bandInfos[E];
-var F=J.width;
-var D;
-if(typeof F=="string"){var H=F.indexOf("%");
-if(H>0){var G=parseInt(F.substr(0,H));
-D=Math.round(G*A/100);
-}else{D=parseInt(F);
-}}else{D=F;
-}I.setBandShiftAndWidth(C,D);
-I.setViewLength(B);
-C+=D;
-}};
-Timeline._Impl.prototype.zoom=function(G,B,F,D){var C=new RegExp("^timeline-band-([0-9]+)$");
-var E=null;
-var A=C.exec(D.id);
-if(A){E=parseInt(A[1]);
-}if(E!=null){this._bands[E].zoom(G,B,F,D);
-}this.paint();
-};
-
-
-/* units.js */
-Timeline.NativeDateUnit=new Object();
-Timeline.NativeDateUnit.createLabeller=function(A,B){return new Timeline.GregorianDateLabeller(A,B);
-};
-Timeline.NativeDateUnit.makeDefaultValue=function(){return new Date();
-};
-Timeline.NativeDateUnit.cloneValue=function(A){return new Date(A.getTime());
-};
-Timeline.NativeDateUnit.getParser=function(A){if(typeof A=="string"){A=A.toLowerCase();
-}return(A=="iso8601"||A=="iso 8601")?Timeline.DateTime.parseIso8601DateTime:Timeline.DateTime.parseGregorianDateTime;
-};
-Timeline.NativeDateUnit.parseFromObject=function(A){return Timeline.DateTime.parseGregorianDateTime(A);
-};
-Timeline.NativeDateUnit.toNumber=function(A){return A.getTime();
-};
-Timeline.NativeDateUnit.fromNumber=function(A){return new Date(A);
-};
-Timeline.NativeDateUnit.compare=function(D,C){var B,A;
-if(typeof D=="object"){B=D.getTime();
-}else{B=Number(D);
-}if(typeof C=="object"){A=C.getTime();
-}else{A=Number(C);
-}return B-A;
-};
-Timeline.NativeDateUnit.earlier=function(B,A){return Timeline.NativeDateUnit.compare(B,A)<0?B:A;
-};
-Timeline.NativeDateUnit.later=function(B,A){return Timeline.NativeDateUnit.compare(B,A)>0?B:A;
-};
-Timeline.NativeDateUnit.change=function(A,B){return new Date(A.getTime()+B);
-};
-
-/******** end of simile-timeline-bundle.js ********/
+/*=================================================
+ *
+ * Coding standards:
+ *
+ * We aim towards Douglas Crockford's Javascript conventions.
+ * See:  http://javascript.crockford.com/code.html
+ * See also: http://www.crockford.com/javascript/javascript.html
+ *
+ * That said, this JS code was written before some recent JS
+ * support libraries became widely used or available.
+ * In particular, the _ character is used to indicate a class function or
+ * variable that should be considered private to the class.
+ *
+ * The code mostly uses accessor methods for getting/setting the private
+ * class variables.
+ *
+ * Over time, we'd like to formalize the convention by using support libraries
+ * which enforce privacy in objects.
+ *
+ * We also want to use jslint:  http://www.jslint.com/
+ *
+ *
+ *==================================================
+ */
+
+
+
+/*==================================================
+ *  Timeline VERSION
+ *==================================================
+ */
+// Note: version is also stored in the build.xml file
+Timeline.version = 'pre 2.4.0';  // use format 'pre 1.2.3' for trunk versions
+Timeline.ajax_lib_version = SimileAjax.version;
+Timeline.display_version = Timeline.version + ' (with Ajax lib ' + Timeline.ajax_lib_version + ')';
+ // cf method Timeline.writeVersion
+
+/*==================================================
+ *  Timeline
+ *==================================================
+ */
+Timeline.strings = {}; // localization string tables
+Timeline.HORIZONTAL = 0;
+Timeline.VERTICAL = 1;
+Timeline._defaultTheme = null;
+
+Timeline.getDefaultLocale = function() {
+    return Timeline.clientLocale;
+};
+
+Timeline.create = function(elmt, bandInfos, orientation, unit) {
+    if (Timeline.timelines == null) {
+        Timeline.timelines = [];
+        // Timeline.timelines array can have null members--Timelines that
+        // once existed on the page, but were later disposed of.
+    }
+
+    var timelineID = Timeline.timelines.length;
+    Timeline.timelines[timelineID] = null; // placeholder until we have the object
+    var new_tl = new Timeline._Impl(elmt, bandInfos, orientation, unit,
+      timelineID);
+    Timeline.timelines[timelineID] = new_tl;
+    return new_tl;
+};
+
+Timeline.createBandInfo = function(params) {
+    var theme = ("theme" in params) ? params.theme : Timeline.getDefaultTheme();
+
+    var eventSource = ("eventSource" in params) ? params.eventSource : null;
+
+    var etherParams = {
+        interval:           SimileAjax.DateTime.gregorianUnitLengths[params.intervalUnit],
+        pixelsPerInterval: params.intervalPixels,
+	theme: theme
+    };
+    if ('startsOn' in params || 'endsOn' in params) {
+	if ('startsOn' in params) {
+	    etherParams.startsOn = params.startsOn;
+	}
+	if ('endsOn' in params) {
+	    etherParams.endsOn = params.endsOn;
+	}
+    } else {
+	etherParams.centersOn = ("date" in params) ? params.date : new Date();
+    }
+    var ether = new Timeline.LinearEther(etherParams);
+
+    var etherPainter = new Timeline.GregorianEtherPainter({
+        unit:       params.intervalUnit,
+        multiple:   ("multiple" in params) ? params.multiple : 1,
+        theme:      theme,
+        align:      ("align" in params) ? params.align : undefined
+    });
+
+    var eventPainterParams = {
+        showText:   ("showEventText" in params) ? params.showEventText : true,
+        theme:      theme
+    };
+    // pass in custom parameters for the event painter
+    if ("eventPainterParams" in params) {
+        for (var prop in params.eventPainterParams) {
+            eventPainterParams[prop] = params.eventPainterParams[prop];
+        }
+    }
+
+    if ("trackHeight" in params) {
+        eventPainterParams.trackHeight = params.trackHeight;
+    }
+    if ("trackGap" in params) {
+        eventPainterParams.trackGap = params.trackGap;
+    }
+
+    var layout = ("overview" in params && params.overview) ? "overview" : ("layout" in params ? params.layout : "original");
+    var eventPainter;
+    if ("eventPainter" in params) {
+        eventPainter = new params.eventPainter(eventPainterParams);
+    } else {
+        switch (layout) {
+            case "overview" :
+                eventPainter = new Timeline.OverviewEventPainter(eventPainterParams);
+                break;
+            case "detailed" :
+                eventPainter = new Timeline.DetailedEventPainter(eventPainterParams);
+                break;
+            default:
+                eventPainter = new Timeline.OriginalEventPainter(eventPainterParams);
+        }
+    }
+
+    return {
+        width:          params.width,
+        eventSource:    eventSource,
+        timeZone:       ("timeZone" in params) ? params.timeZone : 0,
+        ether:          ether,
+        etherPainter:   etherPainter,
+        eventPainter:   eventPainter,
+        theme:          theme,
+        zoomIndex:      ("zoomIndex" in params) ? params.zoomIndex : 0,
+        zoomSteps:      ("zoomSteps" in params) ? params.zoomSteps : null
+    };
+};
+
+Timeline.createHotZoneBandInfo = function(params) {
+    var theme = ("theme" in params) ? params.theme : Timeline.getDefaultTheme();
+
+    var eventSource = ("eventSource" in params) ? params.eventSource : null;
+
+    var ether = new Timeline.HotZoneEther({
+        centersOn:          ("date" in params) ? params.date : new Date(),
+        interval:           SimileAjax.DateTime.gregorianUnitLengths[params.intervalUnit],
+        pixelsPerInterval:  params.intervalPixels,
+        zones:              params.zones,
+        theme:              theme
+    });
+
+    var etherPainter = new Timeline.HotZoneGregorianEtherPainter({
+        unit:       params.intervalUnit,
+        zones:      params.zones,
+        theme:      theme,
+        align:      ("align" in params) ? params.align : undefined
+    });
+
+    var eventPainterParams = {
+        showText:   ("showEventText" in params) ? params.showEventText : true,
+        theme:      theme
+    };
+    // pass in custom parameters for the event painter
+    if ("eventPainterParams" in params) {
+        for (var prop in params.eventPainterParams) {
+            eventPainterParams[prop] = params.eventPainterParams[prop];
+        }
+    }
+    if ("trackHeight" in params) {
+        eventPainterParams.trackHeight = params.trackHeight;
+    }
+    if ("trackGap" in params) {
+        eventPainterParams.trackGap = params.trackGap;
+    }
+
+    var layout = ("overview" in params && params.overview) ? "overview" : ("layout" in params ? params.layout : "original");
+    var eventPainter;
+    if ("eventPainter" in params) {
+        eventPainter = new params.eventPainter(eventPainterParams);
+    } else {
+        switch (layout) {
+            case "overview" :
+                eventPainter = new Timeline.OverviewEventPainter(eventPainterParams);
+                break;
+            case "detailed" :
+                eventPainter = new Timeline.DetailedEventPainter(eventPainterParams);
+                break;
+            default:
+                eventPainter = new Timeline.OriginalEventPainter(eventPainterParams);
+        }
+    }
+    return {
+        width:          params.width,
+        eventSource:    eventSource,
+        timeZone:       ("timeZone" in params) ? params.timeZone : 0,
+        ether:          ether,
+        etherPainter:   etherPainter,
+        eventPainter:   eventPainter,
+        theme:          theme,
+        zoomIndex:      ("zoomIndex" in params) ? params.zoomIndex : 0,
+        zoomSteps:      ("zoomSteps" in params) ? params.zoomSteps : null
+    };
+};
+
+Timeline.getDefaultTheme = function() {
+    if (Timeline._defaultTheme == null) {
+        Timeline._defaultTheme = Timeline.ClassicTheme.create(Timeline.getDefaultLocale());
+    }
+    return Timeline._defaultTheme;
+};
+
+Timeline.setDefaultTheme = function(theme) {
+    Timeline._defaultTheme = theme;
+};
+
+Timeline.loadXML = function(url, f) {
+    var fError = function(statusText, status, xmlhttp) {
+        alert("Failed to load data xml from " + url + "\n" + statusText);
+    };
+    var fDone = function(xmlhttp) {
+        var xml = xmlhttp.responseXML;
+        if (!xml.documentElement && xmlhttp.responseStream) {
+            xml.load(xmlhttp.responseStream);
+        }
+        f(xml, url);
+    };
+    SimileAjax.XmlHttp.get(url, fError, fDone);
+};
+
+
+Timeline.loadJSON = function(url, f) {
+    var fError = function(statusText, status, xmlhttp) {
+        alert("Failed to load json data from " + url + "\n" + statusText);
+    };
+    var fDone = function(xmlhttp) {
+        f(eval('(' + xmlhttp.responseText + ')'), url);
+    };
+    SimileAjax.XmlHttp.get(url, fError, fDone);
+};
+
+Timeline.getTimelineFromID = function(timelineID) {
+    return Timeline.timelines[timelineID];
+};
+
+// Write the current Timeline version as the contents of element with id el_id
+Timeline.writeVersion = function(el_id) {
+  document.getElementById(el_id).innerHTML = this.display_version;
+};
+
+
+
+/*==================================================
+ *  Timeline Implementation object
+ *==================================================
+ */
+Timeline._Impl = function(elmt, bandInfos, orientation, unit, timelineID) {
+    SimileAjax.WindowManager.initialize();
+
+    this._containerDiv = elmt;
+
+    this._bandInfos = bandInfos;
+    this._orientation = orientation == null ? Timeline.HORIZONTAL : orientation;
+    this._unit = (unit != null) ? unit : SimileAjax.NativeDateUnit;
+    this._starting = true; // is the Timeline being created? Used by autoWidth
+                           // functions
+    this._autoResizing = false;
+
+    // autoWidth is a "public" property of the Timeline object
+    this.autoWidth = bandInfos && bandInfos[0] && bandInfos[0].theme &&
+                     bandInfos[0].theme.autoWidth;
+    this.autoWidthAnimationTime = bandInfos && bandInfos[0] && bandInfos[0].theme &&
+                     bandInfos[0].theme.autoWidthAnimationTime;
+    this.timelineID = timelineID; // also public attribute
+    this.timeline_start = bandInfos && bandInfos[0] && bandInfos[0].theme &&
+                     bandInfos[0].theme.timeline_start;
+    this.timeline_stop  = bandInfos && bandInfos[0] && bandInfos[0].theme &&
+                     bandInfos[0].theme.timeline_stop;
+    this.timeline_at_start = false; // already at start or stop? Then won't
+    this.timeline_at_stop = false;  // try to move further in the wrong direction
+
+    this._initialize();
+};
+
+//
+// Public functions used by client sw
+//
+Timeline._Impl.prototype.dispose = function() {
+    for (var i = 0; i < this._bands.length; i++) {
+        this._bands[i].dispose();
+    }
+    this._bands = null;
+    this._bandInfos = null;
+    this._containerDiv.innerHTML = "";
+    // remove from array of Timelines
+    Timeline.timelines[this.timelineID] = null;
+};
+
+Timeline._Impl.prototype.getBandCount = function() {
+    return this._bands.length;
+};
+
+Timeline._Impl.prototype.getBand = function(index) {
+    return this._bands[index];
+};
+
+Timeline._Impl.prototype.finishedEventLoading = function() {
+    // Called by client after events have been loaded into Timeline
+    // Only used if the client has set autoWidth
+    // Sets width to Timeline's requested amount and will shrink down the div if
+    // need be.
+    this._autoWidthCheck(true);
+    this._starting = false;
+};
+
+Timeline._Impl.prototype.layout = function() {
+    // called by client when browser is resized
+    this._autoWidthCheck(true);
+    this._distributeWidths();
+};
+
+Timeline._Impl.prototype.paint = function() {
+    for (var i = 0; i < this._bands.length; i++) {
+        this._bands[i].paint();
+    }
+};
+
+Timeline._Impl.prototype.getDocument = function() {
+    return this._containerDiv.ownerDocument;
+};
+
+Timeline._Impl.prototype.addDiv = function(div) {
+    this._containerDiv.appendChild(div);
+};
+
+Timeline._Impl.prototype.removeDiv = function(div) {
+    this._containerDiv.removeChild(div);
+};
+
+Timeline._Impl.prototype.isHorizontal = function() {
+    return this._orientation == Timeline.HORIZONTAL;
+};
+
+Timeline._Impl.prototype.isVertical = function() {
+    return this._orientation == Timeline.VERTICAL;
+};
+
+Timeline._Impl.prototype.getPixelLength = function() {
+    return this._orientation == Timeline.HORIZONTAL ?
+        this._containerDiv.offsetWidth : this._containerDiv.offsetHeight;
+};
+
+Timeline._Impl.prototype.getPixelWidth = function() {
+    return this._orientation == Timeline.VERTICAL ?
+        this._containerDiv.offsetWidth : this._containerDiv.offsetHeight;
+};
+
+Timeline._Impl.prototype.getUnit = function() {
+    return this._unit;
+};
+
+Timeline._Impl.prototype.getWidthStyle = function() {
+    // which element.style attribute should be changed to affect Timeline's "width"
+    return this._orientation == Timeline.HORIZONTAL ? 'height' : 'width';
+};
+
+Timeline._Impl.prototype.loadXML = function(url, f) {
+    var tl = this;
+
+
+    var fError = function(statusText, status, xmlhttp) {
+        alert("Failed to load data xml from " + url + "\n" + statusText);
+        tl.hideLoadingMessage();
+    };
+    var fDone = function(xmlhttp) {
+        try {
+            var xml = xmlhttp.responseXML;
+            if (!xml.documentElement && xmlhttp.responseStream) {
+                xml.load(xmlhttp.responseStream);
+            }
+            f(xml, url);
+        } finally {
+            tl.hideLoadingMessage();
+        }
+    };
+
+    this.showLoadingMessage();
+    window.setTimeout(function() { SimileAjax.XmlHttp.get(url, fError, fDone); }, 0);
+};
+
+Timeline._Impl.prototype.loadJSON = function(url, f) {
+    var tl = this;
+
+    var fError = function(statusText, status, xmlhttp) {
+        alert("Failed to load json data from " + url + "\n" + statusText);
+        tl.hideLoadingMessage();
+    };
+    var fDone = function(xmlhttp) {
+        try {
+            f(eval('(' + xmlhttp.responseText + ')'), url);
+        } finally {
+            tl.hideLoadingMessage();
+        }
+    };
+
+    this.showLoadingMessage();
+    window.setTimeout(function() { SimileAjax.XmlHttp.get(url, fError, fDone); }, 0);
+};
+
+
+//
+// Private functions used by Timeline object functions
+//
+
+Timeline._Impl.prototype._autoWidthScrollListener = function(band) {
+    band.getTimeline()._autoWidthCheck(false);
+};
+
+// called to re-calculate auto width and adjust the overall Timeline div if needed
+Timeline._Impl.prototype._autoWidthCheck = function(okToShrink) {
+    var timeline = this; // this Timeline
+    var immediateChange = timeline._starting;
+    var newWidth = 0;
+
+    function changeTimelineWidth() {
+        var widthStyle = timeline.getWidthStyle();
+        if (immediateChange) {
+            timeline._containerDiv.style[widthStyle] = newWidth + 'px';
+        } else {
+        	  // animate change
+        	  timeline._autoResizing = true;
+        	  var animateParam ={};
+        	  animateParam[widthStyle] = newWidth + 'px';
+
+        	  SimileAjax.jQuery(timeline._containerDiv).animate(
+        	      animateParam, timeline.autoWidthAnimationTime,
+        	      'linear', function(){timeline._autoResizing = false;});
+        }
+    }
+
+    function checkTimelineWidth() {
+        var targetWidth = 0; // the new desired width
+        var currentWidth = timeline.getPixelWidth();
+
+        if (timeline._autoResizing) {
+        	return; // early return
+        }
+
+        // compute targetWidth
+        for (var i = 0; i < timeline._bands.length; i++) {
+            timeline._bands[i].checkAutoWidth();
+            targetWidth += timeline._bandInfos[i].width;
+        }
+
+        if (targetWidth > currentWidth || okToShrink) {
+            // yes, let's change the size
+            newWidth = targetWidth;
+            changeTimelineWidth();
+            timeline._distributeWidths();
+        }
+    }
+
+    // function's mainline
+    if (!timeline.autoWidth) {
+        return; // early return
+    }
+
+    checkTimelineWidth();
+};
+
+Timeline._Impl.prototype._initialize = function() {
+    var containerDiv = this._containerDiv;
+    var doc = containerDiv.ownerDocument;
+
+    containerDiv.className =
+        containerDiv.className.split(" ").concat("timeline-container").join(" ");
+
+	/*
+	 * Set css-class on container div that will define orientation
+	 */
+	var orientation = (this.isHorizontal()) ? 'horizontal' : 'vertical'
+	containerDiv.className +=' timeline-'+orientation;
+
+
+    while (containerDiv.firstChild) {
+        containerDiv.removeChild(containerDiv.firstChild);
+    }
+
+    /*
+     *  inserting copyright and link to simile
+     */
+    var elmtCopyright = SimileAjax.Graphics.createTranslucentImage(Timeline.urlPrefix + (this.isHorizontal() ? "images/copyright-vertical.png" : "images/copyright.png"));
+    elmtCopyright.className = "timeline-copyright";
+    elmtCopyright.title = "Timeline copyright SIMILE - www.code.google.com/p/simile-widgets/";
+    SimileAjax.DOM.registerEvent(elmtCopyright, "click", function() { window.location = "http://www.simile-widgets.org/"; });
+    containerDiv.appendChild(elmtCopyright);
+
+    /*
+     *  creating bands
+     */
+    this._bands = [];
+    for (var i = 0; i < this._bandInfos.length; i++) {
+        var bandInfo = this._bandInfos[i];
+        var bandClass = bandInfo.bandClass || Timeline._Band;
+        var band = new bandClass(this, this._bandInfos[i], i);
+        this._bands.push(band);
+    }
+    this._distributeWidths();
+
+    /*
+     *  sync'ing bands
+     */
+    for (var i = 0; i < this._bandInfos.length; i++) {
+        var bandInfo = this._bandInfos[i];
+        if ("syncWith" in bandInfo) {
+            this._bands[i].setSyncWithBand(
+                this._bands[bandInfo.syncWith],
+                ("highlight" in bandInfo) ? bandInfo.highlight : false
+            );
+        }
+    }
+
+
+    if (this.autoWidth) {
+        for (var i = 0; i < this._bands.length; i++) {
+            this._bands[i].addOnScrollListener(this._autoWidthScrollListener);
+        }
+    }
+
+
+    /*
+     *  creating loading UI
+     */
+    var message = SimileAjax.Graphics.createMessageBubble(doc);
+    message.containerDiv.className = "timeline-message-container";
+    containerDiv.appendChild(message.containerDiv);
+
+    message.contentDiv.className = "timeline-message";
+    message.contentDiv.innerHTML = "<img src='" + Timeline.urlPrefix + "images/progress-running.gif' /> Loading...";
+
+    this.showLoadingMessage = function() { message.containerDiv.style.display = "block"; };
+    this.hideLoadingMessage = function() { message.containerDiv.style.display = "none"; };
+};
+
+Timeline._Impl.prototype._distributeWidths = function() {
+    var length = this.getPixelLength();
+    var width = this.getPixelWidth();
+    var cumulativeWidth = 0;
+
+    for (var i = 0; i < this._bands.length; i++) {
+        var band = this._bands[i];
+        var bandInfos = this._bandInfos[i];
+        var widthString = bandInfos.width;
+        var bandWidth;
+
+        if (typeof widthString == 'string') {
+          var x =  widthString.indexOf("%");
+          if (x > 0) {
+              var percent = parseInt(widthString.substr(0, x));
+              bandWidth = Math.round(percent * width / 100);
+          } else {
+              bandWidth = parseInt(widthString);
+          }
+        } else {
+        	// was given an integer
+        	bandWidth = widthString;
+        }
+
+        band.setBandShiftAndWidth(cumulativeWidth, bandWidth);
+        band.setViewLength(length);
+
+        cumulativeWidth += bandWidth;
+    }
+};
+
+Timeline._Impl.prototype.shiftOK = function(index, shift) {
+    // Returns true if the proposed shift is ok
+    //
+    // Positive shift means going back in time
+    var going_back = shift > 0,
+        going_forward = shift < 0;
+
+    // Is there an edge?
+    if ((going_back    && this.timeline_start == null) ||
+        (going_forward && this.timeline_stop  == null) ||
+        (shift == 0)) {
+        return (true);  // early return
+    }
+
+    // If any of the bands has noted that it is changing the others,
+    // then this shift is a secondary shift in reaction to the real shift,
+    // which already happened. In such cases, ignore it. (The issue is
+    // that a positive original shift can cause a negative secondary shift,
+    // as the bands adjust.)
+    var secondary_shift = false;
+    for (var i = 0; i < this._bands.length && !secondary_shift; i++) {
+       secondary_shift = this._bands[i].busy();
+    }
+    if (secondary_shift) {
+        return(true); // early return
+    }
+
+    // If we are already at an edge, then don't even think about going any further
+    if ((going_back    && this.timeline_at_start) ||
+        (going_forward && this.timeline_at_stop)) {
+        return (false);  // early return
+    }
+
+    // Need to check all the bands
+    var ok = false; // return value
+    // If any of the bands will be or are showing an ok date, then let the shift proceed.
+    for (var i = 0; i < this._bands.length && !ok; i++) {
+       var band = this._bands[i];
+       if (going_back) {
+           ok = (i == index ? band.getMinVisibleDateAfterDelta(shift) : band.getMinVisibleDate())
+                >= this.timeline_start;
+       } else {
+           ok = (i == index ? band.getMaxVisibleDateAfterDelta(shift) : band.getMaxVisibleDate())
+                <= this.timeline_stop;
+       }
+    }
+
+    // process results
+    if (going_back) {
+       this.timeline_at_start = !ok;
+       this.timeline_at_stop = false;
+    } else {
+       this.timeline_at_stop = !ok;
+       this.timeline_at_start = false;
+    }
+    // This is where you could have an effect once per hitting an
+    // edge of the Timeline. Eg jitter the Timeline
+    //if (!ok) {
+        //alert(going_back ? "At beginning" : "At end");
+    //}
+    return (ok);
+};
+
+Timeline._Impl.prototype.zoom = function (zoomIn, x, y, target) {
+  var matcher = new RegExp("^timeline-band-([0-9]+)$");
+  var bandIndex = null;
+
+  var result = matcher.exec(target.id);
+  if (result) {
+    bandIndex = parseInt(result[1]);
+  }
+
+  if (bandIndex != null) {
+    this._bands[bandIndex].zoom(zoomIn, x, y, target);
+  }
+
+  this.paint();
+};
+
+/*=================================================
+ *
+ * Coding standards:
+ *
+ * We aim towards Douglas Crockford's Javascript conventions.
+ * See:  http://javascript.crockford.com/code.html
+ * See also: http://www.crockford.com/javascript/javascript.html
+ *
+ * That said, this JS code was written before some recent JS
+ * support libraries became widely used or available.
+ * In particular, the _ character is used to indicate a class function or
+ * variable that should be considered private to the class.
+ *
+ * The code mostly uses accessor methods for getting/setting the private
+ * class variables.
+ *
+ * Over time, we'd like to formalize the convention by using support libraries
+ * which enforce privacy in objects.
+ *
+ * We also want to use jslint:  http://www.jslint.com/
+ *
+ *
+ *==================================================
+ */
+
+
+
+/*==================================================
+ *  Band
+ *==================================================
+ */
+Timeline._Band = function(timeline, bandInfo, index) {
+    // hack for easier subclassing
+    if (timeline !== undefined) {
+        this.initialize(timeline, bandInfo, index);
+    }
+};
+
+Timeline._Band.prototype.initialize = function(timeline, bandInfo, index) {
+    // Set up the band's object
+
+    // Munge params: If autoWidth is on for the Timeline, then ensure that
+    // bandInfo.width is an integer
+    if (timeline.autoWidth && typeof bandInfo.width == 'string') {
+        bandInfo.width = bandInfo.width.indexOf("%") > -1 ? 0 : parseInt(bandInfo.width);
+    }
+
+    this._timeline = timeline;
+    this._bandInfo = bandInfo;
+
+    this._index = index;
+
+    this._locale = ("locale" in bandInfo) ? bandInfo.locale : Timeline.getDefaultLocale();
+    this._timeZone = ("timeZone" in bandInfo) ? bandInfo.timeZone : 0;
+    this._labeller = ("labeller" in bandInfo) ? bandInfo.labeller :
+        (("createLabeller" in timeline.getUnit()) ?
+            timeline.getUnit().createLabeller(this._locale, this._timeZone) :
+            new Timeline.GregorianDateLabeller(this._locale, this._timeZone));
+    this._theme = bandInfo.theme;
+    this._zoomIndex = ("zoomIndex" in bandInfo) ? bandInfo.zoomIndex : 0;
+    this._zoomSteps = ("zoomSteps" in bandInfo) ? bandInfo.zoomSteps : null;
+
+    this._dragging = false;
+    this._changing = false;
+    this._originalScrollSpeed = 5; // pixels
+    this._scrollSpeed = this._originalScrollSpeed;
+    this._viewOrthogonalOffset= 0; // vertical offset if the timeline is horizontal, and vice versa
+    this._onScrollListeners = [];
+
+    var b = this;
+    this._syncWithBand = null;
+    this._syncWithBandHandler = function(band) {
+        b._onHighlightBandScroll();
+    };
+    this._selectorListener = function(band) {
+        b._onHighlightBandScroll();
+    };
+
+    /*
+     *  Install a textbox to capture keyboard events
+     */
+    var inputDiv = this._timeline.getDocument().createElement("div");
+    inputDiv.className = "timeline-band-input";
+    this._timeline.addDiv(inputDiv);
+
+    this._keyboardInput = document.createElement("input");
+    this._keyboardInput.type = "text";
+    inputDiv.appendChild(this._keyboardInput);
+    SimileAjax.DOM.registerEventWithObject(this._keyboardInput, "keydown", this, "_onKeyDown");
+    SimileAjax.DOM.registerEventWithObject(this._keyboardInput, "keyup", this, "_onKeyUp");
+
+    /*
+     *  The band's outer most div that slides with respect to the timeline's div
+     */
+    this._div = this._timeline.getDocument().createElement("div");
+    this._div.id = "timeline-band-" + index;
+    this._div.className = "timeline-band timeline-band-" + index;
+    this._timeline.addDiv(this._div);
+
+    SimileAjax.DOM.registerEventWithObject(this._div, "mousedown", this, "_onMouseDown");
+    SimileAjax.DOM.registerEventWithObject(this._div, "mousemove", this, "_onMouseMove");
+    SimileAjax.DOM.registerEventWithObject(this._div, "mouseup", this, "_onMouseUp");
+    SimileAjax.DOM.registerEventWithObject(this._div, "mouseout", this, "_onMouseOut");
+    SimileAjax.DOM.registerEventWithObject(this._div, "dblclick", this, "_onDblClick");
+
+    var mouseWheel = this._theme!= null ? this._theme.mouseWheel : 'scroll'; // theme is not always defined
+    if (mouseWheel === 'zoom' || mouseWheel === 'scroll' || this._zoomSteps) {
+        // capture mouse scroll
+        if (SimileAjax.Platform.browser.isFirefox) {
+            SimileAjax.DOM.registerEventWithObject(this._div, "DOMMouseScroll", this, "_onMouseScroll");
+        } else {
+            SimileAjax.DOM.registerEventWithObject(this._div, "mousewheel", this, "_onMouseScroll");
+        }
+    }
+
+    /*
+     *  The inner div that contains layers
+     */
+    this._innerDiv = this._timeline.getDocument().createElement("div");
+    this._innerDiv.className = "timeline-band-inner";
+    this._div.appendChild(this._innerDiv);
+
+    /*
+     *  Initialize parts of the band
+     */
+    this._ether = bandInfo.ether;
+    bandInfo.ether.initialize(this, timeline);
+
+    this._etherPainter = bandInfo.etherPainter;
+    bandInfo.etherPainter.initialize(this, timeline);
+
+    this._eventSource = bandInfo.eventSource;
+    if (this._eventSource) {
+        this._eventListener = {
+            onAddMany: function() { b._onAddMany(); },
+            onClear:   function() { b._onClear(); }
+        }
+        this._eventSource.addListener(this._eventListener);
+    }
+
+    this._eventPainter = bandInfo.eventPainter;
+    this._eventTracksNeeded = 0;   // set by painter via updateEventTrackInfo
+    this._eventTrackIncrement = 0;
+    bandInfo.eventPainter.initialize(this, timeline);
+
+    this._decorators = ("decorators" in bandInfo) ? bandInfo.decorators : [];
+    for (var i = 0; i < this._decorators.length; i++) {
+        this._decorators[i].initialize(this, timeline);
+    }
+};
+
+Timeline._Band.SCROLL_MULTIPLES = 5;
+
+Timeline._Band.prototype.dispose = function() {
+    this.closeBubble();
+
+    if (this._eventSource) {
+        this._eventSource.removeListener(this._eventListener);
+        this._eventListener = null;
+        this._eventSource = null;
+    }
+
+    this._timeline = null;
+    this._bandInfo = null;
+
+    this._labeller = null;
+    this._ether = null;
+    this._etherPainter = null;
+    this._eventPainter = null;
+    this._decorators = null;
+
+    this._onScrollListeners = null;
+    this._syncWithBandHandler = null;
+    this._selectorListener = null;
+
+    this._div = null;
+    this._innerDiv = null;
+    this._keyboardInput = null;
+};
+
+Timeline._Band.prototype.addOnScrollListener = function(listener) {
+    this._onScrollListeners.push(listener);
+};
+
+Timeline._Band.prototype.removeOnScrollListener = function(listener) {
+    for (var i = 0; i < this._onScrollListeners.length; i++) {
+        if (this._onScrollListeners[i] == listener) {
+            this._onScrollListeners.splice(i, 1);
+            break;
+        }
+    }
+};
+
+Timeline._Band.prototype.setSyncWithBand = function(band, highlight) {
+    if (this._syncWithBand) {
+        this._syncWithBand.removeOnScrollListener(this._syncWithBandHandler);
+    }
+
+    this._syncWithBand = band;
+    this._syncWithBand.addOnScrollListener(this._syncWithBandHandler);
+    this._highlight = highlight;
+    this._positionHighlight();
+};
+
+Timeline._Band.prototype.getLocale = function() {
+    return this._locale;
+};
+
+Timeline._Band.prototype.getTimeZone = function() {
+    return this._timeZone;
+};
+
+Timeline._Band.prototype.getLabeller = function() {
+    return this._labeller;
+};
+
+Timeline._Band.prototype.getIndex = function() {
+    return this._index;
+};
+
+Timeline._Band.prototype.getEther = function() {
+    return this._ether;
+};
+
+Timeline._Band.prototype.getEtherPainter = function() {
+    return this._etherPainter;
+};
+
+Timeline._Band.prototype.getEventSource = function() {
+    return this._eventSource;
+};
+
+Timeline._Band.prototype.getEventPainter = function() {
+    return this._eventPainter;
+};
+
+Timeline._Band.prototype.getTimeline = function() {
+    return this._timeline;
+};
+
+// Autowidth support
+Timeline._Band.prototype.updateEventTrackInfo = function(tracks, increment) {
+    this._eventTrackIncrement = increment; // doesn't vary for a specific band
+
+    if (tracks > this._eventTracksNeeded) {
+        this._eventTracksNeeded = tracks;
+    }
+};
+
+// Autowidth support
+Timeline._Band.prototype.checkAutoWidth = function() {
+    // if a new (larger) width is needed by the band
+    // then: a) updates the band's bandInfo.width
+    //
+    // desiredWidth for the band is
+    //   (number of tracks + margin) * track increment
+    if (! this._timeline.autoWidth) {
+      return; // early return
+    }
+
+    var overviewBand = this._eventPainter.getType() == 'overview';
+    var margin = overviewBand ?
+       this._theme.event.overviewTrack.autoWidthMargin :
+       this._theme.event.track.autoWidthMargin;
+    var desiredWidth = Math.ceil((this._eventTracksNeeded + margin) *
+                       this._eventTrackIncrement);
+    // add offset amount (additional margin)
+    desiredWidth += overviewBand ? this._theme.event.overviewTrack.offset :
+                                   this._theme.event.track.offset;
+    var bandInfo = this._bandInfo;
+
+    if (desiredWidth != bandInfo.width) {
+        bandInfo.width = desiredWidth;
+    }
+};
+
+Timeline._Band.prototype.layout = function() {
+    this.paint();
+};
+
+Timeline._Band.prototype.paint = function() {
+    this._etherPainter.paint();
+    this._paintDecorators();
+    this._paintEvents();
+};
+
+Timeline._Band.prototype.softLayout = function() {
+    this.softPaint();
+};
+
+Timeline._Band.prototype.softPaint = function() {
+    this._etherPainter.softPaint();
+    this._softPaintDecorators();
+    this._softPaintEvents();
+};
+
+Timeline._Band.prototype.setBandShiftAndWidth = function(shift, width) {
+    var inputDiv = this._keyboardInput.parentNode;
+    var middle = shift + Math.floor(width / 2);
+    if (this._timeline.isHorizontal()) {
+        this._div.style.top = shift + "px";
+        this._div.style.height = width + "px";
+
+        inputDiv.style.top = middle + "px";
+        inputDiv.style.left = "-1em";
+    } else {
+        this._div.style.left = shift + "px";
+        this._div.style.width = width + "px";
+
+        inputDiv.style.left = middle + "px";
+        inputDiv.style.top = "-1em";
+    }
+};
+
+Timeline._Band.prototype.getViewWidth = function() {
+    if (this._timeline.isHorizontal()) {
+        return this._div.offsetHeight;
+    } else {
+        return this._div.offsetWidth;
+    }
+};
+
+Timeline._Band.prototype.setViewLength = function(length) {
+    this._viewLength = length;
+    this._recenterDiv();
+    this._onChanging();
+};
+
+Timeline._Band.prototype.getViewLength = function() {
+    return this._viewLength;
+};
+
+Timeline._Band.prototype.getTotalViewLength = function() {
+    return Timeline._Band.SCROLL_MULTIPLES * this._viewLength;
+};
+
+Timeline._Band.prototype.getViewOffset = function() {
+    return this._viewOffset;
+};
+
+Timeline._Band.prototype.getMinDate = function() {
+    return this._ether.pixelOffsetToDate(this._viewOffset);
+};
+
+Timeline._Band.prototype.getMaxDate = function() {
+    return this._ether.pixelOffsetToDate(this._viewOffset + Timeline._Band.SCROLL_MULTIPLES * this._viewLength);
+};
+
+Timeline._Band.prototype.getMinVisibleDate = function() {
+    return this._ether.pixelOffsetToDate(0);
+};
+
+Timeline._Band.prototype.getMinVisibleDateAfterDelta = function(delta) {
+    return this._ether.pixelOffsetToDate(delta);
+};
+
+Timeline._Band.prototype.getMaxVisibleDate = function() {
+    // Max date currently visible on band
+    return this._ether.pixelOffsetToDate(this._viewLength);
+};
+
+Timeline._Band.prototype.getMaxVisibleDateAfterDelta = function(delta) {
+    // Max date visible on band after delta px view change is applied
+    return this._ether.pixelOffsetToDate(this._viewLength + delta);
+};
+
+Timeline._Band.prototype.getCenterVisibleDate = function() {
+    return this._ether.pixelOffsetToDate(this._viewLength / 2);
+};
+
+Timeline._Band.prototype.setMinVisibleDate = function(date) {
+    if (!this._changing) {
+        this._moveEther(Math.round(-this._ether.dateToPixelOffset(date)));
+    }
+};
+
+Timeline._Band.prototype.setMaxVisibleDate = function(date) {
+    if (!this._changing) {
+        this._moveEther(Math.round(this._viewLength - this._ether.dateToPixelOffset(date)));
+    }
+};
+
+Timeline._Band.prototype.setCenterVisibleDate = function(date) {
+    if (!this._changing) {
+        this._moveEther(Math.round(this._viewLength / 2 - this._ether.dateToPixelOffset(date)));
+    }
+};
+
+Timeline._Band.prototype.dateToPixelOffset = function(date) {
+    return this._ether.dateToPixelOffset(date) - this._viewOffset;
+};
+
+Timeline._Band.prototype.pixelOffsetToDate = function(pixels) {
+    return this._ether.pixelOffsetToDate(pixels + this._viewOffset);
+};
+
+Timeline._Band.prototype.getViewOrthogonalOffset = function() {
+    return this._viewOrthogonalOffset;
+};
+
+Timeline._Band.prototype.setViewOrthogonalOffset = function(offset) {
+    this._viewOrthogonalOffset = Math.max(0, offset);
+};
+
+Timeline._Band.prototype.createLayerDiv = function(zIndex, className) {
+    var div = this._timeline.getDocument().createElement("div");
+    div.className = "timeline-band-layer" + (typeof className == "string" ? (" " + className) : "");
+    div.style.zIndex = zIndex;
+    this._innerDiv.appendChild(div);
+
+    var innerDiv = this._timeline.getDocument().createElement("div");
+    innerDiv.className = "timeline-band-layer-inner";
+    if (SimileAjax.Platform.browser.isIE) {
+        innerDiv.style.cursor = "move";
+    } else {
+        innerDiv.style.cursor = "-moz-grab";
+    }
+    div.appendChild(innerDiv);
+
+    return innerDiv;
+};
+
+Timeline._Band.prototype.removeLayerDiv = function(div) {
+    this._innerDiv.removeChild(div.parentNode);
+};
+
+Timeline._Band.prototype.scrollToCenter = function(date, f) {
+    var pixelOffset = this._ether.dateToPixelOffset(date);
+    if (pixelOffset < -this._viewLength / 2) {
+        this.setCenterVisibleDate(this.pixelOffsetToDate(pixelOffset + this._viewLength));
+    } else if (pixelOffset > 3 * this._viewLength / 2) {
+        this.setCenterVisibleDate(this.pixelOffsetToDate(pixelOffset - this._viewLength));
+    }
+    this._autoScroll(Math.round(this._viewLength / 2 - this._ether.dateToPixelOffset(date)), f);
+};
+
+Timeline._Band.prototype.showBubbleForEvent = function(eventID) {
+    var evt = this.getEventSource().getEvent(eventID);
+    if (evt) {
+        var self = this;
+        this.scrollToCenter(evt.getStart(), function() {
+            self._eventPainter.showBubble(evt);
+        });
+    }
+};
+
+Timeline._Band.prototype.zoom = function(zoomIn, x, y, target) {
+  if (!this._zoomSteps) {
+    // zoom disabled
+    return;
+  }
+
+  // shift the x value by our offset
+  x += this._viewOffset;
+
+  var zoomDate = this._ether.pixelOffsetToDate(x);
+  var netIntervalChange = this._ether.zoom(zoomIn);
+  this._etherPainter.zoom(netIntervalChange);
+
+  // shift our zoom date to the far left
+  this._moveEther(Math.round(-this._ether.dateToPixelOffset(zoomDate)));
+  // then shift it back to where the mouse was
+  this._moveEther(x);
+};
+
+Timeline._Band.prototype._onMouseDown = function(innerFrame, evt, target) {
+    this.closeBubble();
+
+    this._dragging = true;
+    this._dragX = evt.clientX;
+    this._dragY = evt.clientY;
+};
+
+Timeline._Band.prototype._onMouseMove = function(innerFrame, evt, target) {
+    if (this._dragging) {
+        var diffX = evt.clientX - this._dragX;
+        var diffY = evt.clientY - this._dragY;
+
+        this._dragX = evt.clientX;
+        this._dragY = evt.clientY;
+
+        if (this._timeline.isHorizontal()) {
+            this._moveEther(diffX, diffY);
+        } else {
+            this._moveEther(diffY, diffX);
+        }
+        this._positionHighlight();
+    }
+};
+
+Timeline._Band.prototype._onMouseUp = function(innerFrame, evt, target) {
+    this._dragging = false;
+    this._keyboardInput.focus();
+};
+
+Timeline._Band.prototype._onMouseOut = function(innerFrame, evt, target) {
+    var coords = SimileAjax.DOM.getEventRelativeCoordinates(evt, innerFrame);
+    coords.x += this._viewOffset;
+    if (coords.x < 0 || coords.x > innerFrame.offsetWidth ||
+        coords.y < 0 || coords.y > innerFrame.offsetHeight) {
+        this._dragging = false;
+    }
+};
+
+Timeline._Band.prototype._onMouseScroll = function(innerFrame, evt, target) {
+  var now = new Date();
+  now = now.getTime();
+
+  if (!this._lastScrollTime || ((now - this._lastScrollTime) > 50)) {
+    // limit 1 scroll per 200ms due to FF3 sending multiple events back to back
+    this._lastScrollTime = now;
+
+    var delta = 0;
+    if (evt.wheelDelta) {
+      delta = evt.wheelDelta/120;
+    } else if (evt.detail) {
+      delta = -evt.detail/3;
+    }
+
+    // either scroll or zoom
+    var mouseWheel = this._theme.mouseWheel;
+
+    if (this._zoomSteps || mouseWheel === 'zoom') {
+      var loc = SimileAjax.DOM.getEventRelativeCoordinates(evt, innerFrame);
+      if (delta != 0) {
+        var zoomIn;
+        if (delta > 0)
+          zoomIn = true;
+        if (delta < 0)
+          zoomIn = false;
+        // call zoom on the timeline so we could zoom multiple bands if desired
+        this._timeline.zoom(zoomIn, loc.x, loc.y, innerFrame);
+      }
+    }
+    else if (mouseWheel === 'scroll') {
+    	var move_amt = 50 * (delta < 0 ? -1 : 1);
+      this._moveEther(move_amt);
+    }
+  }
+
+  // prevent bubble
+  if (evt.stopPropagation) {
+    evt.stopPropagation();
+  }
+  evt.cancelBubble = true;
+
+  // prevent the default action
+  if (evt.preventDefault) {
+    evt.preventDefault();
+  }
+  evt.returnValue = false;
+};
+
+Timeline._Band.prototype._onDblClick = function(innerFrame, evt, target) {
+    var coords = SimileAjax.DOM.getEventRelativeCoordinates(evt, innerFrame);
+    var distance = coords.x - (this._viewLength / 2 - this._viewOffset);
+
+    this._autoScroll(-distance);
+};
+
+Timeline._Band.prototype._onKeyDown = function(keyboardInput, evt, target) {
+    if (!this._dragging) {
+        switch (evt.keyCode) {
+        case 27: // ESC
+            break;
+        case 37: // left arrow
+        case 38: // up arrow
+            this._scrollSpeed = Math.min(50, Math.abs(this._scrollSpeed * 1.05));
+            this._moveEther(this._scrollSpeed);
+            break;
+        case 39: // right arrow
+        case 40: // down arrow
+            this._scrollSpeed = -Math.min(50, Math.abs(this._scrollSpeed * 1.05));
+            this._moveEther(this._scrollSpeed);
+            break;
+        default:
+            return true;
+        }
+        this.closeBubble();
+
+        SimileAjax.DOM.cancelEvent(evt);
+        return false;
+    }
+    return true;
+};
+
+Timeline._Band.prototype._onKeyUp = function(keyboardInput, evt, target) {
+    if (!this._dragging) {
+        this._scrollSpeed = this._originalScrollSpeed;
+
+        switch (evt.keyCode) {
+        case 35: // end
+            this.setCenterVisibleDate(this._eventSource.getLatestDate());
+            break;
+        case 36: // home
+            this.setCenterVisibleDate(this._eventSource.getEarliestDate());
+            break;
+        case 33: // page up
+            this._autoScroll(this._timeline.getPixelLength());
+            break;
+        case 34: // page down
+            this._autoScroll(-this._timeline.getPixelLength());
+            break;
+        default:
+            return true;
+        }
+
+        this.closeBubble();
+
+        SimileAjax.DOM.cancelEvent(evt);
+        return false;
+    }
+    return true;
+};
+
+Timeline._Band.prototype._autoScroll = function(distance, f) {
+    var b = this;
+    var a = SimileAjax.Graphics.createAnimation(
+        function(abs, diff) {
+            b._moveEther(diff);
+        },
+        0,
+        distance,
+        1000,
+        f
+    );
+    a.run();
+};
+
+Timeline._Band.prototype._moveEther = function(shift, orthogonalShift) {
+    if (orthogonalShift === undefined) {
+        orthogonalShift = 0;
+    }
+
+    this.closeBubble();
+
+    // A positive shift means back in time
+    // Check that we're not moving beyond Timeline's limits
+    if (!this._timeline.shiftOK(this._index, shift)) {
+        return; // early return
+    }
+
+    this._viewOffset += shift;
+    this._viewOrthogonalOffset = Math.min(0, this._viewOrthogonalOffset + orthogonalShift);
+
+    this._ether.shiftPixels(-shift);
+    if (this._timeline.isHorizontal()) {
+        this._div.style.left = this._viewOffset + "px";
+    } else {
+        this._div.style.top = this._viewOffset + "px";
+    }
+
+    if (this._viewOffset > -this._viewLength * 0.5 ||
+        this._viewOffset < -this._viewLength * (Timeline._Band.SCROLL_MULTIPLES - 1.5)) {
+
+        this._recenterDiv();
+    } else {
+        this.softLayout();
+    }
+
+    this._onChanging();
+}
+
+Timeline._Band.prototype._onChanging = function() {
+    this._changing = true;
+
+    this._fireOnScroll();
+    this._setSyncWithBandDate();
+
+    this._changing = false;
+};
+
+Timeline._Band.prototype.busy = function() {
+    // Is this band busy changing other bands?
+    return(this._changing);
+};
+
+Timeline._Band.prototype._fireOnScroll = function() {
+    for (var i = 0; i < this._onScrollListeners.length; i++) {
+        this._onScrollListeners[i](this);
+    }
+};
+
+Timeline._Band.prototype._setSyncWithBandDate = function() {
+    if (this._syncWithBand) {
+        var centerDate = this._ether.pixelOffsetToDate(this.getViewLength() / 2);
+        this._syncWithBand.setCenterVisibleDate(centerDate);
+    }
+};
+
+Timeline._Band.prototype._onHighlightBandScroll = function() {
+    if (this._syncWithBand) {
+        var centerDate = this._syncWithBand.getCenterVisibleDate();
+        var centerPixelOffset = this._ether.dateToPixelOffset(centerDate);
+
+        this._moveEther(Math.round(this._viewLength / 2 - centerPixelOffset));
+
+        if (this._highlight) {
+            this._etherPainter.setHighlight(
+                this._syncWithBand.getMinVisibleDate(),
+                this._syncWithBand.getMaxVisibleDate());
+        }
+    }
+};
+
+Timeline._Band.prototype._onAddMany = function() {
+    this._paintEvents();
+};
+
+Timeline._Band.prototype._onClear = function() {
+    this._paintEvents();
+};
+
+Timeline._Band.prototype._positionHighlight = function() {
+    if (this._syncWithBand) {
+        var startDate = this._syncWithBand.getMinVisibleDate();
+        var endDate = this._syncWithBand.getMaxVisibleDate();
+
+        if (this._highlight) {
+            this._etherPainter.setHighlight(startDate, endDate);
+        }
+    }
+};
+
+Timeline._Band.prototype._recenterDiv = function() {
+    this._viewOffset = -this._viewLength * (Timeline._Band.SCROLL_MULTIPLES - 1) / 2;
+    if (this._timeline.isHorizontal()) {
+        this._div.style.left = this._viewOffset + "px";
+        this._div.style.width = (Timeline._Band.SCROLL_MULTIPLES * this._viewLength) + "px";
+    } else {
+        this._div.style.top = this._viewOffset + "px";
+        this._div.style.height = (Timeline._Band.SCROLL_MULTIPLES * this._viewLength) + "px";
+    }
+    this.layout();
+};
+
+Timeline._Band.prototype._paintEvents = function() {
+    this._eventPainter.paint();
+};
+
+Timeline._Band.prototype._softPaintEvents = function() {
+    this._eventPainter.softPaint();
+};
+
+Timeline._Band.prototype._paintDecorators = function() {
+    for (var i = 0; i < this._decorators.length; i++) {
+        this._decorators[i].paint();
+    }
+};
+
+Timeline._Band.prototype._softPaintDecorators = function() {
+    for (var i = 0; i < this._decorators.length; i++) {
+        this._decorators[i].softPaint();
+    }
+};
+
+Timeline._Band.prototype.closeBubble = function() {
+    SimileAjax.WindowManager.cancelPopups();
+};
+/*==================================================
+ *  Classic Theme
+ *==================================================
+ */
+
+
+
+Timeline.ClassicTheme = new Object();
+
+Timeline.ClassicTheme.implementations = [];
+
+Timeline.ClassicTheme.create = function(locale) {
+    if (locale == null) {
+        locale = Timeline.getDefaultLocale();
+    }
+
+    var f = Timeline.ClassicTheme.implementations[locale];
+    if (f == null) {
+        f = Timeline.ClassicTheme._Impl;
+    }
+    return new f();
+};
+
+Timeline.ClassicTheme._Impl = function() {
+    this.firstDayOfWeek = 0; // Sunday
+
+    // Note: Many styles previously set here are now set using CSS
+    //       The comments indicate settings controlled by CSS, not
+    //       lines to be un-commented.
+    //
+    //
+    // Attributes autoWidth, autoWidthAnimationTime, timeline_start
+    // and timeline_stop must be set on the first band's theme.
+    // The other attributes can be set differently for each
+    // band by using different themes for the bands.
+    this.autoWidth = false; // Should the Timeline automatically grow itself, as
+                            // needed when too many events for the available width
+                            // are painted on the visible part of the Timeline?
+    this.autoWidthAnimationTime = 500; // mSec
+    this.timeline_start = null; // Setting a date, eg new Date(Date.UTC(2008,0,17,20,00,00,0)) will prevent the
+                                // Timeline from being moved to anytime before the date.
+    this.timeline_stop = null;  // Use for setting a maximum date. The Timeline will not be able
+                                // to be moved to anytime after this date.
+    this.ether = {
+        backgroundColors: [
+        //    "#EEE",
+        //    "#DDD",
+        //    "#CCC",
+        //    "#AAA"
+        ],
+     //   highlightColor:     "white",
+        highlightOpacity:   50,
+        interval: {
+            line: {
+                show:       true,
+                opacity:    25
+               // color:      "#aaa",
+            },
+            weekend: {
+                opacity:    30
+              //  color:      "#FFFFE0",
+            },
+            marker: {
+                hAlign:     "Bottom",
+                vAlign:     "Right"
+                                        /*
+                hBottomStyler: function(elmt) {
+                    elmt.className = "timeline-ether-marker-bottom";
+                },
+                hBottomEmphasizedStyler: function(elmt) {
+                    elmt.className = "timeline-ether-marker-bottom-emphasized";
+                },
+                hTopStyler: function(elmt) {
+                    elmt.className = "timeline-ether-marker-top";
+                },
+                hTopEmphasizedStyler: function(elmt) {
+                    elmt.className = "timeline-ether-marker-top-emphasized";
+                },
+                */
+
+
+               /*
+                                  vRightStyler: function(elmt) {
+                    elmt.className = "timeline-ether-marker-right";
+                },
+                vRightEmphasizedStyler: function(elmt) {
+                    elmt.className = "timeline-ether-marker-right-emphasized";
+                },
+                vLeftStyler: function(elmt) {
+                    elmt.className = "timeline-ether-marker-left";
+                },
+                vLeftEmphasizedStyler:function(elmt) {
+                    elmt.className = "timeline-ether-marker-left-emphasized";
+                }
+                */
+            }
+        }
+    };
+
+    this.event = {
+        track: {
+                   height: 10, // px. You will need to change the track
+                               //     height if you change the tape height.
+                      gap:  2, // px. Gap between tracks
+                   offset:  2, // px. top margin above tapes
+          autoWidthMargin:  1.5
+          /* autoWidthMargin is only used if autoWidth (see above) is true.
+             The autoWidthMargin setting is used to set how close the bottom of the
+             lowest track is to the edge of the band's div. The units are total track
+             width (tape + label + gap). A min of 0.5 is suggested. Use this setting to
+             move the bottom track's tapes above the axis markers, if needed for your
+             Timeline.
+          */
+        },
+        overviewTrack: {
+                  offset: 20, // px -- top margin above tapes
+              tickHeight:  6, // px
+                  height:  2, // px
+                     gap:  1, // px
+         autoWidthMargin:  5 // This attribute is only used if autoWidth (see above) is true.
+        },
+        tape: {
+            height:         4 // px. For thicker tapes, remember to change track height too.
+        },
+        instant: {
+                           icon: Timeline.urlPrefix + "images/dull-blue-circle.png",
+                                 // default icon. Icon can also be specified per event
+                      iconWidth: 10,
+                     iconHeight: 10,
+               impreciseOpacity: 20, // opacity of the tape when durationEvent is false
+            impreciseIconMargin: 3   // A tape and an icon are painted for imprecise instant
+                                     // events. This attribute is the margin between the
+                                     // bottom of the tape and the top of the icon in that
+                                     // case.
+    //        color:             "#58A0DC",
+    //        impreciseColor:    "#58A0DC",
+        },
+        duration: {
+            impreciseOpacity: 20 // tape opacity for imprecise part of duration events
+      //      color:            "#58A0DC",
+      //      impreciseColor:   "#58A0DC",
+        },
+        label: {
+            backgroundOpacity: 50,// only used in detailed painter
+               offsetFromLine:  3 // px left margin amount from icon's right edge
+      //      backgroundColor:   "white",
+      //      lineColor:         "#58A0DC",
+        },
+        highlightColors: [  // Use with getEventPainter().setHighlightMatcher
+                            // See webapp/examples/examples.js
+            "#FFFF00",
+            "#FFC000",
+            "#FF0000",
+            "#0000FF"
+        ],
+        highlightLabelBackground: false, // When highlighting an event, also change the event's label background?
+        bubble: {
+            width:          250, // px
+            maxHeight:        0, // px Maximum height of bubbles. 0 means no max height.
+                                 // scrollbar will be added for taller bubbles
+            titleStyler: function(elmt) {
+                elmt.className = "timeline-event-bubble-title";
+            },
+            bodyStyler: function(elmt) {
+                elmt.className = "timeline-event-bubble-body";
+            },
+            imageStyler: function(elmt) {
+                elmt.className = "timeline-event-bubble-image";
+            },
+            wikiStyler: function(elmt) {
+                elmt.className = "timeline-event-bubble-wiki";
+            },
+            timeStyler: function(elmt) {
+                elmt.className = "timeline-event-bubble-time";
+            }
+        }
+    };
+
+    this.mouseWheel = 'scroll'; // 'default', 'zoom', 'scroll'
+};/*==================================================
+ *  An "ether" is a object that maps date/time to pixel coordinates.
+ *==================================================
+ */
+
+/*==================================================
+ *  Linear Ether
+ *==================================================
+ */
+
+Timeline.LinearEther = function(params) {
+    this._params = params;
+    this._interval = params.interval;
+    this._pixelsPerInterval = params.pixelsPerInterval;
+};
+
+Timeline.LinearEther.prototype.initialize = function(band, timeline) {
+    this._band = band;
+    this._timeline = timeline;
+    this._unit = timeline.getUnit();
+
+    if ("startsOn" in this._params) {
+        this._start = this._unit.parseFromObject(this._params.startsOn);
+    } else if ("endsOn" in this._params) {
+        this._start = this._unit.parseFromObject(this._params.endsOn);
+        this.shiftPixels(-this._timeline.getPixelLength());
+    } else if ("centersOn" in this._params) {
+        this._start = this._unit.parseFromObject(this._params.centersOn);
+        this.shiftPixels(-this._timeline.getPixelLength() / 2);
+    } else {
+        this._start = this._unit.makeDefaultValue();
+        this.shiftPixels(-this._timeline.getPixelLength() / 2);
+    }
+};
+
+Timeline.LinearEther.prototype.setDate = function(date) {
+    this._start = this._unit.cloneValue(date);
+};
+
+Timeline.LinearEther.prototype.shiftPixels = function(pixels) {
+    var numeric = this._interval * pixels / this._pixelsPerInterval;
+    this._start = this._unit.change(this._start, numeric);
+};
+
+Timeline.LinearEther.prototype.dateToPixelOffset = function(date) {
+    var numeric = this._unit.compare(date, this._start);
+    return this._pixelsPerInterval * numeric / this._interval;
+};
+
+Timeline.LinearEther.prototype.pixelOffsetToDate = function(pixels) {
+    var numeric = pixels * this._interval / this._pixelsPerInterval;
+    return this._unit.change(this._start, numeric);
+};
+
+Timeline.LinearEther.prototype.zoom = function(zoomIn) {
+  var netIntervalChange = 0;
+  var currentZoomIndex = this._band._zoomIndex;
+  var newZoomIndex = currentZoomIndex;
+
+  if (zoomIn && (currentZoomIndex > 0)) {
+    newZoomIndex = currentZoomIndex - 1;
+  }
+
+  if (!zoomIn && (currentZoomIndex < (this._band._zoomSteps.length - 1))) {
+    newZoomIndex = currentZoomIndex + 1;
+  }
+
+  this._band._zoomIndex = newZoomIndex;
+  this._interval =
+    SimileAjax.DateTime.gregorianUnitLengths[this._band._zoomSteps[newZoomIndex].unit];
+  this._pixelsPerInterval = this._band._zoomSteps[newZoomIndex].pixelsPerInterval;
+  netIntervalChange = this._band._zoomSteps[newZoomIndex].unit -
+    this._band._zoomSteps[currentZoomIndex].unit;
+
+  return netIntervalChange;
+};
+
+
+/*==================================================
+ *  Hot Zone Ether
+ *==================================================
+ */
+
+Timeline.HotZoneEther = function(params) {
+    this._params = params;
+    this._interval = params.interval;
+    this._pixelsPerInterval = params.pixelsPerInterval;
+    this._theme = params.theme;
+};
+
+Timeline.HotZoneEther.prototype.initialize = function(band, timeline) {
+    this._band = band;
+    this._timeline = timeline;
+    this._unit = timeline.getUnit();
+
+    this._zones = [{
+        startTime:  Number.NEGATIVE_INFINITY,
+        endTime:    Number.POSITIVE_INFINITY,
+        magnify:    1
+    }];
+    var params = this._params;
+    for (var i = 0; i < params.zones.length; i++) {
+        var zone = params.zones[i];
+        var zoneStart = this._unit.parseFromObject(zone.start);
+        var zoneEnd =   this._unit.parseFromObject(zone.end);
+
+        for (var j = 0; j < this._zones.length && this._unit.compare(zoneEnd, zoneStart) > 0; j++) {
+            var zone2 = this._zones[j];
+
+            if (this._unit.compare(zoneStart, zone2.endTime) < 0) {
+                if (this._unit.compare(zoneStart, zone2.startTime) > 0) {
+                    this._zones.splice(j, 0, {
+                        startTime:   zone2.startTime,
+                        endTime:     zoneStart,
+                        magnify:     zone2.magnify
+                    });
+                    j++;
+
+                    zone2.startTime = zoneStart;
+                }
+
+                if (this._unit.compare(zoneEnd, zone2.endTime) < 0) {
+                    this._zones.splice(j, 0, {
+                        startTime:  zoneStart,
+                        endTime:    zoneEnd,
+                        magnify:    zone.magnify * zone2.magnify
+                    });
+                    j++;
+
+                    zone2.startTime = zoneEnd;
+                    zoneStart = zoneEnd;
+                } else {
+                    zone2.magnify *= zone.magnify;
+                    zoneStart = zone2.endTime;
+                }
+            } // else, try the next existing zone
+        }
+    }
+
+    if ("startsOn" in this._params) {
+        this._start = this._unit.parseFromObject(this._params.startsOn);
+    } else if ("endsOn" in this._params) {
+        this._start = this._unit.parseFromObject(this._params.endsOn);
+        this.shiftPixels(-this._timeline.getPixelLength());
+    } else if ("centersOn" in this._params) {
+        this._start = this._unit.parseFromObject(this._params.centersOn);
+        this.shiftPixels(-this._timeline.getPixelLength() / 2);
+    } else {
+        this._start = this._unit.makeDefaultValue();
+        this.shiftPixels(-this._timeline.getPixelLength() / 2);
+    }
+};
+
+Timeline.HotZoneEther.prototype.setDate = function(date) {
+    this._start = this._unit.cloneValue(date);
+};
+
+Timeline.HotZoneEther.prototype.shiftPixels = function(pixels) {
+    this._start = this.pixelOffsetToDate(pixels);
+};
+
+Timeline.HotZoneEther.prototype.dateToPixelOffset = function(date) {
+    return this._dateDiffToPixelOffset(this._start, date);
+};
+
+Timeline.HotZoneEther.prototype.pixelOffsetToDate = function(pixels) {
+    return this._pixelOffsetToDate(pixels, this._start);
+};
+
+Timeline.HotZoneEther.prototype.zoom = function(zoomIn) {
+  var netIntervalChange = 0;
+  var currentZoomIndex = this._band._zoomIndex;
+  var newZoomIndex = currentZoomIndex;
+
+  if (zoomIn && (currentZoomIndex > 0)) {
+    newZoomIndex = currentZoomIndex - 1;
+  }
+
+  if (!zoomIn && (currentZoomIndex < (this._band._zoomSteps.length - 1))) {
+    newZoomIndex = currentZoomIndex + 1;
+  }
+
+  this._band._zoomIndex = newZoomIndex;
+  this._interval =
+    SimileAjax.DateTime.gregorianUnitLengths[this._band._zoomSteps[newZoomIndex].unit];
+  this._pixelsPerInterval = this._band._zoomSteps[newZoomIndex].pixelsPerInterval;
+  netIntervalChange = this._band._zoomSteps[newZoomIndex].unit -
+    this._band._zoomSteps[currentZoomIndex].unit;
+
+  return netIntervalChange;
+};
+
+Timeline.HotZoneEther.prototype._dateDiffToPixelOffset = function(fromDate, toDate) {
+    var scale = this._getScale();
+    var fromTime = fromDate;
+    var toTime = toDate;
+
+    var pixels = 0;
+    if (this._unit.compare(fromTime, toTime) < 0) {
+        var z = 0;
+        while (z < this._zones.length) {
+            if (this._unit.compare(fromTime, this._zones[z].endTime) < 0) {
+                break;
+            }
+            z++;
+        }
+
+        while (this._unit.compare(fromTime, toTime) < 0) {
+            var zone = this._zones[z];
+            var toTime2 = this._unit.earlier(toTime, zone.endTime);
+
+            pixels += (this._unit.compare(toTime2, fromTime) / (scale / zone.magnify));
+
+            fromTime = toTime2;
+            z++;
+        }
+    } else {
+        var z = this._zones.length - 1;
+        while (z >= 0) {
+            if (this._unit.compare(fromTime, this._zones[z].startTime) > 0) {
+                break;
+            }
+            z--;
+        }
+
+        while (this._unit.compare(fromTime, toTime) > 0) {
+            var zone = this._zones[z];
+            var toTime2 = this._unit.later(toTime, zone.startTime);
+
+            pixels += (this._unit.compare(toTime2, fromTime) / (scale / zone.magnify));
+
+            fromTime = toTime2;
+            z--;
+        }
+    }
+    return pixels;
+};
+
+Timeline.HotZoneEther.prototype._pixelOffsetToDate = function(pixels, fromDate) {
+    var scale = this._getScale();
+    var time = fromDate;
+    if (pixels > 0) {
+        var z = 0;
+        while (z < this._zones.length) {
+            if (this._unit.compare(time, this._zones[z].endTime) < 0) {
+                break;
+            }
+            z++;
+        }
+
+        while (pixels > 0) {
+            var zone = this._zones[z];
+            var scale2 = scale / zone.magnify;
+
+            if (zone.endTime == Number.POSITIVE_INFINITY) {
+                time = this._unit.change(time, pixels * scale2);
+                pixels = 0;
+            } else {
+                var pixels2 = this._unit.compare(zone.endTime, time) / scale2;
+                if (pixels2 > pixels) {
+                    time = this._unit.change(time, pixels * scale2);
+                    pixels = 0;
+                } else {
+                    time = zone.endTime;
+                    pixels -= pixels2;
+                }
+            }
+            z++;
+        }
+    } else {
+        var z = this._zones.length - 1;
+        while (z >= 0) {
+            if (this._unit.compare(time, this._zones[z].startTime) > 0) {
+                break;
+            }
+            z--;
+        }
+
+        pixels = -pixels;
+        while (pixels > 0) {
+            var zone = this._zones[z];
+            var scale2 = scale / zone.magnify;
+
+            if (zone.startTime == Number.NEGATIVE_INFINITY) {
+                time = this._unit.change(time, -pixels * scale2);
+                pixels = 0;
+            } else {
+                var pixels2 = this._unit.compare(time, zone.startTime) / scale2;
+                if (pixels2 > pixels) {
+                    time = this._unit.change(time, -pixels * scale2);
+                    pixels = 0;
+                } else {
+                    time = zone.startTime;
+                    pixels -= pixels2;
+                }
+            }
+            z--;
+        }
+    }
+    return time;
+};
+
+Timeline.HotZoneEther.prototype._getScale = function() {
+    return this._interval / this._pixelsPerInterval;
+};
+/*==================================================
+ *  Gregorian Ether Painter
+ *==================================================
+ */
+
+Timeline.GregorianEtherPainter = function(params) {
+    this._params = params;
+    this._theme = params.theme;
+    this._unit = params.unit;
+    this._multiple = ("multiple" in params) ? params.multiple : 1;
+};
+
+Timeline.GregorianEtherPainter.prototype.initialize = function(band, timeline) {
+    this._band = band;
+    this._timeline = timeline;
+
+    this._backgroundLayer = band.createLayerDiv(0);
+    this._backgroundLayer.setAttribute("name", "ether-background"); // for debugging
+    this._backgroundLayer.className = 'timeline-ether-bg';
+  //  this._backgroundLayer.style.background = this._theme.ether.backgroundColors[band.getIndex()];
+
+
+    this._markerLayer = null;
+    this._lineLayer = null;
+
+    var align = ("align" in this._params && this._params.align != undefined) ? this._params.align :
+        this._theme.ether.interval.marker[timeline.isHorizontal() ? "hAlign" : "vAlign"];
+    var showLine = ("showLine" in this._params) ? this._params.showLine :
+        this._theme.ether.interval.line.show;
+
+    this._intervalMarkerLayout = new Timeline.EtherIntervalMarkerLayout(
+        this._timeline, this._band, this._theme, align, showLine);
+
+    this._highlight = new Timeline.EtherHighlight(
+        this._timeline, this._band, this._theme, this._backgroundLayer);
+}
+
+Timeline.GregorianEtherPainter.prototype.setHighlight = function(startDate, endDate) {
+    this._highlight.position(startDate, endDate);
+}
+
+Timeline.GregorianEtherPainter.prototype.paint = function() {
+    if (this._markerLayer) {
+        this._band.removeLayerDiv(this._markerLayer);
+    }
+    this._markerLayer = this._band.createLayerDiv(100);
+    this._markerLayer.setAttribute("name", "ether-markers"); // for debugging
+    this._markerLayer.style.display = "none";
+
+    if (this._lineLayer) {
+        this._band.removeLayerDiv(this._lineLayer);
+    }
+    this._lineLayer = this._band.createLayerDiv(1);
+    this._lineLayer.setAttribute("name", "ether-lines"); // for debugging
+    this._lineLayer.style.display = "none";
+
+    var minDate = this._band.getMinDate();
+    var maxDate = this._band.getMaxDate();
+
+    var timeZone = this._band.getTimeZone();
+    var labeller = this._band.getLabeller();
+
+    SimileAjax.DateTime.roundDownToInterval(minDate, this._unit, timeZone, this._multiple, this._theme.firstDayOfWeek);
+
+    var p = this;
+    var incrementDate = function(date) {
+        for (var i = 0; i < p._multiple; i++) {
+            SimileAjax.DateTime.incrementByInterval(date, p._unit);
+        }
+    };
+
+    while (minDate.getTime() < maxDate.getTime()) {
+        this._intervalMarkerLayout.createIntervalMarker(
+            minDate, labeller, this._unit, this._markerLayer, this._lineLayer);
+
+        incrementDate(minDate);
+    }
+    this._markerLayer.style.display = "block";
+    this._lineLayer.style.display = "block";
+};
+
+Timeline.GregorianEtherPainter.prototype.softPaint = function() {
+};
+
+Timeline.GregorianEtherPainter.prototype.zoom = function(netIntervalChange) {
+  if (netIntervalChange != 0) {
+    this._unit += netIntervalChange;
+  }
+};
+
+
+/*==================================================
+ *  Hot Zone Gregorian Ether Painter
+ *==================================================
+ */
+
+Timeline.HotZoneGregorianEtherPainter = function(params) {
+    this._params = params;
+    this._theme = params.theme;
+
+    this._zones = [{
+        startTime:  Number.NEGATIVE_INFINITY,
+        endTime:    Number.POSITIVE_INFINITY,
+        unit:       params.unit,
+        multiple:   1
+    }];
+    for (var i = 0; i < params.zones.length; i++) {
+        var zone = params.zones[i];
+        var zoneStart = SimileAjax.DateTime.parseGregorianDateTime(zone.start).getTime();
+        var zoneEnd = SimileAjax.DateTime.parseGregorianDateTime(zone.end).getTime();
+
+        for (var j = 0; j < this._zones.length && zoneEnd > zoneStart; j++) {
+            var zone2 = this._zones[j];
+
+            if (zoneStart < zone2.endTime) {
+                if (zoneStart > zone2.startTime) {
+                    this._zones.splice(j, 0, {
+                        startTime:   zone2.startTime,
+                        endTime:     zoneStart,
+                        unit:        zone2.unit,
+                        multiple:    zone2.multiple
+                    });
+                    j++;
+
+                    zone2.startTime = zoneStart;
+                }
+
+                if (zoneEnd < zone2.endTime) {
+                    this._zones.splice(j, 0, {
+                        startTime:  zoneStart,
+                        endTime:    zoneEnd,
+                        unit:       zone.unit,
+                        multiple:   (zone.multiple) ? zone.multiple : 1
+                    });
+                    j++;
+
+                    zone2.startTime = zoneEnd;
+                    zoneStart = zoneEnd;
+                } else {
+                    zone2.multiple = zone.multiple;
+                    zone2.unit = zone.unit;
+                    zoneStart = zone2.endTime;
+                }
+            } // else, try the next existing zone
+        }
+    }
+};
+
+Timeline.HotZoneGregorianEtherPainter.prototype.initialize = function(band, timeline) {
+    this._band = band;
+    this._timeline = timeline;
+
+    this._backgroundLayer = band.createLayerDiv(0);
+    this._backgroundLayer.setAttribute("name", "ether-background"); // for debugging
+    this._backgroundLayer.className ='timeline-ether-bg';
+    //this._backgroundLayer.style.background = this._theme.ether.backgroundColors[band.getIndex()];
+
+    this._markerLayer = null;
+    this._lineLayer = null;
+
+    var align = ("align" in this._params && this._params.align != undefined) ? this._params.align :
+        this._theme.ether.interval.marker[timeline.isHorizontal() ? "hAlign" : "vAlign"];
+    var showLine = ("showLine" in this._params) ? this._params.showLine :
+        this._theme.ether.interval.line.show;
+
+    this._intervalMarkerLayout = new Timeline.EtherIntervalMarkerLayout(
+        this._timeline, this._band, this._theme, align, showLine);
+
+    this._highlight = new Timeline.EtherHighlight(
+        this._timeline, this._band, this._theme, this._backgroundLayer);
+}
+
+Timeline.HotZoneGregorianEtherPainter.prototype.setHighlight = function(startDate, endDate) {
+    this._highlight.position(startDate, endDate);
+}
+
+Timeline.HotZoneGregorianEtherPainter.prototype.paint = function() {
+    if (this._markerLayer) {
+        this._band.removeLayerDiv(this._markerLayer);
+    }
+    this._markerLayer = this._band.createLayerDiv(100);
+    this._markerLayer.setAttribute("name", "ether-markers"); // for debugging
+    this._markerLayer.style.display = "none";
+
+    if (this._lineLayer) {
+        this._band.removeLayerDiv(this._lineLayer);
+    }
+    this._lineLayer = this._band.createLayerDiv(1);
+    this._lineLayer.setAttribute("name", "ether-lines"); // for debugging
+    this._lineLayer.style.display = "none";
+
+    var minDate = this._band.getMinDate();
+    var maxDate = this._band.getMaxDate();
+
+    var timeZone = this._band.getTimeZone();
+    var labeller = this._band.getLabeller();
+
+    var p = this;
+    var incrementDate = function(date, zone) {
+        for (var i = 0; i < zone.multiple; i++) {
+            SimileAjax.DateTime.incrementByInterval(date, zone.unit);
+        }
+    };
+
+    var zStart = 0;
+    while (zStart < this._zones.length) {
+        if (minDate.getTime() < this._zones[zStart].endTime) {
+            break;
+        }
+        zStart++;
+    }
+    var zEnd = this._zones.length - 1;
+    while (zEnd >= 0) {
+        if (maxDate.getTime() > this._zones[zEnd].startTime) {
+            break;
+        }
+        zEnd--;
+    }
+
+    for (var z = zStart; z <= zEnd; z++) {
+        var zone = this._zones[z];
+
+        var minDate2 = new Date(Math.max(minDate.getTime(), zone.startTime));
+        var maxDate2 = new Date(Math.min(maxDate.getTime(), zone.endTime));
+
+        SimileAjax.DateTime.roundDownToInterval(minDate2, zone.unit, timeZone, zone.multiple, this._theme.firstDayOfWeek);
+        SimileAjax.DateTime.roundUpToInterval(maxDate2, zone.unit, timeZone, zone.multiple, this._theme.firstDayOfWeek);
+
+        while (minDate2.getTime() < maxDate2.getTime()) {
+            this._intervalMarkerLayout.createIntervalMarker(
+                minDate2, labeller, zone.unit, this._markerLayer, this._lineLayer);
+
+            incrementDate(minDate2, zone);
+        }
+    }
+    this._markerLayer.style.display = "block";
+    this._lineLayer.style.display = "block";
+};
+
+Timeline.HotZoneGregorianEtherPainter.prototype.softPaint = function() {
+};
+
+Timeline.HotZoneGregorianEtherPainter.prototype.zoom = function(netIntervalChange) {
+  if (netIntervalChange != 0) {
+    for (var i = 0; i < this._zones.length; ++i) {
+      if (this._zones[i]) {
+        this._zones[i].unit += netIntervalChange;
+      }
+    }
+  }
+};
+
+/*==================================================
+ *  Year Count Ether Painter
+ *==================================================
+ */
+
+Timeline.YearCountEtherPainter = function(params) {
+    this._params = params;
+    this._theme = params.theme;
+    this._startDate = SimileAjax.DateTime.parseGregorianDateTime(params.startDate);
+    this._multiple = ("multiple" in params) ? params.multiple : 1;
+};
+
+Timeline.YearCountEtherPainter.prototype.initialize = function(band, timeline) {
+    this._band = band;
+    this._timeline = timeline;
+
+    this._backgroundLayer = band.createLayerDiv(0);
+    this._backgroundLayer.setAttribute("name", "ether-background"); // for debugging
+    this._backgroundLayer.className = 'timeline-ether-bg';
+   // this._backgroundLayer.style.background = this._theme.ether.backgroundColors[band.getIndex()];
+
+    this._markerLayer = null;
+    this._lineLayer = null;
+
+    var align = ("align" in this._params) ? this._params.align :
+        this._theme.ether.interval.marker[timeline.isHorizontal() ? "hAlign" : "vAlign"];
+    var showLine = ("showLine" in this._params) ? this._params.showLine :
+        this._theme.ether.interval.line.show;
+
+    this._intervalMarkerLayout = new Timeline.EtherIntervalMarkerLayout(
+        this._timeline, this._band, this._theme, align, showLine);
+
+    this._highlight = new Timeline.EtherHighlight(
+        this._timeline, this._band, this._theme, this._backgroundLayer);
+};
+
+Timeline.YearCountEtherPainter.prototype.setHighlight = function(startDate, endDate) {
+    this._highlight.position(startDate, endDate);
+};
+
+Timeline.YearCountEtherPainter.prototype.paint = function() {
+    if (this._markerLayer) {
+        this._band.removeLayerDiv(this._markerLayer);
+    }
+    this._markerLayer = this._band.createLayerDiv(100);
+    this._markerLayer.setAttribute("name", "ether-markers"); // for debugging
+    this._markerLayer.style.display = "none";
+
+    if (this._lineLayer) {
+        this._band.removeLayerDiv(this._lineLayer);
+    }
+    this._lineLayer = this._band.createLayerDiv(1);
+    this._lineLayer.setAttribute("name", "ether-lines"); // for debugging
+    this._lineLayer.style.display = "none";
+
+    var minDate = new Date(this._startDate.getTime());
+    var maxDate = this._band.getMaxDate();
+    var yearDiff = this._band.getMinDate().getUTCFullYear() - this._startDate.getUTCFullYear();
+    minDate.setUTCFullYear(this._band.getMinDate().getUTCFullYear() - yearDiff % this._multiple);
+
+    var p = this;
+    var incrementDate = function(date) {
+        for (var i = 0; i < p._multiple; i++) {
+            SimileAjax.DateTime.incrementByInterval(date, SimileAjax.DateTime.YEAR);
+        }
+    };
+    var labeller = {
+        labelInterval: function(date, intervalUnit) {
+            var diff = date.getUTCFullYear() - p._startDate.getUTCFullYear();
+            return {
+                text: diff,
+                emphasized: diff == 0
+            };
+        }
+    };
+
+    while (minDate.getTime() < maxDate.getTime()) {
+        this._intervalMarkerLayout.createIntervalMarker(
+            minDate, labeller, SimileAjax.DateTime.YEAR, this._markerLayer, this._lineLayer);
+
+        incrementDate(minDate);
+    }
+    this._markerLayer.style.display = "block";
+    this._lineLayer.style.display = "block";
+};
+
+Timeline.YearCountEtherPainter.prototype.softPaint = function() {
+};
+
+/*==================================================
+ *  Quarterly Ether Painter
+ *==================================================
+ */
+
+Timeline.QuarterlyEtherPainter = function(params) {
+    this._params = params;
+    this._theme = params.theme;
+    this._startDate = SimileAjax.DateTime.parseGregorianDateTime(params.startDate);
+};
+
+Timeline.QuarterlyEtherPainter.prototype.initialize = function(band, timeline) {
+    this._band = band;
+    this._timeline = timeline;
+
+    this._backgroundLayer = band.createLayerDiv(0);
+    this._backgroundLayer.setAttribute("name", "ether-background"); // for debugging
+    this._backgroundLayer.className = 'timeline-ether-bg';
+ //   this._backgroundLayer.style.background = this._theme.ether.backgroundColors[band.getIndex()];
+
+    this._markerLayer = null;
+    this._lineLayer = null;
+
+    var align = ("align" in this._params) ? this._params.align :
+        this._theme.ether.interval.marker[timeline.isHorizontal() ? "hAlign" : "vAlign"];
+    var showLine = ("showLine" in this._params) ? this._params.showLine :
+        this._theme.ether.interval.line.show;
+
+    this._intervalMarkerLayout = new Timeline.EtherIntervalMarkerLayout(
+        this._timeline, this._band, this._theme, align, showLine);
+
+    this._highlight = new Timeline.EtherHighlight(
+        this._timeline, this._band, this._theme, this._backgroundLayer);
+};
+
+Timeline.QuarterlyEtherPainter.prototype.setHighlight = function(startDate, endDate) {
+    this._highlight.position(startDate, endDate);
+};
+
+Timeline.QuarterlyEtherPainter.prototype.paint = function() {
+    if (this._markerLayer) {
+        this._band.removeLayerDiv(this._markerLayer);
+    }
+    this._markerLayer = this._band.createLayerDiv(100);
+    this._markerLayer.setAttribute("name", "ether-markers"); // for debugging
+    this._markerLayer.style.display = "none";
+
+    if (this._lineLayer) {
+        this._band.removeLayerDiv(this._lineLayer);
+    }
+    this._lineLayer = this._band.createLayerDiv(1);
+    this._lineLayer.setAttribute("name", "ether-lines"); // for debugging
+    this._lineLayer.style.display = "none";
+
+    var minDate = new Date(0);
+    var maxDate = this._band.getMaxDate();
+
+    minDate.setUTCFullYear(Math.max(this._startDate.getUTCFullYear(), this._band.getMinDate().getUTCFullYear()));
+    minDate.setUTCMonth(this._startDate.getUTCMonth());
+
+    var p = this;
+    var incrementDate = function(date) {
+        date.setUTCMonth(date.getUTCMonth() + 3);
+    };
+    var labeller = {
+        labelInterval: function(date, intervalUnit) {
+            var quarters = (4 + (date.getUTCMonth() - p._startDate.getUTCMonth()) / 3) % 4;
+            if (quarters != 0) {
+                return { text: "Q" + (quarters + 1), emphasized: false };
+            } else {
+                return { text: "Y" + (date.getUTCFullYear() - p._startDate.getUTCFullYear() + 1), emphasized: true };
+            }
+        }
+    };
+
+    while (minDate.getTime() < maxDate.getTime()) {
+        this._intervalMarkerLayout.createIntervalMarker(
+            minDate, labeller, SimileAjax.DateTime.YEAR, this._markerLayer, this._lineLayer);
+
+        incrementDate(minDate);
+    }
+    this._markerLayer.style.display = "block";
+    this._lineLayer.style.display = "block";
+};
+
+Timeline.QuarterlyEtherPainter.prototype.softPaint = function() {
+};
+
+/*==================================================
+ *  Ether Interval Marker Layout
+ *==================================================
+ */
+
+Timeline.EtherIntervalMarkerLayout = function(timeline, band, theme, align, showLine) {
+    var horizontal = timeline.isHorizontal();
+    if (horizontal) {
+        if (align == "Top") {
+            this.positionDiv = function(div, offset) {
+                div.style.left = offset + "px";
+                div.style.top = "0px";
+            };
+        } else {
+            this.positionDiv = function(div, offset) {
+                div.style.left = offset + "px";
+                div.style.bottom = "0px";
+            };
+        }
+    } else {
+        if (align == "Left") {
+            this.positionDiv = function(div, offset) {
+                div.style.top = offset + "px";
+                div.style.left = "0px";
+            };
+        } else {
+            this.positionDiv = function(div, offset) {
+                div.style.top = offset + "px";
+                div.style.right = "0px";
+            };
+        }
+    }
+
+    var markerTheme = theme.ether.interval.marker;
+    var lineTheme = theme.ether.interval.line;
+    var weekendTheme = theme.ether.interval.weekend;
+
+    var stylePrefix = (horizontal ? "h" : "v") + align;
+    var labelStyler = markerTheme[stylePrefix + "Styler"];
+    var emphasizedLabelStyler = markerTheme[stylePrefix + "EmphasizedStyler"];
+    var day = SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.DAY];
+
+    this.createIntervalMarker = function(date, labeller, unit, markerDiv, lineDiv) {
+        var offset = Math.round(band.dateToPixelOffset(date));
+
+        if (showLine && unit != SimileAjax.DateTime.WEEK) {
+            var divLine = timeline.getDocument().createElement("div");
+            divLine.className = "timeline-ether-lines";
+
+            if (lineTheme.opacity < 100) {
+                SimileAjax.Graphics.setOpacity(divLine, lineTheme.opacity);
+            }
+
+            if (horizontal) {
+				//divLine.className += " timeline-ether-lines-vertical";
+				divLine.style.left = offset + "px";
+            } else {
+				//divLine.className += " timeline-ether-lines-horizontal";
+                divLine.style.top = offset + "px";
+            }
+            lineDiv.appendChild(divLine);
+        }
+        if (unit == SimileAjax.DateTime.WEEK) {
+            var firstDayOfWeek = theme.firstDayOfWeek;
+
+            var saturday = new Date(date.getTime() + (6 - firstDayOfWeek - 7) * day);
+            var monday = new Date(saturday.getTime() + 2 * day);
+
+            var saturdayPixel = Math.round(band.dateToPixelOffset(saturday));
+            var mondayPixel = Math.round(band.dateToPixelOffset(monday));
+            var length = Math.max(1, mondayPixel - saturdayPixel);
+
+            var divWeekend = timeline.getDocument().createElement("div");
+			divWeekend.className = 'timeline-ether-weekends'
+
+            if (weekendTheme.opacity < 100) {
+                SimileAjax.Graphics.setOpacity(divWeekend, weekendTheme.opacity);
+            }
+
+            if (horizontal) {
+                divWeekend.style.left = saturdayPixel + "px";
+                divWeekend.style.width = length + "px";
+            } else {
+                divWeekend.style.top = saturdayPixel + "px";
+                divWeekend.style.height = length + "px";
+            }
+            lineDiv.appendChild(divWeekend);
+        }
+
+        var label = labeller.labelInterval(date, unit);
+
+        var div = timeline.getDocument().createElement("div");
+        div.innerHTML = label.text;
+
+
+
+		div.className = 'timeline-date-label'
+		if(label.emphasized) div.className += ' timeline-date-label-em'
+
+        this.positionDiv(div, offset);
+        markerDiv.appendChild(div);
+
+        return div;
+    };
+};
+
+/*==================================================
+ *  Ether Highlight Layout
+ *==================================================
+ */
+
+Timeline.EtherHighlight = function(timeline, band, theme, backgroundLayer) {
+    var horizontal = timeline.isHorizontal();
+
+    this._highlightDiv = null;
+    this._createHighlightDiv = function() {
+        if (this._highlightDiv == null) {
+            this._highlightDiv = timeline.getDocument().createElement("div");
+            this._highlightDiv.setAttribute("name", "ether-highlight"); // for debugging
+            this._highlightDiv.className = 'timeline-ether-highlight'
+
+            var opacity = theme.ether.highlightOpacity;
+            if (opacity < 100) {
+                SimileAjax.Graphics.setOpacity(this._highlightDiv, opacity);
+            }
+
+            backgroundLayer.appendChild(this._highlightDiv);
+        }
+    }
+
+    this.position = function(startDate, endDate) {
+        this._createHighlightDiv();
+
+        var startPixel = Math.round(band.dateToPixelOffset(startDate));
+        var endPixel = Math.round(band.dateToPixelOffset(endDate));
+        var length = Math.max(endPixel - startPixel, 3);
+        if (horizontal) {
+            this._highlightDiv.style.left = startPixel + "px";
+            this._highlightDiv.style.width = length + "px";
+            this._highlightDiv.style.height = (band.getViewWidth() - 4) + "px";
+        } else {
+            this._highlightDiv.style.top = startPixel + "px";
+            this._highlightDiv.style.height = length + "px";
+            this._highlightDiv.style.width = (band.getViewWidth() - 4) + "px";
+        }
+    }
+};
+/*==================================================
+ *  Event Utils
+ *==================================================
+ */
+Timeline.EventUtils = {};
+
+Timeline.EventUtils.getNewEventID = function() {
+    // global across page
+    if (this._lastEventID == null) {
+        this._lastEventID = 0;
+    }
+
+    this._lastEventID += 1;
+    return "e" + this._lastEventID;
+};
+
+Timeline.EventUtils.decodeEventElID = function(elementID) {
+    /*==================================================
+     *
+     * Use this function to decode an event element's id on a band (label div,
+     * tape div or icon img).
+     *
+     * Returns {band: <bandObj>, evt: <eventObj>}
+     *
+     * To enable a single event listener to monitor everything
+     * on a Timeline, a set format is used for the id's of the
+     * elements on the Timeline--
+     *
+     * element id format for labels, icons, tapes:
+     *   labels: label-tl-<timelineID>-<band_index>-<evt.id>
+     *    icons: icon-tl-<timelineID>-<band_index>-<evt.id>
+     *    tapes: tape1-tl-<timelineID>-<band_index>-<evt.id>
+     *           tape2-tl-<timelineID>-<band_index>-<evt.id>
+     *           // some events have more than one tape
+     *    highlight: highlight1-tl-<timelineID>-<band_index>-<evt.id>
+     *               highlight2-tl-<timelineID>-<band_index>-<evt.id>
+     *           // some events have more than one highlight div (future)
+     * Note: use split('-') to get array of the format's parts
+     *
+     * You can then retrieve the timeline object and event object
+     * by using Timeline.getTimeline, Timeline.getBand, or
+     * Timeline.getEvent and passing in the element's id
+     *
+     *==================================================
+     */
+
+    var parts = elementID.split('-');
+    if (parts[1] != 'tl') {
+        alert("Internal Timeline problem 101, please consult support");
+        return {band: null, evt: null}; // early return
+    }
+
+    var timeline = Timeline.getTimelineFromID(parts[2]);
+    var band = timeline.getBand(parts[3]);
+    var evt = band.getEventSource.getEvent(parts[4]);
+
+    return {band: band, evt: evt};
+};
+
+Timeline.EventUtils.encodeEventElID = function(timeline, band, elType, evt) {
+    // elType should be one of {label | icon | tapeN | highlightN}
+    return elType + "-tl-" + timeline.timelineID +
+       "-" + band.getIndex() + "-" + evt.getID();
+};/*==================================================
+ *  Gregorian Date Labeller
+ *==================================================
+ */
+
+Timeline.GregorianDateLabeller = function(locale, timeZone) {
+    this._locale = locale;
+    this._timeZone = timeZone;
+};
+
+Timeline.GregorianDateLabeller.monthNames = [];
+Timeline.GregorianDateLabeller.dayNames = [];
+Timeline.GregorianDateLabeller.labelIntervalFunctions = [];
+
+Timeline.GregorianDateLabeller.getMonthName = function(month, locale) {
+    return Timeline.GregorianDateLabeller.monthNames[locale][month];
+};
+
+Timeline.GregorianDateLabeller.prototype.labelInterval = function(date, intervalUnit) {
+    var f = Timeline.GregorianDateLabeller.labelIntervalFunctions[this._locale];
+    if (f == null) {
+        f = Timeline.GregorianDateLabeller.prototype.defaultLabelInterval;
+    }
+    return f.call(this, date, intervalUnit);
+};
+
+Timeline.GregorianDateLabeller.prototype.labelPrecise = function(date) {
+    return SimileAjax.DateTime.removeTimeZoneOffset(
+        date,
+        this._timeZone //+ (new Date().getTimezoneOffset() / 60)
+    ).toUTCString();
+};
+
+Timeline.GregorianDateLabeller.prototype.defaultLabelInterval = function(date, intervalUnit) {
+    var text;
+    var emphasized = false;
+
+    date = SimileAjax.DateTime.removeTimeZoneOffset(date, this._timeZone);
+
+    switch(intervalUnit) {
+    case SimileAjax.DateTime.MILLISECOND:
+        text = date.getUTCMilliseconds();
+        break;
+    case SimileAjax.DateTime.SECOND:
+        text = date.getUTCSeconds();
+        break;
+    case SimileAjax.DateTime.MINUTE:
+        var m = date.getUTCMinutes();
+        if (m == 0) {
+            text = date.getUTCHours() + ":00";
+            emphasized = true;
+        } else {
+            text = m;
+        }
+        break;
+    case SimileAjax.DateTime.HOUR:
+        text = date.getUTCHours() + "hr";
+        break;
+    case SimileAjax.DateTime.DAY:
+        text = Timeline.GregorianDateLabeller.getMonthName(date.getUTCMonth(), this._locale) + " " + date.getUTCDate();
+        break;
+    case SimileAjax.DateTime.WEEK:
+        text = Timeline.GregorianDateLabeller.getMonthName(date.getUTCMonth(), this._locale) + " " + date.getUTCDate();
+        break;
+    case SimileAjax.DateTime.MONTH:
+        var m = date.getUTCMonth();
+        if (m != 0) {
+            text = Timeline.GregorianDateLabeller.getMonthName(m, this._locale);
+            break;
+        } // else, fall through
+    case SimileAjax.DateTime.YEAR:
+    case SimileAjax.DateTime.DECADE:
+    case SimileAjax.DateTime.CENTURY:
+    case SimileAjax.DateTime.MILLENNIUM:
+        var y = date.getUTCFullYear();
+        if (y > 0) {
+            text = date.getUTCFullYear();
+        } else {
+            text = (1 - y) + "BC";
+        }
+        emphasized =
+            (intervalUnit == SimileAjax.DateTime.MONTH) ||
+            (intervalUnit == SimileAjax.DateTime.DECADE && y % 100 == 0) ||
+            (intervalUnit == SimileAjax.DateTime.CENTURY && y % 1000 == 0);
+        break;
+    default:
+        text = date.toUTCString();
+    }
+    return { text: text, emphasized: emphasized };
+}
+
+/*==================================================
+ *  Default Event Source
+ *==================================================
+ */
+
+
+Timeline.DefaultEventSource = function(eventIndex) {
+    this._events = (eventIndex instanceof Object) ? eventIndex : new SimileAjax.EventIndex();
+    this._listeners = [];
+};
+
+Timeline.DefaultEventSource.prototype.addListener = function(listener) {
+    this._listeners.push(listener);
+};
+
+Timeline.DefaultEventSource.prototype.removeListener = function(listener) {
+    for (var i = 0; i < this._listeners.length; i++) {
+        if (this._listeners[i] == listener) {
+            this._listeners.splice(i, 1);
+            break;
+        }
+    }
+};
+
+Timeline.DefaultEventSource.prototype.loadXML = function(xml, url) {
+    var base = this._getBaseURL(url);
+
+    var wikiURL = xml.documentElement.getAttribute("wiki-url");
+    var wikiSection = xml.documentElement.getAttribute("wiki-section");
+
+    var dateTimeFormat = xml.documentElement.getAttribute("date-time-format");
+    var parseDateTimeFunction = this._events.getUnit().getParser(dateTimeFormat);
+
+    var node = xml.documentElement.firstChild;
+    var added = false;
+    while (node != null) {
+        if (node.nodeType == 1) {
+            var description = "";
+            if (node.firstChild != null && node.firstChild.nodeType == 3) {
+                description = node.firstChild.nodeValue;
+            }
+            // instant event: default is true. Or use values from isDuration or durationEvent
+            var instant = (node.getAttribute("isDuration")    === null &&
+                           node.getAttribute("durationEvent") === null) ||
+                          node.getAttribute("isDuration") == "false" ||
+                          node.getAttribute("durationEvent") == "false";
+
+            var evt = new Timeline.DefaultEventSource.Event( {
+                          id: node.getAttribute("id"),
+                       start: parseDateTimeFunction(node.getAttribute("start")),
+                         end: parseDateTimeFunction(node.getAttribute("end")),
+                 latestStart: parseDateTimeFunction(node.getAttribute("latestStart")),
+                 earliestEnd: parseDateTimeFunction(node.getAttribute("earliestEnd")),
+                     instant: instant,
+                        text: node.getAttribute("title"),
+                 description: description,
+                       image: this._resolveRelativeURL(node.getAttribute("image"), base),
+                        link: this._resolveRelativeURL(node.getAttribute("link") , base),
+                        icon: this._resolveRelativeURL(node.getAttribute("icon") , base),
+                       color: node.getAttribute("color"),
+                   textColor: node.getAttribute("textColor"),
+                   hoverText: node.getAttribute("hoverText"),
+                   classname: node.getAttribute("classname"),
+                   tapeImage: node.getAttribute("tapeImage"),
+                  tapeRepeat: node.getAttribute("tapeRepeat"),
+                     caption: node.getAttribute("caption"),
+                     eventID: node.getAttribute("eventID"),
+                    trackNum: node.getAttribute("trackNum")
+            });
+
+            evt._node = node;
+            evt.getProperty = function(name) {
+                return this._node.getAttribute(name);
+            };
+            evt.setWikiInfo(wikiURL, wikiSection);
+
+            this._events.add(evt);
+
+            added = true;
+        }
+        node = node.nextSibling;
+    }
+
+    if (added) {
+        this._fire("onAddMany", []);
+    }
+};
+
+
+Timeline.DefaultEventSource.prototype.loadJSON = function(data, url) {
+    var base = this._getBaseURL(url);
+    var added = false;
+    if (data && data.events){
+        var wikiURL = ("wikiURL" in data) ? data.wikiURL : null;
+        var wikiSection = ("wikiSection" in data) ? data.wikiSection : null;
+
+        var dateTimeFormat = ("dateTimeFormat" in data) ? data.dateTimeFormat : null;
+        var parseDateTimeFunction = this._events.getUnit().getParser(dateTimeFormat);
+
+        for (var i=0; i < data.events.length; i++){
+            var event = data.events[i];
+            // Fixing issue 33:
+            // instant event: default (for JSON only) is false. Or use values from isDuration or durationEvent
+            // isDuration was negated (see issue 33, so keep that interpretation
+            var instant = event.isDuration || (event.durationEvent != null && !event.durationEvent);
+
+            var evt = new Timeline.DefaultEventSource.Event({
+                          id: ("id" in event) ? event.id : undefined,
+                       start: parseDateTimeFunction(event.start),
+                         end: parseDateTimeFunction(event.end),
+                 latestStart: parseDateTimeFunction(event.latestStart),
+                 earliestEnd: parseDateTimeFunction(event.earliestEnd),
+                     instant: instant,
+                        text: event.title,
+                 description: event.description,
+                       image: this._resolveRelativeURL(event.image, base),
+                        link: this._resolveRelativeURL(event.link , base),
+                        icon: this._resolveRelativeURL(event.icon , base),
+                       color: event.color,
+                   textColor: event.textColor,
+                   hoverText: event.hoverText,
+                   classname: event.classname,
+                   tapeImage: event.tapeImage,
+                  tapeRepeat: event.tapeRepeat,
+                     caption: event.caption,
+                     eventID: event.eventID,
+                    trackNum: event.trackNum
+            });
+            evt._obj = event;
+            evt.getProperty = function(name) {
+                return this._obj[name];
+            };
+            evt.setWikiInfo(wikiURL, wikiSection);
+
+            this._events.add(evt);
+            added = true;
+        }
+    }
+
+    if (added) {
+        this._fire("onAddMany", []);
+    }
+};
+
+/*
+ *  Contributed by Morten Frederiksen, http://www.wasab.dk/morten/
+ */
+Timeline.DefaultEventSource.prototype.loadSPARQL = function(xml, url) {
+    var base = this._getBaseURL(url);
+
+    var dateTimeFormat = 'iso8601';
+    var parseDateTimeFunction = this._events.getUnit().getParser(dateTimeFormat);
+
+    if (xml == null) {
+        return;
+    }
+
+    /*
+     *  Find <results> tag
+     */
+    var node = xml.documentElement.firstChild;
+    while (node != null && (node.nodeType != 1 || node.nodeName != 'results')) {
+        node = node.nextSibling;
+    }
+
+    var wikiURL = null;
+    var wikiSection = null;
+    if (node != null) {
+        wikiURL = node.getAttribute("wiki-url");
+        wikiSection = node.getAttribute("wiki-section");
+
+        node = node.firstChild;
+    }
+
+    var added = false;
+    while (node != null) {
+        if (node.nodeType == 1) {
+            var bindings = { };
+            var binding = node.firstChild;
+            while (binding != null) {
+                if (binding.nodeType == 1 &&
+                    binding.firstChild != null &&
+                    binding.firstChild.nodeType == 1 &&
+                    binding.firstChild.firstChild != null &&
+                    binding.firstChild.firstChild.nodeType == 3) {
+                    bindings[binding.getAttribute('name')] = binding.firstChild.firstChild.nodeValue;
+                }
+                binding = binding.nextSibling;
+            }
+
+            if (bindings["start"] == null && bindings["date"] != null) {
+                bindings["start"] = bindings["date"];
+            }
+
+            // instant event: default is true. Or use values from isDuration or durationEvent
+            var instant = (bindings["isDuration"]    === null &&
+                           bindings["durationEvent"] === null) ||
+                          bindings["isDuration"] == "false" ||
+                          bindings["durationEvent"] == "false";
+
+            var evt = new Timeline.DefaultEventSource.Event({
+                          id: bindings["id"],
+                       start: parseDateTimeFunction(bindings["start"]),
+                         end: parseDateTimeFunction(bindings["end"]),
+                 latestStart: parseDateTimeFunction(bindings["latestStart"]),
+                 earliestEnd: parseDateTimeFunction(bindings["earliestEnd"]),
+                     instant: instant, // instant
+                        text: bindings["title"], // text
+                 description: bindings["description"],
+                       image: this._resolveRelativeURL(bindings["image"], base),
+                        link: this._resolveRelativeURL(bindings["link"] , base),
+                        icon: this._resolveRelativeURL(bindings["icon"] , base),
+                       color: bindings["color"],
+                   textColor: bindings["textColor"],
+                   hoverText: bindings["hoverText"],
+                     caption: bindings["caption"],
+                   classname: bindings["classname"],
+                   tapeImage: bindings["tapeImage"],
+                  tapeRepeat: bindings["tapeRepeat"],
+                     eventID: bindings["eventID"],
+                    trackNum: bindings["trackNum"]
+            });
+            evt._bindings = bindings;
+            evt.getProperty = function(name) {
+                return this._bindings[name];
+            };
+            evt.setWikiInfo(wikiURL, wikiSection);
+
+            this._events.add(evt);
+            added = true;
+        }
+        node = node.nextSibling;
+    }
+
+    if (added) {
+        this._fire("onAddMany", []);
+    }
+};
+
+Timeline.DefaultEventSource.prototype.add = function(evt) {
+    this._events.add(evt);
+    this._fire("onAddOne", [evt]);
+};
+
+Timeline.DefaultEventSource.prototype.addMany = function(events) {
+    for (var i = 0; i < events.length; i++) {
+        this._events.add(events[i]);
+    }
+    this._fire("onAddMany", []);
+};
+
+Timeline.DefaultEventSource.prototype.clear = function() {
+    this._events.removeAll();
+    this._fire("onClear", []);
+};
+
+Timeline.DefaultEventSource.prototype.getEvent = function(id) {
+    return this._events.getEvent(id);
+};
+
+Timeline.DefaultEventSource.prototype.getEventIterator = function(startDate, endDate) {
+    return this._events.getIterator(startDate, endDate);
+};
+
+Timeline.DefaultEventSource.prototype.getEventReverseIterator = function(startDate, endDate) {
+    return this._events.getReverseIterator(startDate, endDate);
+};
+
+Timeline.DefaultEventSource.prototype.getAllEventIterator = function() {
+    return this._events.getAllIterator();
+};
+
+Timeline.DefaultEventSource.prototype.getCount = function() {
+    return this._events.getCount();
+};
+
+Timeline.DefaultEventSource.prototype.getEarliestDate = function() {
+    return this._events.getEarliestDate();
+};
+
+Timeline.DefaultEventSource.prototype.getLatestDate = function() {
+    return this._events.getLatestDate();
+};
+
+Timeline.DefaultEventSource.prototype._fire = function(handlerName, args) {
+    for (var i = 0; i < this._listeners.length; i++) {
+        var listener = this._listeners[i];
+        if (handlerName in listener) {
+            try {
+                listener[handlerName].apply(listener, args);
+            } catch (e) {
+                SimileAjax.Debug.exception(e);
+            }
+        }
+    }
+};
+
+Timeline.DefaultEventSource.prototype._getBaseURL = function(url) {
+    if (url.indexOf("://") < 0) {
+        var url2 = this._getBaseURL(document.location.href);
+        if (url.substr(0,1) == "/") {
+            url = url2.substr(0, url2.indexOf("/", url2.indexOf("://") + 3)) + url;
+        } else {
+            url = url2 + url;
+        }
+    }
+
+    var i = url.lastIndexOf("/");
+    if (i < 0) {
+        return "";
+    } else {
+        return url.substr(0, i+1);
+    }
+};
+
+Timeline.DefaultEventSource.prototype._resolveRelativeURL = function(url, base) {
+    if (url == null || url == "") {
+        return url;
+    } else if (url.indexOf("://") > 0) {
+        return url;
+    } else if (url.substr(0,1) == "/") {
+        return base.substr(0, base.indexOf("/", base.indexOf("://") + 3)) + url;
+    } else {
+        return base + url;
+    }
+};
+
+
+Timeline.DefaultEventSource.Event = function(args) {
+  //
+  // Attention developers!
+  // If you add a new event attribute, please be sure to add it to
+  // all three load functions: loadXML, loadSPARCL, loadJSON.
+  // Thanks!
+  //
+  // args is a hash/object. It supports the following keys. Most are optional
+  //   id            -- an internal id. Really shouldn't be used by events.
+  //                    Timeline library clients should use eventID
+  //   eventID       -- For use by library client when writing custom painters or
+  //                    custom fillInfoBubble
+  //   start
+  //   end
+  //   latestStart
+  //   earliestEnd
+  //   instant      -- boolean. Controls precise/non-precise logic & duration/instant issues
+  //   text         -- event source attribute 'title' -- used as the label on Timelines and in bubbles.
+  //   description  -- used in bubbles
+  //   image        -- used in bubbles
+  //   link         -- used in bubbles
+  //   icon         -- on the Timeline
+  //   color        -- Timeline label and tape color
+  //   textColor    -- Timeline label color, overrides color attribute
+  //   hoverText    -- deprecated, here for backwards compatibility.
+  //                   Superceeded by caption
+  //   caption      -- tooltip-like caption on the Timeline. Uses HTML title attribute
+  //   classname    -- used to set classname in Timeline. Enables better CSS selector rules
+  //   tapeImage    -- background image of the duration event's tape div on the Timeline
+  //   tapeRepeat   -- repeat attribute for tapeImage. {repeat | repeat-x | repeat-y }
+
+  function cleanArg(arg) {
+      // clean up an arg
+      return (args[arg] != null && args[arg] != "") ? args[arg] : null;
+  }
+
+  var id = args.id ? args.id.trim() : "";
+  this._id = id.length > 0 ? id : Timeline.EventUtils.getNewEventID();
+
+  this._instant = args.instant || (args.end == null);
+
+  this._start = args.start;
+  this._end = (args.end != null) ? args.end : args.start;
+
+  this._latestStart = (args.latestStart != null) ?
+                       args.latestStart : (args.instant ? this._end : this._start);
+  this._earliestEnd = (args.earliestEnd != null) ? args.earliestEnd : this._end;
+
+  // check sanity of dates since incorrect dates will later cause calculation errors
+  // when painting
+  var err=[];
+  if (this._start > this._latestStart) {
+          this._latestStart = this._start;
+          err.push("start is > latestStart");}
+  if (this._start > this._earliestEnd) {
+          this._earliestEnd = this._latestStart;
+          err.push("start is > earliestEnd");}
+  if (this._start > this._end) {
+          this._end = this._earliestEnd;
+          err.push("start is > end");}
+  if (this._latestStart > this._earliestEnd) {
+          this._earliestEnd = this._latestStart;
+          err.push("latestStart is > earliestEnd");}
+  if (this._latestStart > this._end) {
+          this._end = this._earliestEnd;
+          err.push("latestStart is > end");}
+  if (this._earliestEnd > this._end) {
+          this._end = this._earliestEnd;
+          err.push("earliestEnd is > end");}
+
+  this._eventID = cleanArg('eventID');
+  this._text = (args.text != null) ? SimileAjax.HTML.deEntify(args.text) : ""; // Change blank titles to ""
+  if (err.length > 0) {
+          this._text += " PROBLEM: " + err.join(", ");
+  }
+
+  this._description = SimileAjax.HTML.deEntify(args.description);
+  this._image = cleanArg('image');
+  this._link =  cleanArg('link');
+  this._title = cleanArg('hoverText');
+  this._title = cleanArg('caption');
+
+  this._icon = cleanArg('icon');
+  this._color = cleanArg('color');
+  this._textColor = cleanArg('textColor');
+  this._classname = cleanArg('classname');
+  this._tapeImage = cleanArg('tapeImage');
+  this._tapeRepeat = cleanArg('tapeRepeat');
+  this._trackNum = cleanArg('trackNum');
+  if (this._trackNum != null) {
+      this._trackNum = parseInt(this._trackNum);
+  }
+
+  this._wikiURL = null;
+  this._wikiSection = null;
+};
+
+Timeline.DefaultEventSource.Event.prototype = {
+    getID:          function() { return this._id; },
+
+    isInstant:      function() { return this._instant; },
+    isImprecise:    function() { return this._start != this._latestStart || this._end != this._earliestEnd; },
+
+    getStart:       function() { return this._start; },
+    getEnd:         function() { return this._end; },
+    getLatestStart: function() { return this._latestStart; },
+    getEarliestEnd: function() { return this._earliestEnd; },
+
+    getEventID:     function() { return this._eventID; },
+    getText:        function() { return this._text; }, // title
+    getDescription: function() { return this._description; },
+    getImage:       function() { return this._image; },
+    getLink:        function() { return this._link; },
+
+    getIcon:        function() { return this._icon; },
+    getColor:       function() { return this._color; },
+    getTextColor:   function() { return this._textColor; },
+    getClassName:   function() { return this._classname; },
+    getTapeImage:   function() { return this._tapeImage; },
+    getTapeRepeat:  function() { return this._tapeRepeat; },
+    getTrackNum:    function() { return this._trackNum; },
+
+    getProperty:    function(name) { return null; },
+
+    getWikiURL:     function() { return this._wikiURL; },
+    getWikiSection: function() { return this._wikiSection; },
+    setWikiInfo: function(wikiURL, wikiSection) {
+        this._wikiURL = wikiURL;
+        this._wikiSection = wikiSection;
+    },
+
+    fillDescription: function(elmt) {
+        elmt.innerHTML = this._description;
+    },
+    fillWikiInfo: function(elmt) {
+        // Many bubbles will not support a wiki link.
+        //
+        // Strategy: assume no wiki link. If we do have
+        // enough parameters for one, then create it.
+        elmt.style.display = "none"; // default
+
+        if (this._wikiURL == null || this._wikiSection == null) {
+          return; // EARLY RETURN
+        }
+
+        // create the wikiID from the property or from the event text (the title)
+        var wikiID = this.getProperty("wikiID");
+        if (wikiID == null || wikiID.length == 0) {
+            wikiID = this.getText(); // use the title as the backup wiki id
+        }
+
+        if (wikiID == null || wikiID.length == 0) {
+          return; // No wikiID. Thus EARLY RETURN
+        }
+
+        // ready to go...
+        elmt.style.display = "inline";
+        wikiID = wikiID.replace(/\s/g, "_");
+        var url = this._wikiURL + this._wikiSection.replace(/\s/g, "_") + "/" + wikiID;
+        var a = document.createElement("a");
+        a.href = url;
+        a.target = "new";
+        a.innerHTML = Timeline.strings[Timeline.clientLocale].wikiLinkLabel;
+
+        elmt.appendChild(document.createTextNode("["));
+        elmt.appendChild(a);
+        elmt.appendChild(document.createTextNode("]"));
+    },
+
+    fillTime: function(elmt, labeller) {
+        if (this._instant) {
+            if (this.isImprecise()) {
+                elmt.appendChild(elmt.ownerDocument.createTextNode(labeller.labelPrecise(this._start)));
+                elmt.appendChild(elmt.ownerDocument.createElement("br"));
+                elmt.appendChild(elmt.ownerDocument.createTextNode(labeller.labelPrecise(this._end)));
+            } else {
+                elmt.appendChild(elmt.ownerDocument.createTextNode(labeller.labelPrecise(this._start)));
+            }
+        } else {
+            if (this.isImprecise()) {
+                elmt.appendChild(elmt.ownerDocument.createTextNode(
+                    labeller.labelPrecise(this._start) + " ~ " + labeller.labelPrecise(this._latestStart)));
+                elmt.appendChild(elmt.ownerDocument.createElement("br"));
+                elmt.appendChild(elmt.ownerDocument.createTextNode(
+                    labeller.labelPrecise(this._earliestEnd) + " ~ " + labeller.labelPrecise(this._end)));
+            } else {
+                elmt.appendChild(elmt.ownerDocument.createTextNode(labeller.labelPrecise(this._start)));
+                elmt.appendChild(elmt.ownerDocument.createElement("br"));
+                elmt.appendChild(elmt.ownerDocument.createTextNode(labeller.labelPrecise(this._end)));
+            }
+        }
+    },
+
+    fillInfoBubble: function(elmt, theme, labeller) {
+        var doc = elmt.ownerDocument;
+
+        var title = this.getText();
+        var link = this.getLink();
+        var image = this.getImage();
+
+        if (image != null) {
+            var img = doc.createElement("img");
+            img.src = image;
+
+            theme.event.bubble.imageStyler(img);
+            elmt.appendChild(img);
+        }
+
+        var divTitle = doc.createElement("div");
+        var textTitle = doc.createTextNode(title);
+        if (link != null) {
+            var a = doc.createElement("a");
+            a.href = link;
+            a.appendChild(textTitle);
+            divTitle.appendChild(a);
+        } else {
+            divTitle.appendChild(textTitle);
+        }
+        theme.event.bubble.titleStyler(divTitle);
+        elmt.appendChild(divTitle);
+
+        var divBody = doc.createElement("div");
+        this.fillDescription(divBody);
+        theme.event.bubble.bodyStyler(divBody);
+        elmt.appendChild(divBody);
+
+        var divTime = doc.createElement("div");
+        this.fillTime(divTime, labeller);
+        theme.event.bubble.timeStyler(divTime);
+        elmt.appendChild(divTime);
+
+        var divWiki = doc.createElement("div");
+        this.fillWikiInfo(divWiki);
+        theme.event.bubble.wikiStyler(divWiki);
+        elmt.appendChild(divWiki);
+    }
+};
+
+
+/*==================================================
+ *  Original Event Painter
+ *==================================================
+ */
+
+/*==================================================
+ *
+ * To enable a single event listener to monitor everything
+ * on a Timeline, we need a way to map from an event's icon,
+ * label or tape element to the associated timeline, band and
+ * specific event.
+ *
+ * Thus a set format is used for the id's of the
+ * events' elements on the Timeline--
+ *
+ * element id format for labels, icons, tapes:
+ *   labels: label-tl-<timelineID>-<band_index>-<evt.id>
+ *    icons: icon-tl-<timelineID>-<band_index>-<evt.id>
+ *    tapes: tape1-tl-<timelineID>-<band_index>-<evt.id>
+ *           tape2-tl-<timelineID>-<band_index>-<evt.id>
+ *           // some events have more than one tape
+ *    highlight: highlight1-tl-<timelineID>-<band_index>-<evt.id>
+ *               highlight2-tl-<timelineID>-<band_index>-<evt.id>
+ *           // some events have more than one highlight div (future)
+ * You can then retrieve the band/timeline objects and event object
+ * by using Timeline.EventUtils.decodeEventElID
+ *
+ *==================================================
+ */
+
+/*
+ *    eventPaintListener functions receive calls about painting.
+ *    function(band, op, evt, els)
+ *       context: 'this' will be an OriginalEventPainter object.
+ *                It has properties and methods for obtaining
+ *                the relevant band, timeline, etc
+ *       band = the band being painted
+ *       op = 'paintStarting' // the painter is about to remove
+ *            all previously painted events, if any. It will
+ *            then start painting all of the visible events that
+ *            pass the filter.
+ *            evt = null, els = null
+ *       op = 'paintEnded' // the painter has finished painting
+ *            all of the visible events that passed the filter
+ *            evt = null, els = null
+ *       op = 'paintedEvent' // the painter just finished painting an event
+ *            evt = event just painted
+ *            els = array of painted elements' divs. Depending on the event,
+ *                  the array could be just a tape or icon (if no label).
+ *                  Or could include label, multiple tape divs (imprecise event),
+ *                  highlight divs. The array is not ordered. The meaning of
+ *                  each el is available by decoding the el's id
+ *      Note that there may be no paintedEvent calls if no events were visible
+ *      or passed the filter.
+ */
+
+Timeline.OriginalEventPainter = function(params) {
+    this._params = params;
+    this._onSelectListeners = [];
+    this._eventPaintListeners = [];
+
+    this._filterMatcher = null;
+    this._highlightMatcher = null;
+    this._frc = null;
+
+    this._eventIdToElmt = {};
+};
+
+Timeline.OriginalEventPainter.prototype.initialize = function(band, timeline) {
+    this._band = band;
+    this._timeline = timeline;
+
+    this._backLayer = null;
+    this._eventLayer = null;
+    this._lineLayer = null;
+    this._highlightLayer = null;
+
+    this._eventIdToElmt = null;
+};
+
+Timeline.OriginalEventPainter.prototype.getType = function() {
+    return 'original';
+};
+
+Timeline.OriginalEventPainter.prototype.addOnSelectListener = function(listener) {
+    this._onSelectListeners.push(listener);
+};
+
+Timeline.OriginalEventPainter.prototype.removeOnSelectListener = function(listener) {
+    for (var i = 0; i < this._onSelectListeners.length; i++) {
+        if (this._onSelectListeners[i] == listener) {
+            this._onSelectListeners.splice(i, 1);
+            break;
+        }
+    }
+};
+
+Timeline.OriginalEventPainter.prototype.addEventPaintListener = function(listener) {
+    this._eventPaintListeners.push(listener);
+};
+
+Timeline.OriginalEventPainter.prototype.removeEventPaintListener = function(listener) {
+    for (var i = 0; i < this._eventPaintListeners.length; i++) {
+        if (this._eventPaintListeners[i] == listener) {
+            this._eventPaintListeners.splice(i, 1);
+            break;
+        }
+    }
+};
+
+Timeline.OriginalEventPainter.prototype.getFilterMatcher = function() {
+    return this._filterMatcher;
+};
+
+Timeline.OriginalEventPainter.prototype.setFilterMatcher = function(filterMatcher) {
+    this._filterMatcher = filterMatcher;
+};
+
+Timeline.OriginalEventPainter.prototype.getHighlightMatcher = function() {
+    return this._highlightMatcher;
+};
+
+Timeline.OriginalEventPainter.prototype.setHighlightMatcher = function(highlightMatcher) {
+    this._highlightMatcher = highlightMatcher;
+};
+
+Timeline.OriginalEventPainter.prototype.paint = function() {
+    // Paints the events for a given section of the band--what is
+    // visible on screen and some extra.
+    var eventSource = this._band.getEventSource();
+    if (eventSource == null) {
+        return;
+    }
+
+    this._eventIdToElmt = {};
+    this._fireEventPaintListeners('paintStarting', null, null);
+    this._prepareForPainting();
+
+    var metrics = this._computeMetrics();
+    var minDate = this._band.getMinDate();
+    var maxDate = this._band.getMaxDate();
+
+    var filterMatcher = (this._filterMatcher != null) ?
+        this._filterMatcher :
+        function(evt) { return true; };
+    var highlightMatcher = (this._highlightMatcher != null) ?
+        this._highlightMatcher :
+        function(evt) { return -1; };
+
+    var iterator = eventSource.getEventReverseIterator(minDate, maxDate);
+    while (iterator.hasNext()) {
+        var evt = iterator.next();
+        if (filterMatcher(evt)) {
+            this.paintEvent(evt, metrics, this._params.theme, highlightMatcher(evt));
+        }
+    }
+
+    this._highlightLayer.style.display = "block";
+    this._lineLayer.style.display = "block";
+    this._eventLayer.style.display = "block";
+    // update the band object for max number of tracks in this section of the ether
+    this._band.updateEventTrackInfo(this._tracks.length, metrics.trackIncrement);
+    this._fireEventPaintListeners('paintEnded', null, null);
+
+    this._setOrthogonalOffset(metrics);
+};
+
+Timeline.OriginalEventPainter.prototype.softPaint = function() {
+    this._setOrthogonalOffset(this._computeMetrics());
+};
+
+Timeline.OriginalEventPainter.prototype._setOrthogonalOffset = function(metrics) {
+    var actualViewWidth = 2 * metrics.trackOffset + this._tracks.length * metrics.trackIncrement;
+    var minOrthogonalOffset = Math.min(0, this._band.getViewWidth() - actualViewWidth);
+    var orthogonalOffset = Math.max(minOrthogonalOffset, this._band.getViewOrthogonalOffset());
+
+    this._highlightLayer.style.top =
+        this._lineLayer.style.top =
+            this._eventLayer.style.top =
+                orthogonalOffset + "px";
+};
+
+Timeline.OriginalEventPainter.prototype._computeMetrics = function() {
+     var eventTheme = this._params.theme.event;
+     var trackHeight = Math.max(eventTheme.track.height, eventTheme.tape.height +
+                         this._frc.getLineHeight());
+     var metrics = {
+            trackOffset: eventTheme.track.offset,
+            trackHeight: trackHeight,
+               trackGap: eventTheme.track.gap,
+         trackIncrement: trackHeight + eventTheme.track.gap,
+                   icon: eventTheme.instant.icon,
+              iconWidth: eventTheme.instant.iconWidth,
+             iconHeight: eventTheme.instant.iconHeight,
+             labelWidth: eventTheme.label.width,
+           maxLabelChar: eventTheme.label.maxLabelChar,
+    impreciseIconMargin: eventTheme.instant.impreciseIconMargin
+     };
+
+     return metrics;
+};
+
+Timeline.OriginalEventPainter.prototype._prepareForPainting = function() {
+    // Remove everything previously painted: highlight, line and event layers.
+    // Prepare blank layers for painting.
+    var band = this._band;
+
+    if (this._backLayer == null) {
+        this._backLayer = this._band.createLayerDiv(0, "timeline-band-events");
+        this._backLayer.style.visibility = "hidden";
+
+        var eventLabelPrototype = document.createElement("span");
+        eventLabelPrototype.className = "timeline-event-label";
+        this._backLayer.appendChild(eventLabelPrototype);
+        this._frc = SimileAjax.Graphics.getFontRenderingContext(eventLabelPrototype);
+    }
+    this._frc.update();
+    this._tracks = [];
+
+    if (this._highlightLayer != null) {
+        band.removeLayerDiv(this._highlightLayer);
+    }
+    this._highlightLayer = band.createLayerDiv(105, "timeline-band-highlights");
+    this._highlightLayer.style.display = "none";
+
+    if (this._lineLayer != null) {
+        band.removeLayerDiv(this._lineLayer);
+    }
+    this._lineLayer = band.createLayerDiv(110, "timeline-band-lines");
+    this._lineLayer.style.display = "none";
+
+    if (this._eventLayer != null) {
+        band.removeLayerDiv(this._eventLayer);
+    }
+    this._eventLayer = band.createLayerDiv(115, "timeline-band-events");
+    this._eventLayer.style.display = "none";
+};
+
+Timeline.OriginalEventPainter.prototype.paintEvent = function(evt, metrics, theme, highlightIndex) {
+    if (evt.isInstant()) {
+        this.paintInstantEvent(evt, metrics, theme, highlightIndex);
+    } else {
+        this.paintDurationEvent(evt, metrics, theme, highlightIndex);
+    }
+};
+
+Timeline.OriginalEventPainter.prototype.paintInstantEvent = function(evt, metrics, theme, highlightIndex) {
+    if (evt.isImprecise()) {
+        this.paintImpreciseInstantEvent(evt, metrics, theme, highlightIndex);
+    } else {
+        this.paintPreciseInstantEvent(evt, metrics, theme, highlightIndex);
+    }
+}
+
+Timeline.OriginalEventPainter.prototype.paintDurationEvent = function(evt, metrics, theme, highlightIndex) {
+    if (evt.isImprecise()) {
+        this.paintImpreciseDurationEvent(evt, metrics, theme, highlightIndex);
+    } else {
+        this.paintPreciseDurationEvent(evt, metrics, theme, highlightIndex);
+    }
+}
+
+Timeline.OriginalEventPainter.prototype.paintPreciseInstantEvent = function(evt, metrics, theme, highlightIndex) {
+    var doc = this._timeline.getDocument();
+    var text = evt.getText();
+
+    var startDate = evt.getStart();
+    var startPixel = Math.round(this._band.dateToPixelOffset(startDate));
+    var iconRightEdge = Math.round(startPixel + metrics.iconWidth / 2);
+    var iconLeftEdge = Math.round(startPixel - metrics.iconWidth / 2);
+
+    var labelDivClassName = this._getLabelDivClassName(evt);
+    var labelSize = this._frc.computeSize(text, labelDivClassName);
+    var labelLeft = iconRightEdge + theme.event.label.offsetFromLine;
+    var labelRight = labelLeft + labelSize.width;
+
+    var rightEdge = labelRight;
+    var track = this._findFreeTrack(evt, rightEdge);
+
+    var labelTop = Math.round(
+        metrics.trackOffset + track * metrics.trackIncrement +
+        metrics.trackHeight / 2 - labelSize.height / 2);
+
+    var iconElmtData = this._paintEventIcon(evt, track, iconLeftEdge, metrics, theme, 0);
+    var labelElmtData = this._paintEventLabel(evt, text, labelLeft, labelTop, labelSize.width,
+        labelSize.height, theme, labelDivClassName, highlightIndex);
+    var els = [iconElmtData.elmt, labelElmtData.elmt];
+
+    var self = this;
+    var clickHandler = function(elmt, domEvt, target) {
+        return self._onClickInstantEvent(iconElmtData.elmt, domEvt, evt);
+    };
+    SimileAjax.DOM.registerEvent(iconElmtData.elmt, "mousedown", clickHandler);
+    SimileAjax.DOM.registerEvent(labelElmtData.elmt, "mousedown", clickHandler);
+
+    var hDiv = this._createHighlightDiv(highlightIndex, iconElmtData, theme, evt);
+    if (hDiv != null) {els.push(hDiv);}
+    this._fireEventPaintListeners('paintedEvent', evt, els);
+
+
+    this._eventIdToElmt[evt.getID()] = iconElmtData.elmt;
+    this._tracks[track] = iconLeftEdge;
+};
+
+Timeline.OriginalEventPainter.prototype.paintImpreciseInstantEvent = function(evt, metrics, theme, highlightIndex) {
+    var doc = this._timeline.getDocument();
+    var text = evt.getText();
+
+    var startDate = evt.getStart();
+    var endDate = evt.getEnd();
+    var startPixel = Math.round(this._band.dateToPixelOffset(startDate));
+    var endPixel = Math.round(this._band.dateToPixelOffset(endDate));
+
+    var iconRightEdge = Math.round(startPixel + metrics.iconWidth / 2);
+    var iconLeftEdge = Math.round(startPixel - metrics.iconWidth / 2);
+
+    var labelDivClassName = this._getLabelDivClassName(evt);
+    var labelSize = this._frc.computeSize(text, labelDivClassName);
+    var labelLeft = iconRightEdge + theme.event.label.offsetFromLine;
+    var labelRight = labelLeft + labelSize.width;
+
+    var rightEdge = Math.max(labelRight, endPixel);
+    var track = this._findFreeTrack(evt, rightEdge);
+    var tapeHeight = theme.event.tape.height;
+    var labelTop = Math.round(
+        metrics.trackOffset + track * metrics.trackIncrement + tapeHeight);
+
+    var iconElmtData = this._paintEventIcon(evt, track, iconLeftEdge, metrics, theme, tapeHeight);
+    var labelElmtData = this._paintEventLabel(evt, text, labelLeft, labelTop, labelSize.width,
+                        labelSize.height, theme, labelDivClassName, highlightIndex);
+
+    var color = evt.getColor();
+    color = color != null ? color : theme.event.instant.impreciseColor;
+
+    var tapeElmtData = this._paintEventTape(evt, track, startPixel, endPixel,
+        color, theme.event.instant.impreciseOpacity, metrics, theme, 0);
+    var els = [iconElmtData.elmt, labelElmtData.elmt, tapeElmtData.elmt];
+
+    var self = this;
+    var clickHandler = function(elmt, domEvt, target) {
+        return self._onClickInstantEvent(iconElmtData.elmt, domEvt, evt);
+    };
+    SimileAjax.DOM.registerEvent(iconElmtData.elmt, "mousedown", clickHandler);
+    SimileAjax.DOM.registerEvent(tapeElmtData.elmt, "mousedown", clickHandler);
+    SimileAjax.DOM.registerEvent(labelElmtData.elmt, "mousedown", clickHandler);
+
+    var hDiv = this._createHighlightDiv(highlightIndex, iconElmtData, theme, evt);
+    if (hDiv != null) {els.push(hDiv);}
+    this._fireEventPaintListeners('paintedEvent', evt, els);
+
+    this._eventIdToElmt[evt.getID()] = iconElmtData.elmt;
+    this._tracks[track] = iconLeftEdge;
+};
+
+Timeline.OriginalEventPainter.prototype.paintPreciseDurationEvent = function(evt, metrics, theme, highlightIndex) {
+    var doc = this._timeline.getDocument();
+    var text = evt.getText();
+
+    var startDate = evt.getStart();
+    var endDate = evt.getEnd();
+    var startPixel = Math.round(this._band.dateToPixelOffset(startDate));
+    var endPixel = Math.round(this._band.dateToPixelOffset(endDate));
+
+    var labelDivClassName = this._getLabelDivClassName(evt);
+    var labelSize = this._frc.computeSize(text, labelDivClassName);
+    var labelLeft = startPixel;
+    var labelRight = labelLeft + labelSize.width;
+
+    var rightEdge = Math.max(labelRight, endPixel);
+    var track = this._findFreeTrack(evt, rightEdge);
+    var labelTop = Math.round(
+        metrics.trackOffset + track * metrics.trackIncrement + theme.event.tape.height);
+
+    var color = evt.getColor();
+    color = color != null ? color : theme.event.duration.color;
+
+    var tapeElmtData = this._paintEventTape(evt, track, startPixel, endPixel, color, 100, metrics, theme, 0);
+    var labelElmtData = this._paintEventLabel(evt, text, labelLeft, labelTop, labelSize.width,
+      labelSize.height, theme, labelDivClassName, highlightIndex);
+    var els = [tapeElmtData.elmt, labelElmtData.elmt];
+
+    var self = this;
+    var clickHandler = function(elmt, domEvt, target) {
+        return self._onClickDurationEvent(tapeElmtData.elmt, domEvt, evt);
+    };
+    SimileAjax.DOM.registerEvent(tapeElmtData.elmt, "mousedown", clickHandler);
+    SimileAjax.DOM.registerEvent(labelElmtData.elmt, "mousedown", clickHandler);
+
+    var hDiv = this._createHighlightDiv(highlightIndex, tapeElmtData, theme, evt);
+    if (hDiv != null) {els.push(hDiv);}
+    this._fireEventPaintListeners('paintedEvent', evt, els);
+
+    this._eventIdToElmt[evt.getID()] = tapeElmtData.elmt;
+    this._tracks[track] = startPixel;
+};
+
+Timeline.OriginalEventPainter.prototype.paintImpreciseDurationEvent = function(evt, metrics, theme, highlightIndex) {
+    var doc = this._timeline.getDocument();
+    var text = evt.getText();
+
+    var startDate = evt.getStart();
+    var latestStartDate = evt.getLatestStart();
+    var endDate = evt.getEnd();
+    var earliestEndDate = evt.getEarliestEnd();
+
+    var startPixel = Math.round(this._band.dateToPixelOffset(startDate));
+    var latestStartPixel = Math.round(this._band.dateToPixelOffset(latestStartDate));
+    var endPixel = Math.round(this._band.dateToPixelOffset(endDate));
+    var earliestEndPixel = Math.round(this._band.dateToPixelOffset(earliestEndDate));
+
+    var labelDivClassName = this._getLabelDivClassName(evt);
+    var labelSize = this._frc.computeSize(text, labelDivClassName);
+    var labelLeft = latestStartPixel;
+    var labelRight = labelLeft + labelSize.width;
+
+    var rightEdge = Math.max(labelRight, endPixel);
+    var track = this._findFreeTrack(evt, rightEdge);
+    var labelTop = Math.round(
+        metrics.trackOffset + track * metrics.trackIncrement + theme.event.tape.height);
+
+    var color = evt.getColor();
+    color = color != null ? color : theme.event.duration.color;
+
+    // Imprecise events can have two event tapes
+    // The imprecise dates tape, uses opacity to be dimmer than precise dates
+    var impreciseTapeElmtData = this._paintEventTape(evt, track, startPixel, endPixel,
+        theme.event.duration.impreciseColor,
+        theme.event.duration.impreciseOpacity, metrics, theme, 0);
+    // The precise dates tape, regular (100%) opacity
+    var tapeElmtData = this._paintEventTape(evt, track, latestStartPixel,
+        earliestEndPixel, color, 100, metrics, theme, 1);
+
+    var labelElmtData = this._paintEventLabel(evt, text, labelLeft, labelTop,
+        labelSize.width, labelSize.height, theme, labelDivClassName, highlightIndex);
+    var els = [impreciseTapeElmtData.elmt, tapeElmtData.elmt, labelElmtData.elmt];
+
+    var self = this;
+    var clickHandler = function(elmt, domEvt, target) {
+        return self._onClickDurationEvent(tapeElmtData.elmt, domEvt, evt);
+    };
+    SimileAjax.DOM.registerEvent(tapeElmtData.elmt, "mousedown", clickHandler);
+    SimileAjax.DOM.registerEvent(labelElmtData.elmt, "mousedown", clickHandler);
+
+    var hDiv = this._createHighlightDiv(highlightIndex, tapeElmtData, theme, evt);
+    if (hDiv != null) {els.push(hDiv);}
+    this._fireEventPaintListeners('paintedEvent', evt, els);
+
+    this._eventIdToElmt[evt.getID()] = tapeElmtData.elmt;
+    this._tracks[track] = startPixel;
+};
+
+Timeline.OriginalEventPainter.prototype._encodeEventElID = function(elType, evt) {
+    return Timeline.EventUtils.encodeEventElID(this._timeline, this._band, elType, evt);
+};
+
+Timeline.OriginalEventPainter.prototype._findFreeTrack = function(event, rightEdge) {
+    var trackAttribute = event.getTrackNum();
+    if (trackAttribute != null) {
+        return trackAttribute; // early return since event includes track number
+    }
+
+    // normal case: find an open track
+    for (var i = 0; i < this._tracks.length; i++) {
+        var t = this._tracks[i];
+        if (t > rightEdge) {
+            break;
+        }
+    }
+    return i;
+};
+
+Timeline.OriginalEventPainter.prototype._paintEventIcon = function(evt, iconTrack, left, metrics, theme, tapeHeight) {
+    // If no tape, then paint the icon in the middle of the track.
+    // If there is a tape, paint the icon below the tape + impreciseIconMargin
+    var icon = evt.getIcon();
+    icon = icon != null ? icon : metrics.icon;
+
+    var top; // top of the icon
+    if (tapeHeight > 0) {
+        top = metrics.trackOffset + iconTrack * metrics.trackIncrement +
+              tapeHeight + metrics.impreciseIconMargin;
+    } else {
+        var middle = metrics.trackOffset + iconTrack * metrics.trackIncrement +
+                     metrics.trackHeight / 2;
+        top = Math.round(middle - metrics.iconHeight / 2);
+    }
+    var img = SimileAjax.Graphics.createTranslucentImage(icon);
+    var iconDiv = this._timeline.getDocument().createElement("div");
+    iconDiv.className = this._getElClassName('timeline-event-icon', evt, 'icon');
+    iconDiv.id = this._encodeEventElID('icon', evt);
+    iconDiv.style.left = left + "px";
+    iconDiv.style.top = top + "px";
+    iconDiv.appendChild(img);
+
+    if(evt._title != null)
+        iconDiv.title = evt._title;
+
+    this._eventLayer.appendChild(iconDiv);
+
+    return {
+        left:   left,
+        top:    top,
+        width:  metrics.iconWidth,
+        height: metrics.iconHeight,
+        elmt:   iconDiv
+    };
+};
+
+Timeline.OriginalEventPainter.prototype._paintEventLabel = function(evt, text, left, top, width,
+    height, theme, labelDivClassName, highlightIndex) {
+    var doc = this._timeline.getDocument();
+
+    var labelDiv = doc.createElement("div");
+    labelDiv.className = labelDivClassName;
+    labelDiv.id = this._encodeEventElID('label', evt);
+    labelDiv.style.left = left + "px";
+    labelDiv.style.width = width + "px";
+    labelDiv.style.top = top + "px";
+    labelDiv.innerHTML = text;
+
+    if(evt._title != null)
+        labelDiv.title = evt._title;
+
+    var color = evt.getTextColor();
+    if (color == null) {
+        color = evt.getColor();
+    }
+    if (color != null) {
+        labelDiv.style.color = color;
+    }
+    if (theme.event.highlightLabelBackground && highlightIndex >= 0) {
+        labelDiv.style.background = this._getHighlightColor(highlightIndex, theme);
+    }
+
+    this._eventLayer.appendChild(labelDiv);
+
+    return {
+        left:   left,
+        top:    top,
+        width:  width,
+        height: height,
+        elmt:   labelDiv
+    };
+};
+
+Timeline.OriginalEventPainter.prototype._paintEventTape = function(
+    evt, iconTrack, startPixel, endPixel, color, opacity, metrics, theme, tape_index) {
+
+    var tapeWidth = endPixel - startPixel;
+    var tapeHeight = theme.event.tape.height;
+    var top = metrics.trackOffset + iconTrack * metrics.trackIncrement;
+
+    var tapeDiv = this._timeline.getDocument().createElement("div");
+    tapeDiv.className = this._getElClassName('timeline-event-tape', evt, 'tape');
+    tapeDiv.id = this._encodeEventElID('tape' + tape_index, evt);
+    tapeDiv.style.left = startPixel + "px";
+    tapeDiv.style.width = tapeWidth + "px";
+    tapeDiv.style.height = tapeHeight + "px";
+    tapeDiv.style.top = top + "px";
+
+    if(evt._title != null)
+        tapeDiv.title = evt._title;
+
+    if(color != null) {
+        tapeDiv.style.backgroundColor = color;
+    }
+
+    var backgroundImage = evt.getTapeImage();
+    var backgroundRepeat = evt.getTapeRepeat();
+    backgroundRepeat = backgroundRepeat != null ? backgroundRepeat : 'repeat';
+    if(backgroundImage != null) {
+      tapeDiv.style.backgroundImage = "url(" + backgroundImage + ")";
+      tapeDiv.style.backgroundRepeat = backgroundRepeat;
+    }
+
+    SimileAjax.Graphics.setOpacity(tapeDiv, opacity);
+
+    this._eventLayer.appendChild(tapeDiv);
+
+    return {
+        left:   startPixel,
+        top:    top,
+        width:  tapeWidth,
+        height: tapeHeight,
+        elmt:   tapeDiv
+    };
+}
+
+Timeline.OriginalEventPainter.prototype._getLabelDivClassName = function(evt) {
+    return this._getElClassName('timeline-event-label', evt, 'label');
+};
+
+Timeline.OriginalEventPainter.prototype._getElClassName = function(elClassName, evt, prefix) {
+    // Prefix and '_' is added to the event's classname. Set to null for no prefix
+    var evt_classname = evt.getClassName(),
+        pieces = [];
+
+    if (evt_classname) {
+      if (prefix) {pieces.push(prefix + '-' + evt_classname + ' ');}
+      pieces.push(evt_classname + ' ');
+    }
+    pieces.push(elClassName);
+    return(pieces.join(''));
+};
+
+Timeline.OriginalEventPainter.prototype._getHighlightColor = function(highlightIndex, theme) {
+    var highlightColors = theme.event.highlightColors;
+    return highlightColors[Math.min(highlightIndex, highlightColors.length - 1)];
+};
+
+Timeline.OriginalEventPainter.prototype._createHighlightDiv = function(highlightIndex, dimensions, theme, evt) {
+    var div = null;
+    if (highlightIndex >= 0) {
+        var doc = this._timeline.getDocument();
+        var color = this._getHighlightColor(highlightIndex, theme);
+
+        div = doc.createElement("div");
+        div.className = this._getElClassName('timeline-event-highlight', evt, 'highlight');
+        div.id = this._encodeEventElID('highlight0', evt); // in future will have other
+                                                           // highlight divs for tapes + icons
+        div.style.position = "absolute";
+        div.style.overflow = "hidden";
+        div.style.left =    (dimensions.left - 2) + "px";
+        div.style.width =   (dimensions.width + 4) + "px";
+        div.style.top =     (dimensions.top - 2) + "px";
+        div.style.height =  (dimensions.height + 4) + "px";
+        div.style.background = color;
+
+        this._highlightLayer.appendChild(div);
+    }
+    return div;
+};
+
+Timeline.OriginalEventPainter.prototype._onClickInstantEvent = function(icon, domEvt, evt) {
+    var c = SimileAjax.DOM.getPageCoordinates(icon);
+    this._showBubble(
+        c.left + Math.ceil(icon.offsetWidth / 2),
+        c.top + Math.ceil(icon.offsetHeight / 2),
+        evt
+    );
+    this._fireOnSelect(evt.getID());
+
+    domEvt.cancelBubble = true;
+    SimileAjax.DOM.cancelEvent(domEvt);
+    return false;
+};
+
+Timeline.OriginalEventPainter.prototype._onClickDurationEvent = function(target, domEvt, evt) {
+    if ("pageX" in domEvt) {
+        var x = domEvt.pageX;
+        var y = domEvt.pageY;
+    } else {
+        var c = SimileAjax.DOM.getPageCoordinates(target);
+        var x = domEvt.offsetX + c.left;
+        var y = domEvt.offsetY + c.top;
+    }
+    this._showBubble(x, y, evt);
+    this._fireOnSelect(evt.getID());
+
+    domEvt.cancelBubble = true;
+    SimileAjax.DOM.cancelEvent(domEvt);
+    return false;
+};
+
+Timeline.OriginalEventPainter.prototype.showBubble = function(evt) {
+    var elmt = this._eventIdToElmt[evt.getID()];
+    if (elmt) {
+        var c = SimileAjax.DOM.getPageCoordinates(elmt);
+        this._showBubble(c.left + elmt.offsetWidth / 2, c.top + elmt.offsetHeight / 2, evt);
+    }
+};
+
+Timeline.OriginalEventPainter.prototype._showBubble = function(x, y, evt) {
+    var div = document.createElement("div");
+    var themeBubble = this._params.theme.event.bubble;
+    evt.fillInfoBubble(div, this._params.theme, this._band.getLabeller());
+
+    SimileAjax.WindowManager.cancelPopups();
+    SimileAjax.Graphics.createBubbleForContentAndPoint(div, x, y,
+        themeBubble.width, null, themeBubble.maxHeight);
+};
+
+Timeline.OriginalEventPainter.prototype._fireOnSelect = function(eventID) {
+    for (var i = 0; i < this._onSelectListeners.length; i++) {
+        this._onSelectListeners[i](eventID);
+    }
+};
+
+Timeline.OriginalEventPainter.prototype._fireEventPaintListeners = function(op, evt, els) {
+    for (var i = 0; i < this._eventPaintListeners.length; i++) {
+        this._eventPaintListeners[i](this._band, op, evt, els);
+    }
+};
+/*==================================================
+ *  Detailed Event Painter
+ *==================================================
+ */
+
+// Note: a number of features from original-painter
+//       are not yet implemented in detailed painter.
+//       Eg classname, id attributes for icons, labels, tapes
+
+Timeline.DetailedEventPainter = function(params) {
+    this._params = params;
+    this._onSelectListeners = [];
+
+    this._filterMatcher = null;
+    this._highlightMatcher = null;
+    this._frc = null;
+
+    this._eventIdToElmt = {};
+};
+
+Timeline.DetailedEventPainter.prototype.initialize = function(band, timeline) {
+    this._band = band;
+    this._timeline = timeline;
+
+    this._backLayer = null;
+    this._eventLayer = null;
+    this._lineLayer = null;
+    this._highlightLayer = null;
+
+    this._eventIdToElmt = null;
+};
+
+Timeline.DetailedEventPainter.prototype.getType = function() {
+    return 'detailed';
+};
+
+Timeline.DetailedEventPainter.prototype.addOnSelectListener = function(listener) {
+    this._onSelectListeners.push(listener);
+};
+
+Timeline.DetailedEventPainter.prototype.removeOnSelectListener = function(listener) {
+    for (var i = 0; i < this._onSelectListeners.length; i++) {
+        if (this._onSelectListeners[i] == listener) {
+            this._onSelectListeners.splice(i, 1);
+            break;
+        }
+    }
+};
+
+Timeline.DetailedEventPainter.prototype.getFilterMatcher = function() {
+    return this._filterMatcher;
+};
+
+Timeline.DetailedEventPainter.prototype.setFilterMatcher = function(filterMatcher) {
+    this._filterMatcher = filterMatcher;
+};
+
+Timeline.DetailedEventPainter.prototype.getHighlightMatcher = function() {
+    return this._highlightMatcher;
+};
+
+Timeline.DetailedEventPainter.prototype.setHighlightMatcher = function(highlightMatcher) {
+    this._highlightMatcher = highlightMatcher;
+};
+
+Timeline.DetailedEventPainter.prototype.paint = function() {
+    var eventSource = this._band.getEventSource();
+    if (eventSource == null) {
+        return;
+    }
+
+    this._eventIdToElmt = {};
+    this._prepareForPainting();
+
+    var eventTheme = this._params.theme.event;
+    var trackHeight = Math.max(eventTheme.track.height, this._frc.getLineHeight());
+    var metrics = {
+        trackOffset:    Math.round(this._band.getViewWidth() / 2 - trackHeight / 2),
+        trackHeight:    trackHeight,
+        trackGap:       eventTheme.track.gap,
+        trackIncrement: trackHeight + eventTheme.track.gap,
+        icon:           eventTheme.instant.icon,
+        iconWidth:      eventTheme.instant.iconWidth,
+        iconHeight:     eventTheme.instant.iconHeight,
+        labelWidth:     eventTheme.label.width
+    }
+
+    var minDate = this._band.getMinDate();
+    var maxDate = this._band.getMaxDate();
+
+    var filterMatcher = (this._filterMatcher != null) ?
+        this._filterMatcher :
+        function(evt) { return true; };
+    var highlightMatcher = (this._highlightMatcher != null) ?
+        this._highlightMatcher :
+        function(evt) { return -1; };
+
+    var iterator = eventSource.getEventReverseIterator(minDate, maxDate);
+    while (iterator.hasNext()) {
+        var evt = iterator.next();
+        if (filterMatcher(evt)) {
+            this.paintEvent(evt, metrics, this._params.theme, highlightMatcher(evt));
+        }
+    }
+
+    this._highlightLayer.style.display = "block";
+    this._lineLayer.style.display = "block";
+    this._eventLayer.style.display = "block";
+    // update the band object for max number of tracks in this section of the ether
+    this._band.updateEventTrackInfo(this._lowerTracks.length + this._upperTracks.length,
+                                 metrics.trackIncrement);
+};
+
+Timeline.DetailedEventPainter.prototype.softPaint = function() {
+};
+
+Timeline.DetailedEventPainter.prototype._prepareForPainting = function() {
+    var band = this._band;
+
+    if (this._backLayer == null) {
+        this._backLayer = this._band.createLayerDiv(0, "timeline-band-events");
+        this._backLayer.style.visibility = "hidden";
+
+        var eventLabelPrototype = document.createElement("span");
+        eventLabelPrototype.className = "timeline-event-label";
+        this._backLayer.appendChild(eventLabelPrototype);
+        this._frc = SimileAjax.Graphics.getFontRenderingContext(eventLabelPrototype);
+    }
+    this._frc.update();
+    this._lowerTracks = [];
+    this._upperTracks = [];
+
+    if (this._highlightLayer != null) {
+        band.removeLayerDiv(this._highlightLayer);
+    }
+    this._highlightLayer = band.createLayerDiv(105, "timeline-band-highlights");
+    this._highlightLayer.style.display = "none";
+
+    if (this._lineLayer != null) {
+        band.removeLayerDiv(this._lineLayer);
+    }
+    this._lineLayer = band.createLayerDiv(110, "timeline-band-lines");
+    this._lineLayer.style.display = "none";
+
+    if (this._eventLayer != null) {
+        band.removeLayerDiv(this._eventLayer);
+    }
+    this._eventLayer = band.createLayerDiv(110, "timeline-band-events");
+    this._eventLayer.style.display = "none";
+};
+
+Timeline.DetailedEventPainter.prototype.paintEvent = function(evt, metrics, theme, highlightIndex) {
+    if (evt.isInstant()) {
+        this.paintInstantEvent(evt, metrics, theme, highlightIndex);
+    } else {
+        this.paintDurationEvent(evt, metrics, theme, highlightIndex);
+    }
+};
+
+Timeline.DetailedEventPainter.prototype.paintInstantEvent = function(evt, metrics, theme, highlightIndex) {
+    if (evt.isImprecise()) {
+        this.paintImpreciseInstantEvent(evt, metrics, theme, highlightIndex);
+    } else {
+        this.paintPreciseInstantEvent(evt, metrics, theme, highlightIndex);
+    }
+}
+
+Timeline.DetailedEventPainter.prototype.paintDurationEvent = function(evt, metrics, theme, highlightIndex) {
+    if (evt.isImprecise()) {
+        this.paintImpreciseDurationEvent(evt, metrics, theme, highlightIndex);
+    } else {
+        this.paintPreciseDurationEvent(evt, metrics, theme, highlightIndex);
+    }
+}
+
+Timeline.DetailedEventPainter.prototype.paintPreciseInstantEvent = function(evt, metrics, theme, highlightIndex) {
+    var doc = this._timeline.getDocument();
+    var text = evt.getText();
+
+    var startDate = evt.getStart();
+    var startPixel = Math.round(this._band.dateToPixelOffset(startDate));
+    var iconRightEdge = Math.round(startPixel + metrics.iconWidth / 2);
+    var iconLeftEdge = Math.round(startPixel - metrics.iconWidth / 2);
+
+    var labelSize = this._frc.computeSize(text);
+    var iconTrack = this._findFreeTrackForSolid(iconRightEdge, startPixel);
+    var iconElmtData = this._paintEventIcon(evt, iconTrack, iconLeftEdge, metrics, theme);
+
+    var labelLeft = iconRightEdge + theme.event.label.offsetFromLine;
+    var labelTrack = iconTrack;
+
+    var iconTrackData = this._getTrackData(iconTrack);
+    if (Math.min(iconTrackData.solid, iconTrackData.text) >= labelLeft + labelSize.width) { // label on the same track, to the right of icon
+        iconTrackData.solid = iconLeftEdge;
+        iconTrackData.text = labelLeft;
+    } else { // label on a different track, below icon
+        iconTrackData.solid = iconLeftEdge;
+
+        labelLeft = startPixel + theme.event.label.offsetFromLine;
+        labelTrack = this._findFreeTrackForText(iconTrack, labelLeft + labelSize.width, function(t) { t.line = startPixel - 2; });
+        this._getTrackData(labelTrack).text = iconLeftEdge;
+
+        this._paintEventLine(evt, startPixel, iconTrack, labelTrack, metrics, theme);
+    }
+
+    var labelTop = Math.round(
+        metrics.trackOffset + labelTrack * metrics.trackIncrement +
+        metrics.trackHeight / 2 - labelSize.height / 2);
+
+    var labelElmtData = this._paintEventLabel(evt, text, labelLeft, labelTop, labelSize.width, labelSize.height, theme);
+
+    var self = this;
+    var clickHandler = function(elmt, domEvt, target) {
+        return self._onClickInstantEvent(iconElmtData.elmt, domEvt, evt);
+    };
+    SimileAjax.DOM.registerEvent(iconElmtData.elmt, "mousedown", clickHandler);
+    SimileAjax.DOM.registerEvent(labelElmtData.elmt, "mousedown", clickHandler);
+
+    this._createHighlightDiv(highlightIndex, iconElmtData, theme);
+
+    this._eventIdToElmt[evt.getID()] = iconElmtData.elmt;
+};
+
+Timeline.DetailedEventPainter.prototype.paintImpreciseInstantEvent = function(evt, metrics, theme, highlightIndex) {
+    var doc = this._timeline.getDocument();
+    var text = evt.getText();
+
+    var startDate = evt.getStart();
+    var endDate = evt.getEnd();
+    var startPixel = Math.round(this._band.dateToPixelOffset(startDate));
+    var endPixel = Math.round(this._band.dateToPixelOffset(endDate));
+
+    var iconRightEdge = Math.round(startPixel + metrics.iconWidth / 2);
+    var iconLeftEdge = Math.round(startPixel - metrics.iconWidth / 2);
+
+    var labelSize = this._frc.computeSize(text);
+    var iconTrack = this._findFreeTrackForSolid(endPixel, startPixel);
+
+    var tapeElmtData = this._paintEventTape(evt, iconTrack, startPixel, endPixel,
+        theme.event.instant.impreciseColor, theme.event.instant.impreciseOpacity, metrics, theme);
+    var iconElmtData = this._paintEventIcon(evt, iconTrack, iconLeftEdge, metrics, theme);
+
+    var iconTrackData = this._getTrackData(iconTrack);
+    iconTrackData.solid = iconLeftEdge;
+
+    var labelLeft = iconRightEdge + theme.event.label.offsetFromLine;
+    var labelRight = labelLeft + labelSize.width;
+    var labelTrack;
+    if (labelRight < endPixel) {
+        labelTrack = iconTrack;
+    } else {
+        labelLeft = startPixel + theme.event.label.offsetFromLine;
+        labelRight = labelLeft + labelSize.width;
+
+        labelTrack = this._findFreeTrackForText(iconTrack, labelRight, function(t) { t.line = startPixel - 2; });
+        this._getTrackData(labelTrack).text = iconLeftEdge;
+
+        this._paintEventLine(evt, startPixel, iconTrack, labelTrack, metrics, theme);
+    }
+    var labelTop = Math.round(
+        metrics.trackOffset + labelTrack * metrics.trackIncrement +
+        metrics.trackHeight / 2 - labelSize.height / 2);
+
+    var labelElmtData = this._paintEventLabel(evt, text, labelLeft, labelTop, labelSize.width, labelSize.height, theme);
+
+    var self = this;
+    var clickHandler = function(elmt, domEvt, target) {
+        return self._onClickInstantEvent(iconElmtData.elmt, domEvt, evt);
+    };
+    SimileAjax.DOM.registerEvent(iconElmtData.elmt, "mousedown", clickHandler);
+    SimileAjax.DOM.registerEvent(tapeElmtData.elmt, "mousedown", clickHandler);
+    SimileAjax.DOM.registerEvent(labelElmtData.elmt, "mousedown", clickHandler);
+
+    this._createHighlightDiv(highlightIndex, iconElmtData, theme);
+
+    this._eventIdToElmt[evt.getID()] = iconElmtData.elmt;
+};
+
+Timeline.DetailedEventPainter.prototype.paintPreciseDurationEvent = function(evt, metrics, theme, highlightIndex) {
+    var doc = this._timeline.getDocument();
+    var text = evt.getText();
+
+    var startDate = evt.getStart();
+    var endDate = evt.getEnd();
+    var startPixel = Math.round(this._band.dateToPixelOffset(startDate));
+    var endPixel = Math.round(this._band.dateToPixelOffset(endDate));
+
+    var labelSize = this._frc.computeSize(text);
+    var tapeTrack = this._findFreeTrackForSolid(endPixel);
+    var color = evt.getColor();
+    color = color != null ? color : theme.event.duration.color;
+
+    var tapeElmtData = this._paintEventTape(evt, tapeTrack, startPixel, endPixel, color, 100, metrics, theme);
+
+    var tapeTrackData = this._getTrackData(tapeTrack);
+    tapeTrackData.solid = startPixel;
+
+    var labelLeft = startPixel + theme.event.label.offsetFromLine;
+    var labelTrack = this._findFreeTrackForText(tapeTrack, labelLeft + labelSize.width, function(t) { t.line = startPixel - 2; });
+    this._getTrackData(labelTrack).text = startPixel - 2;
+
+    this._paintEventLine(evt, startPixel, tapeTrack, labelTrack, metrics, theme);
+
+    var labelTop = Math.round(
+        metrics.trackOffset + labelTrack * metrics.trackIncrement +
+        metrics.trackHeight / 2 - labelSize.height / 2);
+
+    var labelElmtData = this._paintEventLabel(evt, text, labelLeft, labelTop, labelSize.width, labelSize.height, theme);
+
+    var self = this;
+    var clickHandler = function(elmt, domEvt, target) {
+        return self._onClickDurationEvent(tapeElmtData.elmt, domEvt, evt);
+    };
+    SimileAjax.DOM.registerEvent(tapeElmtData.elmt, "mousedown", clickHandler);
+    SimileAjax.DOM.registerEvent(labelElmtData.elmt, "mousedown", clickHandler);
+
+    this._createHighlightDiv(highlightIndex, tapeElmtData, theme);
+
+    this._eventIdToElmt[evt.getID()] = tapeElmtData.elmt;
+};
+
+Timeline.DetailedEventPainter.prototype.paintImpreciseDurationEvent = function(evt, metrics, theme, highlightIndex) {
+    var doc = this._timeline.getDocument();
+    var text = evt.getText();
+
+    var startDate = evt.getStart();
+    var latestStartDate = evt.getLatestStart();
+    var endDate = evt.getEnd();
+    var earliestEndDate = evt.getEarliestEnd();
+
+    var startPixel = Math.round(this._band.dateToPixelOffset(startDate));
+    var latestStartPixel = Math.round(this._band.dateToPixelOffset(latestStartDate));
+    var endPixel = Math.round(this._band.dateToPixelOffset(endDate));
+    var earliestEndPixel = Math.round(this._band.dateToPixelOffset(earliestEndDate));
+
+    var labelSize = this._frc.computeSize(text);
+    var tapeTrack = this._findFreeTrackForSolid(endPixel);
+    var color = evt.getColor();
+    color = color != null ? color : theme.event.duration.color;
+
+    var impreciseTapeElmtData = this._paintEventTape(evt, tapeTrack, startPixel, endPixel,
+        theme.event.duration.impreciseColor, theme.event.duration.impreciseOpacity, metrics, theme);
+    var tapeElmtData = this._paintEventTape(evt, tapeTrack, latestStartPixel, earliestEndPixel, color, 100, metrics, theme);
+
+    var tapeTrackData = this._getTrackData(tapeTrack);
+    tapeTrackData.solid = startPixel;
+
+    var labelLeft = latestStartPixel + theme.event.label.offsetFromLine;
+    var labelTrack = this._findFreeTrackForText(tapeTrack, labelLeft + labelSize.width, function(t) { t.line = latestStartPixel - 2; });
+    this._getTrackData(labelTrack).text = latestStartPixel - 2;
+
+    this._paintEventLine(evt, latestStartPixel, tapeTrack, labelTrack, metrics, theme);
+
+    var labelTop = Math.round(
+        metrics.trackOffset + labelTrack * metrics.trackIncrement +
+        metrics.trackHeight / 2 - labelSize.height / 2);
+
+    var labelElmtData = this._paintEventLabel(evt, text, labelLeft, labelTop, labelSize.width, labelSize.height, theme);
+
+    var self = this;
+    var clickHandler = function(elmt, domEvt, target) {
+        return self._onClickDurationEvent(tapeElmtData.elmt, domEvt, evt);
+    };
+    SimileAjax.DOM.registerEvent(tapeElmtData.elmt, "mousedown", clickHandler);
+    SimileAjax.DOM.registerEvent(labelElmtData.elmt, "mousedown", clickHandler);
+
+    this._createHighlightDiv(highlightIndex, tapeElmtData, theme);
+
+    this._eventIdToElmt[evt.getID()] = tapeElmtData.elmt;
+};
+
+Timeline.DetailedEventPainter.prototype._findFreeTrackForSolid = function(solidEdge, softEdge) {
+    for (var i = 0; true; i++) {
+        if (i < this._lowerTracks.length) {
+            var t = this._lowerTracks[i];
+            if (Math.min(t.solid, t.text) > solidEdge && (!(softEdge) || t.line > softEdge)) {
+                return i;
+            }
+        } else {
+            this._lowerTracks.push({
+                solid:  Number.POSITIVE_INFINITY,
+                text:   Number.POSITIVE_INFINITY,
+                line:   Number.POSITIVE_INFINITY
+            });
+
+            return i;
+        }
+
+        if (i < this._upperTracks.length) {
+            var t = this._upperTracks[i];
+            if (Math.min(t.solid, t.text) > solidEdge && (!(softEdge) || t.line > softEdge)) {
+                return -1 - i;
+            }
+        } else {
+            this._upperTracks.push({
+                solid:  Number.POSITIVE_INFINITY,
+                text:   Number.POSITIVE_INFINITY,
+                line:   Number.POSITIVE_INFINITY
+            });
+
+            return -1 - i;
+        }
+    }
+};
+
+Timeline.DetailedEventPainter.prototype._findFreeTrackForText = function(fromTrack, edge, occupiedTrackVisitor) {
+    var extendUp;
+    var index;
+    var firstIndex;
+    var result;
+
+    if (fromTrack < 0) {
+        extendUp = true;
+        firstIndex = -fromTrack;
+
+        index = this._findFreeUpperTrackForText(firstIndex, edge);
+        result = -1 - index;
+    } else if (fromTrack > 0) {
+        extendUp = false;
+        firstIndex = fromTrack + 1;
+
+        index = this._findFreeLowerTrackForText(firstIndex, edge);
+        result = index;
+    } else {
+        var upIndex = this._findFreeUpperTrackForText(0, edge);
+        var downIndex = this._findFreeLowerTrackForText(1, edge);
+
+        if (downIndex - 1 <= upIndex) {
+            extendUp = false;
+            firstIndex = 1;
+            index = downIndex;
+            result = index;
+        } else {
+            extendUp = true;
+            firstIndex = 0;
+            index = upIndex;
+            result = -1 - index;
+        }
+    }
+
+    if (extendUp) {
+        if (index == this._upperTracks.length) {
+            this._upperTracks.push({
+                solid:  Number.POSITIVE_INFINITY,
+                text:   Number.POSITIVE_INFINITY,
+                line:   Number.POSITIVE_INFINITY
+            });
+        }
+        for (var i = firstIndex; i < index; i++) {
+            occupiedTrackVisitor(this._upperTracks[i]);
+        }
+    } else {
+        if (index == this._lowerTracks.length) {
+            this._lowerTracks.push({
+                solid:  Number.POSITIVE_INFINITY,
+                text:   Number.POSITIVE_INFINITY,
+                line:   Number.POSITIVE_INFINITY
+            });
+        }
+        for (var i = firstIndex; i < index; i++) {
+            occupiedTrackVisitor(this._lowerTracks[i]);
+        }
+    }
+    return result;
+};
+
+Timeline.DetailedEventPainter.prototype._findFreeLowerTrackForText = function(index, edge) {
+    for (; index < this._lowerTracks.length; index++) {
+        var t = this._lowerTracks[index];
+        if (Math.min(t.solid, t.text) >= edge) {
+            break;
+        }
+    }
+    return index;
+};
+
+Timeline.DetailedEventPainter.prototype._findFreeUpperTrackForText = function(index, edge) {
+    for (; index < this._upperTracks.length; index++) {
+        var t = this._upperTracks[index];
+        if (Math.min(t.solid, t.text) >= edge) {
+            break;
+        }
+    }
+    return index;
+};
+
+Timeline.DetailedEventPainter.prototype._getTrackData = function(index) {
+    return (index < 0) ? this._upperTracks[-index - 1] : this._lowerTracks[index];
+};
+
+Timeline.DetailedEventPainter.prototype._paintEventLine = function(evt, left, startTrack, endTrack, metrics, theme) {
+    var top = Math.round(metrics.trackOffset + startTrack * metrics.trackIncrement + metrics.trackHeight / 2);
+    var height = Math.round(Math.abs(endTrack - startTrack) * metrics.trackIncrement);
+
+    var lineStyle = "1px solid " + theme.event.label.lineColor;
+    var lineDiv = this._timeline.getDocument().createElement("div");
+	lineDiv.style.position = "absolute";
+    lineDiv.style.left = left + "px";
+    lineDiv.style.width = theme.event.label.offsetFromLine + "px";
+    lineDiv.style.height = height + "px";
+    if (startTrack > endTrack) {
+        lineDiv.style.top = (top - height) + "px";
+        lineDiv.style.borderTop = lineStyle;
+    } else {
+        lineDiv.style.top = top + "px";
+        lineDiv.style.borderBottom = lineStyle;
+    }
+    lineDiv.style.borderLeft = lineStyle;
+    this._lineLayer.appendChild(lineDiv);
+};
+
+Timeline.DetailedEventPainter.prototype._paintEventIcon = function(evt, iconTrack, left, metrics, theme) {
+    var icon = evt.getIcon();
+    icon = icon != null ? icon : metrics.icon;
+
+    var middle = metrics.trackOffset + iconTrack * metrics.trackIncrement + metrics.trackHeight / 2;
+    var top = Math.round(middle - metrics.iconHeight / 2);
+
+    var img = SimileAjax.Graphics.createTranslucentImage(icon);
+    var iconDiv = this._timeline.getDocument().createElement("div");
+    iconDiv.style.position = "absolute";
+    iconDiv.style.left = left + "px";
+    iconDiv.style.top = top + "px";
+    iconDiv.appendChild(img);
+    iconDiv.style.cursor = "pointer";
+
+    if(evt._title != null)
+        iconDiv.title = evt._title
+
+    this._eventLayer.appendChild(iconDiv);
+
+    return {
+        left:   left,
+        top:    top,
+        width:  metrics.iconWidth,
+        height: metrics.iconHeight,
+        elmt:   iconDiv
+    };
+};
+
+Timeline.DetailedEventPainter.prototype._paintEventLabel = function(evt, text, left, top, width, height, theme) {
+    var doc = this._timeline.getDocument();
+
+    var labelBackgroundDiv = doc.createElement("div");
+    labelBackgroundDiv.style.position = "absolute";
+    labelBackgroundDiv.style.left = left + "px";
+    labelBackgroundDiv.style.width = width + "px";
+    labelBackgroundDiv.style.top = top + "px";
+    labelBackgroundDiv.style.height = height + "px";
+    labelBackgroundDiv.style.backgroundColor = theme.event.label.backgroundColor;
+    SimileAjax.Graphics.setOpacity(labelBackgroundDiv, theme.event.label.backgroundOpacity);
+    this._eventLayer.appendChild(labelBackgroundDiv);
+
+    var labelDiv = doc.createElement("div");
+    labelDiv.style.position = "absolute";
+    labelDiv.style.left = left + "px";
+    labelDiv.style.width = width + "px";
+    labelDiv.style.top = top + "px";
+    labelDiv.innerHTML = text;
+    labelDiv.style.cursor = "pointer";
+
+    if(evt._title != null)
+        labelDiv.title = evt._title;
+
+    var color = evt.getTextColor();
+    if (color == null) {
+        color = evt.getColor();
+    }
+    if (color != null) {
+        labelDiv.style.color = color;
+    }
+
+    this._eventLayer.appendChild(labelDiv);
+
+    return {
+        left:   left,
+        top:    top,
+        width:  width,
+        height: height,
+        elmt:   labelDiv
+    };
+};
+
+Timeline.DetailedEventPainter.prototype._paintEventTape = function(
+    evt, iconTrack, startPixel, endPixel, color, opacity, metrics, theme) {
+
+    var tapeWidth = endPixel - startPixel;
+    var tapeHeight = theme.event.tape.height;
+    var middle = metrics.trackOffset + iconTrack * metrics.trackIncrement + metrics.trackHeight / 2;
+    var top = Math.round(middle - tapeHeight / 2);
+
+    var tapeDiv = this._timeline.getDocument().createElement("div");
+    tapeDiv.style.position = "absolute";
+    tapeDiv.style.left = startPixel + "px";
+    tapeDiv.style.width = tapeWidth + "px";
+    tapeDiv.style.top = top + "px";
+    tapeDiv.style.height = tapeHeight + "px";
+    tapeDiv.style.backgroundColor = color;
+    tapeDiv.style.overflow = "hidden";
+    tapeDiv.style.cursor = "pointer";
+
+    if(evt._title != null)
+        tapeDiv.title = evt._title;
+
+    SimileAjax.Graphics.setOpacity(tapeDiv, opacity);
+
+    this._eventLayer.appendChild(tapeDiv);
+
+    return {
+        left:   startPixel,
+        top:    top,
+        width:  tapeWidth,
+        height: tapeHeight,
+        elmt:   tapeDiv
+    };
+}
+
+Timeline.DetailedEventPainter.prototype._createHighlightDiv = function(highlightIndex, dimensions, theme) {
+    if (highlightIndex >= 0) {
+        var doc = this._timeline.getDocument();
+        var eventTheme = theme.event;
+
+        var color = eventTheme.highlightColors[Math.min(highlightIndex, eventTheme.highlightColors.length - 1)];
+
+        var div = doc.createElement("div");
+        div.style.position = "absolute";
+        div.style.overflow = "hidden";
+        div.style.left =    (dimensions.left - 2) + "px";
+        div.style.width =   (dimensions.width + 4) + "px";
+        div.style.top =     (dimensions.top - 2) + "px";
+        div.style.height =  (dimensions.height + 4) + "px";
+        div.style.background = color;
+
+        this._highlightLayer.appendChild(div);
+    }
+};
+
+Timeline.DetailedEventPainter.prototype._onClickInstantEvent = function(icon, domEvt, evt) {
+    var c = SimileAjax.DOM.getPageCoordinates(icon);
+    this._showBubble(
+        c.left + Math.ceil(icon.offsetWidth / 2),
+        c.top + Math.ceil(icon.offsetHeight / 2),
+        evt
+    );
+    this._fireOnSelect(evt.getID());
+
+    domEvt.cancelBubble = true;
+    SimileAjax.DOM.cancelEvent(domEvt);
+    return false;
+};
+
+Timeline.DetailedEventPainter.prototype._onClickDurationEvent = function(target, domEvt, evt) {
+    if ("pageX" in domEvt) {
+        var x = domEvt.pageX;
+        var y = domEvt.pageY;
+    } else {
+        var c = SimileAjax.DOM.getPageCoordinates(target);
+        var x = domEvt.offsetX + c.left;
+        var y = domEvt.offsetY + c.top;
+    }
+    this._showBubble(x, y, evt);
+    this._fireOnSelect(evt.getID());
+
+    domEvt.cancelBubble = true;
+    SimileAjax.DOM.cancelEvent(domEvt);
+    return false;
+};
+
+Timeline.DetailedEventPainter.prototype.showBubble = function(evt) {
+    var elmt = this._eventIdToElmt[evt.getID()];
+    if (elmt) {
+        var c = SimileAjax.DOM.getPageCoordinates(elmt);
+        this._showBubble(c.left + elmt.offsetWidth / 2, c.top + elmt.offsetHeight / 2, evt);
+    }
+};
+
+Timeline.DetailedEventPainter.prototype._showBubble = function(x, y, evt) {
+    var div = document.createElement("div");
+    var themeBubble = this._params.theme.event.bubble;
+    evt.fillInfoBubble(div, this._params.theme, this._band.getLabeller());
+
+    SimileAjax.WindowManager.cancelPopups();
+    SimileAjax.Graphics.createBubbleForContentAndPoint(div, x, y,
+       themeBubble.width, null, themeBubble.maxHeight);
+};
+
+Timeline.DetailedEventPainter.prototype._fireOnSelect = function(eventID) {
+    for (var i = 0; i < this._onSelectListeners.length; i++) {
+        this._onSelectListeners[i](eventID);
+    }
+};
+/*==================================================
+ *  Overview Event Painter
+ *==================================================
+ */
+
+Timeline.OverviewEventPainter = function(params) {
+    this._params = params;
+    this._onSelectListeners = [];
+
+    this._filterMatcher = null;
+    this._highlightMatcher = null;
+};
+
+Timeline.OverviewEventPainter.prototype.initialize = function(band, timeline) {
+    this._band = band;
+    this._timeline = timeline;
+
+    this._eventLayer = null;
+    this._highlightLayer = null;
+};
+
+Timeline.OverviewEventPainter.prototype.getType = function() {
+    return 'overview';
+};
+
+Timeline.OverviewEventPainter.prototype.addOnSelectListener = function(listener) {
+    this._onSelectListeners.push(listener);
+};
+
+Timeline.OverviewEventPainter.prototype.removeOnSelectListener = function(listener) {
+    for (var i = 0; i < this._onSelectListeners.length; i++) {
+        if (this._onSelectListeners[i] == listener) {
+            this._onSelectListeners.splice(i, 1);
+            break;
+        }
+    }
+};
+
+Timeline.OverviewEventPainter.prototype.getFilterMatcher = function() {
+    return this._filterMatcher;
+};
+
+Timeline.OverviewEventPainter.prototype.setFilterMatcher = function(filterMatcher) {
+    this._filterMatcher = filterMatcher;
+};
+
+Timeline.OverviewEventPainter.prototype.getHighlightMatcher = function() {
+    return this._highlightMatcher;
+};
+
+Timeline.OverviewEventPainter.prototype.setHighlightMatcher = function(highlightMatcher) {
+    this._highlightMatcher = highlightMatcher;
+};
+
+Timeline.OverviewEventPainter.prototype.paint = function() {
+    var eventSource = this._band.getEventSource();
+    if (eventSource == null) {
+        return;
+    }
+
+    this._prepareForPainting();
+
+    var eventTheme = this._params.theme.event;
+    var metrics = {
+        trackOffset:    eventTheme.overviewTrack.offset,
+        trackHeight:    eventTheme.overviewTrack.height,
+        trackGap:       eventTheme.overviewTrack.gap,
+        trackIncrement: eventTheme.overviewTrack.height + eventTheme.overviewTrack.gap
+    }
+
+    var minDate = this._band.getMinDate();
+    var maxDate = this._band.getMaxDate();
+
+    var filterMatcher = (this._filterMatcher != null) ?
+        this._filterMatcher :
+        function(evt) { return true; };
+    var highlightMatcher = (this._highlightMatcher != null) ?
+        this._highlightMatcher :
+        function(evt) { return -1; };
+
+    var iterator = eventSource.getEventReverseIterator(minDate, maxDate);
+    while (iterator.hasNext()) {
+        var evt = iterator.next();
+        if (filterMatcher(evt)) {
+            this.paintEvent(evt, metrics, this._params.theme, highlightMatcher(evt));
+        }
+    }
+
+    this._highlightLayer.style.display = "block";
+    this._eventLayer.style.display = "block";
+    // update the band object for max number of tracks in this section of the ether
+    this._band.updateEventTrackInfo(this._tracks.length, metrics.trackIncrement);
+};
+
+Timeline.OverviewEventPainter.prototype.softPaint = function() {
+};
+
+Timeline.OverviewEventPainter.prototype._prepareForPainting = function() {
+    var band = this._band;
+
+    this._tracks = [];
+
+    if (this._highlightLayer != null) {
+        band.removeLayerDiv(this._highlightLayer);
+    }
+    this._highlightLayer = band.createLayerDiv(105, "timeline-band-highlights");
+    this._highlightLayer.style.display = "none";
+
+    if (this._eventLayer != null) {
+        band.removeLayerDiv(this._eventLayer);
+    }
+    this._eventLayer = band.createLayerDiv(110, "timeline-band-events");
+    this._eventLayer.style.display = "none";
+};
+
+Timeline.OverviewEventPainter.prototype.paintEvent = function(evt, metrics, theme, highlightIndex) {
+    if (evt.isInstant()) {
+        this.paintInstantEvent(evt, metrics, theme, highlightIndex);
+    } else {
+        this.paintDurationEvent(evt, metrics, theme, highlightIndex);
+    }
+};
+
+Timeline.OverviewEventPainter.prototype.paintInstantEvent = function(evt, metrics, theme, highlightIndex) {
+    var startDate = evt.getStart();
+    var startPixel = Math.round(this._band.dateToPixelOffset(startDate));
+
+    var color = evt.getColor(),
+        klassName = evt.getClassName();
+    if (klassName) {
+      color = null;
+    } else {
+      color = color != null ? color : theme.event.duration.color;
+    }
+
+    var tickElmtData = this._paintEventTick(evt, startPixel, color, 100, metrics, theme);
+
+    this._createHighlightDiv(highlightIndex, tickElmtData, theme);
+};
+
+Timeline.OverviewEventPainter.prototype.paintDurationEvent = function(evt, metrics, theme, highlightIndex) {
+    var latestStartDate = evt.getLatestStart();
+    var earliestEndDate = evt.getEarliestEnd();
+
+    var latestStartPixel = Math.round(this._band.dateToPixelOffset(latestStartDate));
+    var earliestEndPixel = Math.round(this._band.dateToPixelOffset(earliestEndDate));
+
+    var tapeTrack = 0;
+    for (; tapeTrack < this._tracks.length; tapeTrack++) {
+        if (earliestEndPixel < this._tracks[tapeTrack]) {
+            break;
+        }
+    }
+    this._tracks[tapeTrack] = earliestEndPixel;
+
+    var color = evt.getColor(),
+        klassName = evt.getClassName();
+    if (klassName) {
+      color = null;
+    } else {
+      color = color != null ? color : theme.event.duration.color;
+    }
+
+    var tapeElmtData = this._paintEventTape(evt, tapeTrack, latestStartPixel, earliestEndPixel,
+      color, 100, metrics, theme, klassName);
+
+    this._createHighlightDiv(highlightIndex, tapeElmtData, theme);
+};
+
+Timeline.OverviewEventPainter.prototype._paintEventTape = function(
+    evt, track, left, right, color, opacity, metrics, theme, klassName) {
+
+    var top = metrics.trackOffset + track * metrics.trackIncrement;
+    var width = right - left;
+    var height = metrics.trackHeight;
+
+    var tapeDiv = this._timeline.getDocument().createElement("div");
+    tapeDiv.className = 'timeline-small-event-tape'
+    if (klassName) {tapeDiv.className += ' small-' + klassName;}
+    tapeDiv.style.left = left + "px";
+    tapeDiv.style.width = width + "px";
+    tapeDiv.style.top = top + "px";
+    tapeDiv.style.height = height + "px";
+
+    if (color) {
+      tapeDiv.style.backgroundColor = color; // set color here if defined by event. Else use css
+    }
+ //   tapeDiv.style.overflow = "hidden";   // now set in css
+ //   tapeDiv.style.position = "absolute";
+    if(opacity<100) SimileAjax.Graphics.setOpacity(tapeDiv, opacity);
+
+    this._eventLayer.appendChild(tapeDiv);
+
+    return {
+        left:   left,
+        top:    top,
+        width:  width,
+        height: height,
+        elmt:   tapeDiv
+    };
+}
+
+Timeline.OverviewEventPainter.prototype._paintEventTick = function(
+    evt, left, color, opacity, metrics, theme) {
+
+    var height = theme.event.overviewTrack.tickHeight;
+    var top = metrics.trackOffset - height;
+    var width = 1;
+
+    var tickDiv = this._timeline.getDocument().createElement("div");
+	  tickDiv.className = 'timeline-small-event-icon'
+    tickDiv.style.left = left + "px";
+    tickDiv.style.top = top + "px";
+  //  tickDiv.style.width = width + "px";
+  //  tickDiv.style.position = "absolute";
+  //  tickDiv.style.height = height + "px";
+  //  tickDiv.style.backgroundColor = color;
+  //  tickDiv.style.overflow = "hidden";
+
+    var klassName = evt.getClassName()
+    if (klassName) {tickDiv.className +=' small-' + klassName};
+
+    if(opacity<100) {SimileAjax.Graphics.setOpacity(tickDiv, opacity)};
+
+    this._eventLayer.appendChild(tickDiv);
+
+    return {
+        left:   left,
+        top:    top,
+        width:  width,
+        height: height,
+        elmt:   tickDiv
+    };
+}
+
+Timeline.OverviewEventPainter.prototype._createHighlightDiv = function(highlightIndex, dimensions, theme) {
+    if (highlightIndex >= 0) {
+        var doc = this._timeline.getDocument();
+        var eventTheme = theme.event;
+
+        var color = eventTheme.highlightColors[Math.min(highlightIndex, eventTheme.highlightColors.length - 1)];
+
+        var div = doc.createElement("div");
+        div.style.position = "absolute";
+        div.style.overflow = "hidden";
+        div.style.left =    (dimensions.left - 1) + "px";
+        div.style.width =   (dimensions.width + 2) + "px";
+        div.style.top =     (dimensions.top - 1) + "px";
+        div.style.height =  (dimensions.height + 2) + "px";
+        div.style.background = color;
+
+        this._highlightLayer.appendChild(div);
+    }
+};
+
+Timeline.OverviewEventPainter.prototype.showBubble = function(evt) {
+    // not implemented
+};
+/*==================================================
+ *  Compact Event Painter
+ *==================================================
+ */
+
+Timeline.CompactEventPainter = function(params) {
+    this._params = params;
+    this._onSelectListeners = [];
+
+    this._filterMatcher = null;
+    this._highlightMatcher = null;
+    this._frc = null;
+
+    this._eventIdToElmt = {};
+};
+
+Timeline.CompactEventPainter.prototype.getType = function() {
+    return 'compact';
+};
+
+Timeline.CompactEventPainter.prototype.initialize = function(band, timeline) {
+    this._band = band;
+    this._timeline = timeline;
+
+    this._backLayer = null;
+    this._eventLayer = null;
+    this._lineLayer = null;
+    this._highlightLayer = null;
+
+    this._eventIdToElmt = null;
+};
+
+Timeline.CompactEventPainter.prototype.addOnSelectListener = function(listener) {
+    this._onSelectListeners.push(listener);
+};
+
+Timeline.CompactEventPainter.prototype.removeOnSelectListener = function(listener) {
+    for (var i = 0; i < this._onSelectListeners.length; i++) {
+        if (this._onSelectListeners[i] == listener) {
+            this._onSelectListeners.splice(i, 1);
+            break;
+        }
+    }
+};
+
+Timeline.CompactEventPainter.prototype.getFilterMatcher = function() {
+    return this._filterMatcher;
+};
+
+Timeline.CompactEventPainter.prototype.setFilterMatcher = function(filterMatcher) {
+    this._filterMatcher = filterMatcher;
+};
+
+Timeline.CompactEventPainter.prototype.getHighlightMatcher = function() {
+    return this._highlightMatcher;
+};
+
+Timeline.CompactEventPainter.prototype.setHighlightMatcher = function(highlightMatcher) {
+    this._highlightMatcher = highlightMatcher;
+};
+
+Timeline.CompactEventPainter.prototype.paint = function() {
+    var eventSource = this._band.getEventSource();
+    if (eventSource == null) {
+        return;
+    }
+
+    this._eventIdToElmt = {};
+    this._prepareForPainting();
+
+    var metrics = this._computeMetrics();
+    var minDate = this._band.getMinDate();
+    var maxDate = this._band.getMaxDate();
+
+    var filterMatcher = (this._filterMatcher != null) ?
+        this._filterMatcher :
+        function(evt) { return true; };
+
+    var highlightMatcher = (this._highlightMatcher != null) ?
+        this._highlightMatcher :
+        function(evt) { return -1; };
+
+    var iterator = eventSource.getEventIterator(minDate, maxDate);
+
+    var stackConcurrentPreciseInstantEvents = "stackConcurrentPreciseInstantEvents" in this._params && typeof this._params.stackConcurrentPreciseInstantEvents == "object";
+    var collapseConcurrentPreciseInstantEvents = "collapseConcurrentPreciseInstantEvents" in this._params && this._params.collapseConcurrentPreciseInstantEvents;
+    if (collapseConcurrentPreciseInstantEvents || stackConcurrentPreciseInstantEvents) {
+        var bufferedEvents = [];
+        var previousInstantEvent = null;
+
+        while (iterator.hasNext()) {
+            var evt = iterator.next();
+            if (filterMatcher(evt)) {
+                if (!evt.isInstant() || evt.isImprecise()) {
+                    this.paintEvent(evt, metrics, this._params.theme, highlightMatcher(evt));
+                } else if (previousInstantEvent != null &&
+                        previousInstantEvent.getStart().getTime() == evt.getStart().getTime()) {
+                    bufferedEvents[bufferedEvents.length - 1].push(evt);
+                } else {
+                    bufferedEvents.push([ evt ]);
+                    previousInstantEvent = evt;
+                }
+            }
+        }
+
+        for (var i = 0; i < bufferedEvents.length; i++) {
+            var compositeEvents = bufferedEvents[i];
+            if (compositeEvents.length == 1) {
+                this.paintEvent(compositeEvents[0], metrics, this._params.theme, highlightMatcher(evt));
+            } else {
+                var match = -1;
+                for (var j = 0; match < 0 && j < compositeEvents.length; j++) {
+                    match = highlightMatcher(compositeEvents[j]);
+                }
+
+                if (stackConcurrentPreciseInstantEvents) {
+                    this.paintStackedPreciseInstantEvents(compositeEvents, metrics, this._params.theme, match);
+                } else {
+                    this.paintCompositePreciseInstantEvents(compositeEvents, metrics, this._params.theme, match);
+                }
+            }
+        }
+    } else {
+        while (iterator.hasNext()) {
+            var evt = iterator.next();
+            if (filterMatcher(evt)) {
+                this.paintEvent(evt, metrics, this._params.theme, highlightMatcher(evt));
+            }
+        }
+    }
+
+    this._highlightLayer.style.display = "block";
+    this._lineLayer.style.display = "block";
+    this._eventLayer.style.display = "block";
+
+    this._setOrthogonalOffset(metrics);
+};
+
+Timeline.CompactEventPainter.prototype.softPaint = function() {
+    this._setOrthogonalOffset(this._computeMetrics());
+};
+
+Timeline.CompactEventPainter.prototype._setOrthogonalOffset = function(metrics) {
+    var actualViewWidth = 2 * metrics.trackOffset + this._tracks.length * metrics.trackHeight;
+    var minOrthogonalOffset = Math.min(0, this._band.getViewWidth() - actualViewWidth);
+    var orthogonalOffset = Math.max(minOrthogonalOffset, this._band.getViewOrthogonalOffset());
+
+    this._highlightLayer.style.top =
+        this._lineLayer.style.top =
+            this._eventLayer.style.top =
+                orthogonalOffset + "px";
+};
+
+Timeline.CompactEventPainter.prototype._computeMetrics = function() {
+    var theme = this._params.theme;
+    var eventTheme = theme.event;
+
+    var metrics = {
+        trackOffset:            "trackOffset" in this._params ? this._params.trackOffset : 10,
+        trackHeight:            "trackHeight" in this._params ? this._params.trackHeight : 10,
+
+        tapeHeight:             theme.event.tape.height,
+        tapeBottomMargin:       "tapeBottomMargin" in this._params ? this._params.tapeBottomMargin : 2,
+
+        labelBottomMargin:      "labelBottomMargin" in this._params ? this._params.labelBottomMargin : 5,
+        labelRightMargin:       "labelRightMargin" in this._params ? this._params.labelRightMargin : 5,
+
+        defaultIcon:            eventTheme.instant.icon,
+        defaultIconWidth:       eventTheme.instant.iconWidth,
+        defaultIconHeight:      eventTheme.instant.iconHeight,
+
+        customIconWidth:        "iconWidth" in this._params ? this._params.iconWidth : eventTheme.instant.iconWidth,
+        customIconHeight:       "iconHeight" in this._params ? this._params.iconHeight : eventTheme.instant.iconHeight,
+
+        iconLabelGap:           "iconLabelGap" in this._params ? this._params.iconLabelGap : 2,
+        iconBottomMargin:       "iconBottomMargin" in this._params ? this._params.iconBottomMargin : 2
+    };
+    if ("compositeIcon" in this._params) {
+        metrics.compositeIcon = this._params.compositeIcon;
+        metrics.compositeIconWidth = this._params.compositeIconWidth || metrics.customIconWidth;
+        metrics.compositeIconHeight = this._params.compositeIconHeight || metrics.customIconHeight;
+    } else {
+        metrics.compositeIcon = metrics.defaultIcon;
+        metrics.compositeIconWidth = metrics.defaultIconWidth;
+        metrics.compositeIconHeight = metrics.defaultIconHeight;
+    }
+    metrics.defaultStackIcon = "icon" in this._params.stackConcurrentPreciseInstantEvents ?
+        this._params.stackConcurrentPreciseInstantEvents.icon : metrics.defaultIcon;
+    metrics.defaultStackIconWidth = "iconWidth" in this._params.stackConcurrentPreciseInstantEvents ?
+        this._params.stackConcurrentPreciseInstantEvents.iconWidth : metrics.defaultIconWidth;
+    metrics.defaultStackIconHeight = "iconHeight" in this._params.stackConcurrentPreciseInstantEvents ?
+        this._params.stackConcurrentPreciseInstantEvents.iconHeight : metrics.defaultIconHeight;
+
+    return metrics;
+};
+
+Timeline.CompactEventPainter.prototype._prepareForPainting = function() {
+    var band = this._band;
+
+    if (this._backLayer == null) {
+        this._backLayer = this._band.createLayerDiv(0, "timeline-band-events");
+        this._backLayer.style.visibility = "hidden";
+
+        var eventLabelPrototype = document.createElement("span");
+        eventLabelPrototype.className = "timeline-event-label";
+        this._backLayer.appendChild(eventLabelPrototype);
+        this._frc = SimileAjax.Graphics.getFontRenderingContext(eventLabelPrototype);
+    }
+    this._frc.update();
+    this._tracks = [];
+
+    if (this._highlightLayer != null) {
+        band.removeLayerDiv(this._highlightLayer);
+    }
+    this._highlightLayer = band.createLayerDiv(105, "timeline-band-highlights");
+    this._highlightLayer.style.display = "none";
+
+    if (this._lineLayer != null) {
+        band.removeLayerDiv(this._lineLayer);
+    }
+    this._lineLayer = band.createLayerDiv(110, "timeline-band-lines");
+    this._lineLayer.style.display = "none";
+
+    if (this._eventLayer != null) {
+        band.removeLayerDiv(this._eventLayer);
+    }
+    this._eventLayer = band.createLayerDiv(115, "timeline-band-events");
+    this._eventLayer.style.display = "none";
+};
+
+Timeline.CompactEventPainter.prototype.paintEvent = function(evt, metrics, theme, highlightIndex) {
+    if (evt.isInstant()) {
+        this.paintInstantEvent(evt, metrics, theme, highlightIndex);
+    } else {
+        this.paintDurationEvent(evt, metrics, theme, highlightIndex);
+    }
+};
+
+Timeline.CompactEventPainter.prototype.paintInstantEvent = function(evt, metrics, theme, highlightIndex) {
+    if (evt.isImprecise()) {
+        this.paintImpreciseInstantEvent(evt, metrics, theme, highlightIndex);
+    } else {
+        this.paintPreciseInstantEvent(evt, metrics, theme, highlightIndex);
+    }
+}
+
+Timeline.CompactEventPainter.prototype.paintDurationEvent = function(evt, metrics, theme, highlightIndex) {
+    if (evt.isImprecise()) {
+        this.paintImpreciseDurationEvent(evt, metrics, theme, highlightIndex);
+    } else {
+        this.paintPreciseDurationEvent(evt, metrics, theme, highlightIndex);
+    }
+}
+
+Timeline.CompactEventPainter.prototype.paintPreciseInstantEvent = function(evt, metrics, theme, highlightIndex) {
+    var commonData = {
+        tooltip: evt.getProperty("tooltip") || evt.getText()
+    };
+
+    var iconData = {
+        url: evt.getIcon()
+    };
+    if (iconData.url == null) {
+        iconData.url = metrics.defaultIcon;
+        iconData.width = metrics.defaultIconWidth;
+        iconData.height = metrics.defaultIconHeight;
+        iconData.className = "timeline-event-icon-default";
+    } else {
+        iconData.width = evt.getProperty("iconWidth") || metrics.customIconWidth;
+        iconData.height = evt.getProperty("iconHeight") || metrics.customIconHeight;
+    }
+
+    var labelData = {
+        text:       evt.getText(),
+        color:      evt.getTextColor() || evt.getColor(),
+        className:  evt.getClassName()
+    };
+
+    var result = this.paintTapeIconLabel(
+        evt.getStart(),
+        commonData,
+        null, // no tape data
+        iconData,
+        labelData,
+        metrics,
+        theme,
+        highlightIndex
+    );
+
+    var self = this;
+    var clickHandler = function(elmt, domEvt, target) {
+        return self._onClickInstantEvent(result.iconElmtData.elmt, domEvt, evt);
+    };
+    SimileAjax.DOM.registerEvent(result.iconElmtData.elmt, "mousedown", clickHandler);
+    SimileAjax.DOM.registerEvent(result.labelElmtData.elmt, "mousedown", clickHandler);
+
+    this._eventIdToElmt[evt.getID()] = result.iconElmtData.elmt;
+};
+
+Timeline.CompactEventPainter.prototype.paintCompositePreciseInstantEvents = function(events, metrics, theme, highlightIndex) {
+    var evt = events[0];
+
+    var tooltips = [];
+    for (var i = 0; i < events.length; i++) {
+        tooltips.push(events[i].getProperty("tooltip") || events[i].getText());
+    }
+    var commonData = {
+        tooltip: tooltips.join("; ")
+    };
+
+    var iconData = {
+        url: metrics.compositeIcon,
+        width: metrics.compositeIconWidth,
+        height: metrics.compositeIconHeight,
+        className: "timeline-event-icon-composite"
+    };
+
+    var labelData = {
+        text: String.substitute(this._params.compositeEventLabelTemplate, [ events.length ])
+    };
+
+    var result = this.paintTapeIconLabel(
+        evt.getStart(),
+        commonData,
+        null, // no tape data
+        iconData,
+        labelData,
+        metrics,
+        theme,
+        highlightIndex
+    );
+
+    var self = this;
+    var clickHandler = function(elmt, domEvt, target) {
+        return self._onClickMultiplePreciseInstantEvent(result.iconElmtData.elmt, domEvt, events);
+    };
+
+    SimileAjax.DOM.registerEvent(result.iconElmtData.elmt, "mousedown", clickHandler);
+    SimileAjax.DOM.registerEvent(result.labelElmtData.elmt, "mousedown", clickHandler);
+
+    for (var i = 0; i < events.length; i++) {
+        this._eventIdToElmt[events[i].getID()] = result.iconElmtData.elmt;
+    }
+};
+
+Timeline.CompactEventPainter.prototype.paintStackedPreciseInstantEvents = function(events, metrics, theme, highlightIndex) {
+    var limit = "limit" in this._params.stackConcurrentPreciseInstantEvents ?
+        this._params.stackConcurrentPreciseInstantEvents.limit : 10;
+    var moreMessageTemplate = "moreMessageTemplate" in this._params.stackConcurrentPreciseInstantEvents ?
+        this._params.stackConcurrentPreciseInstantEvents.moreMessageTemplate : "%0 More Events";
+    var showMoreMessage = limit <= events.length - 2; // We want at least 2 more events above the limit.
+                                                      // Otherwise we'd need the singular case of "1 More Event"
+
+    var band = this._band;
+    var getPixelOffset = function(date) {
+        return Math.round(band.dateToPixelOffset(date));
+    };
+    var getIconData = function(evt) {
+        var iconData = {
+            url: evt.getIcon()
+        };
+        if (iconData.url == null) {
+            iconData.url = metrics.defaultStackIcon;
+            iconData.width = metrics.defaultStackIconWidth;
+            iconData.height = metrics.defaultStackIconHeight;
+            iconData.className = "timeline-event-icon-stack timeline-event-icon-default";
+        } else {
+            iconData.width = evt.getProperty("iconWidth") || metrics.customIconWidth;
+            iconData.height = evt.getProperty("iconHeight") || metrics.customIconHeight;
+            iconData.className = "timeline-event-icon-stack";
+        }
+        return iconData;
+    };
+
+    var firstIconData = getIconData(events[0]);
+    var horizontalIncrement = 5;
+    var leftIconEdge = 0;
+    var totalLabelWidth = 0;
+    var totalLabelHeight = 0;
+    var totalIconHeight = 0;
+
+    var records = [];
+    for (var i = 0; i < events.length && (!showMoreMessage || i < limit); i++) {
+        var evt = events[i];
+        var text = evt.getText();
+        var iconData = getIconData(evt);
+        var labelSize = this._frc.computeSize(text);
+        var record = {
+            text:       text,
+            iconData:   iconData,
+            labelSize:  labelSize,
+            iconLeft:   firstIconData.width + i * horizontalIncrement - iconData.width
+        };
+        record.labelLeft = firstIconData.width + i * horizontalIncrement + metrics.iconLabelGap;
+        record.top = totalLabelHeight;
+        records.push(record);
+
+        leftIconEdge = Math.min(leftIconEdge, record.iconLeft);
+        totalLabelHeight += labelSize.height;
+        totalLabelWidth = Math.max(totalLabelWidth, record.labelLeft + labelSize.width);
+        totalIconHeight = Math.max(totalIconHeight, record.top + iconData.height);
+    }
+    if (showMoreMessage) {
+        var moreMessage = String.substitute(moreMessageTemplate, [ events.length - limit ]);
+
+        var moreMessageLabelSize = this._frc.computeSize(moreMessage);
+        var moreMessageLabelLeft = firstIconData.width + (limit - 1) * horizontalIncrement + metrics.iconLabelGap;
+        var moreMessageLabelTop = totalLabelHeight;
+
+        totalLabelHeight += moreMessageLabelSize.height;
+        totalLabelWidth = Math.max(totalLabelWidth, moreMessageLabelLeft + moreMessageLabelSize.width);
+    }
+    totalLabelWidth += metrics.labelRightMargin;
+    totalLabelHeight += metrics.labelBottomMargin;
+    totalIconHeight += metrics.iconBottomMargin;
+
+    var anchorPixel = getPixelOffset(events[0].getStart());
+    var newTracks = [];
+
+    var trackCount = Math.ceil(Math.max(totalIconHeight, totalLabelHeight) / metrics.trackHeight);
+    var rightIconEdge = firstIconData.width + (events.length - 1) * horizontalIncrement;
+    for (var i = 0; i < trackCount; i++) {
+        newTracks.push({ start: leftIconEdge, end: rightIconEdge });
+    }
+    var labelTrackCount = Math.ceil(totalLabelHeight / metrics.trackHeight);
+    for (var i = 0; i < labelTrackCount; i++) {
+        var track = newTracks[i];
+        track.end = Math.max(track.end, totalLabelWidth);
+    }
+
+    var firstTrack = this._fitTracks(anchorPixel, newTracks);
+    var verticalPixelOffset = firstTrack * metrics.trackHeight + metrics.trackOffset;
+
+    var iconStackDiv = this._timeline.getDocument().createElement("div");
+    iconStackDiv.className = 'timeline-event-icon-stack';
+    iconStackDiv.style.position = "absolute";
+    iconStackDiv.style.overflow = "visible";
+    iconStackDiv.style.left = anchorPixel + "px";
+    iconStackDiv.style.top = verticalPixelOffset + "px";
+    iconStackDiv.style.width = rightIconEdge + "px";
+    iconStackDiv.style.height = totalIconHeight + "px";
+    iconStackDiv.innerHTML = "<div style='position: relative'></div>";
+    this._eventLayer.appendChild(iconStackDiv);
+
+    var self = this;
+    var onMouseOver = function(domEvt) {
+        try {
+            var n = parseInt(this.getAttribute("index"));
+            var childNodes = iconStackDiv.firstChild.childNodes;
+            for (var i = 0; i < childNodes.length; i++) {
+                var child = childNodes[i];
+                if (i == n) {
+                    child.style.zIndex = childNodes.length;
+                } else {
+                    child.style.zIndex = childNodes.length - i;
+                }
+            }
+        } catch (e) {
+        }
+    };
+    var paintEvent = function(index) {
+        var record = records[index];
+        var evt = events[index];
+        var tooltip = evt.getProperty("tooltip") || evt.getText();
+
+        var labelElmtData = self._paintEventLabel(
+            { tooltip: tooltip },
+            { text: record.text },
+            anchorPixel + record.labelLeft,
+            verticalPixelOffset + record.top,
+            record.labelSize.width,
+            record.labelSize.height,
+            theme
+        );
+        labelElmtData.elmt.setAttribute("index", index);
+        labelElmtData.elmt.onmouseover = onMouseOver;
+
+        var img = SimileAjax.Graphics.createTranslucentImage(record.iconData.url);
+        var iconDiv = self._timeline.getDocument().createElement("div");
+        iconDiv.className = 'timeline-event-icon' + ("className" in record.iconData ? (" " + record.iconData.className) : "");
+        iconDiv.style.left = record.iconLeft + "px";
+        iconDiv.style.top = record.top + "px";
+        iconDiv.style.zIndex = (records.length - index);
+        iconDiv.appendChild(img);
+        iconDiv.setAttribute("index", index);
+        iconDiv.onmouseover = onMouseOver;
+
+        iconStackDiv.firstChild.appendChild(iconDiv);
+
+        var clickHandler = function(elmt, domEvt, target) {
+            return self._onClickInstantEvent(labelElmtData.elmt, domEvt, evt);
+        };
+
+        SimileAjax.DOM.registerEvent(iconDiv, "mousedown", clickHandler);
+        SimileAjax.DOM.registerEvent(labelElmtData.elmt, "mousedown", clickHandler);
+
+        self._eventIdToElmt[evt.getID()] = iconDiv;
+    };
+    for (var i = 0; i < records.length; i++) {
+        paintEvent(i);
+    }
+
+    if (showMoreMessage) {
+        var moreEvents = events.slice(limit);
+        var moreMessageLabelElmtData = this._paintEventLabel(
+            { tooltip: moreMessage },
+            { text: moreMessage },
+            anchorPixel + moreMessageLabelLeft,
+            verticalPixelOffset + moreMessageLabelTop,
+            moreMessageLabelSize.width,
+            moreMessageLabelSize.height,
+            theme
+        );
+
+        var moreMessageClickHandler = function(elmt, domEvt, target) {
+            return self._onClickMultiplePreciseInstantEvent(moreMessageLabelElmtData.elmt, domEvt, moreEvents);
+        };
+        SimileAjax.DOM.registerEvent(moreMessageLabelElmtData.elmt, "mousedown", moreMessageClickHandler);
+
+        for (var i = 0; i < moreEvents.length; i++) {
+            this._eventIdToElmt[moreEvents[i].getID()] = moreMessageLabelElmtData.elmt;
+        }
+    }
+    //this._createHighlightDiv(highlightIndex, iconElmtData, theme);
+};
+
+Timeline.CompactEventPainter.prototype.paintImpreciseInstantEvent = function(evt, metrics, theme, highlightIndex) {
+    var commonData = {
+        tooltip: evt.getProperty("tooltip") || evt.getText()
+    };
+
+    var tapeData = {
+        start:          evt.getStart(),
+        end:            evt.getEnd(),
+        latestStart:    evt.getLatestStart(),
+        earliestEnd:    evt.getEarliestEnd(),
+        isInstant:      true
+    };
+
+    var iconData = {
+        url: evt.getIcon()
+    };
+    if (iconData.url == null) {
+        iconData = null;
+    } else {
+        iconData.width = evt.getProperty("iconWidth") || metrics.customIconWidth;
+        iconData.height = evt.getProperty("iconHeight") || metrics.customIconHeight;
+    }
+
+    var labelData = {
+        text:       evt.getText(),
+        color:      evt.getTextColor() || evt.getColor(),
+        className:  evt.getClassName()
+    };
+
+    var result = this.paintTapeIconLabel(
+        evt.getStart(),
+        commonData,
+        tapeData, // no tape data
+        iconData,
+        labelData,
+        metrics,
+        theme,
+        highlightIndex
+    );
+
+    var self = this;
+    var clickHandler = iconData != null ?
+        function(elmt, domEvt, target) {
+            return self._onClickInstantEvent(result.iconElmtData.elmt, domEvt, evt);
+        } :
+        function(elmt, domEvt, target) {
+            return self._onClickInstantEvent(result.labelElmtData.elmt, domEvt, evt);
+        };
+
+    SimileAjax.DOM.registerEvent(result.labelElmtData.elmt, "mousedown", clickHandler);
+    SimileAjax.DOM.registerEvent(result.impreciseTapeElmtData.elmt, "mousedown", clickHandler);
+
+    if (iconData != null) {
+        SimileAjax.DOM.registerEvent(result.iconElmtData.elmt, "mousedown", clickHandler);
+        this._eventIdToElmt[evt.getID()] = result.iconElmtData.elmt;
+    } else {
+        this._eventIdToElmt[evt.getID()] = result.labelElmtData.elmt;
+    }
+};
+
+Timeline.CompactEventPainter.prototype.paintPreciseDurationEvent = function(evt, metrics, theme, highlightIndex) {
+    var commonData = {
+        tooltip: evt.getProperty("tooltip") || evt.getText()
+    };
+
+    var tapeData = {
+        start:          evt.getStart(),
+        end:            evt.getEnd(),
+        isInstant:      false
+    };
+
+    var iconData = {
+        url: evt.getIcon()
+    };
+    if (iconData.url == null) {
+        iconData = null;
+    } else {
+        iconData.width = evt.getProperty("iconWidth") || metrics.customIconWidth;
+        iconData.height = evt.getProperty("iconHeight") || metrics.customIconHeight;
+    }
+
+    var labelData = {
+        text:       evt.getText(),
+        color:      evt.getTextColor() || evt.getColor(),
+        className:  evt.getClassName()
+    };
+
+    var result = this.paintTapeIconLabel(
+        evt.getLatestStart(),
+        commonData,
+        tapeData, // no tape data
+        iconData,
+        labelData,
+        metrics,
+        theme,
+        highlightIndex
+    );
+
+    var self = this;
+    var clickHandler = iconData != null ?
+        function(elmt, domEvt, target) {
+            return self._onClickInstantEvent(result.iconElmtData.elmt, domEvt, evt);
+        } :
+        function(elmt, domEvt, target) {
+            return self._onClickInstantEvent(result.labelElmtData.elmt, domEvt, evt);
+        };
+
+    SimileAjax.DOM.registerEvent(result.labelElmtData.elmt, "mousedown", clickHandler);
+    SimileAjax.DOM.registerEvent(result.tapeElmtData.elmt, "mousedown", clickHandler);
+
+    if (iconData != null) {
+        SimileAjax.DOM.registerEvent(result.iconElmtData.elmt, "mousedown", clickHandler);
+        this._eventIdToElmt[evt.getID()] = result.iconElmtData.elmt;
+    } else {
+        this._eventIdToElmt[evt.getID()] = result.labelElmtData.elmt;
+    }
+};
+
+Timeline.CompactEventPainter.prototype.paintImpreciseDurationEvent = function(evt, metrics, theme, highlightIndex) {
+    var commonData = {
+        tooltip: evt.getProperty("tooltip") || evt.getText()
+    };
+
+    var tapeData = {
+        start:          evt.getStart(),
+        end:            evt.getEnd(),
+        latestStart:    evt.getLatestStart(),
+        earliestEnd:    evt.getEarliestEnd(),
+        isInstant:      false
+    };
+
+    var iconData = {
+        url: evt.getIcon()
+    };
+    if (iconData.url == null) {
+        iconData = null;
+    } else {
+        iconData.width = evt.getProperty("iconWidth") || metrics.customIconWidth;
+        iconData.height = evt.getProperty("iconHeight") || metrics.customIconHeight;
+    }
+
+    var labelData = {
+        text:       evt.getText(),
+        color:      evt.getTextColor() || evt.getColor(),
+        className:  evt.getClassName()
+    };
+
+    var result = this.paintTapeIconLabel(
+        evt.getLatestStart(),
+        commonData,
+        tapeData, // no tape data
+        iconData,
+        labelData,
+        metrics,
+        theme,
+        highlightIndex
+    );
+
+    var self = this;
+    var clickHandler = iconData != null ?
+        function(elmt, domEvt, target) {
+            return self._onClickInstantEvent(result.iconElmtData.elmt, domEvt, evt);
+        } :
+        function(elmt, domEvt, target) {
+            return self._onClickInstantEvent(result.labelElmtData.elmt, domEvt, evt);
+        };
+
+    SimileAjax.DOM.registerEvent(result.labelElmtData.elmt, "mousedown", clickHandler);
+    SimileAjax.DOM.registerEvent(result.tapeElmtData.elmt, "mousedown", clickHandler);
+
+    if (iconData != null) {
+        SimileAjax.DOM.registerEvent(result.iconElmtData.elmt, "mousedown", clickHandler);
+        this._eventIdToElmt[evt.getID()] = result.iconElmtData.elmt;
+    } else {
+        this._eventIdToElmt[evt.getID()] = result.labelElmtData.elmt;
+    }
+};
+
+Timeline.CompactEventPainter.prototype.paintTapeIconLabel = function(
+    anchorDate,
+    commonData,
+    tapeData,
+    iconData,
+    labelData,
+    metrics,
+    theme,
+    highlightIndex
+) {
+    var band = this._band;
+    var getPixelOffset = function(date) {
+        return Math.round(band.dateToPixelOffset(date));
+    };
+
+    var anchorPixel = getPixelOffset(anchorDate);
+    var newTracks = [];
+
+    var tapeHeightOccupied = 0;         // how many pixels (vertically) the tape occupies, including bottom margin
+    var tapeTrackCount = 0;             // how many tracks the tape takes up, usually just 1
+    var tapeLastTrackExtraSpace = 0;    // on the last track that the tape occupies, how many pixels are left (for icon and label to occupy as well)
+    if (tapeData != null) {
+        tapeHeightOccupied = metrics.tapeHeight + metrics.tapeBottomMargin;
+        tapeTrackCount = Math.ceil(metrics.tapeHeight / metrics.trackHeight);
+
+        var tapeEndPixelOffset = getPixelOffset(tapeData.end) - anchorPixel;
+        var tapeStartPixelOffset = getPixelOffset(tapeData.start) - anchorPixel;
+
+        for (var t = 0; t < tapeTrackCount; t++) {
+            newTracks.push({ start: tapeStartPixelOffset, end: tapeEndPixelOffset });
+        }
+
+        tapeLastTrackExtraSpace = metrics.trackHeight - (tapeHeightOccupied % metrics.tapeHeight);
+    }
+
+    var iconStartPixelOffset = 0;        // where the icon starts compared to the anchor pixel;
+                                         // this can be negative if the icon is center-aligned around the anchor
+    var iconHorizontalSpaceOccupied = 0; // how many pixels the icon take up from the anchor pixel,
+                                         // including the gap between the icon and the label
+    if (iconData != null) {
+        if ("iconAlign" in iconData && iconData.iconAlign == "center") {
+            iconStartPixelOffset = -Math.floor(iconData.width / 2);
+        }
+        iconHorizontalSpaceOccupied = iconStartPixelOffset + iconData.width + metrics.iconLabelGap;
+
+        if (tapeTrackCount > 0) {
+            newTracks[tapeTrackCount - 1].end = Math.max(newTracks[tapeTrackCount - 1].end, iconHorizontalSpaceOccupied);
+        }
+
+        var iconHeight = iconData.height + metrics.iconBottomMargin + tapeLastTrackExtraSpace;
+        while (iconHeight > 0) {
+            newTracks.push({ start: iconStartPixelOffset, end: iconHorizontalSpaceOccupied });
+            iconHeight -= metrics.trackHeight;
+        }
+    }
+
+    var text = labelData.text;
+    var labelSize = this._frc.computeSize(text);
+    var labelHeight = labelSize.height + metrics.labelBottomMargin + tapeLastTrackExtraSpace;
+    var labelEndPixelOffset = iconHorizontalSpaceOccupied + labelSize.width + metrics.labelRightMargin;
+    if (tapeTrackCount > 0) {
+        newTracks[tapeTrackCount - 1].end = Math.max(newTracks[tapeTrackCount - 1].end, labelEndPixelOffset);
+    }
+    for (var i = 0; labelHeight > 0; i++) {
+        if (tapeTrackCount + i < newTracks.length) {
+            var track = newTracks[tapeTrackCount + i];
+            track.end = labelEndPixelOffset;
+        } else {
+            newTracks.push({ start: 0, end: labelEndPixelOffset });
+        }
+        labelHeight -= metrics.trackHeight;
+    }
+
+    /*
+     *  Try to fit the new track on top of the existing tracks, then
+     *  render the various elements.
+     */
+    var firstTrack = this._fitTracks(anchorPixel, newTracks);
+    var verticalPixelOffset = firstTrack * metrics.trackHeight + metrics.trackOffset;
+    var result = {};
+
+    result.labelElmtData = this._paintEventLabel(
+        commonData,
+        labelData,
+        anchorPixel + iconHorizontalSpaceOccupied,
+        verticalPixelOffset + tapeHeightOccupied,
+        labelSize.width,
+        labelSize.height,
+        theme
+    );
+
+    if (tapeData != null) {
+        if ("latestStart" in tapeData || "earliestEnd" in tapeData) {
+            result.impreciseTapeElmtData = this._paintEventTape(
+                commonData,
+                tapeData,
+                metrics.tapeHeight,
+                verticalPixelOffset,
+                getPixelOffset(tapeData.start),
+                getPixelOffset(tapeData.end),
+                theme.event.duration.impreciseColor,
+                theme.event.duration.impreciseOpacity,
+                metrics,
+                theme
+            );
+        }
+        if (!tapeData.isInstant && "start" in tapeData && "end" in tapeData) {
+            result.tapeElmtData = this._paintEventTape(
+                commonData,
+                tapeData,
+                metrics.tapeHeight,
+                verticalPixelOffset,
+                anchorPixel,
+                getPixelOffset("earliestEnd" in tapeData ? tapeData.earliestEnd : tapeData.end),
+                tapeData.color,
+                100,
+                metrics,
+                theme
+            );
+        }
+    }
+
+    if (iconData != null) {
+        result.iconElmtData = this._paintEventIcon(
+            commonData,
+            iconData,
+            verticalPixelOffset + tapeHeightOccupied,
+            anchorPixel + iconStartPixelOffset,
+            metrics,
+            theme
+        );
+    }
+    //this._createHighlightDiv(highlightIndex, iconElmtData, theme);
+
+    return result;
+};
+
+Timeline.CompactEventPainter.prototype._fitTracks = function(anchorPixel, newTracks) {
+    var firstTrack;
+    for (firstTrack = 0; firstTrack < this._tracks.length; firstTrack++) {
+        var fit = true;
+        for (var j = 0; j < newTracks.length && (firstTrack + j) < this._tracks.length; j++) {
+            var existingTrack = this._tracks[firstTrack + j];
+            var newTrack = newTracks[j];
+            if (anchorPixel + newTrack.start < existingTrack) {
+                fit = false;
+                break;
+            }
+        }
+
+        if (fit) {
+            break;
+        }
+    }
+    for (var i = 0; i < newTracks.length; i++) {
+        this._tracks[firstTrack + i] = anchorPixel + newTracks[i].end;
+    }
+
+    return firstTrack;
+};
+
+
+Timeline.CompactEventPainter.prototype._paintEventIcon = function(commonData, iconData, top, left, metrics, theme) {
+    var img = SimileAjax.Graphics.createTranslucentImage(iconData.url);
+    var iconDiv = this._timeline.getDocument().createElement("div");
+    iconDiv.className = 'timeline-event-icon' + ("className" in iconData ? (" " + iconData.className) : "");
+    iconDiv.style.left = left + "px";
+    iconDiv.style.top = top + "px";
+    iconDiv.appendChild(img);
+
+    if ("tooltip" in commonData && typeof commonData.tooltip == "string") {
+        iconDiv.title = commonData.tooltip;
+    }
+
+    this._eventLayer.appendChild(iconDiv);
+
+    return {
+        left:   left,
+        top:    top,
+        width:  metrics.iconWidth,
+        height: metrics.iconHeight,
+        elmt:   iconDiv
+    };
+};
+
+Timeline.CompactEventPainter.prototype._paintEventLabel = function(commonData, labelData, left, top, width, height, theme) {
+    var doc = this._timeline.getDocument();
+
+    var labelDiv = doc.createElement("div");
+    labelDiv.className = 'timeline-event-label';
+
+    labelDiv.style.left = left + "px";
+    labelDiv.style.width = (width + 1) + "px";
+    labelDiv.style.top = top + "px";
+    labelDiv.innerHTML = labelData.text;
+
+    if ("tooltip" in commonData && typeof commonData.tooltip == "string") {
+        labelDiv.title = commonData.tooltip;
+    }
+    if ("color" in labelData && typeof labelData.color == "string") {
+        labelDiv.style.color = labelData.color;
+    }
+    if ("className" in labelData && typeof labelData.className == "string") {
+        labelDiv.className += ' ' + labelData.className;
+    }
+
+    this._eventLayer.appendChild(labelDiv);
+
+    return {
+        left:   left,
+        top:    top,
+        width:  width,
+        height: height,
+        elmt:   labelDiv
+    };
+};
+
+Timeline.CompactEventPainter.prototype._paintEventTape = function(
+    commonData, tapeData, height, top, startPixel, endPixel, color, opacity, metrics, theme) {
+
+    var width = endPixel - startPixel;
+
+    var tapeDiv = this._timeline.getDocument().createElement("div");
+    tapeDiv.className = "timeline-event-tape"
+
+    tapeDiv.style.left = startPixel + "px";
+    tapeDiv.style.top = top + "px";
+    tapeDiv.style.width = width + "px";
+    tapeDiv.style.height = height + "px";
+
+    if ("tooltip" in commonData && typeof commonData.tooltip == "string") {
+        tapeDiv.title = commonData.tooltip;
+    }
+    if (color != null && typeof tapeData.color == "string") {
+        tapeDiv.style.backgroundColor = color;
+    }
+
+    if ("backgroundImage" in tapeData && typeof tapeData.backgroundImage == "string") {
+        tapeDiv.style.backgroundImage = "url(" + backgroundImage + ")";
+        tapeDiv.style.backgroundRepeat =
+            ("backgroundRepeat" in tapeData && typeof tapeData.backgroundRepeat == "string")
+                ? tapeData.backgroundRepeat : 'repeat';
+    }
+
+    SimileAjax.Graphics.setOpacity(tapeDiv, opacity);
+
+    if ("className" in tapeData && typeof tapeData.className == "string") {
+        tapeDiv.className += ' ' + tapeData.className;
+    }
+
+    this._eventLayer.appendChild(tapeDiv);
+
+    return {
+        left:   startPixel,
+        top:    top,
+        width:  width,
+        height: height,
+        elmt:   tapeDiv
+    };
+}
+
+Timeline.CompactEventPainter.prototype._createHighlightDiv = function(highlightIndex, dimensions, theme) {
+    if (highlightIndex >= 0) {
+        var doc = this._timeline.getDocument();
+        var eventTheme = theme.event;
+
+        var color = eventTheme.highlightColors[Math.min(highlightIndex, eventTheme.highlightColors.length - 1)];
+
+        var div = doc.createElement("div");
+        div.style.position = "absolute";
+        div.style.overflow = "hidden";
+        div.style.left =    (dimensions.left - 2) + "px";
+        div.style.width =   (dimensions.width + 4) + "px";
+        div.style.top =     (dimensions.top - 2) + "px";
+        div.style.height =  (dimensions.height + 4) + "px";
+//        div.style.background = color;
+
+        this._highlightLayer.appendChild(div);
+    }
+};
+
+Timeline.CompactEventPainter.prototype._onClickMultiplePreciseInstantEvent = function(icon, domEvt, events) {
+    var c = SimileAjax.DOM.getPageCoordinates(icon);
+    this._showBubble(
+        c.left + Math.ceil(icon.offsetWidth / 2),
+        c.top + Math.ceil(icon.offsetHeight / 2),
+        events
+    );
+
+    var ids = [];
+    for (var i = 0; i < events.length; i++) {
+        ids.push(events[i].getID());
+    }
+    this._fireOnSelect(ids);
+
+    domEvt.cancelBubble = true;
+    SimileAjax.DOM.cancelEvent(domEvt);
+
+    return false;
+};
+
+Timeline.CompactEventPainter.prototype._onClickInstantEvent = function(icon, domEvt, evt) {
+    var c = SimileAjax.DOM.getPageCoordinates(icon);
+    this._showBubble(
+        c.left + Math.ceil(icon.offsetWidth / 2),
+        c.top + Math.ceil(icon.offsetHeight / 2),
+        [evt]
+    );
+    this._fireOnSelect([evt.getID()]);
+
+    domEvt.cancelBubble = true;
+    SimileAjax.DOM.cancelEvent(domEvt);
+    return false;
+};
+
+Timeline.CompactEventPainter.prototype._onClickDurationEvent = function(target, domEvt, evt) {
+    if ("pageX" in domEvt) {
+        var x = domEvt.pageX;
+        var y = domEvt.pageY;
+    } else {
+        var c = SimileAjax.DOM.getPageCoordinates(target);
+        var x = domEvt.offsetX + c.left;
+        var y = domEvt.offsetY + c.top;
+    }
+    this._showBubble(x, y, [evt]);
+    this._fireOnSelect([evt.getID()]);
+
+    domEvt.cancelBubble = true;
+    SimileAjax.DOM.cancelEvent(domEvt);
+    return false;
+};
+
+Timeline.CompactEventPainter.prototype.showBubble = function(evt) {
+    var elmt = this._eventIdToElmt[evt.getID()];
+    if (elmt) {
+        var c = SimileAjax.DOM.getPageCoordinates(elmt);
+        this._showBubble(c.left + elmt.offsetWidth / 2, c.top + elmt.offsetHeight / 2, [evt]);
+    }
+};
+
+Timeline.CompactEventPainter.prototype._showBubble = function(x, y, evts) {
+    var div = document.createElement("div");
+
+    evts = ("fillInfoBubble" in evts) ? [evts] : evts;
+    for (var i = 0; i < evts.length; i++) {
+        var div2 = document.createElement("div");
+        div.appendChild(div2);
+
+        evts[i].fillInfoBubble(div2, this._params.theme, this._band.getLabeller());
+    }
+
+    SimileAjax.WindowManager.cancelPopups();
+    SimileAjax.Graphics.createBubbleForContentAndPoint(div, x, y, this._params.theme.event.bubble.width);
+};
+
+Timeline.CompactEventPainter.prototype._fireOnSelect = function(eventIDs) {
+    for (var i = 0; i < this._onSelectListeners.length; i++) {
+        this._onSelectListeners[i](eventIDs);
+    }
+};
+/*==================================================
+ *  Span Highlight Decorator
+ *==================================================
+ */
+
+Timeline.SpanHighlightDecorator = function(params) {
+    // When evaluating params, test against null. Not "p in params". Testing against
+    // null enables caller to explicitly request the default. Testing against "in" means
+    // that the param has to be ommitted to get the default.
+    this._unit = params.unit != null ? params.unit : SimileAjax.NativeDateUnit;
+    this._startDate = (typeof params.startDate == "string") ?
+        this._unit.parseFromObject(params.startDate) : params.startDate;
+    this._endDate = (typeof params.endDate == "string") ?
+        this._unit.parseFromObject(params.endDate) : params.endDate;
+    this._startLabel = params.startLabel != null ? params.startLabel : ""; // not null!
+    this._endLabel   = params.endLabel   != null ? params.endLabel   : ""; // not null!
+    this._color = params.color;
+    this._cssClass = params.cssClass != null ? params.cssClass : null;
+    this._opacity = params.opacity != null ? params.opacity : 100;
+         // Default z is 10, behind everything but background grid.
+         // If inFront, then place just behind events, in front of everything else
+    this._zIndex = (params.inFront != null && params.inFront) ? 113 : 10;
+};
+
+Timeline.SpanHighlightDecorator.prototype.initialize = function(band, timeline) {
+    this._band = band;
+    this._timeline = timeline;
+
+    this._layerDiv = null;
+};
+
+Timeline.SpanHighlightDecorator.prototype.paint = function() {
+    if (this._layerDiv != null) {
+        this._band.removeLayerDiv(this._layerDiv);
+    }
+    this._layerDiv = this._band.createLayerDiv(this._zIndex);
+    this._layerDiv.setAttribute("name", "span-highlight-decorator"); // for debugging
+    this._layerDiv.style.display = "none";
+
+    var minDate = this._band.getMinDate();
+    var maxDate = this._band.getMaxDate();
+
+    if (this._unit.compare(this._startDate, maxDate) < 0 &&
+        this._unit.compare(this._endDate, minDate) > 0) {
+
+        minDate = this._unit.later(minDate, this._startDate);
+        maxDate = this._unit.earlier(maxDate, this._endDate);
+
+        var minPixel = this._band.dateToPixelOffset(minDate);
+        var maxPixel = this._band.dateToPixelOffset(maxDate);
+
+        var doc = this._timeline.getDocument();
+
+        var createTable = function() {
+            var table = doc.createElement("table");
+            table.insertRow(0).insertCell(0);
+            return table;
+        };
+
+        var div = doc.createElement("div");
+        div.className='timeline-highlight-decorator'
+        if(this._cssClass) {
+        	  div.className += ' ' + this._cssClass;
+        }
+        if(this._color != null) {
+        	  div.style.backgroundColor = this._color;
+        }
+        if (this._opacity < 100) {
+            SimileAjax.Graphics.setOpacity(div, this._opacity);
+        }
+        this._layerDiv.appendChild(div);
+
+        var tableStartLabel = createTable();
+        tableStartLabel.className = 'timeline-highlight-label timeline-highlight-label-start'
+        var tdStart =  tableStartLabel.rows[0].cells[0]
+        tdStart.innerHTML = this._startLabel;
+        if (this._cssClass) {
+        	  tdStart.className = 'label_' + this._cssClass;
+        }
+        this._layerDiv.appendChild(tableStartLabel);
+
+        var tableEndLabel = createTable();
+        tableEndLabel.className = 'timeline-highlight-label timeline-highlight-label-end'
+        var tdEnd = tableEndLabel.rows[0].cells[0]
+        tdEnd.innerHTML = this._endLabel;
+        if (this._cssClass) {
+        	   tdEnd.className = 'label_' + this._cssClass;
+        }
+        this._layerDiv.appendChild(tableEndLabel);
+
+        if (this._timeline.isHorizontal()){
+            div.style.left = minPixel + "px";
+            div.style.width = (maxPixel - minPixel) + "px";
+
+            tableStartLabel.style.right = (this._band.getTotalViewLength() - minPixel) + "px";
+            tableStartLabel.style.width = (this._startLabel.length) + "em";
+
+            tableEndLabel.style.left = maxPixel + "px";
+            tableEndLabel.style.width = (this._endLabel.length) + "em";
+
+        } else {
+            div.style.top = minPixel + "px";
+            div.style.height = (maxPixel - minPixel) + "px";
+
+            tableStartLabel.style.bottom = minPixel + "px";
+            tableStartLabel.style.height = "1.5px";
+
+            tableEndLabel.style.top = maxPixel + "px";
+            tableEndLabel.style.height = "1.5px";
+        }
+    }
+    this._layerDiv.style.display = "block";
+};
+
+Timeline.SpanHighlightDecorator.prototype.softPaint = function() {
+};
+
+/*==================================================
+ *  Point Highlight Decorator
+ *==================================================
+ */
+
+Timeline.PointHighlightDecorator = function(params) {
+    this._unit = params.unit != null ? params.unit : SimileAjax.NativeDateUnit;
+    this._date = (typeof params.date == "string") ?
+        this._unit.parseFromObject(params.date) : params.date;
+    this._width = params.width != null ? params.width : 10;
+      // Since the width is used to calculate placements (see minPixel, below), we
+      // specify width here, not in css.
+    this._color = params.color;
+    this._cssClass = params.cssClass != null ? params.cssClass : '';
+    this._opacity = params.opacity != null ? params.opacity : 100;
+};
+
+Timeline.PointHighlightDecorator.prototype.initialize = function(band, timeline) {
+    this._band = band;
+    this._timeline = timeline;
+    this._layerDiv = null;
+};
+
+Timeline.PointHighlightDecorator.prototype.paint = function() {
+    if (this._layerDiv != null) {
+        this._band.removeLayerDiv(this._layerDiv);
+    }
+    this._layerDiv = this._band.createLayerDiv(10);
+    this._layerDiv.setAttribute("name", "span-highlight-decorator"); // for debugging
+    this._layerDiv.style.display = "none";
+
+    var minDate = this._band.getMinDate();
+    var maxDate = this._band.getMaxDate();
+
+    if (this._unit.compare(this._date, maxDate) < 0 &&
+        this._unit.compare(this._date, minDate) > 0) {
+
+        var pixel = this._band.dateToPixelOffset(this._date);
+        var minPixel = pixel - Math.round(this._width / 2);
+
+        var doc = this._timeline.getDocument();
+
+        var div = doc.createElement("div");
+        div.className='timeline-highlight-point-decorator';
+        div.className += ' ' + this._cssClass;
+
+        if(this._color != null) {
+        	  div.style.backgroundColor = this._color;
+        }
+        if (this._opacity < 100) {
+            SimileAjax.Graphics.setOpacity(div, this._opacity);
+        }
+        this._layerDiv.appendChild(div);
+
+        if (this._timeline.isHorizontal()) {
+            div.style.left = minPixel + "px";
+            div.style.width = this._width;
+        } else {
+            div.style.top = minPixel + "px";
+            div.style.height = this._width;
+        }
+    }
+    this._layerDiv.style.display = "block";
+};
+
+Timeline.PointHighlightDecorator.prototype.softPaint = function() {
+};
+/*==================================================
+ *  Default Unit
+ *==================================================
+ */
+
+Timeline.NativeDateUnit = new Object();
+
+Timeline.NativeDateUnit.createLabeller = function(locale, timeZone) {
+    return new Timeline.GregorianDateLabeller(locale, timeZone);
+};
+
+Timeline.NativeDateUnit.makeDefaultValue = function() {
+    return new Date();
+};
+
+Timeline.NativeDateUnit.cloneValue = function(v) {
+    return new Date(v.getTime());
+};
+
+Timeline.NativeDateUnit.getParser = function(format) {
+    if (typeof format == "string") {
+        format = format.toLowerCase();
+    }
+    return (format == "iso8601" || format == "iso 8601") ?
+        Timeline.DateTime.parseIso8601DateTime : 
+        Timeline.DateTime.parseGregorianDateTime;
+};
+
+Timeline.NativeDateUnit.parseFromObject = function(o) {
+    return Timeline.DateTime.parseGregorianDateTime(o);
+};
+
+Timeline.NativeDateUnit.toNumber = function(v) {
+    return v.getTime();
+};
+
+Timeline.NativeDateUnit.fromNumber = function(n) {
+    return new Date(n);
+};
+
+Timeline.NativeDateUnit.compare = function(v1, v2) {
+    var n1, n2;
+    if (typeof v1 == "object") {
+        n1 = v1.getTime();
+    } else {
+        n1 = Number(v1);
+    }
+    if (typeof v2 == "object") {
+        n2 = v2.getTime();
+    } else {
+        n2 = Number(v2);
+    }
+
+    return n1 - n2;
+};
+
+Timeline.NativeDateUnit.earlier = function(v1, v2) {
+    return Timeline.NativeDateUnit.compare(v1, v2) < 0 ? v1 : v2;
+};
+
+Timeline.NativeDateUnit.later = function(v1, v2) {
+    return Timeline.NativeDateUnit.compare(v1, v2) > 0 ? v1 : v2;
+};
+
+Timeline.NativeDateUnit.change = function(v, n) {
+    return new Date(v.getTime() + n);
+};
+
+/*==================================================
+ *  Common localization strings
+ *==================================================
+ */
+
+Timeline.strings["fr"] = {
+    wikiLinkLabel:  "Discute"
+};
+
+/*==================================================
+ *  Localization of labellers.js
+ *==================================================
+ */
+
+Timeline.GregorianDateLabeller.monthNames["fr"] = [
+    "jan", "fev", "mar", "avr", "mai", "jui", "jui", "aou", "sep", "oct", "nov", "dec"
+];
+/*==================================================
+ *  Common localization strings
+ *==================================================
+ */
+
+Timeline.strings["en"] = {
+    wikiLinkLabel:  "Discuss"
+};
+
+/*==================================================
+ *  Localization of labellers.js
+ *==================================================
+ */
+
+Timeline.GregorianDateLabeller.monthNames["en"] = [
+    "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+];
+
+Timeline.GregorianDateLabeller.dayNames["en"] = [
+    "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
+];
--- a/web/data/cubicweb.timeline-ext.js	Wed Apr 01 17:25:40 2009 +0200
+++ b/web/data/cubicweb.timeline-ext.js	Wed Apr 01 17:26:36 2009 +0200
@@ -9,7 +9,7 @@
 /* provide our own custom date parser since the default
  * one only understands iso8601 and gregorian dates
  */
-Timeline.NativeDateUnit.getParser = function(format) {
+SimileAjax.NativeDateUnit.getParser = Timeline.NativeDateUnit.getParser = function(format) {
     if (typeof format == "string") {
 	if (format.indexOf('%') != -1) {
 	    return function(datestring) {
--- a/web/data/timeline-bundle.css	Wed Apr 01 17:25:40 2009 +0200
+++ b/web/data/timeline-bundle.css	Wed Apr 01 17:26:36 2009 +0200
@@ -1,171 +1,175 @@
-
-
-/*------------------- Horizontal / Vertical lines ----------------*/
-
-/* style for ethers */
-.timeline-ether-lines{border-color:#666; border-style:dotted; position:absolute;}
-
-.timeline-horizontal .timeline-ether-lines{border-width:0 0 0 1px; height:100%; top: 0; width: 1px;}
-
-.timeline-vertical .timeline-ether-lines{border-width:1px 0 0; height:1px; left: 0; width: 100%;}
-
-
-
-
-/*---------------- Weekends ---------------------------*/
-
-.timeline-ether-weekends{
-	position:absolute;
-	background-color:#FFFFE0;
-}
-
-.timeline-vertical .timeline-ether-weekends{left:0;width:100%;}
-
-.timeline-horizontal .timeline-ether-weekends{top:0; height:100%;}
-
-
-
-/*-------------------------- HIGHLIGHT DECORATORS -------------------*/
-.timeline-highlight-decorator,
-.timeline-highlight-point-decorator{
-	position:absolute;
-	overflow:hidden;
-}
-.timeline-horizontal .timeline-highlight-point-decorator,
-.timeline-horizontal .timeline-highlight-decorator{
-	width:10px;
-	top:0;
-    height:100%;
-}
-
-.timeline-vertical .timeline-highlight-point-decorator,
-.timeline-vertical .timeline-highlight-decorator{
-	height:10px;
-	width:100%;
-	left:0;
-}
-
-.timeline-highlight-decorator{background-color:#FFC080;}
-.timeline-highlight-point-decorator{background-color:#ff5;}
-
-
-
-/*---------------------------- LABELS -------------------------*/
-.timeline-highlight-label{position:absolute;overflow:hidden;font-size:200%;font-weight:bold;color:#999;}
-
-
-/*---------------- VERTICAL LABEL -------------------*/
-.timeline-horizontal .timeline-highlight-label{top:0;height:100%;}
-.timeline-horizontal .timeline-highlight-label td{vertical-align:middle;}
-.timeline-horizontal .timeline-highlight-label-start{text-align:right;}
-.timeline-horizontal .timeline-highlight-label-end{text-align:left;}
-
-
-/*---------------- HORIZONTAL LABEL -------------------*/
-.timeline-vertical .timeline-highlight-label{left:0;width:100%;}
-.timeline-vertical .timeline-highlight-label td{vertical-align:top;}
-.timeline-vertical .timeline-highlight-label-start{text-align:center;}
-.timeline-vertical .timeline-highlight-label-end{text-align:center;}
-
-
-
-/*-------------------------------- DATE LABELS --------------------------------*/
-.timeline-date-label{position:absolute; border:solid #aaa; color:#aaa;	width:5em; height:1.5em;}
-.timeline-date-label-em{color:#000;}
-
-/* horizontal */
-.timeline-horizontal .timeline-date-label{padding-left:2px;}
-.timeline-horizontal .timeline-date-label{border-width:0 0 0 1px;}
-.timeline-horizontal .timeline-date-label-em{height:2em}
+div.simileAjax-bubble-container {
+    margin:     0px;
+    padding:    0px;
+    border:     none;
+    position:   absolute;
+    z-index:    1000;
+}
+
+div.simileAjax-bubble-innerContainer {
+    margin:     0px;
+    padding:    0px;
+    border:     none;
+    position:   relative;
+    width:      100%;
+    height:     100%;
+    overflow:   visible;
+}
 
-/* vertical */
-.timeline-vertical .timeline-date-label{padding-top:2px;}
-.timeline-vertical .timeline-date-label{border-width:1px 0 0;}
-.timeline-vertical .timeline-date-label-em{width:7em}
-
-/*------------------------------- Ether.highlight -------------------------*/
-.timeline-ether-highlight{position:absolute; background-color:#fff;}
-.timeline-horizontal .timeline-ether-highlight{top:2px;}
-.timeline-vertical .timeline-ether-highlight{left:2px;}
-
-
-
-/*------------------------------ EVENTS ------------------------------------*/
-.timeline-event-icon, .timeline-event-label,.timeline-event-tape{
-	position:absolute;
-	cursor:pointer;
-}
-
-.timeline-event-tape,
-.timeline-small-event-tape,
-.timeline-small-event-icon{
-	background-color:#58A0DC;
-	overflow:hidden;
-}
-
-.timeline-small-event-tape,
-.timeline-small-event-icon{
-	position:absolute;
-}
-
-.timeline-event-tape{height:4px;}
-
-.timeline-small-event-tape{height:2px;}
-.timeline-small-event-icon{width:1px; height:6px;}
- 
- 
-
-/*--------------------------------- TIMELINE-------------------------*/
-.timeline-ether-bg{width:100%; height:100%;}
-.timeline-band-0 .timeline-ether-bg{background-color:#eee}
-.timeline-band-1 .timeline-ether-bg{background-color:#ddd}
-.timeline-band-2 .timeline-ether-bg{background-color:#ccc}
-.timeline-band-3 .timeline-ether-bg{background-color:#aaa}
-.timeline-duration-event {
-    position: absolute;
-    overflow: hidden;
-    border: 1px solid blue;
-}
-
-.timeline-instant-event2 {
-    position: absolute;
-    overflow: hidden;
-    border-left: 1px solid blue;
-    padding-left: 2px;
-}
-
-.timeline-instant-event {
-    position: absolute;
-    overflow: hidden;
-}
-
-.timeline-event-bubble-title {
-    font-weight: bold;
-    border-bottom: 1px solid #888;
-    margin-bottom: 0.5em;
-}
-
-.timeline-event-bubble-body {
-}
-
-.timeline-event-bubble-wiki {
-    margin:     0.5em;
-    text-align: right;
-    color:      #A0A040;
-}
-.timeline-event-bubble-wiki a {
-    color:      #A0A040;
-}
-
-.timeline-event-bubble-time {
-    color: #aaa;
-}
-
-.timeline-event-bubble-image {
-    float: right;
-    padding-left: 5px;
-    padding-bottom: 5px;
-}.timeline-container {
+div.simileAjax-bubble-contentContainer {
+    margin:     0px;
+    padding:    0px;
+    border:     none;
+    position:   absolute;
+    left:       0px;
+    top:        0px;
+    width:      100%;
+    height:     100%;
+    overflow:   auto;
+    background: white;
+}
+
+div.simileAjax-bubble-border-left {
+    position:   absolute;
+    left:       -50px;
+    top:        0px;
+    width:      50px;
+    height:     100%;
+}
+div.simileAjax-bubble-border-left-pngTranslucent {
+    background: url(../images/bubble-left.png) top right repeat-y;
+}
+
+div.simileAjax-bubble-border-right {
+    position:   absolute;
+    right:      -50px;
+    top:        0px;
+    width:      50px;
+    height:     100%;
+}
+.simileAjax-bubble-border-right-pngTranslucent {
+    background: url(../images/bubble-right.png) top left repeat-y;
+}
+
+div.simileAjax-bubble-border-top {
+    position:   absolute;
+    top:        -50px;
+    left:       0px;
+    width:      100%;
+    height:     50px;
+}
+.simileAjax-bubble-border-top-pngTranslucent {
+    background: url(../images/bubble-top.png) bottom left repeat-x;
+}
+
+div.simileAjax-bubble-border-bottom {
+    position:   absolute;
+    bottom:     -50px;
+    left:       0px;
+    width:      100%;
+    height:     50px;
+}
+.simileAjax-bubble-border-bottom-pngTranslucent {
+    background: url(../images/bubble-bottom.png) top left repeat-x;
+}
+
+div.simileAjax-bubble-border-top-left {
+    position:   absolute;
+    top:        -50px;
+    left:       -50px;
+    width:      50px;
+    height:     50px;
+}
+.simileAjax-bubble-border-top-left-pngTranslucent {
+    background: url(../images/bubble-top-left.png) bottom right no-repeat;
+}
+
+div.simileAjax-bubble-border-top-right {
+    position:   absolute;
+    top:        -50px;
+    right:      -50px;
+    width:      50px;
+    height:     50px;
+}
+.simileAjax-bubble-border-top-right-pngTranslucent {
+    background: url(../images/bubble-top-right.png) bottom left no-repeat;
+}
+
+div.simileAjax-bubble-border-bottom-left {
+    position:   absolute;
+    bottom:     -50px;
+    left:       -50px;
+    width:      50px;
+    height:     50px;
+}
+.simileAjax-bubble-border-bottom-left-pngTranslucent {
+    background: url(../images/bubble-bottom-left.png) top right no-repeat;
+}
+
+div.simileAjax-bubble-border-bottom-right {
+    position:   absolute;
+    bottom:     -50px;
+    right:      -50px;
+    width:      50px;
+    height:     50px;
+}
+.simileAjax-bubble-border-bottom-right-pngTranslucent {
+    background: url(../images/bubble-bottom-right.png) top left no-repeat;
+}
+
+div.simileAjax-bubble-arrow-point-left {
+    position:   absolute;
+    left:       -100px;
+    width:      100px;
+    height:     49px;
+}
+.simileAjax-bubble-arrow-point-left-pngTranslucent {
+    background: url(../images/bubble-arrow-point-left.png) center right no-repeat;
+}
+
+div.simileAjax-bubble-arrow-point-right {
+    position:   absolute;
+    right:      -100px;
+    width:      100px;
+    height:     49px;
+}
+.simileAjax-bubble-arrow-point-right-pngTranslucent {
+    background: url(../images/bubble-arrow-point-right.png) center left no-repeat;
+}
+
+div.simileAjax-bubble-arrow-point-up {
+    position:   absolute;
+    top:        -100px;
+    width:      49px;
+    height:     100px;
+}
+.simileAjax-bubble-arrow-point-up-pngTranslucent {
+    background: url(../images/bubble-arrow-point-up.png) bottom center no-repeat;
+}
+
+div.simileAjax-bubble-arrow-point-down {
+    position:   absolute;
+    bottom:     -100px;
+    width:      49px;
+    height:     100px;
+}
+.simileAjax-bubble-arrow-point-down-pngTranslucent {
+    background: url(../images/bubble-arrow-point-down.png) bottom center no-repeat;
+}
+
+
+div.simileAjax-bubble-close {
+    position:   absolute;
+    right:      -10px;
+    top:        -12px;
+    width:      16px;
+    height:     16px;
+    cursor:     pointer;
+}
+.simileAjax-bubble-close-pngTranslucent {
+    background: url(../images/close-button.png) no-repeat;
+}
+.timeline-container {
     position: relative;
     overflow: hidden;
 }
@@ -230,3 +234,168 @@
     height:     100%;
 }
 
+
+
+/*------------------- Horizontal / Vertical lines ----------------*/
+
+/* style for ethers */
+.timeline-ether-lines{border-color:#666; border-style:dotted; position:absolute;}
+.timeline-horizontal .timeline-ether-lines{border-width:0 0 0 1px; height:100%; top: 0; width: 1px;}
+.timeline-vertical .timeline-ether-lines{border-width:1px 0 0; height:1px; left: 0; width: 100%;}
+
+
+
+/*---------------- Weekends ---------------------------*/
+.timeline-ether-weekends{
+	position:absolute;
+	background-color:#FFFFE0;
+}
+
+.timeline-vertical .timeline-ether-weekends{left:0;width:100%;}
+.timeline-horizontal .timeline-ether-weekends{top:0; height:100%;}
+
+
+/*-------------------------- HIGHLIGHT DECORATORS -------------------*/
+/* Used for decorators, not used for Timeline Highlight              */
+.timeline-highlight-decorator,
+.timeline-highlight-point-decorator{
+	position:absolute;
+	overflow:hidden;
+}
+
+/* Width of horizontal decorators and Height of vertical decorators is
+   set in the decorator function params */
+.timeline-horizontal .timeline-highlight-point-decorator,
+.timeline-horizontal .timeline-highlight-decorator{
+	top:0;
+  height:100%;
+}
+
+.timeline-vertical .timeline-highlight-point-decorator,
+.timeline-vertical .timeline-highlight-decorator{
+	width:100%;
+	left:0;
+}
+
+.timeline-highlight-decorator{background-color:#FFC080;}
+.timeline-highlight-point-decorator{background-color:#ff5;}
+
+
+/*---------------------------- LABELS -------------------------*/
+.timeline-highlight-label {
+  position:absolute; overflow:hidden; font-size:200%;
+  font-weight:bold; color:#999; }
+
+
+/*---------------- VERTICAL LABEL -------------------*/
+.timeline-horizontal .timeline-highlight-label {top:0; height:100%;}
+.timeline-horizontal .timeline-highlight-label td {vertical-align:middle;}
+.timeline-horizontal .timeline-highlight-label-start {text-align:right;}
+.timeline-horizontal .timeline-highlight-label-end {text-align:left;}
+
+
+/*---------------- HORIZONTAL LABEL -------------------*/
+.timeline-vertical .timeline-highlight-label {left:0;width:100%;}
+.timeline-vertical .timeline-highlight-label td {vertical-align:top;}
+.timeline-vertical .timeline-highlight-label-start {text-align:center;}
+.timeline-vertical .timeline-highlight-label-end {text-align:center;}
+
+
+/*-------------------------------- DATE LABELS --------------------------------*/
+.timeline-date-label {
+  position: absolute;
+  border: solid #aaa;
+  color: #aaa;
+  width: 5em;
+  height: 1.5em;}
+.timeline-date-label-em {color: #000;}
+
+/* horizontal */
+.timeline-horizontal .timeline-date-label{padding-left:2px;}
+.timeline-horizontal .timeline-date-label{border-width:0 0 0 1px;}
+.timeline-horizontal .timeline-date-label-em{height:2em}
+
+/* vertical */
+.timeline-vertical .timeline-date-label{padding-top:2px;}
+.timeline-vertical .timeline-date-label{border-width:1px 0 0;}
+.timeline-vertical .timeline-date-label-em{width:7em}
+
+
+/*------------------------------- Ether.highlight -------------------------*/
+.timeline-ether-highlight{position:absolute; background-color:#fff;}
+.timeline-horizontal .timeline-ether-highlight{top:2px;}
+.timeline-vertical .timeline-ether-highlight{left:2px;}
+
+
+/*------------------------------ EVENTS ------------------------------------*/
+.timeline-event-icon, .timeline-event-label,.timeline-event-tape{
+	position:absolute;
+	cursor:pointer;
+}
+
+.timeline-event-tape,
+.timeline-small-event-tape,
+.timeline-small-event-icon{
+	background-color:#58A0DC;
+	overflow:hidden;
+}
+
+.timeline-small-event-tape,
+.timeline-small-event-icon{
+	position:absolute;
+}
+
+.timeline-small-event-icon{width:1px; height:6px;}
+
+  
+/*--------------------------------- TIMELINE-------------------------*/
+.timeline-ether-bg{width:100%; height:100%;}
+.timeline-band-0 .timeline-ether-bg{background-color:#eee}
+.timeline-band-1 .timeline-ether-bg{background-color:#ddd}
+.timeline-band-2 .timeline-ether-bg{background-color:#ccc}
+.timeline-band-3 .timeline-ether-bg{background-color:#aaa}
+.timeline-duration-event {
+    position: absolute;
+    overflow: hidden;
+    border: 1px solid blue;
+}
+
+.timeline-instant-event2 {
+    position: absolute;
+    overflow: hidden;
+    border-left: 1px solid blue;
+    padding-left: 2px;
+}
+
+.timeline-instant-event {
+    position: absolute;
+    overflow: hidden;
+}
+
+.timeline-event-bubble-title {
+    font-weight: bold;
+    border-bottom: 1px solid #888;
+    margin-bottom: 0.5em;
+}
+
+.timeline-event-bubble-body {
+}
+
+.timeline-event-bubble-wiki {
+    margin:     0.5em;
+    text-align: right;
+    color:      #A0A040;
+}
+.timeline-event-bubble-wiki a {
+    color:      #A0A040;
+}
+
+.timeline-event-bubble-time {
+    color: #aaa;
+}
+
+.timeline-event-bubble-image {
+    float: right;
+    padding-left: 5px;
+    padding-bottom: 5px;
+}
\ No newline at end of file
--- a/web/htmlwidgets.py	Wed Apr 01 17:25:40 2009 +0200
+++ b/web/htmlwidgets.py	Wed Apr 01 17:26:36 2009 +0200
@@ -4,7 +4,7 @@
 serialization time
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 
--- a/web/views/baseviews.py	Wed Apr 01 17:25:40 2009 +0200
+++ b/web/views/baseviews.py	Wed Apr 01 17:26:36 2009 +0200
@@ -673,12 +673,8 @@
     content_type = 'text/xml'
     http_cache_manager = MaxAgeHTTPCacheManager
     cache_max_age = 60*60*2 # stay in http cache for 2 hours by default 
-    
-    def cell_call(self, row, col):
-        self.wview('rssitem', self.rset, row=row, col=col)
-        
-    def call(self):
-        """display a list of entities by calling their <item_vid> view"""
+
+    def _open(self):
         req = self.req
         self.w(u'<?xml version="1.0" encoding="%s"?>\n' % req.encoding)
         self.w(u'''<rss version="2.0"
@@ -691,26 +687,31 @@
         params = req.form.copy()
         params.pop('vid', None)
         self.w(u'    <link>%s</link>\n' % html_escape(self.build_url(**params)))
-        self.w(u'    <items>\n')
-        self.w(u'      <rdf:Seq>\n')
-        for entity in self.rset.entities():
-            self.w(u'      <rdf:li resource="%s" />\n' % html_escape(entity.absolute_url()))
-        self.w(u'      </rdf:Seq>\n')
-        self.w(u'    </items>\n')
+
+    def _close(self):
         self.w(u'  </channel>\n')
+        self.w(u'</rss>')       
+        
+    def call(self):
+        """display a list of entities by calling their <item_vid> view"""
+        self._open()
         for i in xrange(self.rset.rowcount):
             self.cell_call(i, 0)
-        self.w(u'</rss>')       
+        self._close()
+
+    def cell_call(self, row, col):
+        self.wview('rssitem', self.rset, row=row, col=col)
 
 class RssItemView(EntityView):
     id = 'rssitem'
     date_format = '%%Y-%%m-%%dT%%H:%%M%+03i:00' % (timezone / 3600)
+    add_div_section = False
 
     def cell_call(self, row, col):
         entity = self.complete_entity(row, col)
         self.w(u'<item rdf:about="%s">\n' % html_escape(entity.absolute_url()))
         self.render_title_link(entity)
-        self._marker('description', entity.dc_date(self.description))
+        self._marker('description', html_escape(entity.dc_description()))
         self._marker('dc:date', entity.dc_date(self.date_format))
         self.render_entity_creator(entity)
         self.w(u'</item>\n')
@@ -721,12 +722,12 @@
            
     def render_entity_creator(self, entity):
         if entity.creator:
-            self.w(u'<author>')
-            self._marker('name', entity.creator.name())
+            self._marker('dc:creator', entity.creator.name())
             email = entity.creator.get_email()
             if email:
-                self._marker('email', email)
-            self.w(u'</author>')       
+                self.w(u'<author>')
+                self.w(email)
+                self.w(u'</author>')       
         
     def _marker(self, marker, value):
         if value:
--- a/web/views/editcontroller.py	Wed Apr 01 17:25:40 2009 +0200
+++ b/web/views/editcontroller.py	Wed Apr 01 17:26:36 2009 +0200
@@ -166,13 +166,13 @@
         self.delete_entities(self.req.edited_eids(withtype=True))
         return self.reset()
 
-    def _needs_edition(self, rtype, formparams):
+    def _needs_edition(self, rtype, formparams, entity):
         """returns True and and the new value if `rtype` was edited"""
         editkey = 'edits-%s' % rtype
         if not editkey in formparams:
             return False, None # not edited
         value = formparams.get(rtype) or None
-        if (formparams.get(editkey) or None) == value:
+        if entity.has_eid() and (formparams.get(editkey) or None) == value:
             return False, None # not modified
         if value == INTERNAL_FIELD_VALUE:
             value = None        
@@ -183,7 +183,7 @@
         attribute described by the given schema if necessary
         """
         attr = rschema.type
-        edition_needed, value = self._needs_edition(attr, formparams)
+        edition_needed, value = self._needs_edition(attr, formparams, entity)
         if not edition_needed:
             return
         # test if entity class defines a special handler for this attribute
--- a/web/views/iprogress.py	Wed Apr 01 17:25:40 2009 +0200
+++ b/web/views/iprogress.py	Wed Apr 01 17:26:36 2009 +0200
@@ -1,7 +1,7 @@
 """Specific views for entities implementing IProgress
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 
--- a/web/widgets.py	Wed Apr 01 17:25:40 2009 +0200
+++ b/web/widgets.py	Wed Apr 01 17:26:36 2009 +0200
@@ -449,7 +449,7 @@
 
 
 class YesNoRadioWidget(CheckBoxWidget):
-    
+    html_attributes = Widget.html_attributes | set(('disabled',))
     def _edit_render(self, entity):
         value = self.current_value(entity)
         dvalue = self.current_display_value(entity)