[web] split timeline in an individual cube

Closes #1279544

authorJulien Cristau <julien.cristau@logilab.fr>
changesetcda1bdc3652e
branchdefault
phasepublic
hiddenno
parent revision#fdcc500002ef [devtools] don't hide warnings and errors during tests
child revision#684215aca046 Remove remote repository-access-through-pyro support
files modified by this revision
__pkginfo__.py
doc/3.21.rst
doc/tools/pyjsrest.py
web/data/cubicweb.timeline-bundle.js
web/data/cubicweb.timeline-ext.js
web/data/cubicweb.widgets.js
web/data/timeline-bundle.css
web/data/timeline/blue-circle.png
web/data/timeline/bubble-arrows.png
web/data/timeline/bubble-body-and-arrows.png
web/data/timeline/bubble-body.png
web/data/timeline/bubble-bottom-arrow.png
web/data/timeline/bubble-bottom-left.png
web/data/timeline/bubble-bottom-right.png
web/data/timeline/bubble-bottom.png
web/data/timeline/bubble-left-arrow.png
web/data/timeline/bubble-left.png
web/data/timeline/bubble-right-arrow.png
web/data/timeline/bubble-right.png
web/data/timeline/bubble-top-arrow.png
web/data/timeline/bubble-top-left.png
web/data/timeline/bubble-top-right.png
web/data/timeline/bubble-top.png
web/data/timeline/close-button.png
web/data/timeline/copyright-vertical.png
web/data/timeline/copyright.png
web/data/timeline/dark-blue-circle.png
web/data/timeline/dark-green-circle.png
web/data/timeline/dark-red-circle.png
web/data/timeline/dull-blue-circle.png
web/data/timeline/dull-green-circle.png
web/data/timeline/dull-red-circle.png
web/data/timeline/gray-circle.png
web/data/timeline/green-circle.png
web/data/timeline/message-bottom-left.png
web/data/timeline/message-bottom-right.png
web/data/timeline/message-left.png
web/data/timeline/message-right.png
web/data/timeline/message-top-left.png
web/data/timeline/message-top-right.png
web/data/timeline/message.png
web/data/timeline/progress-running.gif
web/data/timeline/red-circle.png
web/data/timeline/sundial.png
web/data/timeline/top-bubble.png
web/views/timeline.py
# HG changeset patch
# User Julien Cristau <julien.cristau@logilab.fr>
# Date 1417431178 -3600
# Mon Dec 01 11:52:58 2014 +0100
# Node ID cda1bdc3652e8ab3e6ab8f51308abfe3dbc3f901
# Parent fdcc500002ef13d2e3d0e44535ac2c1d6b8f81c8
[web] split timeline in an individual cube

Closes #1279544

diff --git a/__pkginfo__.py b/__pkginfo__.py
@@ -113,12 +113,10 @@
1            for filename in listdir(_server_migration_dir)]],
2          # web data
3          [join('share', 'cubicweb', 'cubes', 'shared', 'data'),
4           [join(_data_dir, fname) for fname in listdir(_data_dir)
5            if not isdir(join(_data_dir, fname))]],
6 -        [join('share', 'cubicweb', 'cubes', 'shared', 'data', 'timeline'),
7 -         [join(_data_dir, 'timeline', fname) for fname in listdir(join(_data_dir, 'timeline'))]],
8          [join('share', 'cubicweb', 'cubes', 'shared', 'data', 'images'),
9           [join(_data_dir, 'images', fname) for fname in listdir(join(_data_dir, 'images'))]],
10          [join('share', 'cubicweb', 'cubes', 'shared', 'data', 'jquery-treeview'),
11           [join(_data_dir, 'jquery-treeview', fname) for fname in listdir(join(_data_dir, 'jquery-treeview'))
12            if not isdir(join(_data_dir, 'jquery-treeview', fname))]],
diff --git a/doc/3.21.rst b/doc/3.21.rst
@@ -1,8 +1,15 @@
13  What's new in CubicWeb 3.21?
14  ============================
15 
16 +Code movement
17 +-------------
18 +
19 +The cubicweb.web.views.timeline module (providing the timeline-json, timeline
20 +and static-timeline views) has moved to a standalone cube_
21 +
22 +.. _cube: https://www.cubicweb.org/project/cubicweb-timeline
23 
24  Deprecated code drops
25  ---------------------
26 
27  * the user_callback api has been removed; people should use plain
diff --git a/doc/tools/pyjsrest.py b/doc/tools/pyjsrest.py
@@ -132,11 +132,10 @@
28      'cubicweb.flot',
29      'cubicweb.calendar',
30      'cubicweb.preferences',
31      'cubicweb.edition',
32      'cubicweb.reledit',
33 -    'cubicweb.timeline-ext',
34  ]
35 
36  FILES_TO_IGNORE = set([
37      'jquery.js',
38      'jquery.treeview.js',
@@ -150,10 +149,9 @@
39 
40      'cubicweb.fckcwconfig.js',
41      'cubicweb.fckcwconfig-full.js',
42      'cubicweb.goa.js',
43      'cubicweb.compat.js',
44 -    'cubicweb.timeline-bundle.js',
45      ])
46 
47  if __name__ == '__main__':
48      parse_js_files()
diff --git a/web/data/cubicweb.timeline-bundle.js b/web/data/cubicweb.timeline-bundle.js
@@ -1,10129 +0,0 @@
49 -/**
50 - *  This file contains timeline utilities
51 - *  :organization: Logilab
52 - */
53 -
54 -var SimileAjax_urlPrefix = BASE_URL + 'data/';
55 -var Timeline_urlPrefix = BASE_URL + 'data/';
56 -
57 -/*
58 - *  Simile Ajax API
59 - *
60 - *  Include this file in your HTML file as follows::
61 - *
62 - *    <script src="http://simile.mit.edu/ajax/api/simile-ajax-api.js" type="text/javascript"></script>
63 - *
64 - *
65 - */
66 -
67 -if (typeof SimileAjax == "undefined") {
68 -    var SimileAjax = {
69 -        loaded:                 false,
70 -        loadingScriptsCount:    0,
71 -        error:                  null,
72 -        params:                 { bundle:"true" }
73 -    };
74 -
75 -    SimileAjax.Platform = new Object();
76 -        /*
77 -            HACK: We need these 2 things here because we cannot simply append
78 -            a <script> element containing code that accesses SimileAjax.Platform
79 -            to initialize it because IE executes that <script> code first
80 -            before it loads ajax.js and platform.js.
81 -        */
82 -
83 -    var getHead = function(doc) {
84 -        return doc.getElementsByTagName("head")[0];
85 -    };
86 -
87 -    SimileAjax.findScript = function(doc, substring) {
88 -        var heads = doc.documentElement.getElementsByTagName("head");
89 -        for (var h = 0; h < heads.length; h++) {
90 -            var node = heads[h].firstChild;
91 -            while (node != null) {
92 -                if (node.nodeType == 1 && node.tagName.toLowerCase() == "script") {
93 -                    var url = node.src;
94 -                    var i = url.indexOf(substring);
95 -                    if (i >= 0) {
96 -                        return url;
97 -                    }
98 -                }
99 -                node = node.nextSibling;
100 -            }
101 -        }
102 -        return null;
103 -    };
104 -    SimileAjax.includeJavascriptFile = function(doc, url, onerror, charset) {
105 -        onerror = onerror || "";
106 -        if (doc.body == null) {
107 -            try {
108 -                var q = "'" + onerror.replace( /'/g, '&apos' ) + "'"; // "
109 -                doc.write("<script src='" + url + "' onerror="+ q +
110 -                          (charset ? " charset='"+ charset +"'" : "") +
111 -                          " type='text/javascript'>"+ onerror + "</script>");
112 -                return;
113 -            } catch (e) {
114 -                // fall through
115 -            }
116 -        }
117 -
118 -        var script = doc.createElement("script");
119 -        if (onerror) {
120 -            try { script.innerHTML = onerror; } catch(e) {}
121 -            script.setAttribute("onerror", onerror);
122 -        }
123 -        if (charset) {
124 -            script.setAttribute("charset", charset);
125 -        }
126 -        script.type = "text/javascript";
127 -        script.language = "JavaScript";
128 -        script.src = url;
129 -        return getHead(doc).appendChild(script);
130 -    };
131 -    SimileAjax.includeJavascriptFiles = function(doc, urlPrefix, filenames) {
132 -        for (var i = 0; i < filenames.length; i++) {
133 -            SimileAjax.includeJavascriptFile(doc, urlPrefix + filenames[i]);
134 -        }
135 -        SimileAjax.loadingScriptsCount += filenames.length;
136 -        // XXX adim SimileAjax.includeJavascriptFile(doc, SimileAjax.urlPrefix + "scripts/signal.js?" + filenames.length);
137 -    };
138 -    SimileAjax.includeCssFile = function(doc, url) {
139 -        if (doc.body == null) {
140 -            try {
141 -                doc.write("<link rel='stylesheet' href='" + url + "' type='text/css'/>");
142 -                return;
143 -            } catch (e) {
144 -                // fall through
145 -            }
146 -        }
147 -
148 -        var link = doc.createElement("link");
149 -        link.setAttribute("rel", "stylesheet");
150 -        link.setAttribute("type", "text/css");
151 -        link.setAttribute("href", url);
152 -        getHead(doc).appendChild(link);
153 -    };
154 -    SimileAjax.includeCssFiles = function(doc, urlPrefix, filenames) {
155 -        for (var i = 0; i < filenames.length; i++) {
156 -            SimileAjax.includeCssFile(doc, urlPrefix + filenames[i]);
157 -        }
158 -    };
159 -
160 -    /**
161 -     * Append into urls each string in suffixes after prefixing it with urlPrefix.
162 -     * @param {Array} urls
163 -     * @param {String} urlPrefix
164 -     * @param {Array} suffixes
165 -     */
166 -    SimileAjax.prefixURLs = function(urls, urlPrefix, suffixes) {
167 -        for (var i = 0; i < suffixes.length; i++) {
168 -            urls.push(urlPrefix + suffixes[i]);
169 -        }
170 -    };
171 -
172 -    /**
173 -     * Parse out the query parameters from a URL
174 -     * @param {String} url    the url to parse, or location.href if undefined
175 -     * @param {Object} to     optional object to extend with the parameters
176 -     * @param {Object} types  optional object mapping keys to value types
177 -     *        (String, Number, Boolean or Array, String by default)
178 -     * @return a key/value Object whose keys are the query parameter names
179 -     * @type Object
180 -     */
181 -    SimileAjax.parseURLParameters = function(url, to, types) {
182 -        to = to || {};
183 -        types = types || {};
184 -
185 -        if (typeof url == "undefined") {
186 -            url = location.href;
187 -        }
188 -        var q = url.indexOf("?");
189 -        if (q < 0) {
190 -            return to;
191 -        }
192 -        url = (url+"#").slice(q+1, url.indexOf("#")); // toss the URL fragment
193 -
194 -        var params = url.split("&"), param, parsed = {};
195 -        var decode = window.decodeURIComponent || unescape;
196 -        for (var i = 0; param = params[i]; i++) {
197 -            var eq = param.indexOf("=");
198 -            var name = decode(param.slice(0,eq));
199 -            var old = parsed[name];
200 -            if (typeof old == "undefined") {
201 -                old = [];
202 -            } else if (!(old instanceof Array)) {
203 -                old = [old];
204 -            }
205 -            parsed[name] = old.concat(decode(param.slice(eq+1)));
206 -        }
207 -        for (var i in parsed) {
208 -            if (!parsed.hasOwnProperty(i)) continue;
209 -            var type = types[i] || String;
210 -            var data = parsed[i];
211 -            if (!(data instanceof Array)) {
212 -                data = [data];
213 -            }
214 -            if (type === Boolean && data[0] == "false") {
215 -                to[i] = false; // because Boolean("false") === true
216 -            } else {
217 -                to[i] = type.apply(this, data);
218 -            }
219 -        }
220 -        return to;
221 -    };
222 -
223 -    (function() {
224 -        var javascriptFiles = [
225 -            "jquery-1.2.6.js",
226 -            "platform.js",
227 -            "debug.js",
228 -            "xmlhttp.js",
229 -            "json.js",
230 -            "dom.js",
231 -            "graphics.js",
232 -            "date-time.js",
233 -            "string.js",
234 -            "html.js",
235 -            "data-structure.js",
236 -            "units.js",
237 -
238 -            "ajax.js",
239 -            "history.js",
240 -            "window-manager.js"
241 -        ];
242 -        var cssFiles = [
243 -            "graphics.css"
244 -        ];
245 -
246 -        if (typeof SimileAjax_urlPrefix == "string") {
247 -            SimileAjax.urlPrefix = SimileAjax_urlPrefix;
248 -        } else {
249 -            var url = SimileAjax.findScript(document, "simile-ajax-api.js");
250 -            if (url == null) {
251 -                SimileAjax.error = new Error("Failed to derive URL prefix for Simile Ajax API code files");
252 -                return;
253 -            }
254 -
255 -            SimileAjax.urlPrefix = url.substr(0, url.indexOf("simile-ajax-api.js"));
256 -        }
257 -
258 -        SimileAjax.parseURLParameters(url, SimileAjax.params, {bundle:Boolean});
259 -//         if (SimileAjax.params.bundle) {
260 -//             SimileAjax.includeJavascriptFiles(document, SimileAjax.urlPrefix, [ "simile-ajax-bundle.js" ]);
261 -//         } else {
262 -//             SimileAjax.includeJavascriptFiles(document, SimileAjax.urlPrefix + "scripts/", javascriptFiles);
263 -//         }
264 -//         SimileAjax.includeCssFiles(document, SimileAjax.urlPrefix + "styles/", cssFiles);
265 -
266 -        SimileAjax.loaded = true;
267 -    })();
268 -}
269 -/*
270 - *  Platform Utility Functions and Constants
271 - *
272 - */
273 -
274 -/*  This must be called after our jQuery has been loaded
275 -    but before control returns to user-code.
276 -*/
277 -SimileAjax.jQuery = jQuery;
278 -// SimileAjax.jQuery = jQuery.noConflict(true);
279 -if (typeof window["$"] == "undefined") {
280 -    window.$ = SimileAjax.jQuery;
281 -}
282 -
283 -SimileAjax.Platform.os = {
284 -    isMac:   false,
285 -    isWin:   false,
286 -    isWin32: false,
287 -    isUnix:  false
288 -};
289 -SimileAjax.Platform.browser = {
290 -    isIE:           false,
291 -    isNetscape:     false,
292 -    isMozilla:      false,
293 -    isFirefox:      false,
294 -    isOpera:        false,
295 -    isSafari:       false,
296 -
297 -    majorVersion:   0,
298 -    minorVersion:   0
299 -};
300 -
301 -(function() {
302 -    var an = navigator.appName.toLowerCase();
303 -	var ua = navigator.userAgent.toLowerCase();
304 -
305 -    /*
306 -     *  Operating system
307 -     */
308 -	SimileAjax.Platform.os.isMac = (ua.indexOf('mac') != -1);
309 -	SimileAjax.Platform.os.isWin = (ua.indexOf('win') != -1);
310 -	SimileAjax.Platform.os.isWin32 = SimileAjax.Platform.isWin && (
311 -        ua.indexOf('95') != -1 ||
312 -        ua.indexOf('98') != -1 ||
313 -        ua.indexOf('nt') != -1 ||
314 -        ua.indexOf('win32') != -1 ||
315 -        ua.indexOf('32bit') != -1
316 -    );
317 -	SimileAjax.Platform.os.isUnix = (ua.indexOf('x11') != -1);
318 -
319 -    /*
320 -     *  Browser
321 -     */
322 -    SimileAjax.Platform.browser.isIE = (an.indexOf("microsoft") != -1);
323 -    SimileAjax.Platform.browser.isNetscape = (an.indexOf("netscape") != -1);
324 -    SimileAjax.Platform.browser.isMozilla = (ua.indexOf("mozilla") != -1);
325 -    SimileAjax.Platform.browser.isFirefox = (ua.indexOf("firefox") != -1);
326 -    SimileAjax.Platform.browser.isOpera = (an.indexOf("opera") != -1);
327 -    SimileAjax.Platform.browser.isSafari = (an.indexOf("safari") != -1);
328 -
329 -    var parseVersionString = function(s) {
330 -        var a = s.split(".");
331 -        SimileAjax.Platform.browser.majorVersion = parseInt(a[0]);
332 -        SimileAjax.Platform.browser.minorVersion = parseInt(a[1]);
333 -    };
334 -    var indexOf = function(s, sub, start) {
335 -        var i = s.indexOf(sub, start);
336 -        return i >= 0 ? i : s.length;
337 -    };
338 -
339 -    if (SimileAjax.Platform.browser.isMozilla) {
340 -        var offset = ua.indexOf("mozilla/");
341 -        if (offset >= 0) {
342 -            parseVersionString(ua.substring(offset + 8, indexOf(ua, " ", offset)));
343 -        }
344 -    }
345 -    if (SimileAjax.Platform.browser.isIE) {
346 -        var offset = ua.indexOf("msie ");
347 -        if (offset >= 0) {
348 -            parseVersionString(ua.substring(offset + 5, indexOf(ua, ";", offset)));
349 -        }
350 -    }
351 -    if (SimileAjax.Platform.browser.isNetscape) {
352 -        var offset = ua.indexOf("rv:");
353 -        if (offset >= 0) {
354 -            parseVersionString(ua.substring(offset + 3, indexOf(ua, ")", offset)));
355 -        }
356 -    }
357 -    if (SimileAjax.Platform.browser.isFirefox) {
358 -        var offset = ua.indexOf("firefox/");
359 -        if (offset >= 0) {
360 -            parseVersionString(ua.substring(offset + 8, indexOf(ua, " ", offset)));
361 -        }
362 -    }
363 -
364 -    if (!("localeCompare" in String.prototype)) {
365 -        String.prototype.localeCompare = function (s) {
366 -            if (this < s) return -1;
367 -            else if (this > s) return 1;
368 -            else return 0;
369 -        };
370 -    }
371 -})();
372 -
373 -SimileAjax.Platform.getDefaultLocale = function() {
374 -    return SimileAjax.Platform.clientLocale;
375 -};
376 -/*
377 - *  Debug Utility Functions
378 - *
379 - */
380 -
381 -SimileAjax.Debug = {
382 -    silent: false
383 -};
384 -
385 -SimileAjax.Debug.log = function(msg) {
386 -    var f;
387 -    if ("console" in window && "log" in window.console) { // FireBug installed
388 -        f = function(msg2) {
389 -            console.log(msg2);
390 -        }
391 -    } else {
392 -        f = function(msg2) {
393 -            if (!SimileAjax.Debug.silent) {
394 -                alert(msg2);
395 -            }
396 -        }
397 -    }
398 -    SimileAjax.Debug.log = f;
399 -    f(msg);
400 -};
401 -
402 -SimileAjax.Debug.warn = function(msg) {
403 -    var f;
404 -    if ("console" in window && "warn" in window.console) { // FireBug installed
405 -        f = function(msg2) {
406 -            console.warn(msg2);
407 -        }
408 -    } else {
409 -        f = function(msg2) {
410 -            if (!SimileAjax.Debug.silent) {
411 -                alert(msg2);
412 -            }
413 -        }
414 -    }
415 -    SimileAjax.Debug.warn = f;
416 -    f(msg);
417 -};
418 -
419 -SimileAjax.Debug.exception = function(e, msg) {
420 -    var f, params = SimileAjax.parseURLParameters();
421 -    if (params.errors == "throw" || SimileAjax.params.errors == "throw") {
422 -        f = function(e2, msg2) {
423 -            throw(e2); // do not hide from browser's native debugging features
424 -        };
425 -    } else if ("console" in window && "error" in window.console) { // FireBug installed
426 -        f = function(e2, msg2) {
427 -            if (msg2 != null) {
428 -                console.error(msg2 + " %o", e2);
429 -            } else {
430 -                console.error(e2);
431 -            }
432 -            throw(e2); // do not hide from browser's native debugging features
433 -        };
434 -    } else {
435 -        f = function(e2, msg2) {
436 -            if (!SimileAjax.Debug.silent) {
437 -                alert("Caught exception: " + msg2 + "\n\nDetails: " + ("description" in e2 ? e2.description : e2));
438 -            }
439 -            throw(e2); // do not hide from browser's native debugging features
440 -        };
441 -    }
442 -    SimileAjax.Debug.exception = f;
443 -    f(e, msg);
444 -};
445 -
446 -SimileAjax.Debug.objectToString = function(o) {
447 -    return SimileAjax.Debug._objectToString(o, "");
448 -};
449 -
450 -SimileAjax.Debug._objectToString = function(o, indent) {
451 -    var indent2 = indent + " ";
452 -    if (typeof o == "object") {
453 -        var s = "{";
454 -        for (n in o) {
455 -            s += indent2 + n + ": " + SimileAjax.Debug._objectToString(o[n], indent2) + "\n";
456 -        }
457 -        s += indent + "}";
458 -        return s;
459 -    } else if (typeof o == "array") {
460 -        var s = "[";
461 -        for (var n = 0; n < o.length; n++) {
462 -            s += SimileAjax.Debug._objectToString(o[n], indent2) + "\n";
463 -        }
464 -        s += indent + "]";
465 -        return s;
466 -    } else {
467 -        return o;
468 -    }
469 -};
470 -/**
471 - * @fileOverview XmlHttp utility functions
472 - * @name SimileAjax.XmlHttp
473 - */
474 -
475 -SimileAjax.XmlHttp = new Object();
476 -
477 -/**
478 - *  Callback for XMLHttp onRequestStateChange.
479 - */
480 -SimileAjax.XmlHttp._onReadyStateChange = function(xmlhttp, fError, fDone) {
481 -    switch (xmlhttp.readyState) {
482 -    // 1: Request not yet made
483 -    // 2: Contact established with server but nothing downloaded yet
484 -    // 3: Called multiple while downloading in progress
485 -
486 -    // Download complete
487 -    case 4:
488 -        try {
489 -            if (xmlhttp.status == 0     // file:// urls, works on Firefox
490 -             || xmlhttp.status == 200   // http:// urls
491 -            ) {
492 -                if (fDone) {
493 -                    fDone(xmlhttp);
494 -                }
495 -            } else {
496 -                if (fError) {
497 -                    fError(
498 -                        xmlhttp.statusText,
499 -                        xmlhttp.status,
500 -                        xmlhttp
501 -                    );
502 -                }
503 -            }
504 -        } catch (e) {
505 -            SimileAjax.Debug.exception("XmlHttp: Error handling onReadyStateChange", e);
506 -        }
507 -        break;
508 -    }
509 -};
510 -
511 -/**
512 - *  Creates an XMLHttpRequest object. On the first run, this
513 - *  function creates a platform-specific function for
514 - *  instantiating an XMLHttpRequest object and then replaces
515 - *  itself with that function.
516 - */
517 -SimileAjax.XmlHttp._createRequest = function() {
518 -    if (SimileAjax.Platform.browser.isIE) {
519 -        var programIDs = [
520 -        "Msxml2.XMLHTTP",
521 -        "Microsoft.XMLHTTP",
522 -        "Msxml2.XMLHTTP.4.0"
523 -        ];
524 -        for (var i = 0; i < programIDs.length; i++) {
525 -            try {
526 -                var programID = programIDs[i];
527 -                var f = function() {
528 -                    return new ActiveXObject(programID);
529 -                };
530 -                var o = f();
531 -
532 -                // We are replacing the SimileAjax._createXmlHttpRequest
533 -                // function with this inner function as we've
534 -                // found out that it works. This is so that we
535 -                // don't have to do all the testing over again
536 -                // on subsequent calls.
537 -                SimileAjax.XmlHttp._createRequest = f;
538 -
539 -                return o;
540 -            } catch (e) {
541 -                // silent
542 -            }
543 -        }
544 -        // fall through to try new XMLHttpRequest();
545 -    }
546 -
547 -    try {
548 -        var f = function() {
549 -            return new XMLHttpRequest();
550 -        };
551 -        var o = f();
552 -
553 -        // We are replacing the SimileAjax._createXmlHttpRequest
554 -        // function with this inner function as we've
555 -        // found out that it works. This is so that we
556 -        // don't have to do all the testing over again
557 -        // on subsequent calls.
558 -        SimileAjax.XmlHttp._createRequest = f;
559 -
560 -        return o;
561 -    } catch (e) {
562 -        throw new Error("Failed to create an XMLHttpRequest object");
563 -    }
564 -};
565 -
566 -/**
567 - * Performs an asynchronous HTTP GET.
568 - *
569 - * @param {Function} fError a function of the form
570 -     function(statusText, statusCode, xmlhttp)
571 - * @param {Function} fDone a function of the form function(xmlhttp)
572 - */
573 -SimileAjax.XmlHttp.get = function(url, fError, fDone) {
574 -    var xmlhttp = SimileAjax.XmlHttp._createRequest();
575 -
576 -    xmlhttp.open("GET", url, true);
577 -    xmlhttp.onreadystatechange = function() {
578 -        SimileAjax.XmlHttp._onReadyStateChange(xmlhttp, fError, fDone);
579 -    };
580 -    xmlhttp.send(null);
581 -};
582 -
583 -/**
584 - * Performs an asynchronous HTTP POST.
585 - *
586 - * @param {Function} fError a function of the form
587 -     function(statusText, statusCode, xmlhttp)
588 - * @param {Function} fDone a function of the form function(xmlhttp)
589 - */
590 -SimileAjax.XmlHttp.post = function(url, body, fError, fDone) {
591 -    var xmlhttp = SimileAjax.XmlHttp._createRequest();
592 -
593 -    xmlhttp.open("POST", url, true);
594 -    xmlhttp.onreadystatechange = function() {
595 -        SimileAjax.XmlHttp._onReadyStateChange(xmlhttp, fError, fDone);
596 -    };
597 -    xmlhttp.send(body);
598 -};
599 -
600 -SimileAjax.XmlHttp._forceXML = function(xmlhttp) {
601 -    try {
602 -        xmlhttp.overrideMimeType("text/xml");
603 -    } catch (e) {
604 -        xmlhttp.setrequestheader("Content-Type", "text/xml");
605 -    }
606 -};/*
607 - *  Copied directly from http://www.json.org/json.js.
608 - */
609 -
610 -/*
611 -    json.js
612 -    2006-04-28
613 -
614 -    This file adds these methods to JavaScript:
615 -
616 -        object.toJSONString()
617 -
618 -            This method produces a JSON text from an object. The
619 -            object must not contain any cyclical references.
620 -
621 -        array.toJSONString()
622 -
623 -            This method produces a JSON text from an array. The
624 -            array must not contain any cyclical references.
625 -
626 -        string.parseJSON()
627 -
628 -            This method parses a JSON text to produce an object or
629 -            array. It will return false if there is an error.
630 -*/
631 -
632 -SimileAjax.JSON = new Object();
633 -
634 -(function () {
635 -    var m = {
636 -        '\b': '\\b',
637 -        '\t': '\\t',
638 -        '\n': '\\n',
639 -        '\f': '\\f',
640 -        '\r': '\\r',
641 -        '"' : '\\"',
642 -        '\\': '\\\\'
643 -    };
644 -    var s = {
645 -        array: function (x) {
646 -            var a = ['['], b, f, i, l = x.length, v;
647 -            for (i = 0; i < l; i += 1) {
648 -                v = x[i];
649 -                f = s[typeof v];
650 -                if (f) {
651 -                    v = f(v);
652 -                    if (typeof v == 'string') {
653 -                        if (b) {
654 -                            a[a.length] = ',';
655 -                        }
656 -                        a[a.length] = v;
657 -                        b = true;
658 -                    }
659 -                }
660 -            }
661 -            a[a.length] = ']';
662 -            return a.join('');
663 -        },
664 -        'boolean': function (x) {
665 -            return String(x);
666 -        },
667 -        'null': function (x) {
668 -            return "null";
669 -        },
670 -        number: function (x) {
671 -            return isFinite(x) ? String(x) : 'null';
672 -        },
673 -        object: function (x) {
674 -            if (x) {
675 -                if (x instanceof Array) {
676 -                    return s.array(x);
677 -                }
678 -                var a = ['{'], b, f, i, v;
679 -                for (i in x) {
680 -                    v = x[i];
681 -                    f = s[typeof v];
682 -                    if (f) {
683 -                        v = f(v);
684 -                        if (typeof v == 'string') {
685 -                            if (b) {
686 -                                a[a.length] = ',';
687 -                            }
688 -                            a.push(s.string(i), ':', v);
689 -                            b = true;
690 -                        }
691 -                    }
692 -                }
693 -                a[a.length] = '}';
694 -                return a.join('');
695 -            }
696 -            return 'null';
697 -        },
698 -        string: function (x) {
699 -            if (/["\\\x00-\x1f]/.test(x)) {
700 -                x = x.replace(/([\x00-\x1f\\"])/g, function(a, b) {
701 -                    var c = m[b];
702 -                    if (c) {
703 -                        return c;
704 -                    }
705 -                    c = b.charCodeAt();
706 -                    return '\\u00' +
707 -                        Math.floor(c / 16).toString(16) +
708 -                        (c % 16).toString(16);
709 -                });
710 -            }
711 -            return '"' + x + '"';
712 -        }
713 -    };
714 -
715 -    SimileAjax.JSON.toJSONString = function(o) {
716 -        if (o instanceof Object) {
717 -            return s.object(o);
718 -        } else if (o instanceof Array) {
719 -            return s.array(o);
720 -        } else {
721 -            return o.toString();
722 -        }
723 -    };
724 -
725 -    SimileAjax.JSON.parseJSON = function () {
726 -        try {
727 -            return !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
728 -                    this.replace(/"(\\.|[^"\\])*"/g, ''))) &&
729 -                eval('(' + this + ')');
730 -        } catch (e) {
731 -            return false;
732 -        }
733 -    };
734 -})();
735 -/*
736 - *  DOM Utility Functions
737 - *
738 - */
739 -
740 -SimileAjax.DOM = new Object();
741 -
742 -SimileAjax.DOM.registerEventWithObject = function(elmt, eventName, obj, handlerName) {
743 -    SimileAjax.DOM.registerEvent(elmt, eventName, function(elmt2, evt, target) {
744 -        return obj[handlerName].call(obj, elmt2, evt, target);
745 -    });
746 -};
747 -
748 -SimileAjax.DOM.registerEvent = function(elmt, eventName, handler) {
749 -    var handler2 = function(evt) {
750 -        evt = (evt) ? evt : ((event) ? event : null);
751 -        if (evt) {
752 -            var target = (evt.target) ?
753 -                evt.target : ((evt.srcElement) ? evt.srcElement : null);
754 -            if (target) {
755 -                target = (target.nodeType == 1 || target.nodeType == 9) ?
756 -                    target : target.parentNode;
757 -            }
758 -
759 -            return handler(elmt, evt, target);
760 -        }
761 -        return true;
762 -    }
763 -
764 -    if (SimileAjax.Platform.browser.isIE) {
765 -        elmt.attachEvent("on" + eventName, handler2);
766 -    } else {
767 -        elmt.addEventListener(eventName, handler2, false);
768 -    }
769 -};
770 -
771 -SimileAjax.DOM.getPageCoordinates = function(elmt) {
772 -    var left = 0;
773 -    var top = 0;
774 -
775 -    if (elmt.nodeType != 1) {
776 -        elmt = elmt.parentNode;
777 -    }
778 -
779 -    var elmt2 = elmt;
780 -    while (elmt2 != null) {
781 -        left += elmt2.offsetLeft;
782 -        top += elmt2.offsetTop;
783 -        elmt2 = elmt2.offsetParent;
784 -    }
785 -
786 -    var body = document.body;
787 -    while (elmt != null && elmt != body) {
788 -        if ("scrollLeft" in elmt) {
789 -            left -= elmt.scrollLeft;
790 -            top -= elmt.scrollTop;
791 -        }
792 -        elmt = elmt.parentNode;
793 -    }
794 -
795 -    return { left: left, top: top };
796 -};
797 -
798 -SimileAjax.DOM.getSize = function(elmt) {
799 -	var w = this.getStyle(elmt,"width");
800 -	var h = this.getStyle(elmt,"height");
801 -	if (w.indexOf("px") > -1) w = w.replace("px","");
802 -	if (h.indexOf("px") > -1) h = h.replace("px","");
803 -	return {
804 -		w: w,
805 -		h: h
806 -	}
807 -}
808 -
809 -SimileAjax.DOM.getStyle = function(elmt, styleProp) {
810 -    if (elmt.currentStyle) { // IE
811 -        var style = elmt.currentStyle[styleProp];
812 -    } else if (window.getComputedStyle) { // standard DOM
813 -        var style = document.defaultView.getComputedStyle(elmt, null).getPropertyValue(styleProp);
814 -    } else {
815 -    	var style = "";
816 -    }
817 -    return style;
818 -}
819 -
820 -SimileAjax.DOM.getEventRelativeCoordinates = function(evt, elmt) {
821 -    if (SimileAjax.Platform.browser.isIE) {
822 -      if (evt.type == "mousewheel") {
823 -        var coords = SimileAjax.DOM.getPageCoordinates(elmt);
824 -        return {
825 -          x: evt.clientX - coords.left,
826 -          y: evt.clientY - coords.top
827 -        };
828 -      } else {
829 -        return {
830 -          x: evt.offsetX,
831 -          y: evt.offsetY
832 -        };
833 -      }
834 -    } else {
835 -        var coords = SimileAjax.DOM.getPageCoordinates(elmt);
836 -
837 -        if ((evt.type == "DOMMouseScroll") &&
838 -          SimileAjax.Platform.browser.isFirefox &&
839 -          (SimileAjax.Platform.browser.majorVersion == 2)) {
840 -          // Due to: https://bugzilla.mozilla.org/show_bug.cgi?id=352179
841 -
842 -          return {
843 -            x: evt.screenX - coords.left,
844 -            y: evt.screenY - coords.top
845 -          };
846 -        } else {
847 -          return {
848 -              x: evt.pageX - coords.left,
849 -              y: evt.pageY - coords.top
850 -          };
851 -        }
852 -    }
853 -};
854 -
855 -SimileAjax.DOM.getEventPageCoordinates = function(evt) {
856 -    if (SimileAjax.Platform.browser.isIE) {
857 -        return {
858 -            x: evt.clientX + document.body.scrollLeft,
859 -            y: evt.clientY + document.body.scrollTop
860 -        };
861 -    } else {
862 -        return {
863 -            x: evt.pageX,
864 -            y: evt.pageY
865 -        };
866 -    }
867 -};
868 -
869 -SimileAjax.DOM.hittest = function(x, y, except) {
870 -    return SimileAjax.DOM._hittest(document.body, x, y, except);
871 -};
872 -
873 -SimileAjax.DOM._hittest = function(elmt, x, y, except) {
874 -    var childNodes = elmt.childNodes;
875 -    outer: for (var i = 0; i < childNodes.length; i++) {
876 -        var childNode = childNodes[i];
877 -        for (var j = 0; j < except.length; j++) {
878 -            if (childNode == except[j]) {
879 -                continue outer;
880 -            }
881 -        }
882 -
883 -        if (childNode.offsetWidth == 0 && childNode.offsetHeight == 0) {
884 -            /*
885 -             *  Sometimes SPAN elements have zero width and height but
886 -             *  they have children like DIVs that cover non-zero areas.
887 -             */
888 -            var hitNode = SimileAjax.DOM._hittest(childNode, x, y, except);
889 -            if (hitNode != childNode) {
890 -                return hitNode;
891 -            }
892 -        } else {
893 -            var top = 0;
894 -            var left = 0;
895 -
896 -            var node = childNode;
897 -            while (node) {
898 -                top += node.offsetTop;
899 -                left += node.offsetLeft;
900 -                node = node.offsetParent;
901 -            }
902 -
903 -            if (left <= x && top <= y && (x - left) < childNode.offsetWidth && (y - top) < childNode.offsetHeight) {
904 -                return SimileAjax.DOM._hittest(childNode, x, y, except);
905 -            } else if (childNode.nodeType == 1 && childNode.tagName == "TR") {
906 -                /*
907 -                 *  Table row might have cells that span several rows.
908 -                 */
909 -                var childNode2 = SimileAjax.DOM._hittest(childNode, x, y, except);
910 -                if (childNode2 != childNode) {
911 -                    return childNode2;
912 -                }
913 -            }
914 -        }
915 -    }
916 -    return elmt;
917 -};
918 -
919 -SimileAjax.DOM.cancelEvent = function(evt) {
920 -    evt.returnValue = false;
921 -    evt.cancelBubble = true;
922 -    if ("preventDefault" in evt) {
923 -        evt.preventDefault();
924 -    }
925 -};
926 -
927 -SimileAjax.DOM.appendClassName = function(elmt, className) {
928 -    var classes = elmt.className.split(" ");
929 -    for (var i = 0; i < classes.length; i++) {
930 -        if (classes[i] == className) {
931 -            return;
932 -        }
933 -    }
934 -    classes.push(className);
935 -    elmt.className = classes.join(" ");
936 -};
937 -
938 -SimileAjax.DOM.createInputElement = function(type) {
939 -    var div = document.createElement("div");
940 -    div.innerHTML = "<input type='" + type + "' />";
941 -
942 -    return div.firstChild;
943 -};
944 -
945 -SimileAjax.DOM.createDOMFromTemplate = function(template) {
946 -    var result = {};
947 -    result.elmt = SimileAjax.DOM._createDOMFromTemplate(template, result, null);
948 -
949 -    return result;
950 -};
951 -
952 -SimileAjax.DOM._createDOMFromTemplate = function(templateNode, result, parentElmt) {
953 -    if (templateNode == null) {
954 -        /*
955 -        var node = doc.createTextNode("--null--");
956 -        if (parentElmt != null) {
957 -            parentElmt.appendChild(node);
958 -        }
959 -        return node;
960 -        */
961 -        return null;
962 -    } else if (typeof templateNode != "object") {
963 -        var node = document.createTextNode(templateNode);
964 -        if (parentElmt != null) {
965 -            parentElmt.appendChild(node);
966 -        }
967 -        return node;
968 -    } else {
969 -        var elmt = null;
970 -        if ("tag" in templateNode) {
971 -            var tag = templateNode.tag;
972 -            if (parentElmt != null) {
973 -                if (tag == "tr") {
974 -                    elmt = parentElmt.insertRow(parentElmt.rows.length);
975 -                } else if (tag == "td") {
976 -                    elmt = parentElmt.insertCell(parentElmt.cells.length);
977 -                }
978 -            }
979 -            if (elmt == null) {
980 -                elmt = tag == "input" ?
981 -                    SimileAjax.DOM.createInputElement(templateNode.type) :
982 -                    document.createElement(tag);
983 -
984 -                if (parentElmt != null) {
985 -                    parentElmt.appendChild(elmt);
986 -                }
987 -            }
988 -        } else {
989 -            elmt = templateNode.elmt;
990 -            if (parentElmt != null) {
991 -                parentElmt.appendChild(elmt);
992 -            }
993 -        }
994 -
995 -        for (var attribute in templateNode) {
996 -            var value = templateNode[attribute];
997 -
998 -            if (attribute == "field") {
999 -                result[value] = elmt;
1000 -
1001 -            } else if (attribute == "className") {
1002 -                elmt.className = value;
1003 -            } else if (attribute == "id") {
1004 -                elmt.id = value;
1005 -            } else if (attribute == "title") {
1006 -                elmt.title = value;
1007 -            } else if (attribute == "type" && elmt.tagName == "input") {
1008 -                // do nothing
1009 -            } else if (attribute == "style") {
1010 -                for (n in value) {
1011 -                    var v = value[n];
1012 -                    if (n == "float") {
1013 -                        n = SimileAjax.Platform.browser.isIE ? "styleFloat" : "cssFloat";
1014 -                    }
1015 -                    elmt.style[n] = v;
1016 -                }
1017 -            } else if (attribute == "children") {
1018 -                for (var i = 0; i < value.length; i++) {
1019 -                    SimileAjax.DOM._createDOMFromTemplate(value[i], result, elmt);
1020 -                }
1021 -            } else if (attribute != "tag" && attribute != "elmt") {
1022 -                elmt.setAttribute(attribute, value);
1023 -            }
1024 -        }
1025 -        return elmt;
1026 -    }
1027 -}
1028 -
1029 -SimileAjax.DOM._cachedParent = null;
1030 -SimileAjax.DOM.createElementFromString = function(s) {
1031 -    if (SimileAjax.DOM._cachedParent == null) {
1032 -        SimileAjax.DOM._cachedParent = document.createElement("div");
1033 -    }
1034 -    SimileAjax.DOM._cachedParent.innerHTML = s;
1035 -    return SimileAjax.DOM._cachedParent.firstChild;
1036 -};
1037 -
1038 -SimileAjax.DOM.createDOMFromString = function(root, s, fieldElmts) {
1039 -    var elmt = typeof root == "string" ? document.createElement(root) : root;
1040 -    elmt.innerHTML = s;
1041 -
1042 -    var dom = { elmt: elmt };
1043 -    SimileAjax.DOM._processDOMChildrenConstructedFromString(dom, elmt, fieldElmts != null ? fieldElmts : {} );
1044 -
1045 -    return dom;
1046 -};
1047 -
1048 -SimileAjax.DOM._processDOMConstructedFromString = function(dom, elmt, fieldElmts) {
1049 -    var id = elmt.id;
1050 -    if (id != null && id.length > 0) {
1051 -        elmt.removeAttribute("id");
1052 -        if (id in fieldElmts) {
1053 -            var parentElmt = elmt.parentNode;
1054 -            parentElmt.insertBefore(fieldElmts[id], elmt);
1055 -            parentElmt.removeChild(elmt);
1056 -
1057 -            dom[id] = fieldElmts[id];
1058 -            return;
1059 -        } else {
1060 -            dom[id] = elmt;
1061 -        }
1062 -    }
1063 -
1064 -    if (elmt.hasChildNodes()) {
1065 -        SimileAjax.DOM._processDOMChildrenConstructedFromString(dom, elmt, fieldElmts);
1066 -    }
1067 -};
1068 -
1069 -SimileAjax.DOM._processDOMChildrenConstructedFromString = function(dom, elmt, fieldElmts) {
1070 -    var node = elmt.firstChild;
1071 -    while (node != null) {
1072 -        var node2 = node.nextSibling;
1073 -        if (node.nodeType == 1) {
1074 -            SimileAjax.DOM._processDOMConstructedFromString(dom, node, fieldElmts);
1075 -        }
1076 -        node = node2;
1077 -    }
1078 -};
1079 -/**
1080 - * @fileOverview Graphics utility functions and constants
1081 - * @name SimileAjax.Graphics
1082 - */
1083 -
1084 -SimileAjax.Graphics = new Object();
1085 -
1086 -/**
1087 - * A boolean value indicating whether PNG translucency is supported on the
1088 - * user's browser or not.
1089 - *
1090 - * @type Boolean
1091 - */
1092 -SimileAjax.Graphics.pngIsTranslucent = (!SimileAjax.Platform.browser.isIE) || (SimileAjax.Platform.browser.majorVersion > 6);
1093 -if (!SimileAjax.Graphics.pngIsTranslucent) {
1094 -    SimileAjax.includeCssFile(document, SimileAjax.urlPrefix + "styles/graphics-ie6.css");
1095 -}
1096 -
1097 -/*
1098 - *  Opacity, translucency
1099 - *
1100 - */
1101 -SimileAjax.Graphics._createTranslucentImage1 = function(url, verticalAlign) {
1102 -    var elmt = document.createElement("img");
1103 -    elmt.setAttribute("src", url);
1104 -    if (verticalAlign != null) {
1105 -        elmt.style.verticalAlign = verticalAlign;
1106 -    }
1107 -    return elmt;
1108 -};
1109 -SimileAjax.Graphics._createTranslucentImage2 = function(url, verticalAlign) {
1110 -    var elmt = document.createElement("img");
1111 -    elmt.style.width = "1px";  // just so that IE will calculate the size property
1112 -    elmt.style.height = "1px";
1113 -    elmt.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + url +"', sizingMethod='image')";
1114 -    elmt.style.verticalAlign = (verticalAlign != null) ? verticalAlign : "middle";
1115 -    return elmt;
1116 -};
1117 -
1118 -/**
1119 - * Creates a DOM element for an <code>img</code> tag using the URL given. This
1120 - * is a convenience method that automatically includes the necessary CSS to
1121 - * allow for translucency, even on IE.
1122 - *
1123 - * @function
1124 - * @param {String} url the URL to the image
1125 - * @param {String} verticalAlign the CSS value for the image's vertical-align
1126 - * @return {Element} a DOM element containing the <code>img</code> tag
1127 - */
1128 -SimileAjax.Graphics.createTranslucentImage = SimileAjax.Graphics.pngIsTranslucent ?
1129 -    SimileAjax.Graphics._createTranslucentImage1 :
1130 -    SimileAjax.Graphics._createTranslucentImage2;
1131 -
1132 -SimileAjax.Graphics._createTranslucentImageHTML1 = function(url, verticalAlign) {
1133 -    return "<img src=\"" + url + "\"" +
1134 -        (verticalAlign != null ? " style=\"vertical-align: " + verticalAlign + ";\"" : "") +
1135 -        " />";
1136 -};
1137 -SimileAjax.Graphics._createTranslucentImageHTML2 = function(url, verticalAlign) {
1138 -    var style =
1139 -        "width: 1px; height: 1px; " +
1140 -        "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + url +"', sizingMethod='image');" +
1141 -        (verticalAlign != null ? " vertical-align: " + verticalAlign + ";" : "");
1142 -
1143 -    return "<img src='" + url + "' style=\"" + style + "\" />";
1144 -};
1145 -
1146 -/**
1147 - * Creates an HTML string for an <code>img</code> tag using the URL given.
1148 - * This is a convenience method that automatically includes the necessary CSS
1149 - * to allow for translucency, even on IE.
1150 - *
1151 - * @function
1152 - * @param {String} url the URL to the image
1153 - * @param {String} verticalAlign the CSS value for the image's vertical-align
1154 - * @return {String} a string containing the <code>img</code> tag
1155 - */
1156 -SimileAjax.Graphics.createTranslucentImageHTML = SimileAjax.Graphics.pngIsTranslucent ?
1157 -    SimileAjax.Graphics._createTranslucentImageHTML1 :
1158 -    SimileAjax.Graphics._createTranslucentImageHTML2;
1159 -
1160 -/**
1161 - * Sets the opacity on the given DOM element.
1162 - *
1163 - * @param {Element} elmt the DOM element to set the opacity on
1164 - * @param {Number} opacity an integer from 0 to 100 specifying the opacity
1165 - */
1166 -SimileAjax.Graphics.setOpacity = function(elmt, opacity) {
1167 -    if (SimileAjax.Platform.browser.isIE) {
1168 -        elmt.style.filter = "progid:DXImageTransform.Microsoft.Alpha(Style=0,Opacity=" + opacity + ")";
1169 -    } else {
1170 -        var o = (opacity / 100).toString();
1171 -        elmt.style.opacity = o;
1172 -        elmt.style.MozOpacity = o;
1173 -    }
1174 -};
1175 -
1176 -/*
1177 - *  Bubble
1178 - *
1179 - */
1180 -
1181 -SimileAjax.Graphics.bubbleConfig = {
1182 -    containerCSSClass:              "simileAjax-bubble-container",
1183 -    innerContainerCSSClass:         "simileAjax-bubble-innerContainer",
1184 -    contentContainerCSSClass:       "simileAjax-bubble-contentContainer",
1185 -
1186 -    borderGraphicSize:              50,
1187 -    borderGraphicCSSClassPrefix:    "simileAjax-bubble-border-",
1188 -
1189 -    arrowGraphicTargetOffset:       33,  // from tip of arrow to the side of the graphic that touches the content of the bubble
1190 -    arrowGraphicLength:             100, // dimension of arrow graphic along the direction that the arrow points
1191 -    arrowGraphicWidth:              49,  // dimension of arrow graphic perpendicular to the direction that the arrow points
1192 -    arrowGraphicCSSClassPrefix:     "simileAjax-bubble-arrow-",
1193 -
1194 -    closeGraphicCSSClass:           "simileAjax-bubble-close",
1195 -
1196 -    extraPadding:                   20
1197 -};
1198 -
1199 -/**
1200 - * Creates a nice, rounded bubble popup with the given content in a div,
1201 - * page coordinates and a suggested width. The bubble will point to the
1202 - * location on the page as described by pageX and pageY.  All measurements
1203 - * should be given in pixels.
1204 - *
1205 - * @param {Element} the content div
1206 - * @param {Number} pageX the x coordinate of the point to point to
1207 - * @param {Number} pageY the y coordinate of the point to point to
1208 - * @param {Number} contentWidth a suggested width of the content
1209 - * @param {String} orientation a string ("top", "bottom", "left", or "right")
1210 - *   that describes the orientation of the arrow on the bubble
1211 - * @param {Number} maxHeight. Add a scrollbar div if bubble would be too tall.
1212 - *   Default of 0 or null means no maximum
1213 - */
1214 -SimileAjax.Graphics.createBubbleForContentAndPoint = function(
1215 -       div, pageX, pageY, contentWidth, orientation, maxHeight) {
1216 -    if (typeof contentWidth != "number") {
1217 -        contentWidth = 300;
1218 -    }
1219 -    if (typeof maxHeight != "number") {
1220 -        maxHeight = 0;
1221 -    }
1222 -
1223 -    div.style.position = "absolute";
1224 -    div.style.left = "-5000px";
1225 -    div.style.top = "0px";
1226 -    div.style.width = contentWidth + "px";
1227 -    document.body.appendChild(div);
1228 -
1229 -    window.setTimeout(function() {
1230 -        var width = div.scrollWidth + 10;
1231 -        var height = div.scrollHeight + 10;
1232 -        var scrollDivW = 0; // width of the possible inner container when we want vertical scrolling
1233 -        if (maxHeight > 0 && height > maxHeight) {
1234 -          height = maxHeight;
1235 -          scrollDivW = width - 25;
1236 -        }
1237 -
1238 -        var bubble = SimileAjax.Graphics.createBubbleForPoint(pageX, pageY, width, height, orientation);
1239 -
1240 -        document.body.removeChild(div);
1241 -        div.style.position = "static";
1242 -        div.style.left = "";
1243 -        div.style.top = "";
1244 -
1245 -        // create a scroll div if needed
1246 -        if (scrollDivW > 0) {
1247 -          var scrollDiv = document.createElement("div");
1248 -          div.style.width = "";
1249 -          scrollDiv.style.width = scrollDivW + "px";
1250 -          scrollDiv.appendChild(div);
1251 -          bubble.content.appendChild(scrollDiv);
1252 -        } else {
1253 -          div.style.width = width + "px";
1254 -          bubble.content.appendChild(div);
1255 -        }
1256 -    }, 200);
1257 -};
1258 -
1259 -/**
1260 - * Creates a nice, rounded bubble popup with the given page coordinates and
1261 - * content dimensions.  The bubble will point to the location on the page
1262 - * as described by pageX and pageY.  All measurements should be given in
1263 - * pixels.
1264 - *
1265 - * @param {Number} pageX the x coordinate of the point to point to
1266 - * @param {Number} pageY the y coordinate of the point to point to
1267 - * @param {Number} contentWidth the width of the content box in the bubble
1268 - * @param {Number} contentHeight the height of the content box in the bubble
1269 - * @param {String} orientation a string ("top", "bottom", "left", or "right")
1270 - *   that describes the orientation of the arrow on the bubble
1271 - * @return {Element} a DOM element for the newly created bubble
1272 - */
1273 -SimileAjax.Graphics.createBubbleForPoint = function(pageX, pageY, contentWidth, contentHeight, orientation) {
1274 -    contentWidth = parseInt(contentWidth, 10); // harden against bad input bugs
1275 -    contentHeight = parseInt(contentHeight, 10); // getting numbers-as-strings
1276 -
1277 -    var bubbleConfig = SimileAjax.Graphics.bubbleConfig;
1278 -    var pngTransparencyClassSuffix =
1279 -        SimileAjax.Graphics.pngIsTranslucent ? "pngTranslucent" : "pngNotTranslucent";
1280 -
1281 -    var bubbleWidth = contentWidth + 2 * bubbleConfig.borderGraphicSize;
1282 -    var bubbleHeight = contentHeight + 2 * bubbleConfig.borderGraphicSize;
1283 -
1284 -    var generatePngSensitiveClass = function(className) {
1285 -        return className + " " + className + "-" + pngTransparencyClassSuffix;
1286 -    };
1287 -
1288 -    /*
1289 -     *  Render container divs
1290 -     */
1291 -    var div = document.createElement("div");
1292 -    div.className = generatePngSensitiveClass(bubbleConfig.containerCSSClass);
1293 -    div.style.width = contentWidth + "px";
1294 -    div.style.height = contentHeight + "px";
1295 -
1296 -    var divInnerContainer = document.createElement("div");
1297 -    divInnerContainer.className = generatePngSensitiveClass(bubbleConfig.innerContainerCSSClass);
1298 -    div.appendChild(divInnerContainer);
1299 -
1300 -    /*
1301 -     *  Create layer for bubble
1302 -     */
1303 -    var close = function() {
1304 -        if (!bubble._closed) {
1305 -            document.body.removeChild(bubble._div);
1306 -            bubble._doc = null;
1307 -            bubble._div = null;
1308 -            bubble._content = null;
1309 -            bubble._closed = true;
1310 -        }
1311 -    }
1312 -    var bubble = { _closed: false };
1313 -    var layer = SimileAjax.WindowManager.pushLayer(close, true, div);
1314 -    bubble._div = div;
1315 -    bubble.close = function() { SimileAjax.WindowManager.popLayer(layer); }
1316 -
1317 -    /*
1318 -     *  Render border graphics
1319 -     */
1320 -    var createBorder = function(classNameSuffix) {
1321 -        var divBorderGraphic = document.createElement("div");
1322 -        divBorderGraphic.className = generatePngSensitiveClass(bubbleConfig.borderGraphicCSSClassPrefix + classNameSuffix);
1323 -        divInnerContainer.appendChild(divBorderGraphic);
1324 -    };
1325 -    createBorder("top-left");
1326 -    createBorder("top-right");
1327 -    createBorder("bottom-left");
1328 -    createBorder("bottom-right");
1329 -    createBorder("left");
1330 -    createBorder("right");
1331 -    createBorder("top");
1332 -    createBorder("bottom");
1333 -
1334 -    /*
1335 -     *  Render content
1336 -     */
1337 -    var divContentContainer = document.createElement("div");
1338 -    divContentContainer.className = generatePngSensitiveClass(bubbleConfig.contentContainerCSSClass);
1339 -    divInnerContainer.appendChild(divContentContainer);
1340 -    bubble.content = divContentContainer;
1341 -
1342 -    /*
1343 -     *  Render close button
1344 -     */
1345 -    var divClose = document.createElement("div");
1346 -    divClose.className = generatePngSensitiveClass(bubbleConfig.closeGraphicCSSClass);
1347 -    divInnerContainer.appendChild(divClose);
1348 -    SimileAjax.WindowManager.registerEventWithObject(divClose, "click", bubble, "close");
1349 -
1350 -    (function() {
1351 -        var dims = SimileAjax.Graphics.getWindowDimensions();
1352 -        var docWidth = dims.w;
1353 -        var docHeight = dims.h;
1354 -
1355 -        var halfArrowGraphicWidth = Math.ceil(bubbleConfig.arrowGraphicWidth / 2);
1356 -
1357 -        var createArrow = function(classNameSuffix) {
1358 -            var divArrowGraphic = document.createElement("div");
1359 -            divArrowGraphic.className = generatePngSensitiveClass(bubbleConfig.arrowGraphicCSSClassPrefix + "point-" + classNameSuffix);
1360 -            divInnerContainer.appendChild(divArrowGraphic);
1361 -            return divArrowGraphic;
1362 -        };
1363 -
1364 -        if (pageX - halfArrowGraphicWidth - bubbleConfig.borderGraphicSize - bubbleConfig.extraPadding > 0 &&
1365 -            pageX + halfArrowGraphicWidth + bubbleConfig.borderGraphicSize + bubbleConfig.extraPadding < docWidth) {
1366 -
1367 -            /*
1368 -             *  Bubble can be positioned above or below the target point.
1369 -             */
1370 -
1371 -            var left = pageX - Math.round(contentWidth / 2);
1372 -            left = pageX < (docWidth / 2) ?
1373 -                Math.max(left, bubbleConfig.extraPadding + bubbleConfig.borderGraphicSize) :
1374 -                Math.min(left, docWidth - bubbleConfig.extraPadding - bubbleConfig.borderGraphicSize - contentWidth);
1375 -
1376 -            if ((orientation && orientation == "top") ||
1377 -                (!orientation &&
1378 -                    (pageY
1379 -                        - bubbleConfig.arrowGraphicTargetOffset
1380 -                        - contentHeight
1381 -                        - bubbleConfig.borderGraphicSize
1382 -                        - bubbleConfig.extraPadding > 0))) {
1383 -
1384 -                /*
1385 -                 *  Position bubble above the target point.
1386 -                 */
1387 -
1388 -                var divArrow = createArrow("down");
1389 -                divArrow.style.left = (pageX - halfArrowGraphicWidth - left) + "px";
1390 -
1391 -                div.style.left = left + "px";
1392 -                div.style.top = (pageY - bubbleConfig.arrowGraphicTargetOffset - contentHeight) + "px";
1393 -
1394 -                return;
1395 -            } else if ((orientation && orientation == "bottom") ||
1396 -                (!orientation &&
1397 -                    (pageY
1398 -                        + bubbleConfig.arrowGraphicTargetOffset
1399 -                        + contentHeight
1400 -                        + bubbleConfig.borderGraphicSize
1401 -                        + bubbleConfig.extraPadding < docHeight))) {
1402 -
1403 -                /*
1404 -                 *  Position bubble below the target point.
1405 -                 */
1406 -
1407 -                var divArrow = createArrow("up");
1408 -                divArrow.style.left = (pageX - halfArrowGraphicWidth - left) + "px";
1409 -
1410 -                div.style.left = left + "px";
1411 -                div.style.top = (pageY + bubbleConfig.arrowGraphicTargetOffset) + "px";
1412 -
1413 -                return;
1414 -            }
1415 -        }
1416 -
1417 -        var top = pageY - Math.round(contentHeight / 2);
1418 -        top = pageY < (docHeight / 2) ?
1419 -            Math.max(top, bubbleConfig.extraPadding + bubbleConfig.borderGraphicSize) :
1420 -            Math.min(top, docHeight - bubbleConfig.extraPadding - bubbleConfig.borderGraphicSize - contentHeight);
1421 -
1422 -        if ((orientation && orientation == "left") ||
1423 -            (!orientation &&
1424 -                (pageX
1425 -                    - bubbleConfig.arrowGraphicTargetOffset
1426 -                    - contentWidth
1427 -                    - bubbleConfig.borderGraphicSize
1428 -                    - bubbleConfig.extraPadding > 0))) {
1429 -
1430 -            /*
1431 -             *  Position bubble left of the target point.
1432 -             */
1433 -
1434 -            var divArrow = createArrow("right");
1435 -            divArrow.style.top = (pageY - halfArrowGraphicWidth - top) + "px";
1436 -
1437 -            div.style.top = top + "px";
1438 -            div.style.left = (pageX - bubbleConfig.arrowGraphicTargetOffset - contentWidth) + "px";
1439 -        } else {
1440 -
1441 -            /*
1442 -             *  Position bubble right of the target point, as the last resort.
1443 -             */
1444 -
1445 -            var divArrow = createArrow("left");
1446 -            divArrow.style.top = (pageY - halfArrowGraphicWidth - top) + "px";
1447 -
1448 -            div.style.top = top + "px";
1449 -            div.style.left = (pageX + bubbleConfig.arrowGraphicTargetOffset) + "px";
1450 -        }
1451 -    })();
1452 -
1453 -    document.body.appendChild(div);
1454 -
1455 -    return bubble;
1456 -};
1457 -
1458 -SimileAjax.Graphics.getWindowDimensions = function() {
1459 -    if (typeof window.innerHeight == 'number') {
1460 -        return { w:window.innerWidth, h:window.innerHeight }; // Non-IE
1461 -    } else if (document.documentElement && document.documentElement.clientHeight) {
1462 -        return { // IE6+, in "standards compliant mode"
1463 -            w:document.documentElement.clientWidth,
1464 -            h:document.documentElement.clientHeight
1465 -        };
1466 -    } else if (document.body && document.body.clientHeight) {
1467 -        return { // IE 4 compatible
1468 -            w:document.body.clientWidth,
1469 -            h:document.body.clientHeight
1470 -        };
1471 -    }
1472 -};
1473 -
1474 -
1475 -/**
1476 - * Creates a floating, rounded message bubble in the center of the window for
1477 - * displaying modal information, e.g. "Loading..."
1478 - *
1479 - * @param {Document} doc the root document for the page to render on
1480 - * @param {Object} an object with two properties, contentDiv and containerDiv,
1481 - *   consisting of the newly created DOM elements
1482 - */
1483 -SimileAjax.Graphics.createMessageBubble = function(doc) {
1484 -    var containerDiv = doc.createElement("div");
1485 -    if (SimileAjax.Graphics.pngIsTranslucent) {
1486 -        var topDiv = doc.createElement("div");
1487 -        topDiv.style.height = "33px";
1488 -        topDiv.style.background = "url(" + SimileAjax.urlPrefix + "images/message-top-left.png) top left no-repeat";
1489 -        topDiv.style.paddingLeft = "44px";
1490 -        containerDiv.appendChild(topDiv);
1491 -
1492 -        var topRightDiv = doc.createElement("div");
1493 -        topRightDiv.style.height = "33px";
1494 -        topRightDiv.style.background = "url(" + SimileAjax.urlPrefix + "images/message-top-right.png) top right no-repeat";
1495 -        topDiv.appendChild(topRightDiv);
1496 -
1497 -        var middleDiv = doc.createElement("div");
1498 -        middleDiv.style.background = "url(" + SimileAjax.urlPrefix + "images/message-left.png) top left repeat-y";
1499 -        middleDiv.style.paddingLeft = "44px";
1500 -        containerDiv.appendChild(middleDiv);
1501 -
1502 -        var middleRightDiv = doc.createElement("div");
1503 -        middleRightDiv.style.background = "url(" + SimileAjax.urlPrefix + "images/message-right.png) top right repeat-y";
1504 -        middleRightDiv.style.paddingRight = "44px";
1505 -        middleDiv.appendChild(middleRightDiv);
1506 -
1507 -        var contentDiv = doc.createElement("div");
1508 -        middleRightDiv.appendChild(contentDiv);
1509 -
1510 -        var bottomDiv = doc.createElement("div");
1511 -        bottomDiv.style.height = "55px";
1512 -        bottomDiv.style.background = "url(" + SimileAjax.urlPrefix + "images/message-bottom-left.png) bottom left no-repeat";
1513 -        bottomDiv.style.paddingLeft = "44px";
1514 -        containerDiv.appendChild(bottomDiv);
1515 -
1516 -        var bottomRightDiv = doc.createElement("div");
1517 -        bottomRightDiv.style.height = "55px";
1518 -        bottomRightDiv.style.background = "url(" + SimileAjax.urlPrefix + "images/message-bottom-right.png) bottom right no-repeat";
1519 -        bottomDiv.appendChild(bottomRightDiv);
1520 -    } else {
1521 -        containerDiv.style.border = "2px solid #7777AA";
1522 -        containerDiv.style.padding = "20px";
1523 -        containerDiv.style.background = "white";
1524 -        SimileAjax.Graphics.setOpacity(containerDiv, 90);
1525 -
1526 -        var contentDiv = doc.createElement("div");
1527 -        containerDiv.appendChild(contentDiv);
1528 -    }
1529 -
1530 -    return {
1531 -        containerDiv:   containerDiv,
1532 -        contentDiv:     contentDiv
1533 -    };
1534 -};
1535 -
1536 -/*
1537 - *  Animation
1538 - *
1539 - */
1540 -
1541 -/**
1542 - * Creates an animation for a function, and an interval of values.  The word
1543 - * "animation" here is used in the sense of repeatedly calling a function with
1544 - * a current value from within an interval, and a delta value.
1545 - *
1546 - * @param {Function} f a function to be called every 50 milliseconds throughout
1547 - *   the animation duration, of the form f(current, delta), where current is
1548 - *   the current value within the range and delta is the current change.
1549 - * @param {Number} from a starting value
1550 - * @param {Number} to an ending value
1551 - * @param {Number} duration the duration of the animation in milliseconds
1552 - * @param {Function} [cont] an optional function that is called at the end of
1553 - *   the animation, i.e. a continuation.
1554 - * @return {SimileAjax.Graphics._Animation} a new animation object
1555 - */
1556 -SimileAjax.Graphics.createAnimation = function(f, from, to, duration, cont) {
1557 -    return new SimileAjax.Graphics._Animation(f, from, to, duration, cont);
1558 -};
1559 -
1560 -SimileAjax.Graphics._Animation = function(f, from, to, duration, cont) {
1561 -    this.f = f;
1562 -    this.cont = (typeof cont == "function") ? cont : function() {};
1563 -
1564 -    this.from = from;
1565 -    this.to = to;
1566 -    this.current = from;
1567 -
1568 -    this.duration = duration;
1569 -    this.start = new Date().getTime();
1570 -    this.timePassed = 0;
1571 -};
1572 -
1573 -/**
1574 - * Runs this animation.
1575 - */
1576 -SimileAjax.Graphics._Animation.prototype.run = function() {
1577 -    var a = this;
1578 -    window.setTimeout(function() { a.step(); }, 50);
1579 -};
1580 -
1581 -/**
1582 - * Increments this animation by one step, and then continues the animation with
1583 - * <code>run()</code>.
1584 - */
1585 -SimileAjax.Graphics._Animation.prototype.step = function() {
1586 -    this.timePassed += 50;
1587 -
1588 -    var timePassedFraction = this.timePassed / this.duration;
1589 -    var parameterFraction = -Math.cos(timePassedFraction * Math.PI) / 2 + 0.5;
1590 -    var current = parameterFraction * (this.to - this.from) + this.from;
1591 -
1592 -    try {
1593 -        this.f(current, current - this.current);
1594 -    } catch (e) {
1595 -    }
1596 -    this.current = current;
1597 -
1598 -    if (this.timePassed < this.duration) {
1599 -        this.run();
1600 -    } else {
1601 -        this.f(this.to, 0);
1602 -        this["cont"]();
1603 -    }
1604 -};
1605 -
1606 -/*
1607 - *  CopyPasteButton
1608 - *
1609 - *  Adapted from http://spaces.live.com/editorial/rayozzie/demo/liveclip/liveclipsample/techPreview.html.
1610 - *
1611 - */
1612 -
1613 -/**
1614 - * Creates a button and textarea for displaying structured data and copying it
1615 - * to the clipboard.  The data is dynamically generated by the given
1616 - * createDataFunction parameter.
1617 - *
1618 - * @param {String} image an image URL to use as the background for the
1619 - *   generated box
1620 - * @param {Number} width the width in pixels of the generated box
1621 - * @param {Number} height the height in pixels of the generated box
1622 - * @param {Function} createDataFunction a function that is called with no
1623 - *   arguments to generate the structured data
1624 - * @return a new DOM element
1625 - */
1626 -SimileAjax.Graphics.createStructuredDataCopyButton = function(image, width, height, createDataFunction) {
1627 -    var div = document.createElement("div");
1628 -    div.style.position = "relative";
1629 -    div.style.display = "inline";
1630 -    div.style.width = width + "px";
1631 -    div.style.height = height + "px";
1632 -    div.style.overflow = "hidden";
1633 -    div.style.margin = "2px";
1634 -
1635 -    if (SimileAjax.Graphics.pngIsTranslucent) {
1636 -        div.style.background = "url(" + image + ") no-repeat";
1637 -    } else {
1638 -        div.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + image +"', sizingMethod='image')";
1639 -    }
1640 -
1641 -    var style;
1642 -    if (SimileAjax.Platform.browser.isIE) {
1643 -        style = "filter:alpha(opacity=0)";
1644 -    } else {
1645 -        style = "opacity: 0";
1646 -    }
1647 -    div.innerHTML = "<textarea rows='1' autocomplete='off' value='none' style='" + style + "' />";
1648 -
1649 -    var textarea = div.firstChild;
1650 -    textarea.style.width = width + "px";
1651 -    textarea.style.height = height + "px";
1652 -    textarea.onmousedown = function(evt) {
1653 -        evt = (evt) ? evt : ((event) ? event : null);
1654 -        if (evt.button == 2) {
1655 -            textarea.value = createDataFunction();
1656 -            textarea.select();
1657 -        }
1658 -    };
1659 -
1660 -    return div;
1661 -};
1662 -
1663 -/*
1664 - *  getWidthHeight
1665 - *
1666 - */
1667 -SimileAjax.Graphics.getWidthHeight = function(el) {
1668 -    // RETURNS hash {width:  w, height: h} in pixels
1669 -
1670 -    var w, h;
1671 -    // offsetWidth rounds on FF, so doesn't work for us.
1672 -    // See https://bugzilla.mozilla.org/show_bug.cgi?id=458617
1673 -    if (el.getBoundingClientRect == null) {
1674 -    	// use offsetWidth
1675 -      w = el.offsetWidth;
1676 -      h = el.offsetHeight;
1677 -    } else {
1678 -    	// use getBoundingClientRect
1679 -      var rect = el.getBoundingClientRect();

1680 -      w = Math.ceil(rect.right - rect.left);

1681 -    	h = Math.ceil(rect.bottom - rect.top);
1682 -    }
1683 -    return {
1684 -        width:  w,
1685 -        height: h
1686 -    };
1687 -};
1688 -
1689 -
1690 -/*
1691 - *  FontRenderingContext
1692 - *
1693 - */
1694 -SimileAjax.Graphics.getFontRenderingContext = function(elmt, width) {
1695 -    return new SimileAjax.Graphics._FontRenderingContext(elmt, width);
1696 -};
1697 -
1698 -SimileAjax.Graphics._FontRenderingContext = function(elmt, width) {
1699 -    this._elmt = elmt;
1700 -    this._elmt.style.visibility = "hidden";
1701 -    if (typeof width == "string") {
1702 -        this._elmt.style.width = width;
1703 -    } else if (typeof width == "number") {
1704 -        this._elmt.style.width = width + "px";
1705 -    }
1706 -};
1707 -
1708 -SimileAjax.Graphics._FontRenderingContext.prototype.dispose = function() {
1709 -    this._elmt = null;
1710 -};
1711 -
1712 -SimileAjax.Graphics._FontRenderingContext.prototype.update = function() {
1713 -    this._elmt.innerHTML = "A";
1714 -    this._lineHeight = this._elmt.offsetHeight;
1715 -};
1716 -
1717 -SimileAjax.Graphics._FontRenderingContext.prototype.computeSize = function(text, className) {
1718 -    // className arg is optional
1719 -    var el = this._elmt;
1720 -    el.innerHTML = text;
1721 -    el.className = className === undefined ? '' : className;
1722 -    var wh = SimileAjax.Graphics.getWidthHeight(el);

1723 -    el.className = ''; // reset for the next guy
1724 -
1725 -    return wh;
1726 -};
1727 -
1728 -SimileAjax.Graphics._FontRenderingContext.prototype.getLineHeight = function() {
1729 -    return this._lineHeight;
1730 -};
1731 -
1732 -/**
1733 - * @fileOverview A collection of date/time utility functions
1734 - * @name SimileAjax.DateTime
1735 - */
1736 -
1737 -SimileAjax.DateTime = new Object();
1738 -
1739 -SimileAjax.DateTime.MILLISECOND    = 0;
1740 -SimileAjax.DateTime.SECOND         = 1;
1741 -SimileAjax.DateTime.MINUTE         = 2;
1742 -SimileAjax.DateTime.HOUR           = 3;
1743 -SimileAjax.DateTime.DAY            = 4;
1744 -SimileAjax.DateTime.WEEK           = 5;
1745 -SimileAjax.DateTime.MONTH          = 6;
1746 -SimileAjax.DateTime.YEAR           = 7;
1747 -SimileAjax.DateTime.DECADE         = 8;
1748 -SimileAjax.DateTime.CENTURY        = 9;
1749 -SimileAjax.DateTime.MILLENNIUM     = 10;
1750 -
1751 -SimileAjax.DateTime.EPOCH          = -1;
1752 -SimileAjax.DateTime.ERA            = -2;
1753 -
1754 -/**
1755 - * An array of unit lengths, expressed in milliseconds, of various lengths of
1756 - * time.  The array indices are predefined and stored as properties of the
1757 - * SimileAjax.DateTime object, e.g. SimileAjax.DateTime.YEAR.
1758 - * @type Array
1759 - */
1760 -SimileAjax.DateTime.gregorianUnitLengths = [];
1761 -    (function() {
1762 -        var d = SimileAjax.DateTime;
1763 -        var a = d.gregorianUnitLengths;
1764 -
1765 -        a[d.MILLISECOND] = 1;
1766 -        a[d.SECOND]      = 1000;
1767 -        a[d.MINUTE]      = a[d.SECOND] * 60;
1768 -        a[d.HOUR]        = a[d.MINUTE] * 60;
1769 -        a[d.DAY]         = a[d.HOUR] * 24;
1770 -        a[d.WEEK]        = a[d.DAY] * 7;
1771 -        a[d.MONTH]       = a[d.DAY] * 31;
1772 -        a[d.YEAR]        = a[d.DAY] * 365;
1773 -        a[d.DECADE]      = a[d.YEAR] * 10;
1774 -        a[d.CENTURY]     = a[d.YEAR] * 100;
1775 -        a[d.MILLENNIUM]  = a[d.YEAR] * 1000;
1776 -    })();
1777 -
1778 -SimileAjax.DateTime._dateRegexp = new RegExp(
1779 -    "^(-?)([0-9]{4})(" + [
1780 -        "(-?([0-9]{2})(-?([0-9]{2}))?)", // -month-dayOfMonth
1781 -        "(-?([0-9]{3}))",                // -dayOfYear
1782 -        "(-?W([0-9]{2})(-?([1-7]))?)"    // -Wweek-dayOfWeek
1783 -    ].join("|") + ")?$"
1784 -);
1785 -SimileAjax.DateTime._timezoneRegexp = new RegExp(
1786 -    "Z|(([-+])([0-9]{2})(:?([0-9]{2}))?)$"
1787 -);
1788 -SimileAjax.DateTime._timeRegexp = new RegExp(
1789 -    "^([0-9]{2})(:?([0-9]{2})(:?([0-9]{2})(\.([0-9]+))?)?)?$"
1790 -);
1791 -
1792 -/**
1793 - * Takes a date object and a string containing an ISO 8601 date and sets the
1794 - * the date using information parsed from the string.  Note that this method
1795 - * does not parse any time information.
1796 - *
1797 - * @param {Date} dateObject the date object to modify
1798 - * @param {String} string an ISO 8601 string to parse
1799 - * @return {Date} the modified date object
1800 - */
1801 -SimileAjax.DateTime.setIso8601Date = function(dateObject, string) {
1802 -    /*
1803 -     *  This function has been adapted from dojo.date, v.0.3.0
1804 -     *  http://dojotoolkit.org/.
1805 -     */
1806 -
1807 -    var d = string.match(SimileAjax.DateTime._dateRegexp);
1808 -    if(!d) {
1809 -        throw new Error("Invalid date string: " + string);
1810 -    }
1811 -
1812 -    var sign = (d[1] == "-") ? -1 : 1; // BC or AD
1813 -    var year = sign * d[2];
1814 -    var month = d[5];
1815 -    var date = d[7];
1816 -    var dayofyear = d[9];
1817 -    var week = d[11];
1818 -    var dayofweek = (d[13]) ? d[13] : 1;
1819 -
1820 -    dateObject.setUTCFullYear(year);
1821 -    if (dayofyear) {
1822 -        dateObject.setUTCMonth(0);
1823 -        dateObject.setUTCDate(Number(dayofyear));
1824 -    } else if (week) {
1825 -        dateObject.setUTCMonth(0);
1826 -        dateObject.setUTCDate(1);
1827 -        var gd = dateObject.getUTCDay();
1828 -        var day =  (gd) ? gd : 7;
1829 -        var offset = Number(dayofweek) + (7 * Number(week));
1830 -
1831 -        if (day <= 4) {
1832 -            dateObject.setUTCDate(offset + 1 - day);
1833 -        } else {
1834 -            dateObject.setUTCDate(offset + 8 - day);
1835 -        }
1836 -    } else {
1837 -        if (month) {
1838 -            dateObject.setUTCDate(1);
1839 -            dateObject.setUTCMonth(month - 1);
1840 -        }
1841 -        if (date) {
1842 -            dateObject.setUTCDate(date);
1843 -        }
1844 -    }
1845 -
1846 -    return dateObject;
1847 -};
1848 -
1849 -/**
1850 - * Takes a date object and a string containing an ISO 8601 time and sets the
1851 - * the time using information parsed from the string.  Note that this method
1852 - * does not parse any date information.
1853 - *
1854 - * @param {Date} dateObject the date object to modify
1855 - * @param {String} string an ISO 8601 string to parse
1856 - * @return {Date} the modified date object
1857 - */
1858 -SimileAjax.DateTime.setIso8601Time = function (dateObject, string) {
1859 -    /*
1860 -     *  This function has been adapted from dojo.date, v.0.3.0
1861 -     *  http://dojotoolkit.org/.
1862 -     */
1863 -
1864 -    var d = string.match(SimileAjax.DateTime._timeRegexp);
1865 -    if(!d) {
1866 -        SimileAjax.Debug.warn("Invalid time string: " + string);
1867 -        return false;
1868 -    }
1869 -    var hours = d[1];
1870 -    var mins = Number((d[3]) ? d[3] : 0);
1871 -    var secs = (d[5]) ? d[5] : 0;
1872 -    var ms = d[7] ? (Number("0." + d[7]) * 1000) : 0;
1873 -
1874 -    dateObject.setUTCHours(hours);
1875 -    dateObject.setUTCMinutes(mins);
1876 -    dateObject.setUTCSeconds(secs);
1877 -    dateObject.setUTCMilliseconds(ms);
1878 -
1879 -    return dateObject;
1880 -};
1881 -
1882 -/**
1883 - * The timezone offset in minutes in the user's browser.
1884 - * @type Number
1885 - */
1886 -SimileAjax.DateTime.timezoneOffset = new Date().getTimezoneOffset();
1887 -
1888 -/**
1889 - * Takes a date object and a string containing an ISO 8601 date and time and
1890 - * sets the date object using information parsed from the string.
1891 - *
1892 - * @param {Date} dateObject the date object to modify
1893 - * @param {String} string an ISO 8601 string to parse
1894 - * @return {Date} the modified date object
1895 - */
1896 -SimileAjax.DateTime.setIso8601 = function (dateObject, string){
1897 -    /*
1898 -     *  This function has been adapted from dojo.date, v.0.3.0
1899 -     *  http://dojotoolkit.org/.
1900 -     */
1901 -
1902 -    var offset = null;
1903 -    var comps = (string.indexOf("T") == -1) ? string.split(" ") : string.split("T");
1904 -
1905 -    SimileAjax.DateTime.setIso8601Date(dateObject, comps[0]);
1906 -    if (comps.length == 2) {
1907 -        // first strip timezone info from the end
1908 -        var d = comps[1].match(SimileAjax.DateTime._timezoneRegexp);
1909 -        if (d) {
1910 -            if (d[0] == 'Z') {
1911 -                offset = 0;
1912 -            } else {
1913 -                offset = (Number(d[3]) * 60) + Number(d[5]);
1914 -                offset *= ((d[2] == '-') ? 1 : -1);
1915 -            }
1916 -            comps[1] = comps[1].substr(0, comps[1].length - d[0].length);
1917 -        }
1918 -
1919 -        SimileAjax.DateTime.setIso8601Time(dateObject, comps[1]);
1920 -    }
1921 -    if (offset == null) {
1922 -        offset = dateObject.getTimezoneOffset(); // local time zone if no tz info
1923 -    }
1924 -    dateObject.setTime(dateObject.getTime() + offset * 60000);
1925 -
1926 -    return dateObject;
1927 -};
1928 -
1929 -/**
1930 - * Takes a string containing an ISO 8601 date and returns a newly instantiated
1931 - * date object with the parsed date and time information from the string.
1932 - *
1933 - * @param {String} string an ISO 8601 string to parse
1934 - * @return {Date} a new date object created from the string
1935 - */
1936 -SimileAjax.DateTime.parseIso8601DateTime = function (string) {
1937 -    try {
1938 -        return SimileAjax.DateTime.setIso8601(new Date(0), string);
1939 -    } catch (e) {
1940 -        return null;
1941 -    }
1942 -};
1943 -
1944 -/**
1945 - * Takes a string containing a Gregorian date and time and returns a newly
1946 - * instantiated date object with the parsed date and time information from the
1947 - * string.  If the param is actually an instance of Date instead of a string,
1948 - * simply returns the given date instead.
1949 - *
1950 - * @param {Object} o an object, to either return or parse as a string
1951 - * @return {Date} the date object
1952 - */
1953 -SimileAjax.DateTime.parseGregorianDateTime = function(o) {
1954 -    if (o == null) {
1955 -        return null;
1956 -    } else if (o instanceof Date) {
1957 -        return o;
1958 -    }
1959 -
1960 -    var s = o.toString();
1961 -    if (s.length > 0 && s.length < 8) {
1962 -        var space = s.indexOf(" ");
1963 -        if (space > 0) {
1964 -            var year = parseInt(s.substr(0, space));
1965 -            var suffix = s.substr(space + 1);
1966 -            if (suffix.toLowerCase() == "bc") {
1967 -                year = 1 - year;
1968 -            }
1969 -        } else {
1970 -            var year = parseInt(s);
1971 -        }
1972 -
1973 -        var d = new Date(0);
1974 -        d.setUTCFullYear(year);
1975 -
1976 -        return d;
1977 -    }
1978 -
1979 -    try {
1980 -        return new Date(Date.parse(s));
1981 -    } catch (e) {
1982 -        return null;
1983 -    }
1984 -};
1985 -
1986 -/**
1987 - * Rounds date objects down to the nearest interval or multiple of an interval.
1988 - * This method modifies the given date object, converting it to the given
1989 - * timezone if specified.
1990 - *
1991 - * @param {Date} date the date object to round
1992 - * @param {Number} intervalUnit a constant, integer index specifying an
1993 - *   interval, e.g. SimileAjax.DateTime.HOUR
1994 - * @param {Number} timeZone a timezone shift, given in hours
1995 - * @param {Number} multiple a multiple of the interval to round by
1996 - * @param {Number} firstDayOfWeek an integer specifying the first day of the
1997 - *   week, 0 corresponds to Sunday, 1 to Monday, etc.
1998 - */
1999 -SimileAjax.DateTime.roundDownToInterval = function(date, intervalUnit, timeZone, multiple, firstDayOfWeek) {
2000 -    var timeShift = timeZone *
2001 -        SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.HOUR];
2002 -
2003 -    var date2 = new Date(date.getTime() + timeShift);
2004 -    var clearInDay = function(d) {
2005 -        d.setUTCMilliseconds(0);
2006 -        d.setUTCSeconds(0);
2007 -        d.setUTCMinutes(0);
2008 -        d.setUTCHours(0);
2009 -    };
2010 -    var clearInYear = function(d) {
2011 -        clearInDay(d);
2012 -        d.setUTCDate(1);
2013 -        d.setUTCMonth(0);
2014 -    };
2015 -
2016 -    switch(intervalUnit) {
2017 -    case SimileAjax.DateTime.MILLISECOND:
2018 -        var x = date2.getUTCMilliseconds();
2019 -        date2.setUTCMilliseconds(x - (x % multiple));
2020 -        break;
2021 -    case SimileAjax.DateTime.SECOND:
2022 -        date2.setUTCMilliseconds(0);
2023 -
2024 -        var x = date2.getUTCSeconds();
2025 -        date2.setUTCSeconds(x - (x % multiple));
2026 -        break;
2027 -    case SimileAjax.DateTime.MINUTE:
2028 -        date2.setUTCMilliseconds(0);
2029 -        date2.setUTCSeconds(0);
2030 -
2031 -        var x = date2.getUTCMinutes();
2032 -        date2.setTime(date2.getTime() -
2033 -            (x % multiple) * SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.MINUTE]);
2034 -        break;
2035 -    case SimileAjax.DateTime.HOUR:
2036 -        date2.setUTCMilliseconds(0);
2037 -        date2.setUTCSeconds(0);
2038 -        date2.setUTCMinutes(0);
2039 -
2040 -        var x = date2.getUTCHours();
2041 -        date2.setUTCHours(x - (x % multiple));
2042 -        break;
2043 -    case SimileAjax.DateTime.DAY:
2044 -        clearInDay(date2);
2045 -        break;
2046 -    case SimileAjax.DateTime.WEEK:
2047 -        clearInDay(date2);
2048 -        var d = (date2.getUTCDay() + 7 - firstDayOfWeek) % 7;
2049 -        date2.setTime(date2.getTime() -
2050 -            d * SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.DAY]);
2051 -        break;
2052 -    case SimileAjax.DateTime.MONTH:
2053 -        clearInDay(date2);
2054 -        date2.setUTCDate(1);
2055 -
2056 -        var x = date2.getUTCMonth();
2057 -        date2.setUTCMonth(x - (x % multiple));
2058 -        break;
2059 -    case SimileAjax.DateTime.YEAR:
2060 -        clearInYear(date2);
2061 -
2062 -        var x = date2.getUTCFullYear();
2063 -        date2.setUTCFullYear(x - (x % multiple));
2064 -        break;
2065 -    case SimileAjax.DateTime.DECADE:
2066 -        clearInYear(date2);
2067 -        date2.setUTCFullYear(Math.floor(date2.getUTCFullYear() / 10) * 10);
2068 -        break;
2069 -    case SimileAjax.DateTime.CENTURY:
2070 -        clearInYear(date2);
2071 -        date2.setUTCFullYear(Math.floor(date2.getUTCFullYear() / 100) * 100);
2072 -        break;
2073 -    case SimileAjax.DateTime.MILLENNIUM:
2074 -        clearInYear(date2);
2075 -        date2.setUTCFullYear(Math.floor(date2.getUTCFullYear() / 1000) * 1000);
2076 -        break;
2077 -    }
2078 -
2079 -    date.setTime(date2.getTime() - timeShift);
2080 -};
2081 -
2082 -/**
2083 - * Rounds date objects up to the nearest interval or multiple of an interval.
2084 - * This method modifies the given date object, converting it to the given
2085 - * timezone if specified.
2086 - *
2087 - * @param {Date} date the date object to round
2088 - * @param {Number} intervalUnit a constant, integer index specifying an
2089 - *   interval, e.g. SimileAjax.DateTime.HOUR
2090 - * @param {Number} timeZone a timezone shift, given in hours
2091 - * @param {Number} multiple a multiple of the interval to round by
2092 - * @param {Number} firstDayOfWeek an integer specifying the first day of the
2093 - *   week, 0 corresponds to Sunday, 1 to Monday, etc.
2094 - * @see SimileAjax.DateTime.roundDownToInterval
2095 - */
2096 -SimileAjax.DateTime.roundUpToInterval = function(date, intervalUnit, timeZone, multiple, firstDayOfWeek) {
2097 -    var originalTime = date.getTime();
2098 -    SimileAjax.DateTime.roundDownToInterval(date, intervalUnit, timeZone, multiple, firstDayOfWeek);
2099 -    if (date.getTime() < originalTime) {
2100 -        date.setTime(date.getTime() +
2101 -            SimileAjax.DateTime.gregorianUnitLengths[intervalUnit] * multiple);
2102 -    }
2103 -};
2104 -
2105 -/**
2106 - * Increments a date object by a specified interval, taking into
2107 - * consideration the timezone.
2108 - *
2109 - * @param {Date} date the date object to increment
2110 - * @param {Number} intervalUnit a constant, integer index specifying an
2111 - *   interval, e.g. SimileAjax.DateTime.HOUR
2112 - * @param {Number} timeZone the timezone offset in hours
2113 - */
2114 -SimileAjax.DateTime.incrementByInterval = function(date, intervalUnit, timeZone) {
2115 -    timeZone = (typeof timeZone == 'undefined') ? 0 : timeZone;
2116 -
2117 -    var timeShift = timeZone *
2118 -        SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.HOUR];
2119 -
2120 -    var date2 = new Date(date.getTime() + timeShift);
2121 -
2122 -    switch(intervalUnit) {
2123 -    case SimileAjax.DateTime.MILLISECOND:
2124 -        date2.setTime(date2.getTime() + 1)
2125 -        break;
2126 -    case SimileAjax.DateTime.SECOND:
2127 -        date2.setTime(date2.getTime() + 1000);
2128 -        break;
2129 -    case SimileAjax.DateTime.MINUTE:
2130 -        date2.setTime(date2.getTime() +
2131 -            SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.MINUTE]);
2132 -        break;
2133 -    case SimileAjax.DateTime.HOUR:
2134 -        date2.setTime(date2.getTime() +
2135 -            SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.HOUR]);
2136 -        break;
2137 -    case SimileAjax.DateTime.DAY:
2138 -        date2.setUTCDate(date2.getUTCDate() + 1);
2139 -        break;
2140 -    case SimileAjax.DateTime.WEEK:
2141 -        date2.setUTCDate(date2.getUTCDate() + 7);
2142 -        break;
2143 -    case SimileAjax.DateTime.MONTH:
2144 -        date2.setUTCMonth(date2.getUTCMonth() + 1);
2145 -        break;
2146 -    case SimileAjax.DateTime.YEAR:
2147 -        date2.setUTCFullYear(date2.getUTCFullYear() + 1);
2148 -        break;
2149 -    case SimileAjax.DateTime.DECADE:
2150 -        date2.setUTCFullYear(date2.getUTCFullYear() + 10);
2151 -        break;
2152 -    case SimileAjax.DateTime.CENTURY:
2153 -        date2.setUTCFullYear(date2.getUTCFullYear() + 100);
2154 -        break;
2155 -    case SimileAjax.DateTime.MILLENNIUM:
2156 -        date2.setUTCFullYear(date2.getUTCFullYear() + 1000);
2157 -        break;
2158 -    }
2159 -
2160 -    date.setTime(date2.getTime() - timeShift);
2161 -};
2162 -
2163 -/**
2164 - * Returns a new date object with the given time offset removed.
2165 - *
2166 - * @param {Date} date the starting date
2167 - * @param {Number} timeZone a timezone specified in an hour offset to remove
2168 - * @return {Date} a new date object with the offset removed
2169 - */
2170 -SimileAjax.DateTime.removeTimeZoneOffset = function(date, timeZone) {
2171 -    return new Date(date.getTime() +
2172 -        timeZone * SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.HOUR]);
2173 -};
2174 -
2175 -/**
2176 - * Returns the timezone of the user's browser.
2177 - *
2178 - * @return {Number} the timezone in the user's locale in hours
2179 - */
2180 -SimileAjax.DateTime.getTimezone = function() {
2181 -    var d = new Date().getTimezoneOffset();
2182 -    return d / -60;
2183 -};
2184 -/*
2185 - *  String Utility Functions and Constants
2186 - *
2187 - */
2188 -
2189 -String.prototype.trim = function() {
2190 -    return this.replace(/^\s+|\s+$/g, '');
2191 -};
2192 -
2193 -String.prototype.startsWith = function(prefix) {
2194 -    return this.length >= prefix.length && this.substr(0, prefix.length) == prefix;
2195 -};
2196 -
2197 -String.prototype.endsWith = function(suffix) {
2198 -    return this.length >= suffix.length && this.substr(this.length - suffix.length) == suffix;
2199 -};
2200 -
2201 -String.substitute = function(s, objects) {
2202 -    var result = "";
2203 -    var start = 0;
2204 -    while (start < s.length - 1) {
2205 -        var percent = s.indexOf("%", start);
2206 -        if (percent < 0 || percent == s.length - 1) {
2207 -            break;
2208 -        } else if (percent > start && s.charAt(percent - 1) == "\\") {
2209 -            result += s.substring(start, percent - 1) + "%";
2210 -            start = percent + 1;
2211 -        } else {
2212 -            var n = parseInt(s.charAt(percent + 1));
2213 -            if (isNaN(n) || n >= objects.length) {
2214 -                result += s.substring(start, percent + 2);
2215 -            } else {
2216 -                result += s.substring(start, percent) + objects[n].toString();
2217 -            }
2218 -            start = percent + 2;
2219 -        }
2220 -    }
2221 -
2222 -    if (start < s.length) {
2223 -        result += s.substring(start);
2224 -    }
2225 -    return result;
2226 -};
2227 -/*
2228 - *  HTML Utility Functions
2229 - *
2230 - */
2231 -
2232 -SimileAjax.HTML = new Object();
2233 -
2234 -SimileAjax.HTML._e2uHash = {};
2235 -(function() {
2236 -    var e2uHash = SimileAjax.HTML._e2uHash;
2237 -    e2uHash['nbsp']= '\u00A0[space]';
2238 -    e2uHash['iexcl']= '\u00A1';
2239 -    e2uHash['cent']= '\u00A2';
2240 -    e2uHash['pound']= '\u00A3';
2241 -    e2uHash['curren']= '\u00A4';
2242 -    e2uHash['yen']= '\u00A5';
2243 -    e2uHash['brvbar']= '\u00A6';
2244 -    e2uHash['sect']= '\u00A7';
2245 -    e2uHash['uml']= '\u00A8';
2246 -    e2uHash['copy']= '\u00A9';
2247 -    e2uHash['ordf']= '\u00AA';
2248 -    e2uHash['laquo']= '\u00AB';
2249 -    e2uHash['not']= '\u00AC';
2250 -    e2uHash['shy']= '\u00AD';
2251 -    e2uHash['reg']= '\u00AE';
2252 -    e2uHash['macr']= '\u00AF';
2253 -    e2uHash['deg']= '\u00B0';
2254 -    e2uHash['plusmn']= '\u00B1';
2255 -    e2uHash['sup2']= '\u00B2';
2256 -    e2uHash['sup3']= '\u00B3';
2257 -    e2uHash['acute']= '\u00B4';
2258 -    e2uHash['micro']= '\u00B5';
2259 -    e2uHash['para']= '\u00B6';
2260 -    e2uHash['middot']= '\u00B7';
2261 -    e2uHash['cedil']= '\u00B8';
2262 -    e2uHash['sup1']= '\u00B9';
2263 -    e2uHash['ordm']= '\u00BA';
2264 -    e2uHash['raquo']= '\u00BB';
2265 -    e2uHash['frac14']= '\u00BC';
2266 -    e2uHash['frac12']= '\u00BD';
2267 -    e2uHash['frac34']= '\u00BE';
2268 -    e2uHash['iquest']= '\u00BF';
2269 -    e2uHash['Agrave']= '\u00C0';
2270 -    e2uHash['Aacute']= '\u00C1';
2271 -    e2uHash['Acirc']= '\u00C2';
2272 -    e2uHash['Atilde']= '\u00C3';
2273 -    e2uHash['Auml']= '\u00C4';
2274 -    e2uHash['Aring']= '\u00C5';
2275 -    e2uHash['AElig']= '\u00C6';
2276 -    e2uHash['Ccedil']= '\u00C7';
2277 -    e2uHash['Egrave']= '\u00C8';
2278 -    e2uHash['Eacute']= '\u00C9';
2279 -    e2uHash['Ecirc']= '\u00CA';
2280 -    e2uHash['Euml']= '\u00CB';
2281 -    e2uHash['Igrave']= '\u00CC';
2282 -    e2uHash['Iacute']= '\u00CD';
2283 -    e2uHash['Icirc']= '\u00CE';
2284 -    e2uHash['Iuml']= '\u00CF';
2285 -    e2uHash['ETH']= '\u00D0';
2286 -    e2uHash['Ntilde']= '\u00D1';
2287 -    e2uHash['Ograve']= '\u00D2';
2288 -    e2uHash['Oacute']= '\u00D3';
2289 -    e2uHash['Ocirc']= '\u00D4';
2290 -    e2uHash['Otilde']= '\u00D5';
2291 -    e2uHash['Ouml']= '\u00D6';
2292 -    e2uHash['times']= '\u00D7';
2293 -    e2uHash['Oslash']= '\u00D8';
2294 -    e2uHash['Ugrave']= '\u00D9';
2295 -    e2uHash['Uacute']= '\u00DA';
2296 -    e2uHash['Ucirc']= '\u00DB';
2297 -    e2uHash['Uuml']= '\u00DC';
2298 -    e2uHash['Yacute']= '\u00DD';
2299 -    e2uHash['THORN']= '\u00DE';
2300 -    e2uHash['szlig']= '\u00DF';
2301 -    e2uHash['agrave']= '\u00E0';
2302 -    e2uHash['aacute']= '\u00E1';
2303 -    e2uHash['acirc']= '\u00E2';
2304 -    e2uHash['atilde']= '\u00E3';
2305 -    e2uHash['auml']= '\u00E4';
2306 -    e2uHash['aring']= '\u00E5';
2307 -    e2uHash['aelig']= '\u00E6';
2308 -    e2uHash['ccedil']= '\u00E7';
2309 -    e2uHash['egrave']= '\u00E8';
2310 -    e2uHash['eacute']= '\u00E9';
2311 -    e2uHash['ecirc']= '\u00EA';
2312 -    e2uHash['euml']= '\u00EB';
2313 -    e2uHash['igrave']= '\u00EC';
2314 -    e2uHash['iacute']= '\u00ED';
2315 -    e2uHash['icirc']= '\u00EE';
2316 -    e2uHash['iuml']= '\u00EF';
2317 -    e2uHash['eth']= '\u00F0';
2318 -    e2uHash['ntilde']= '\u00F1';
2319 -    e2uHash['ograve']= '\u00F2';
2320 -    e2uHash['oacute']= '\u00F3';
2321 -    e2uHash['ocirc']= '\u00F4';
2322 -    e2uHash['otilde']= '\u00F5';
2323 -    e2uHash['ouml']= '\u00F6';
2324 -    e2uHash['divide']= '\u00F7';
2325 -    e2uHash['oslash']= '\u00F8';
2326 -    e2uHash['ugrave']= '\u00F9';
2327 -    e2uHash['uacute']= '\u00FA';
2328 -    e2uHash['ucirc']= '\u00FB';
2329 -    e2uHash['uuml']= '\u00FC';
2330 -    e2uHash['yacute']= '\u00FD';
2331 -    e2uHash['thorn']= '\u00FE';
2332 -    e2uHash['yuml']= '\u00FF';
2333 -    e2uHash['quot']= '\u0022';
2334 -    e2uHash['amp']= '\u0026';
2335 -    e2uHash['lt']= '\u003C';
2336 -    e2uHash['gt']= '\u003E';
2337 -    e2uHash['OElig']= '';
2338 -    e2uHash['oelig']= '\u0153';
2339 -    e2uHash['Scaron']= '\u0160';
2340 -    e2uHash['scaron']= '\u0161';
2341 -    e2uHash['Yuml']= '\u0178';
2342 -    e2uHash['circ']= '\u02C6';
2343 -    e2uHash['tilde']= '\u02DC';
2344 -    e2uHash['ensp']= '\u2002';
2345 -    e2uHash['emsp']= '\u2003';
2346 -    e2uHash['thinsp']= '\u2010';
2347 -    e2uHash['zwnj']= '\u200C';
2348 -    e2uHash['zwj']= '\u200D';
2349 -    e2uHash['lrm']= '\u200E';
2350 -    e2uHash['rlm']= '\u200F';
2351 -    e2uHash['ndash']= '\u2013';
2352 -    e2uHash['mdash']= '\u2014';
2353 -    e2uHash['lsquo']= '\u2018';
2354 -    e2uHash['rsquo']= '\u2019';
2355 -    e2uHash['sbquo']= '\u201A';
2356 -    e2uHash['ldquo']= '\u201C';
2357 -    e2uHash['rdquo']= '\u201D';
2358 -    e2uHash['bdquo']= '\u201E';
2359 -    e2uHash['dagger']= '\u2020';
2360 -    e2uHash['Dagger']= '\u2021';
2361 -    e2uHash['permil']= '\u2030';
2362 -    e2uHash['lsaquo']= '\u2039';
2363 -    e2uHash['rsaquo']= '\u203A';
2364 -    e2uHash['euro']= '\u20AC';
2365 -    e2uHash['fnof']= '\u0192';
2366 -    e2uHash['Alpha']= '\u0391';
2367 -    e2uHash['Beta']= '\u0392';
2368 -    e2uHash['Gamma']= '\u0393';
2369 -    e2uHash['Delta']= '\u0394';
2370 -    e2uHash['Epsilon']= '\u0395';
2371 -    e2uHash['Zeta']= '\u0396';
2372 -    e2uHash['Eta']= '\u0397';
2373 -    e2uHash['Theta']= '\u0398';
2374 -    e2uHash['Iota']= '\u0399';
2375 -    e2uHash['Kappa']= '\u039A';
2376 -    e2uHash['Lambda']= '\u039B';
2377 -    e2uHash['Mu']= '\u039C';
2378 -    e2uHash['Nu']= '\u039D';
2379 -    e2uHash['Xi']= '\u039E';
2380 -    e2uHash['Omicron']= '\u039F';
2381 -    e2uHash['Pi']= '\u03A0';
2382 -    e2uHash['Rho']= '\u03A1';
2383 -    e2uHash['Sigma']= '\u03A3';
2384 -    e2uHash['Tau']= '\u03A4';
2385 -    e2uHash['Upsilon']= '\u03A5';
2386 -    e2uHash['Phi']= '\u03A6';
2387 -    e2uHash['Chi']= '\u03A7';
2388 -    e2uHash['Psi']= '\u03A8';
2389 -    e2uHash['Omega']= '\u03A9';
2390 -    e2uHash['alpha']= '\u03B1';
2391 -    e2uHash['beta']= '\u03B2';
2392 -    e2uHash['gamma']= '\u03B3';
2393 -    e2uHash['delta']= '\u03B4';
2394 -    e2uHash['epsilon']= '\u03B5';
2395 -    e2uHash['zeta']= '\u03B6';
2396 -    e2uHash['eta']= '\u03B7';
2397 -    e2uHash['theta']= '\u03B8';
2398 -    e2uHash['iota']= '\u03B9';
2399 -    e2uHash['kappa']= '\u03BA';
2400 -    e2uHash['lambda']= '\u03BB';
2401 -    e2uHash['mu']= '\u03BC';
2402 -    e2uHash['nu']= '\u03BD';
2403 -    e2uHash['xi']= '\u03BE';
2404 -    e2uHash['omicron']= '\u03BF';
2405 -    e2uHash['pi']= '\u03C0';
2406 -    e2uHash['rho']= '\u03C1';
2407 -    e2uHash['sigmaf']= '\u03C2';
2408 -    e2uHash['sigma']= '\u03C3';
2409 -    e2uHash['tau']= '\u03C4';
2410 -    e2uHash['upsilon']= '\u03C5';
2411 -    e2uHash['phi']= '\u03C6';
2412 -    e2uHash['chi']= '\u03C7';
2413 -    e2uHash['psi']= '\u03C8';
2414 -    e2uHash['omega']= '\u03C9';
2415 -    e2uHash['thetasym']= '\u03D1';
2416 -    e2uHash['upsih']= '\u03D2';
2417 -    e2uHash['piv']= '\u03D6';
2418 -    e2uHash['bull']= '\u2022';
2419 -    e2uHash['hellip']= '\u2026';
2420 -    e2uHash['prime']= '\u2032';
2421 -    e2uHash['Prime']= '\u2033';
2422 -    e2uHash['oline']= '\u203E';
2423 -    e2uHash['frasl']= '\u2044';
2424 -    e2uHash['weierp']= '\u2118';
2425 -    e2uHash['image']= '\u2111';
2426 -    e2uHash['real']= '\u211C';
2427 -    e2uHash['trade']= '\u2122';
2428 -    e2uHash['alefsym']= '\u2135';
2429 -    e2uHash['larr']= '\u2190';
2430 -    e2uHash['uarr']= '\u2191';
2431 -    e2uHash['rarr']= '\u2192';
2432 -    e2uHash['darr']= '\u2193';
2433 -    e2uHash['harr']= '\u2194';
2434 -    e2uHash['crarr']= '\u21B5';
2435 -    e2uHash['lArr']= '\u21D0';
2436 -    e2uHash['uArr']= '\u21D1';
2437 -    e2uHash['rArr']= '\u21D2';
2438 -    e2uHash['dArr']= '\u21D3';
2439 -    e2uHash['hArr']= '\u21D4';
2440 -    e2uHash['forall']= '\u2200';
2441 -    e2uHash['part']= '\u2202';
2442 -    e2uHash['exist']= '\u2203';
2443 -    e2uHash['empty']= '\u2205';
2444 -    e2uHash['nabla']= '\u2207';
2445 -    e2uHash['isin']= '\u2208';
2446 -    e2uHash['notin']= '\u2209';
2447 -    e2uHash['ni']= '\u220B';
2448 -    e2uHash['prod']= '\u220F';
2449 -    e2uHash['sum']= '\u2211';
2450 -    e2uHash['minus']= '\u2212';
2451 -    e2uHash['lowast']= '\u2217';
2452 -    e2uHash['radic']= '\u221A';
2453 -    e2uHash['prop']= '\u221D';
2454 -    e2uHash['infin']= '\u221E';
2455 -    e2uHash['ang']= '\u2220';
2456 -    e2uHash['and']= '\u2227';
2457 -    e2uHash['or']= '\u2228';
2458 -    e2uHash['cap']= '\u2229';
2459 -    e2uHash['cup']= '\u222A';
2460 -    e2uHash['int']= '\u222B';
2461 -    e2uHash['there4']= '\u2234';
2462 -    e2uHash['sim']= '\u223C';
2463 -    e2uHash['cong']= '\u2245';
2464 -    e2uHash['asymp']= '\u2248';
2465 -    e2uHash['ne']= '\u2260';
2466 -    e2uHash['equiv']= '\u2261';
2467 -    e2uHash['le']= '\u2264';
2468 -    e2uHash['ge']= '\u2265';
2469 -    e2uHash['sub']= '\u2282';
2470 -    e2uHash['sup']= '\u2283';
2471 -    e2uHash['nsub']= '\u2284';
2472 -    e2uHash['sube']= '\u2286';
2473 -    e2uHash['supe']= '\u2287';
2474 -    e2uHash['oplus']= '\u2295';
2475 -    e2uHash['otimes']= '\u2297';
2476 -    e2uHash['perp']= '\u22A5';
2477 -    e2uHash['sdot']= '\u22C5';
2478 -    e2uHash['lceil']= '\u2308';
2479 -    e2uHash['rceil']= '\u2309';
2480 -    e2uHash['lfloor']= '\u230A';
2481 -    e2uHash['rfloor']= '\u230B';
2482 -    e2uHash['lang']= '\u2329';
2483 -    e2uHash['rang']= '\u232A';
2484 -    e2uHash['loz']= '\u25CA';
2485 -    e2uHash['spades']= '\u2660';
2486 -    e2uHash['clubs']= '\u2663';
2487 -    e2uHash['hearts']= '\u2665';
2488 -    e2uHash['diams']= '\u2666';
2489 -})();
2490 -
2491 -SimileAjax.HTML.deEntify = function(s) {
2492 -    var e2uHash = SimileAjax.HTML._e2uHash;
2493 -
2494 -    var re = /&(\w+?);/;
2495 -    while (re.test(s)) {
2496 -        var m = s.match(re);
2497 -        s = s.replace(re, e2uHash[m[1]]);
2498 -    }
2499 -    return s;
2500 -};/**
2501 - * A basic set (in the mathematical sense) data structure
2502 - *
2503 - * @constructor
2504 - * @param {Array or SimileAjax.Set} [a] an initial collection
2505 - */
2506 -SimileAjax.Set = function(a) {
2507 -    this._hash = {};
2508 -    this._count = 0;
2509 -
2510 -    if (a instanceof Array) {
2511 -        for (var i = 0; i < a.length; i++) {
2512 -            this.add(a[i]);
2513 -        }
2514 -    } else if (a instanceof SimileAjax.Set) {
2515 -        this.addSet(a);
2516 -    }
2517 -}
2518 -
2519 -/**
2520 - * Adds the given object to this set, assuming there it does not already exist
2521 - *
2522 - * @param {Object} o the object to add
2523 - * @return {Boolean} true if the object was added, false if not
2524 - */
2525 -SimileAjax.Set.prototype.add = function(o) {
2526 -    if (!(o in this._hash)) {
2527 -        this._hash[o] = true;
2528 -        this._count++;
2529 -        return true;
2530 -    }
2531 -    return false;
2532 -}
2533 -
2534 -/**
2535 - * Adds each element in the given set to this set
2536 - *
2537 - * @param {SimileAjax.Set} set the set of elements to add
2538 - */
2539 -SimileAjax.Set.prototype.addSet = function(set) {
2540 -    for (var o in set._hash) {
2541 -        this.add(o);
2542 -    }
2543 -}
2544 -
2545 -/**
2546 - * Removes the given element from this set
2547 - *
2548 - * @param {Object} o the object to remove
2549 - * @return {Boolean} true if the object was successfully removed,
2550 - *   false otherwise
2551 - */
2552 -SimileAjax.Set.prototype.remove = function(o) {
2553 -    if (o in this._hash) {
2554 -        delete this._hash[o];
2555 -        this._count--;
2556 -        return true;
2557 -    }
2558 -    return false;
2559 -}
2560 -
2561 -/**
2562 - * Removes the elements in this set that correspond to the elements in the
2563 - * given set
2564 - *
2565 - * @param {SimileAjax.Set} set the set of elements to remove
2566 - */
2567 -SimileAjax.Set.prototype.removeSet = function(set) {
2568 -    for (var o in set._hash) {
2569 -        this.remove(o);
2570 -    }
2571 -}
2572 -
2573 -/**
2574 - * Removes all elements in this set that are not present in the given set, i.e.
2575 - * modifies this set to the intersection of the two sets
2576 - *
2577 - * @param {SimileAjax.Set} set the set to intersect
2578 - */
2579 -SimileAjax.Set.prototype.retainSet = function(set) {
2580 -    for (var o in this._hash) {
2581 -        if (!set.contains(o)) {
2582 -            delete this._hash[o];
2583 -            this._count--;
2584 -        }
2585 -    }
2586 -}
2587 -
2588 -/**
2589 - * Returns whether or not the given element exists in this set
2590 - *
2591 - * @param {SimileAjax.Set} o the object to test for
2592 - * @return {Boolean} true if the object is present, false otherwise
2593 - */
2594 -SimileAjax.Set.prototype.contains = function(o) {
2595 -    return (o in this._hash);
2596 -}
2597 -
2598 -/**
2599 - * Returns the number of elements in this set
2600 - *
2601 - * @return {Number} the number of elements in this set
2602 - */
2603 -SimileAjax.Set.prototype.size = function() {
2604 -    return this._count;
2605 -}
2606 -
2607 -/**
2608 - * Returns the elements of this set as an array
2609 - *
2610 - * @return {Array} a new array containing the elements of this set
2611 - */
2612 -SimileAjax.Set.prototype.toArray = function() {
2613 -    var a = [];
2614 -    for (var o in this._hash) {
2615 -        a.push(o);
2616 -    }
2617 -    return a;
2618 -}
2619 -
2620 -/**
2621 - * Iterates through the elements of this set, order unspecified, executing the
2622 - * given function on each element until the function returns true
2623 - *
2624 - * @param {Function} f a function of form f(element)
2625 - */
2626 -SimileAjax.Set.prototype.visit = function(f) {
2627 -    for (var o in this._hash) {
2628 -        if (f(o) == true) {
2629 -            break;
2630 -        }
2631 -    }
2632 -}
2633 -
2634 -/**
2635 - * A sorted array data structure
2636 - *
2637 - * @constructor
2638 - */
2639 -SimileAjax.SortedArray = function(compare, initialArray) {
2640 -    this._a = (initialArray instanceof Array) ? initialArray : [];
2641 -    this._compare = compare;
2642 -};
2643 -
2644 -SimileAjax.SortedArray.prototype.add = function(elmt) {
2645 -    var sa = this;
2646 -    var index = this.find(function(elmt2) {
2647 -        return sa._compare(elmt2, elmt);
2648 -    });
2649 -
2650 -    if (index < this._a.length) {
2651 -        this._a.splice(index, 0, elmt);
2652 -    } else {
2653 -        this._a.push(elmt);
2654 -    }
2655 -};
2656 -
2657 -SimileAjax.SortedArray.prototype.remove = function(elmt) {
2658 -    var sa = this;
2659 -    var index = this.find(function(elmt2) {
2660 -        return sa._compare(elmt2, elmt);
2661 -    });
2662 -
2663 -    while (index < this._a.length && this._compare(this._a[index], elmt) == 0) {
2664 -        if (this._a[index] == elmt) {
2665 -            this._a.splice(index, 1);
2666 -            return true;
2667 -        } else {
2668 -            index++;
2669 -        }
2670 -    }
2671 -    return false;
2672 -};
2673 -
2674 -SimileAjax.SortedArray.prototype.removeAll = function() {
2675 -    this._a = [];
2676 -};
2677 -
2678 -SimileAjax.SortedArray.prototype.elementAt = function(index) {
2679 -    return this._a[index];
2680 -};
2681 -
2682 -SimileAjax.SortedArray.prototype.length = function() {
2683 -    return this._a.length;
2684 -};
2685 -
2686 -SimileAjax.SortedArray.prototype.find = function(compare) {
2687 -    var a = 0;
2688 -    var b = this._a.length;
2689 -
2690 -    while (a < b) {
2691 -        var mid = Math.floor((a + b) / 2);
2692 -        var c = compare(this._a[mid]);
2693 -        if (mid == a) {
2694 -            return c < 0 ? a+1 : a;
2695 -        } else if (c < 0) {
2696 -            a = mid;
2697 -        } else {
2698 -            b = mid;
2699 -        }
2700 -    }
2701 -    return a;
2702 -};
2703 -
2704 -SimileAjax.SortedArray.prototype.getFirst = function() {
2705 -    return (this._a.length > 0) ? this._a[0] : null;
2706 -};
2707 -
2708 -SimileAjax.SortedArray.prototype.getLast = function() {
2709 -    return (this._a.length > 0) ? this._a[this._a.length - 1] : null;
2710 -};
2711 -
2712 -/*
2713 - *  Event Index
2714 - *
2715 - */
2716 -
2717 -SimileAjax.EventIndex = function(unit) {
2718 -    var eventIndex = this;
2719 -
2720 -    this._unit = (unit != null) ? unit : SimileAjax.NativeDateUnit;
2721 -    this._events = new SimileAjax.SortedArray(
2722 -        function(event1, event2) {
2723 -            return eventIndex._unit.compare(event1.getStart(), event2.getStart());
2724 -        }
2725 -    );
2726 -    this._idToEvent = {};
2727 -    this._indexed = true;
2728 -};
2729 -
2730 -SimileAjax.EventIndex.prototype.getUnit = function() {
2731 -    return this._unit;
2732 -};
2733 -
2734 -SimileAjax.EventIndex.prototype.getEvent = function(id) {
2735 -    return this._idToEvent[id];
2736 -};
2737 -
2738 -SimileAjax.EventIndex.prototype.add = function(evt) {
2739 -    this._events.add(evt);
2740 -    this._idToEvent[evt.getID()] = evt;
2741 -    this._indexed = false;
2742 -};
2743 -
2744 -SimileAjax.EventIndex.prototype.removeAll = function() {
2745 -    this._events.removeAll();
2746 -    this._idToEvent = {};
2747 -    this._indexed = false;
2748 -};
2749 -
2750 -SimileAjax.EventIndex.prototype.getCount = function() {
2751 -    return this._events.length();
2752 -};
2753 -
2754 -SimileAjax.EventIndex.prototype.getIterator = function(startDate, endDate) {
2755 -    if (!this._indexed) {
2756 -        this._index();
2757 -    }
2758 -    return new SimileAjax.EventIndex._Iterator(this._events, startDate, endDate, this._unit);
2759 -};
2760 -
2761 -SimileAjax.EventIndex.prototype.getReverseIterator = function(startDate, endDate) {
2762 -    if (!this._indexed) {
2763 -        this._index();
2764 -    }
2765 -    return new SimileAjax.EventIndex._ReverseIterator(this._events, startDate, endDate, this._unit);
2766 -};
2767 -
2768 -SimileAjax.EventIndex.prototype.getAllIterator = function() {
2769 -    return new SimileAjax.EventIndex._AllIterator(this._events);
2770 -};
2771 -
2772 -SimileAjax.EventIndex.prototype.getEarliestDate = function() {
2773 -    var evt = this._events.getFirst();
2774 -    return (evt == null) ? null : evt.getStart();
2775 -};
2776 -
2777 -SimileAjax.EventIndex.prototype.getLatestDate = function() {
2778 -    var evt = this._events.getLast();
2779 -    if (evt == null) {
2780 -        return null;
2781 -    }
2782 -
2783 -    if (!this._indexed) {
2784 -        this._index();
2785 -    }
2786 -
2787 -    var index = evt._earliestOverlapIndex;
2788 -    var date = this._events.elementAt(index).getEnd();
2789 -    for (var i = index + 1; i < this._events.length(); i++) {
2790 -        date = this._unit.later(date, this._events.elementAt(i).getEnd());
2791 -    }
2792 -
2793 -    return date;
2794 -};
2795 -
2796 -SimileAjax.EventIndex.prototype._index = function() {
2797 -    /*
2798 -     *  For each event, we want to find the earliest preceding
2799 -     *  event that overlaps with it, if any.
2800 -     */
2801 -
2802 -    var l = this._events.length();
2803 -    for (var i = 0; i < l; i++) {
2804 -        var evt = this._events.elementAt(i);
2805 -        evt._earliestOverlapIndex = i;
2806 -    }
2807 -
2808 -    var toIndex = 1;
2809 -    for (var i = 0; i < l; i++) {
2810 -        var evt = this._events.elementAt(i);
2811 -        var end = evt.getEnd();
2812 -
2813 -        toIndex = Math.max(toIndex, i + 1);
2814 -        while (toIndex < l) {
2815 -            var evt2 = this._events.elementAt(toIndex);
2816 -            var start2 = evt2.getStart();
2817 -
2818 -            if (this._unit.compare(start2, end) < 0) {
2819 -                evt2._earliestOverlapIndex = i;
2820 -                toIndex++;
2821 -            } else {
2822 -                break;
2823 -            }
2824 -        }
2825 -    }
2826 -    this._indexed = true;
2827 -};
2828 -
2829 -SimileAjax.EventIndex._Iterator = function(events, startDate, endDate, unit) {
2830 -    this._events = events;
2831 -    this._startDate = startDate;
2832 -    this._endDate = endDate;
2833 -    this._unit = unit;
2834 -
2835 -    this._currentIndex = events.find(function(evt) {
2836 -        return unit.compare(evt.getStart(), startDate);
2837 -    });
2838 -    if (this._currentIndex - 1 >= 0) {
2839 -        this._currentIndex = this._events.elementAt(this._currentIndex - 1)._earliestOverlapIndex;
2840 -    }
2841 -    this._currentIndex--;
2842 -
2843 -    this._maxIndex = events.find(function(evt) {
2844 -        return unit.compare(evt.getStart(), endDate);
2845 -    });
2846 -
2847 -    this._hasNext = false;
2848 -    this._next = null;
2849 -    this._findNext();
2850 -};
2851 -
2852 -SimileAjax.EventIndex._Iterator.prototype = {
2853 -    hasNext: function() { return this._hasNext; },
2854 -    next: function() {
2855 -        if (this._hasNext) {
2856 -            var next = this._next;
2857 -            this._findNext();
2858 -
2859 -            return next;
2860 -        } else {
2861 -            return null;
2862 -        }
2863 -    },
2864 -    _findNext: function() {
2865 -        var unit = this._unit;
2866 -        while ((++this._currentIndex) < this._maxIndex) {
2867 -            var evt = this._events.elementAt(this._currentIndex);
2868 -            if (unit.compare(evt.getStart(), this._endDate) < 0 &&
2869 -                unit.compare(evt.getEnd(), this._startDate) > 0) {
2870 -
2871 -                this._next = evt;
2872 -                this._hasNext = true;
2873 -                return;
2874 -            }
2875 -        }
2876 -        this._next = null;
2877 -        this._hasNext = false;
2878 -    }
2879 -};
2880 -
2881 -SimileAjax.EventIndex._ReverseIterator = function(events, startDate, endDate, unit) {
2882 -    this._events = events;
2883 -    this._startDate = startDate;
2884 -    this._endDate = endDate;
2885 -    this._unit = unit;
2886 -
2887 -    this._minIndex = events.find(function(evt) {
2888 -        return unit.compare(evt.getStart(), startDate);
2889 -    });
2890 -    if (this._minIndex - 1 >= 0) {
2891 -        this._minIndex = this._events.elementAt(this._minIndex - 1)._earliestOverlapIndex;
2892 -    }
2893 -
2894 -    this._maxIndex = events.find(function(evt) {
2895 -        return unit.compare(evt.getStart(), endDate);
2896 -    });
2897 -
2898 -    this._currentIndex = this._maxIndex;
2899 -    this._hasNext = false;
2900 -    this._next = null;
2901 -    this._findNext();
2902 -};
2903 -
2904 -SimileAjax.EventIndex._ReverseIterator.prototype = {
2905 -    hasNext: function() { return this._hasNext; },
2906 -    next: function() {
2907 -        if (this._hasNext) {
2908 -            var next = this._next;
2909 -            this._findNext();
2910 -
2911 -            return next;
2912 -        } else {
2913 -            return null;
2914 -        }
2915 -    },
2916 -    _findNext: function() {
2917 -        var unit = this._unit;
2918 -        while ((--this._currentIndex) >= this._minIndex) {
2919 -            var evt = this._events.elementAt(this._currentIndex);
2920 -            if (unit.compare(evt.getStart(), this._endDate) < 0 &&
2921 -                unit.compare(evt.getEnd(), this._startDate) > 0) {
2922 -
2923 -                this._next = evt;
2924 -                this._hasNext = true;
2925 -                return;
2926 -            }
2927 -        }
2928 -        this._next = null;
2929 -        this._hasNext = false;
2930 -    }
2931 -};
2932 -
2933 -SimileAjax.EventIndex._AllIterator = function(events) {
2934 -    this._events = events;
2935 -    this._index = 0;
2936 -};
2937 -
2938 -SimileAjax.EventIndex._AllIterator.prototype = {
2939 -    hasNext: function() {
2940 -        return this._index < this._events.length();
2941 -    },
2942 -    next: function() {
2943 -        return this._index < this._events.length() ?
2944 -            this._events.elementAt(this._index++) : null;
2945 -    }
2946 -};/*
2947 - *  Default Unit

2948 - *
2949 - */

2950 -

2951 -SimileAjax.NativeDateUnit = new Object();

2952 -

2953 -SimileAjax.NativeDateUnit.makeDefaultValue = function() {

2954 -    return new Date();

2955 -};

2956 -

2957 -SimileAjax.NativeDateUnit.cloneValue = function(v) {

2958 -    return new Date(v.getTime());

2959 -};

2960 -

2961 -SimileAjax.NativeDateUnit.getParser = function(format) {

2962 -    if (typeof format == "string") {

2963 -        format = format.toLowerCase();

2964 -    }

2965 -    return (format == "iso8601" || format == "iso 8601") ?

2966 -        SimileAjax.DateTime.parseIso8601DateTime : 

2967 -        SimileAjax.DateTime.parseGregorianDateTime;

2968 -};

2969 -

2970 -SimileAjax.NativeDateUnit.parseFromObject = function(o) {

2971 -    return SimileAjax.DateTime.parseGregorianDateTime(o);

2972 -};

2973 -

2974 -SimileAjax.NativeDateUnit.toNumber = function(v) {

2975 -    return v.getTime();

2976 -};

2977 -

2978 -SimileAjax.NativeDateUnit.fromNumber = function(n) {

2979 -    return new Date(n);

2980 -};

2981 -

2982 -SimileAjax.NativeDateUnit.compare = function(v1, v2) {

2983 -    var n1, n2;

2984 -    if (typeof v1 == "object") {

2985 -        n1 = v1.getTime();

2986 -    } else {

2987 -        n1 = Number(v1);

2988 -    }

2989 -    if (typeof v2 == "object") {

2990 -        n2 = v2.getTime();

2991 -    } else {

2992 -        n2 = Number(v2);

2993 -    }

2994 -    

2995 -    return n1 - n2;

2996 -};

2997 -

2998 -SimileAjax.NativeDateUnit.earlier = function(v1, v2) {

2999 -    return SimileAjax.NativeDateUnit.compare(v1, v2) < 0 ? v1 : v2;

3000 -};

3001 -

3002 -SimileAjax.NativeDateUnit.later = function(v1, v2) {

3003 -    return SimileAjax.NativeDateUnit.compare(v1, v2) > 0 ? v1 : v2;

3004 -};

3005 -

3006 -SimileAjax.NativeDateUnit.change = function(v, n) {

3007 -    return new Date(v.getTime() + n);

3008 -};

3009 -

3010 -/*
3011 - *  General, miscellaneous SimileAjax stuff
3012 - *
3013 - */
3014 -
3015 -SimileAjax.ListenerQueue = function(wildcardHandlerName) {
3016 -    this._listeners = [];
3017 -    this._wildcardHandlerName = wildcardHandlerName;
3018 -};
3019 -
3020 -SimileAjax.ListenerQueue.prototype.add = function(listener) {
3021 -    this._listeners.push(listener);
3022 -};
3023 -
3024 -SimileAjax.ListenerQueue.prototype.remove = function(listener) {
3025 -    var listeners = this._listeners;
3026 -    for (var i = 0; i < listeners.length; i++) {
3027 -        if (listeners[i] == listener) {
3028 -            listeners.splice(i, 1);
3029 -            break;
3030 -        }
3031 -    }
3032 -};
3033 -
3034 -SimileAjax.ListenerQueue.prototype.fire = function(handlerName, args) {
3035 -    var listeners = [].concat(this._listeners);
3036 -    for (var i = 0; i < listeners.length; i++) {
3037 -        var listener = listeners[i];
3038 -        if (handlerName in listener) {
3039 -            try {
3040 -                listener[handlerName].apply(listener, args);
3041 -            } catch (e) {
3042 -                SimileAjax.Debug.exception("Error firing event of name " + handlerName, e);
3043 -            }
3044 -        } else if (this._wildcardHandlerName != null &&
3045 -            this._wildcardHandlerName in listener) {
3046 -            try {
3047 -                listener[this._wildcardHandlerName].apply(listener, [ handlerName ]);
3048 -            } catch (e) {
3049 -                SimileAjax.Debug.exception("Error firing event of name " + handlerName + " to wildcard handler", e);
3050 -            }
3051 -        }
3052 -    }
3053 -};
3054 -
3055 -/*
3056 - *  History
3057 - *
3058 - *  This is a singleton that keeps track of undoable user actions and
3059 - *  performs undos and redos in response to the browser's Back and
3060 - *  Forward buttons.
3061 - *
3062 - *  Call addAction(action) to register an undoable user action. action
3063 - *  must have 4 fields:
3064 - *
3065 - *      perform: an argument-less function that carries out the action
3066 - *      undo:    an argument-less function that undos the action
3067 - *      label:   a short, user-friendly string describing the action
3068 - *      uiLayer: the UI layer on which the action takes place
3069 - *
3070 - *  By default, the history keeps track of upto 10 actions. You can
3071 - *  configure this behavior by setting
3072 - *      SimileAjax.History.maxHistoryLength
3073 - *  to a different number.
3074 - *
3075 - *  An iframe is inserted into the document's body element to track
3076 - *  onload events.
3077 - *
3078 - */
3079 -
3080 -SimileAjax.History = {
3081 -    maxHistoryLength:       10,
3082 -    historyFile:            "__history__.html",
3083 -    enabled:               true,
3084 -
3085 -    _initialized:           false,
3086 -    _listeners:             new SimileAjax.ListenerQueue(),
3087 -
3088 -    _actions:               [],
3089 -    _baseIndex:             0,
3090 -    _currentIndex:          0,
3091 -
3092 -    _plainDocumentTitle:    document.title
3093 -};
3094 -
3095 -SimileAjax.History.formatHistoryEntryTitle = function(actionLabel) {
3096 -    return SimileAjax.History._plainDocumentTitle + " {" + actionLabel + "}";
3097 -};
3098 -
3099 -SimileAjax.History.initialize = function() {
3100 -    if (SimileAjax.History._initialized) {
3101 -        return;
3102 -    }
3103 -
3104 -    if (SimileAjax.History.enabled) {
3105 -        var iframe = document.createElement("iframe");
3106 -        iframe.id = "simile-ajax-history";
3107 -        iframe.style.position = "absolute";
3108 -        iframe.style.width = "10px";
3109 -        iframe.style.height = "10px";
3110 -        iframe.style.top = "0px";
3111 -        iframe.style.left = "0px";
3112 -        iframe.style.visibility = "hidden";
3113 -        iframe.src = SimileAjax.History.historyFile + "?0";
3114 -
3115 -        document.body.appendChild(iframe);
3116 -        SimileAjax.DOM.registerEvent(iframe, "load", SimileAjax.History._handleIFrameOnLoad);
3117 -
3118 -        SimileAjax.History._iframe = iframe;
3119 -    }
3120 -    SimileAjax.History._initialized = true;
3121 -};
3122 -
3123 -SimileAjax.History.addListener = function(listener) {
3124 -    SimileAjax.History.initialize();
3125 -
3126 -    SimileAjax.History._listeners.add(listener);
3127 -};
3128 -
3129 -SimileAjax.History.removeListener = function(listener) {
3130 -    SimileAjax.History.initialize();
3131 -
3132 -    SimileAjax.History._listeners.remove(listener);
3133 -};
3134 -
3135 -SimileAjax.History.addAction = function(action) {
3136 -    SimileAjax.History.initialize();
3137 -
3138 -    SimileAjax.History._listeners.fire("onBeforePerform", [ action ]);
3139 -    window.setTimeout(function() {
3140 -        try {
3141 -            action.perform();
3142 -            SimileAjax.History._listeners.fire("onAfterPerform", [ action ]);
3143 -
3144 -            if (SimileAjax.History.enabled) {
3145 -                SimileAjax.History._actions = SimileAjax.History._actions.slice(
3146 -                    0, SimileAjax.History._currentIndex - SimileAjax.History._baseIndex);
3147 -
3148 -                SimileAjax.History._actions.push(action);
3149 -                SimileAjax.History._currentIndex++;
3150 -
3151 -                var diff = SimileAjax.History._actions.length - SimileAjax.History.maxHistoryLength;
3152 -                if (diff > 0) {
3153 -                    SimileAjax.History._actions = SimileAjax.History._actions.slice(diff);
3154 -                    SimileAjax.History._baseIndex += diff;
3155 -                }
3156 -
3157 -                try {
3158 -                    SimileAjax.History._iframe.contentWindow.location.search =
3159 -                        "?" + SimileAjax.History._currentIndex;
3160 -                } catch (e) {
3161 -                    /*
3162 -                     *  We can't modify location.search most probably because it's a file:// url.
3163 -                     *  We'll just going to modify the document's title.
3164 -                     */
3165 -                    var title = SimileAjax.History.formatHistoryEntryTitle(action.label);
3166 -                    document.title = title;
3167 -                }
3168 -            }
3169 -        } catch (e) {
3170 -            SimileAjax.Debug.exception(e, "Error adding action {" + action.label + "} to history");
3171 -        }
3172 -    }, 0);
3173 -};
3174 -
3175 -SimileAjax.History.addLengthyAction = function(perform, undo, label) {
3176 -    SimileAjax.History.addAction({
3177 -        perform:    perform,
3178 -        undo:       undo,
3179 -        label:      label,
3180 -        uiLayer:    SimileAjax.WindowManager.getBaseLayer(),
3181 -        lengthy:    true
3182 -    });
3183 -};
3184 -
3185 -SimileAjax.History._handleIFrameOnLoad = function() {
3186 -    /*
3187 -     *  This function is invoked when the user herself
3188 -     *  navigates backward or forward. We need to adjust
3189 -     *  the application's state accordingly.
3190 -     */
3191 -
3192 -    try {
3193 -        var q = SimileAjax.History._iframe.contentWindow.location.search;
3194 -        var c = (q.length == 0) ? 0 : Math.max(0, parseInt(q.substr(1)));
3195 -
3196 -        var finishUp = function() {
3197 -            var diff = c - SimileAjax.History._currentIndex;
3198 -            SimileAjax.History._currentIndex += diff;
3199 -            SimileAjax.History._baseIndex += diff;
3200 -
3201 -            SimileAjax.History._iframe.contentWindow.location.search = "?" + c;
3202 -        };
3203 -
3204 -        if (c < SimileAjax.History._currentIndex) { // need to undo
3205 -            SimileAjax.History._listeners.fire("onBeforeUndoSeveral", []);
3206 -            window.setTimeout(function() {
3207 -                while (SimileAjax.History._currentIndex > c &&
3208 -                       SimileAjax.History._currentIndex > SimileAjax.History._baseIndex) {
3209 -
3210 -                    SimileAjax.History._currentIndex--;
3211 -
3212 -                    var action = SimileAjax.History._actions[SimileAjax.History._currentIndex - SimileAjax.History._baseIndex];
3213 -
3214 -                    try {
3215 -                        action.undo();
3216 -                    } catch (e) {
3217 -                        SimileAjax.Debug.exception(e, "History: Failed to undo action {" + action.label + "}");
3218 -                    }
3219 -                }
3220 -
3221 -                SimileAjax.History._listeners.fire("onAfterUndoSeveral", []);
3222 -                finishUp();
3223 -            }, 0);
3224 -        } else if (c > SimileAjax.History._currentIndex) { // need to redo
3225 -            SimileAjax.History._listeners.fire("onBeforeRedoSeveral", []);
3226 -            window.setTimeout(function() {
3227 -                while (SimileAjax.History._currentIndex < c &&
3228 -                       SimileAjax.History._currentIndex - SimileAjax.History._baseIndex < SimileAjax.History._actions.length) {
3229 -
3230 -                    var action = SimileAjax.History._actions[SimileAjax.History._currentIndex - SimileAjax.History._baseIndex];
3231 -
3232 -                    try {
3233 -                        action.perform();
3234 -                    } catch (e) {
3235 -                        SimileAjax.Debug.exception(e, "History: Failed to redo action {" + action.label + "}");
3236 -                    }
3237 -
3238 -                    SimileAjax.History._currentIndex++;
3239 -                }
3240 -
3241 -                SimileAjax.History._listeners.fire("onAfterRedoSeveral", []);
3242 -                finishUp();
3243 -            }, 0);
3244 -        } else {
3245 -            var index = SimileAjax.History._currentIndex - SimileAjax.History._baseIndex - 1;
3246 -            var title = (index >= 0 && index < SimileAjax.History._actions.length) ?
3247 -                SimileAjax.History.formatHistoryEntryTitle(SimileAjax.History._actions[index].label) :
3248 -                SimileAjax.History._plainDocumentTitle;
3249 -
3250 -            SimileAjax.History._iframe.contentWindow.document.title = title;
3251 -            document.title = title;
3252 -        }
3253 -    } catch (e) {
3254 -        // silent
3255 -    }
3256 -};
3257 -
3258 -SimileAjax.History.getNextUndoAction = function() {
3259 -    try {
3260 -        var index = SimileAjax.History._currentIndex - SimileAjax.History._baseIndex - 1;
3261 -        return SimileAjax.History._actions[index];
3262 -    } catch (e) {
3263 -        return null;
3264 -    }
3265 -};
3266 -
3267 -SimileAjax.History.getNextRedoAction = function() {
3268 -    try {
3269 -        var index = SimileAjax.History._currentIndex - SimileAjax.History._baseIndex;
3270 -        return SimileAjax.History._actions[index];
3271 -    } catch (e) {
3272 -        return null;
3273 -    }
3274 -};
3275 -/**
3276 - * @fileOverview UI layers and window-wide dragging
3277 - * @name SimileAjax.WindowManager
3278 - */
3279 -
3280 -/**
3281 - *  This is a singleton that keeps track of UI layers (modal and
3282 - *  modeless) and enables/disables UI elements based on which layers
3283 - *  they belong to. It also provides window-wide dragging
3284 - *  implementation.
3285 - */
3286 -SimileAjax.WindowManager = {
3287 -    _initialized:       false,
3288 -    _listeners:         [],
3289 -
3290 -    _draggedElement:                null,
3291 -    _draggedElementCallback:        null,
3292 -    _dropTargetHighlightElement:    null,
3293 -    _lastCoords:                    null,
3294 -    _ghostCoords:                   null,
3295 -    _draggingMode:                  "",
3296 -    _dragging:                      false,
3297 -
3298 -    _layers:            []
3299 -};
3300 -
3301 -SimileAjax.WindowManager.initialize = function() {
3302 -    if (SimileAjax.WindowManager._initialized) {
3303 -        return;
3304 -    }
3305 -
3306 -    SimileAjax.DOM.registerEvent(document.body, "mousedown", SimileAjax.WindowManager._onBodyMouseDown);
3307 -    SimileAjax.DOM.registerEvent(document.body, "mousemove", SimileAjax.WindowManager._onBodyMouseMove);
3308 -    SimileAjax.DOM.registerEvent(document.body, "mouseup",   SimileAjax.WindowManager._onBodyMouseUp);
3309 -    SimileAjax.DOM.registerEvent(document, "keydown",       SimileAjax.WindowManager._onBodyKeyDown);
3310 -    SimileAjax.DOM.registerEvent(document, "keyup",         SimileAjax.WindowManager._onBodyKeyUp);
3311 -
3312 -    SimileAjax.WindowManager._layers.push({index: 0});
3313 -
3314 -    SimileAjax.WindowManager._historyListener = {
3315 -        onBeforeUndoSeveral:    function() {},
3316 -        onAfterUndoSeveral:     function() {},
3317 -        onBeforeUndo:           function() {},
3318 -        onAfterUndo:            function() {},
3319 -
3320 -        onBeforeRedoSeveral:    function() {},
3321 -        onAfterRedoSeveral:     function() {},
3322 -        onBeforeRedo:           function() {},
3323 -        onAfterRedo:            function() {}
3324 -    };
3325 -    SimileAjax.History.addListener(SimileAjax.WindowManager._historyListener);
3326 -
3327 -    SimileAjax.WindowManager._initialized = true;
3328 -};
3329 -
3330 -SimileAjax.WindowManager.getBaseLayer = function() {
3331 -    SimileAjax.WindowManager.initialize();
3332 -    return SimileAjax.WindowManager._layers[0];
3333 -};
3334 -
3335 -SimileAjax.WindowManager.getHighestLayer = function() {
3336 -    SimileAjax.WindowManager.initialize();
3337 -    return SimileAjax.WindowManager._layers[SimileAjax.WindowManager._layers.length - 1];
3338 -};
3339 -
3340 -SimileAjax.WindowManager.registerEventWithObject = function(elmt, eventName, obj, handlerName, layer) {
3341 -    SimileAjax.WindowManager.registerEvent(
3342 -        elmt,
3343 -        eventName,
3344 -        function(elmt2, evt, target) {
3345 -            return obj[handlerName].call(obj, elmt2, evt, target);
3346 -        },
3347 -        layer
3348 -    );
3349 -};
3350 -
3351 -SimileAjax.WindowManager.registerEvent = function(elmt, eventName, handler, layer) {
3352 -    if (layer == null) {
3353 -        layer = SimileAjax.WindowManager.getHighestLayer();
3354 -    }
3355 -
3356 -    var handler2 = function(elmt, evt, target) {
3357 -        if (SimileAjax.WindowManager._canProcessEventAtLayer(layer)) {
3358 -            SimileAjax.WindowManager._popToLayer(layer.index);
3359 -            try {
3360 -                handler(elmt, evt, target);
3361 -            } catch (e) {
3362 -                SimileAjax.Debug.exception(e);
3363 -            }
3364 -        }
3365 -        SimileAjax.DOM.cancelEvent(evt);
3366 -        return false;
3367 -    }
3368 -
3369 -    SimileAjax.DOM.registerEvent(elmt, eventName, handler2);
3370 -};
3371 -
3372 -SimileAjax.WindowManager.pushLayer = function(f, ephemeral, elmt) {
3373 -    var layer = { onPop: f, index: SimileAjax.WindowManager._layers.length, ephemeral: (ephemeral), elmt: elmt };
3374 -    SimileAjax.WindowManager._layers.push(layer);
3375 -
3376 -    return layer;
3377 -};
3378 -
3379 -SimileAjax.WindowManager.popLayer = function(layer) {
3380 -    for (var i = 1; i < SimileAjax.WindowManager._layers.length; i++) {
3381 -        if (SimileAjax.WindowManager._layers[i] == layer) {
3382 -            SimileAjax.WindowManager._popToLayer(i - 1);
3383 -            break;
3384 -        }
3385 -    }
3386 -};
3387 -
3388 -SimileAjax.WindowManager.popAllLayers = function() {
3389 -    SimileAjax.WindowManager._popToLayer(0);
3390 -};
3391 -
3392 -SimileAjax.WindowManager.registerForDragging = function(elmt, callback, layer) {
3393 -    SimileAjax.WindowManager.registerEvent(
3394 -        elmt,
3395 -        "mousedown",
3396 -        function(elmt, evt, target) {
3397 -            SimileAjax.WindowManager._handleMouseDown(elmt, evt, callback);
3398 -        },
3399 -        layer
3400 -    );
3401 -};
3402 -
3403 -SimileAjax.WindowManager._popToLayer = function(level) {
3404 -    while (level+1 < SimileAjax.WindowManager._layers.length) {
3405 -        try {
3406 -            var layer = SimileAjax.WindowManager._layers.pop();
3407 -            if (layer.onPop != null) {
3408 -                layer.onPop();
3409 -            }
3410 -        } catch (e) {
3411 -        }
3412 -    }
3413 -};
3414 -
3415 -SimileAjax.WindowManager._canProcessEventAtLayer = function(layer) {
3416 -    if (layer.index == (SimileAjax.WindowManager._layers.length - 1)) {
3417 -        return true;
3418 -    }
3419 -    for (var i = layer.index + 1; i < SimileAjax.WindowManager._layers.length; i++) {
3420 -        if (!SimileAjax.WindowManager._layers[i].ephemeral) {
3421 -            return false;
3422 -        }
3423 -    }
3424 -    return true;
3425 -};
3426 -
3427 -SimileAjax.WindowManager.cancelPopups = function(evt) {
3428 -    var evtCoords = (evt) ? SimileAjax.DOM.getEventPageCoordinates(evt) : { x: -1, y: -1 };
3429 -
3430 -    var i = SimileAjax.WindowManager._layers.length - 1;
3431 -    while (i > 0 && SimileAjax.WindowManager._layers[i].ephemeral) {
3432 -        var layer = SimileAjax.WindowManager._layers[i];
3433 -        if (layer.elmt != null) { // if event falls within main element of layer then don't cancel
3434 -            var elmt = layer.elmt;
3435 -            var elmtCoords = SimileAjax.DOM.getPageCoordinates(elmt);
3436 -            if (evtCoords.x >= elmtCoords.left && evtCoords.x < (elmtCoords.left + elmt.offsetWidth) &&
3437 -                evtCoords.y >= elmtCoords.top && evtCoords.y < (elmtCoords.top + elmt.offsetHeight)) {
3438 -                break;
3439 -            }
3440 -        }
3441 -        i--;
3442 -    }
3443 -    SimileAjax.WindowManager._popToLayer(i);
3444 -};
3445 -
3446 -SimileAjax.WindowManager._onBodyMouseDown = function(elmt, evt, target) {
3447 -    if (!("eventPhase" in evt) || evt.eventPhase == evt.BUBBLING_PHASE) {
3448 -        SimileAjax.WindowManager.cancelPopups(evt);
3449 -    }
3450 -};
3451 -
3452 -SimileAjax.WindowManager._handleMouseDown = function(elmt, evt, callback) {
3453 -    SimileAjax.WindowManager._draggedElement = elmt;
3454 -    SimileAjax.WindowManager._draggedElementCallback = callback;
3455 -    SimileAjax.WindowManager._lastCoords = { x: evt.clientX, y: evt.clientY };
3456 -
3457 -    SimileAjax.DOM.cancelEvent(evt);
3458 -    return false;
3459 -};
3460 -
3461 -SimileAjax.WindowManager._onBodyKeyDown = function(elmt, evt, target) {
3462 -    if (SimileAjax.WindowManager._dragging) {
3463 -        if (evt.keyCode == 27) { // esc
3464 -            SimileAjax.WindowManager._cancelDragging();
3465 -        } else if ((evt.keyCode == 17 || evt.keyCode == 16) && SimileAjax.WindowManager._draggingMode != "copy") {
3466 -            SimileAjax.WindowManager._draggingMode = "copy";
3467 -
3468 -            var img = SimileAjax.Graphics.createTranslucentImage(SimileAjax.urlPrefix + "images/copy.png");
3469 -            img.style.position = "absolute";
3470 -            img.style.left = (SimileAjax.WindowManager._ghostCoords.left - 16) + "px";
3471 -            img.style.top = (SimileAjax.WindowManager._ghostCoords.top) + "px";
3472 -            document.body.appendChild(img);
3473 -
3474 -            SimileAjax.WindowManager._draggingModeIndicatorElmt = img;
3475 -        }
3476 -    }
3477 -};
3478 -
3479 -SimileAjax.WindowManager._onBodyKeyUp = function(elmt, evt, target) {
3480 -    if (SimileAjax.WindowManager._dragging) {
3481 -        if (evt.keyCode == 17 || evt.keyCode == 16) {
3482 -            SimileAjax.WindowManager._draggingMode = "";
3483 -            if (SimileAjax.WindowManager._draggingModeIndicatorElmt != null) {
3484 -                document.body.removeChild(SimileAjax.WindowManager._draggingModeIndicatorElmt);
3485 -                SimileAjax.WindowManager._draggingModeIndicatorElmt = null;
3486 -            }
3487 -        }
3488 -    }
3489 -};
3490 -
3491 -SimileAjax.WindowManager._onBodyMouseMove = function(elmt, evt, target) {
3492 -    if (SimileAjax.WindowManager._draggedElement != null) {
3493 -        var callback = SimileAjax.WindowManager._draggedElementCallback;
3494 -
3495 -        var lastCoords = SimileAjax.WindowManager._lastCoords;
3496 -        var diffX = evt.clientX - lastCoords.x;
3497 -        var diffY = evt.clientY - lastCoords.y;
3498 -
3499 -        if (!SimileAjax.WindowManager._dragging) {
3500 -            if (Math.abs(diffX) > 5 || Math.abs(diffY) > 5) {
3501 -                try {
3502 -                    if ("onDragStart" in callback) {
3503 -                        callback.onDragStart();
3504 -                    }
3505 -
3506 -                    if ("ghost" in callback && callback.ghost) {
3507 -                        var draggedElmt = SimileAjax.WindowManager._draggedElement;
3508 -
3509 -                        SimileAjax.WindowManager._ghostCoords = SimileAjax.DOM.getPageCoordinates(draggedElmt);
3510 -                        SimileAjax.WindowManager._ghostCoords.left += diffX;
3511 -                        SimileAjax.WindowManager._ghostCoords.top += diffY;
3512 -
3513 -                        var ghostElmt = draggedElmt.cloneNode(true);
3514 -                        ghostElmt.style.position = "absolute";
3515 -                        ghostElmt.style.left = SimileAjax.WindowManager._ghostCoords.left + "px";
3516 -                        ghostElmt.style.top = SimileAjax.WindowManager._ghostCoords.top + "px";
3517 -                        ghostElmt.style.zIndex = 1000;
3518 -                        SimileAjax.Graphics.setOpacity(ghostElmt, 50);
3519 -
3520 -                        document.body.appendChild(ghostElmt);
3521 -                        callback._ghostElmt = ghostElmt;
3522 -                    }
3523 -
3524 -                    SimileAjax.WindowManager._dragging = true;
3525 -                    SimileAjax.WindowManager._lastCoords = { x: evt.clientX, y: evt.clientY };
3526 -
3527 -                    document.body.focus();
3528 -                } catch (e) {
3529 -                    SimileAjax.Debug.exception("WindowManager: Error handling mouse down", e);
3530 -                    SimileAjax.WindowManager._cancelDragging();
3531 -                }
3532 -            }
3533 -        } else {
3534 -            try {
3535 -                SimileAjax.WindowManager._lastCoords = { x: evt.clientX, y: evt.clientY };
3536 -
3537 -                if ("onDragBy" in callback) {
3538 -                    callback.onDragBy(diffX, diffY);
3539 -                }
3540 -
3541 -                if ("_ghostElmt" in callback) {
3542 -                    var ghostElmt = callback._ghostElmt;
3543 -
3544 -                    SimileAjax.WindowManager._ghostCoords.left += diffX;
3545 -                    SimileAjax.WindowManager._ghostCoords.top += diffY;
3546 -
3547 -                    ghostElmt.style.left = SimileAjax.WindowManager._ghostCoords.left + "px";
3548 -                    ghostElmt.style.top = SimileAjax.WindowManager._ghostCoords.top + "px";
3549 -                    if (SimileAjax.WindowManager._draggingModeIndicatorElmt != null) {
3550 -                        var indicatorElmt = SimileAjax.WindowManager._draggingModeIndicatorElmt;
3551 -
3552 -                        indicatorElmt.style.left = (SimileAjax.WindowManager._ghostCoords.left - 16) + "px";
3553 -                        indicatorElmt.style.top = SimileAjax.WindowManager._ghostCoords.top + "px";
3554 -                    }
3555 -
3556 -                    if ("droppable" in callback && callback.droppable) {
3557 -                        var coords = SimileAjax.DOM.getEventPageCoordinates(evt);
3558 -                        var target = SimileAjax.DOM.hittest(
3559 -                            coords.x, coords.y,
3560 -                            [   SimileAjax.WindowManager._ghostElmt,
3561 -                                SimileAjax.WindowManager._dropTargetHighlightElement
3562 -                            ]
3563 -                        );
3564 -                        target = SimileAjax.WindowManager._findDropTarget(target);
3565 -
3566 -                        if (target != SimileAjax.WindowManager._potentialDropTarget) {
3567 -                            if (SimileAjax.WindowManager._dropTargetHighlightElement != null) {
3568 -                                document.body.removeChild(SimileAjax.WindowManager._dropTargetHighlightElement);
3569 -
3570 -                                SimileAjax.WindowManager._dropTargetHighlightElement = null;
3571 -                                SimileAjax.WindowManager._potentialDropTarget = null;
3572 -                            }
3573 -
3574 -                            var droppable = false;
3575 -                            if (target != null) {
3576 -                                if ((!("canDropOn" in callback) || callback.canDropOn(target)) &&
3577 -                                    (!("canDrop" in target) || target.canDrop(SimileAjax.WindowManager._draggedElement))) {
3578 -
3579 -                                    droppable = true;
3580 -                                }
3581 -                            }
3582 -
3583 -                            if (droppable) {
3584 -                                var border = 4;
3585 -                                var targetCoords = SimileAjax.DOM.getPageCoordinates(target);
3586 -                                var highlight = document.createElement("div");
3587 -                                highlight.style.border = border + "px solid yellow";
3588 -                                highlight.style.backgroundColor = "yellow";
3589 -                                highlight.style.position = "absolute";
3590 -                                highlight.style.left = targetCoords.left + "px";
3591 -                                highlight.style.top = targetCoords.top + "px";
3592 -                                highlight.style.width = (target.offsetWidth - border * 2) + "px";
3593 -                                highlight.style.height = (target.offsetHeight - border * 2) + "px";
3594 -                                SimileAjax.Graphics.setOpacity(highlight, 30);
3595 -                                document.body.appendChild(highlight);
3596 -
3597 -                                SimileAjax.WindowManager._potentialDropTarget = target;
3598 -                                SimileAjax.WindowManager._dropTargetHighlightElement = highlight;
3599 -                            }
3600 -                        }
3601 -                    }
3602 -                }
3603 -            } catch (e) {
3604 -                SimileAjax.Debug.exception("WindowManager: Error handling mouse move", e);
3605 -                SimileAjax.WindowManager._cancelDragging();
3606 -            }
3607 -        }
3608 -
3609 -        SimileAjax.DOM.cancelEvent(evt);
3610 -        return false;
3611 -    }
3612 -};
3613 -
3614 -SimileAjax.WindowManager._onBodyMouseUp = function(elmt, evt, target) {
3615 -    if (SimileAjax.WindowManager._draggedElement != null) {
3616 -        try {
3617 -            if (SimileAjax.WindowManager._dragging) {
3618 -                var callback = SimileAjax.WindowManager._draggedElementCallback;
3619 -                if ("onDragEnd" in callback) {
3620 -                    callback.onDragEnd();
3621 -                }
3622 -                if ("droppable" in callback && callback.droppable) {
3623 -                    var dropped = false;
3624 -
3625 -                    var target = SimileAjax.WindowManager._potentialDropTarget;
3626 -                    if (target != null) {
3627 -                        if ((!("canDropOn" in callback) || callback.canDropOn(target)) &&
3628 -                            (!("canDrop" in target) || target.canDrop(SimileAjax.WindowManager._draggedElement))) {
3629 -
3630 -                            if ("onDropOn" in callback) {
3631 -                                callback.onDropOn(target);
3632 -                            }
3633 -                            target.ondrop(SimileAjax.WindowManager._draggedElement, SimileAjax.WindowManager._draggingMode);
3634 -
3635 -                            dropped = true;
3636 -                        }
3637 -                    }
3638 -
3639 -                    if (!dropped) {
3640 -                        // TODO: do holywood explosion here
3641 -                    }
3642 -                }
3643 -            }
3644 -        } finally {
3645 -            SimileAjax.WindowManager._cancelDragging();
3646 -        }
3647 -
3648 -        SimileAjax.DOM.cancelEvent(evt);
3649 -        return false;
3650 -    }
3651 -};
3652 -
3653 -SimileAjax.WindowManager._cancelDragging = function() {
3654 -    var callback = SimileAjax.WindowManager._draggedElementCallback;
3655 -    if ("_ghostElmt" in callback) {
3656 -        var ghostElmt = callback._ghostElmt;
3657 -        document.body.removeChild(ghostElmt);
3658 -
3659 -        delete callback._ghostElmt;
3660 -    }
3661 -    if (SimileAjax.WindowManager._dropTargetHighlightElement != null) {
3662 -        document.body.removeChild(SimileAjax.WindowManager._dropTargetHighlightElement);
3663 -        SimileAjax.WindowManager._dropTargetHighlightElement = null;
3664 -    }
3665 -    if (SimileAjax.WindowManager._draggingModeIndicatorElmt != null) {
3666 -        document.body.removeChild(SimileAjax.WindowManager._draggingModeIndicatorElmt);
3667 -        SimileAjax.WindowManager._draggingModeIndicatorElmt = null;
3668 -    }
3669 -
3670 -    SimileAjax.WindowManager._draggedElement = null;
3671 -    SimileAjax.WindowManager._draggedElementCallback = null;
3672 -    SimileAjax.WindowManager._potentialDropTarget = null;
3673 -    SimileAjax.WindowManager._dropTargetHighlightElement = null;
3674 -    SimileAjax.WindowManager._lastCoords = null;
3675 -    SimileAjax.WindowManager._ghostCoords = null;
3676 -    SimileAjax.WindowManager._draggingMode = "";
3677 -    SimileAjax.WindowManager._dragging = false;
3678 -};
3679 -
3680 -SimileAjax.WindowManager._findDropTarget = function(elmt) {
3681 -    while (elmt != null) {
3682 -        if ("ondrop" in elmt && (typeof elmt.ondrop) == "function") {
3683 -            break;
3684 -        }
3685 -        elmt = elmt.parentNode;
3686 -    }
3687 -    return elmt;
3688 -};
3689 -/*
3690 - *  Timeline API
3691 - *
3692 - *  This file will load all the Javascript files
3693 - *  necessary to make the standard timeline work.
3694 - *  It also detects the default locale.
3695 - *
3696 - *  To run from the MIT copy of Timeline:
3697 - *  Include this file in your HTML file as follows:
3698 - *
3699 - *    <script src="http://api.simile-widgets.org/timeline/2.3.1/timeline-api.js"
3700 - *     type="text/javascript"></script>
3701 - *
3702 - *
3703 - * To host the Timeline files on your own server:
3704 - *   1) Install the Timeline and Simile-Ajax files onto your webserver using
3705 - *      timeline_libraries.zip or timeline_source.zip
3706 - *
3707 - *   2) Set global js variables used to send parameters to this script:
3708 - *        var Timeline_ajax_url -- url for simile-ajax-api.js
3709 - *        var Timeline_urlPrefix -- url for the *directory* that contains timeline-api.js
3710 - *            Include trailing slash
3711 - *        var Timeline_parameters='bundle=true'; // you must set bundle to true if you are using
3712 - *                                               // timeline_libraries.zip since only the
3713 - *                                               // bundled libraries are included
3714 - *
3715 - * eg your html page would include
3716 - *
3717 - *   <script>
3718 - *     var Timeline_ajax_url="http://YOUR_SERVER/javascripts/timeline/timeline_ajax/simile-ajax-api.js";
3719 - *     var Timeline_urlPrefix='http://YOUR_SERVER/javascripts/timeline/timeline_js/';
3720 - *     var Timeline_parameters='bundle=true';
3721 - *   </script>
3722 - *   <script src="http://YOUR_SERVER/javascripts/timeline/timeline_js/timeline-api.js"
3723 - *     type="text/javascript">
3724 - *   </script>
3725 - *
3726 - * SCRIPT PARAMETERS
3727 - * This script auto-magically figures out locale and has defaults for other parameters
3728 - * To set parameters explicity, set js global variable Timeline_parameters or include as
3729 - * parameters on the url using GET style. Eg the two next lines pass the same parameters:
3730 - *     Timeline_parameters='bundle=true';                    // pass parameter via js variable
3731 - *     <script src="http://....timeline-api.js?bundle=true"  // pass parameter via url
3732 - *
3733 - * Parameters
3734 - *   timeline-use-local-resources --
3735 - *   bundle -- true: use the single js bundle file; false: load individual files (for debugging)
3736 - *   locales --
3737 - *   defaultLocale --
3738 - *   forceLocale -- force locale to be a particular value--used for debugging. Normally locale is determined
3739 - *                  by browser's and server's locale settings.
3740 - *
3741 - * DEBUGGING
3742 - * If you have a problem with Timeline, the first step is to use the unbundled Javascript files. To do so:
3743 - * To use the unbundled Timeline and Ajax libraries

3744 - * Change

3745 - *   <script src="http://api.simile-widgets.org/timeline/2.3.1/api/timeline-api.js?bundle=true" type="text/javascript"></script>

3746 - * To

3747 - *   <script>var Timeline_ajax_url = "http://api.simile-widgets.org/ajax/2.2.1/simile-ajax-api.js?bundle=false"</script>

3748 - *   <script src="http://api.simile-widgets.org/timeline/2.3.1/api/timeline-api.js?bundle=false" type="text/javascript"></script>

3749 - *
3750 - * Note that the Ajax version is usually NOT the same as the Timeline version.
3751 - * See variable simile_ajax_ver below for the current version
3752 - *
3753 - *
3754 - */
3755 -
3756 -(function() {
3757 -
3758 -    var simile_ajax_ver = "2.2.1"; // ===========>>>  current Simile-Ajax version
3759 -
3760 -    var useLocalResources = false;
3761 -    if (document.location.search.length > 0) {
3762 -        var params = document.location.search.substr(1).split("&");
3763 -        for (var i = 0; i < params.length; i++) {
3764 -            if (params[i] == "timeline-use-local-resources") {
3765 -                useLocalResources = true;
3766 -            }
3767 -        }
3768 -    };
3769 -
3770 -    var loadMe = function() {
3771 -        if ("Timeline" in window) {
3772 -            return;
3773 -        }
3774 -
3775 -        window.Timeline = new Object();
3776 -        window.Timeline.DateTime = window.SimileAjax.DateTime; // for backward compatibility
3777 -
3778 -        var bundle = false;
3779 -        var javascriptFiles = [
3780 -            "timeline.js",
3781 -            "band.js",
3782 -            "themes.js",
3783 -            "ethers.js",
3784 -            "ether-painters.js",
3785 -            "event-utils.js",
3786 -            "labellers.js",
3787 -            "sources.js",
3788 -            "original-painter.js",
3789 -            "detailed-painter.js",
3790 -            "overview-painter.js",
3791 -            "compact-painter.js",
3792 -            "decorators.js",
3793 -            "units.js"
3794 -        ];
3795 -        var cssFiles = [
3796 -            "timeline.css",
3797 -            "ethers.css",
3798 -            "events.css"
3799 -        ];
3800 -
3801 -        var localizedJavascriptFiles = [
3802 -            "timeline.js",
3803 -            "labellers.js"
3804 -        ];
3805 -        var localizedCssFiles = [
3806 -        ];
3807 -
3808 -        // ISO-639 language codes, ISO-3166 country codes (2 characters)
3809 -        var supportedLocales = [
3810 -            "cs",       // Czech
3811 -            "de",       // German
3812 -            "en",       // English
3813 -            "es",       // Spanish
3814 -            "fr",       // French
3815 -            "it",       // Italian
3816 -            "nl",       // Dutch (The Netherlands)
3817 -            "ru",       // Russian
3818 -            "se",       // Swedish
3819 -            "tr",       // Turkish
3820 -            "vi",       // Vietnamese
3821 -            "zh"        // Chinese
3822 -        ];
3823 -
3824 -        try {
3825 -            var desiredLocales = [ "en" ],
3826 -                defaultServerLocale = "en",
3827 -                forceLocale = null;
3828 -
3829 -            var parseURLParameters = function(parameters) {
3830 -                var params = parameters.split("&");
3831 -                for (var p = 0; p < params.length; p++) {
3832 -                    var pair = params[p].split("=");
3833 -                    if (pair[0] == "locales") {
3834 -                        desiredLocales = desiredLocales.concat(pair[1].split(","));
3835 -                    } else if (pair[0] == "defaultLocale") {
3836 -                        defaultServerLocale = pair[1];
3837 -                    } else if (pair[0] == "forceLocale") {
3838 -                        forceLocale = pair[1];
3839 -                        desiredLocales = desiredLocales.concat(pair[1].split(","));
3840 -                    } else if (pair[0] == "bundle") {
3841 -                        bundle = pair[1] != "false";
3842 -                    }
3843 -                }
3844 -            };
3845 -
3846 -            (function() {
3847 -                if (typeof Timeline_urlPrefix == "string") {
3848 -                    Timeline.urlPrefix = Timeline_urlPrefix;
3849 -                    if (typeof Timeline_parameters == "string") {
3850 -                        parseURLParameters(Timeline_parameters);
3851 -                    }
3852 -                } else {
3853 -                    var heads = document.documentElement.getElementsByTagName("head");
3854 -                    for (var h = 0; h < heads.length; h++) {
3855 -                        var scripts = heads[h].getElementsByTagName("script");
3856 -                        for (var s = 0; s < scripts.length; s++) {
3857 -                            var url = scripts[s].src;
3858 -                            var i = url.indexOf("timeline-api.js");
3859 -                            if (i >= 0) {
3860 -                                Timeline.urlPrefix = url.substr(0, i);
3861 -                                var q = url.indexOf("?");
3862 -                                if (q > 0) {
3863 -                                    parseURLParameters(url.substr(q + 1));
3864 -                                }
3865 -                                return;
3866 -                            }
3867 -                        }
3868 -                    }
3869 -                    throw new Error("Failed to derive URL prefix for Timeline API code files");
3870 -                }
3871 -            })();
3872 -
3873 -            var includeJavascriptFiles = function(urlPrefix, filenames) {
3874 -                SimileAjax.includeJavascriptFiles(document, urlPrefix, filenames);
3875 -            }
3876 -            var includeCssFiles = function(urlPrefix, filenames) {
3877 -                SimileAjax.includeCssFiles(document, urlPrefix, filenames);
3878 -            }
3879 -
3880 -            /*
3881 -             *  Include non-localized files
3882 -             */
3883 -            if (bundle) {
3884 -                includeJavascriptFiles(Timeline.urlPrefix, [ "timeline-bundle.js" ]);
3885 -                includeCssFiles(Timeline.urlPrefix, [ "timeline-bundle.css" ]);
3886 -            } else {
3887 -                // XXX adim includeJavascriptFiles(Timeline.urlPrefix + "scripts/", javascriptFiles);
3888 -                // XXX adim includeCssFiles(Timeline.urlPrefix + "styles/", cssFiles);
3889 -            }
3890 -
3891 -            /*
3892 -             *  Include localized files
3893 -             */
3894 -            var loadLocale = [];
3895 -            loadLocale[defaultServerLocale] = true;
3896 -
3897 -            var tryExactLocale = function(locale) {
3898 -                for (var l = 0; l < supportedLocales.length; l++) {
3899 -                    if (locale == supportedLocales[l]) {
3900 -                        loadLocale[locale] = true;
3901 -                        return true;
3902 -                    }
3903 -                }
3904 -                return false;
3905 -            }
3906 -            var tryLocale = function(locale) {
3907 -                if (tryExactLocale(locale)) {
3908 -                    return locale;
3909 -                }
3910 -
3911 -                var dash = locale.indexOf("-");
3912 -                if (dash > 0 && tryExactLocale(locale.substr(0, dash))) {
3913 -                    return locale.substr(0, dash);
3914 -                }
3915 -
3916 -                return null;
3917 -            }
3918 -
3919 -            for (var l = 0; l < desiredLocales.length; l++) {
3920 -                tryLocale(desiredLocales[l]);
3921 -            }
3922 -
3923 -            var defaultClientLocale = defaultServerLocale;
3924 -            var defaultClientLocales = ("language" in navigator ? navigator.language : navigator.browserLanguage).split(";");
3925 -            for (var l = 0; l < defaultClientLocales.length; l++) {
3926 -                var locale = tryLocale(defaultClientLocales[l]);
3927 -                if (locale != null) {
3928 -                    defaultClientLocale = locale;
3929 -                    break;
3930 -                }
3931 -            }
3932 -
3933 -            for (var l = 0; l < supportedLocales.length; l++) {
3934 -                var locale = supportedLocales[l];
3935 -                if (loadLocale[locale]) {
3936 -                    // XXX adim includeJavascriptFiles(Timeline.urlPrefix + "scripts/l10n/" + locale + "/", localizedJavascriptFiles);
3937 -                    // XXX adim includeCssFiles(Timeline.urlPrefix + "styles/l10n/" + locale + "/", localizedCssFiles);
3938 -                }
3939 -            }
3940 -
3941 -            if (forceLocale == null) {
3942 -              Timeline.serverLocale = defaultServerLocale;
3943 -              Timeline.clientLocale = defaultClientLocale;
3944 -            } else {
3945 -              Timeline.serverLocale = forceLocale;
3946 -              Timeline.clientLocale = forceLocale;
3947 -            }
3948 -        } catch (e) {
3949 -            alert(e);
3950 -        }
3951 -    };
3952 -
3953 -    /*
3954 -     *  Load SimileAjax if it's not already loaded
3955 -     */
3956 -    if (typeof SimileAjax == "undefined") {
3957 -        window.SimileAjax_onLoad = loadMe;
3958 -
3959 -        var url = useLocalResources ?
3960 -            "http://127.0.0.1:9999/ajax/api/simile-ajax-api.js?bundle=false" :
3961 -            "http://api.simile-widgets.org/ajax/" + simile_ajax_ver + "/simile-ajax-api.js";
3962 -        if (typeof Timeline_ajax_url == "string") {
3963 -           url = Timeline_ajax_url;
3964 -        }
3965 -        var createScriptElement = function() {
3966 -            var script = document.createElement("script");
3967 -            script.type = "text/javascript";
3968 -            script.language = "JavaScript";
3969 -            script.src = url;
3970 -            document.getElementsByTagName("head")[0].appendChild(script);
3971 -        }
3972 -        if (document.body == null) {
3973 -            try {
3974 -                document.write("<script src='" + url + "' type='text/javascript'></script>");
3975 -            } catch (e) {
3976 -                createScriptElement();
3977 -            }
3978 -        } else {
3979 -            createScriptElement();
3980 -        }
3981 -    } else {
3982 -        loadMe();
3983 -    }
3984 -})();
3985 -/*
3986 - *
3987 - * Coding standards:
3988 - *
3989 - * We aim towards Douglas Crockford's Javascript conventions.
3990 - * See:  http://javascript.crockford.com/code.html
3991 - * See also: http://www.crockford.com/javascript/javascript.html
3992 - *
3993 - * That said, this JS code was written before some recent JS
3994 - * support libraries became widely used or available.
3995 - * In particular, the _ character is used to indicate a class function or
3996 - * variable that should be considered private to the class.
3997 - *
3998 - * The code mostly uses accessor methods for getting/setting the private
3999 - * class variables.
4000 - *
4001 - * Over time, we'd like to formalize the convention by using support libraries
4002 - * which enforce privacy in objects.
4003 - *
4004 - * We also want to use jslint:  http://www.jslint.com/
4005 - *
4006 - *
4007 - *
4008 - */
4009 -
4010 -
4011 -
4012 -/*
4013 - *  Timeline VERSION
4014 - *
4015 - */
4016 -// Note: version is also stored in the build.xml file
4017 -Timeline.version = 'pre 2.4.0';  // use format 'pre 1.2.3' for trunk versions
4018 -Timeline.ajax_lib_version = SimileAjax.version;
4019 -Timeline.display_version = Timeline.version + ' (with Ajax lib ' + Timeline.ajax_lib_version + ')';
4020 - // cf method Timeline.writeVersion
4021 -
4022 -/*
4023 - *  Timeline
4024 - *
4025 - */
4026 -Timeline.strings = {}; // localization string tables
4027 -Timeline.HORIZONTAL = 0;
4028 -Timeline.VERTICAL = 1;
4029 -Timeline._defaultTheme = null;
4030 -
4031 -Timeline.getDefaultLocale = function() {
4032 -    return Timeline.clientLocale;
4033 -};
4034 -
4035 -Timeline.create = function(elmt, bandInfos, orientation, unit) {
4036 -    if (Timeline.timelines == null) {
4037 -        Timeline.timelines = [];
4038 -        // Timeline.timelines array can have null members--Timelines that
4039 -        // once existed on the page, but were later disposed of.
4040 -    }
4041 -
4042 -    var timelineID = Timeline.timelines.length;
4043 -    Timeline.timelines[timelineID] = null; // placeholder until we have the object
4044 -    var new_tl = new Timeline._Impl(elmt, bandInfos, orientation, unit,
4045 -      timelineID);
4046 -    Timeline.timelines[timelineID] = new_tl;
4047 -    return new_tl;
4048 -};
4049 -
4050 -Timeline.createBandInfo = function(params) {
4051 -    var theme = ("theme" in params) ? params.theme : Timeline.getDefaultTheme();
4052 -
4053 -    var eventSource = ("eventSource" in params) ? params.eventSource : null;
4054 -
4055 -    var etherParams = {
4056 -        interval:           SimileAjax.DateTime.gregorianUnitLengths[params.intervalUnit],
4057 -        pixelsPerInterval: params.intervalPixels,
4058 -	theme: theme
4059 -    };
4060 -    if ('startsOn' in params || 'endsOn' in params) {
4061 -	if ('startsOn' in params) {
4062 -	    etherParams.startsOn = params.startsOn;
4063 -	}
4064 -	if ('endsOn' in params) {
4065 -	    etherParams.endsOn = params.endsOn;
4066 -	}
4067 -    } else {
4068 -	etherParams.centersOn = ("date" in params) ? params.date : new Date();
4069 -    }
4070 -    var ether = new Timeline.LinearEther(etherParams);
4071 -
4072 -    var etherPainter = new Timeline.GregorianEtherPainter({
4073 -        unit:       params.intervalUnit,
4074 -        multiple:   ("multiple" in params) ? params.multiple : 1,
4075 -        theme:      theme,
4076 -        align:      ("align" in params) ? params.align : undefined
4077 -    });
4078 -
4079 -    var eventPainterParams = {
4080 -        showText:   ("showEventText" in params) ? params.showEventText : true,
4081 -        theme:      theme
4082 -    };
4083 -    // pass in custom parameters for the event painter
4084 -    if ("eventPainterParams" in params) {
4085 -        for (var prop in params.eventPainterParams) {
4086 -            eventPainterParams[prop] = params.eventPainterParams[prop];
4087 -        }
4088 -    }
4089 -
4090 -    if ("trackHeight" in params) {
4091 -        eventPainterParams.trackHeight = params.trackHeight;
4092 -    }
4093 -    if ("trackGap" in params) {
4094 -        eventPainterParams.trackGap = params.trackGap;
4095 -    }
4096 -
4097 -    var layout = ("overview" in params && params.overview) ? "overview" : ("layout" in params ? params.layout : "original");
4098 -    var eventPainter;
4099 -    if ("eventPainter" in params) {
4100 -        eventPainter = new params.eventPainter(eventPainterParams);
4101 -    } else {
4102 -        switch (layout) {
4103 -            case "overview" :
4104 -                eventPainter = new Timeline.OverviewEventPainter(eventPainterParams);
4105 -                break;
4106 -            case "detailed" :
4107 -                eventPainter = new Timeline.DetailedEventPainter(eventPainterParams);
4108 -                break;
4109 -            default:
4110 -                eventPainter = new Timeline.OriginalEventPainter(eventPainterParams);
4111 -        }
4112 -    }
4113 -
4114 -    return {
4115 -        width:          params.width,
4116 -        eventSource:    eventSource,
4117 -        timeZone:       ("timeZone" in params) ? params.timeZone : 0,
4118 -        ether:          ether,
4119 -        etherPainter:   etherPainter,
4120 -        eventPainter:   eventPainter,
4121 -        theme:          theme,
4122 -        zoomIndex:      ("zoomIndex" in params) ? params.zoomIndex : 0,
4123 -        zoomSteps:      ("zoomSteps" in params) ? params.zoomSteps : null
4124 -    };
4125 -};
4126 -
4127 -Timeline.createHotZoneBandInfo = function(params) {
4128 -    var theme = ("theme" in params) ? params.theme : Timeline.getDefaultTheme();
4129 -
4130 -    var eventSource = ("eventSource" in params) ? params.eventSource : null;
4131 -
4132 -    var ether = new Timeline.HotZoneEther({
4133 -        centersOn:          ("date" in params) ? params.date : new Date(),
4134 -        interval:           SimileAjax.DateTime.gregorianUnitLengths[params.intervalUnit],
4135 -        pixelsPerInterval:  params.intervalPixels,
4136 -        zones:              params.zones,
4137 -        theme:              theme
4138 -    });
4139 -
4140 -    var etherPainter = new Timeline.HotZoneGregorianEtherPainter({
4141 -        unit:       params.intervalUnit,
4142 -        zones:      params.zones,
4143 -        theme:      theme,
4144 -        align:      ("align" in params) ? params.align : undefined
4145 -    });
4146 -
4147 -    var eventPainterParams = {
4148 -        showText:   ("showEventText" in params) ? params.showEventText : true,
4149 -        theme:      theme
4150 -    };
4151 -    // pass in custom parameters for the event painter
4152 -    if ("eventPainterParams" in params) {
4153 -        for (var prop in params.eventPainterParams) {
4154 -            eventPainterParams[prop] = params.eventPainterParams[prop];
4155 -        }
4156 -    }
4157 -    if ("trackHeight" in params) {
4158 -        eventPainterParams.trackHeight = params.trackHeight;
4159 -    }
4160 -    if ("trackGap" in params) {
4161 -        eventPainterParams.trackGap = params.trackGap;
4162 -    }
4163 -
4164 -    var layout = ("overview" in params && params.overview) ? "overview" : ("layout" in params ? params.layout : "original");
4165 -    var eventPainter;
4166 -    if ("eventPainter" in params) {
4167 -        eventPainter = new params.eventPainter(eventPainterParams);
4168 -    } else {
4169 -        switch (layout) {
4170 -            case "overview" :
4171 -                eventPainter = new Timeline.OverviewEventPainter(eventPainterParams);
4172 -                break;
4173 -            case "detailed" :
4174 -                eventPainter = new Timeline.DetailedEventPainter(eventPainterParams);
4175 -                break;
4176 -            default:
4177 -                eventPainter = new Timeline.OriginalEventPainter(eventPainterParams);
4178 -        }
4179 -    }
4180 -    return {
4181 -        width:          params.width,
4182 -        eventSource:    eventSource,
4183 -        timeZone:       ("timeZone" in params) ? params.timeZone : 0,
4184 -        ether:          ether,
4185 -        etherPainter:   etherPainter,
4186 -        eventPainter:   eventPainter,
4187 -        theme:          theme,
4188 -        zoomIndex:      ("zoomIndex" in params) ? params.zoomIndex : 0,
4189 -        zoomSteps:      ("zoomSteps" in params) ? params.zoomSteps : null
4190 -    };
4191 -};
4192 -
4193 -Timeline.getDefaultTheme = function() {
4194 -    if (Timeline._defaultTheme == null) {
4195 -        Timeline._defaultTheme = Timeline.ClassicTheme.create(Timeline.getDefaultLocale());
4196 -    }
4197 -    return Timeline._defaultTheme;
4198 -};
4199 -
4200 -Timeline.setDefaultTheme = function(theme) {
4201 -    Timeline._defaultTheme = theme;
4202 -};
4203 -
4204 -Timeline.loadXML = function(url, f) {
4205 -    var fError = function(statusText, status, xmlhttp) {
4206 -        alert("Failed to load data xml from " + url + "\n" + statusText);
4207 -    };
4208 -    var fDone = function(xmlhttp) {
4209 -        var xml = xmlhttp.responseXML;
4210 -        if (!xml.documentElement && xmlhttp.responseStream) {
4211 -            xml.load(xmlhttp.responseStream);
4212 -        }
4213 -        f(xml, url);
4214 -    };
4215 -    SimileAjax.XmlHttp.get(url, fError, fDone);
4216 -};
4217 -
4218 -
4219 -Timeline.loadJSON = function(url, f) {
4220 -    var fError = function(statusText, status, xmlhttp) {
4221 -        alert("Failed to load json data from " + url + "\n" + statusText);
4222 -    };
4223 -    var fDone = function(xmlhttp) {
4224 -        f(eval('(' + xmlhttp.responseText + ')'), url);
4225 -    };
4226 -    SimileAjax.XmlHttp.get(url, fError, fDone);
4227 -};
4228 -
4229 -Timeline.getTimelineFromID = function(timelineID) {
4230 -    return Timeline.timelines[timelineID];
4231 -};
4232 -
4233 -// Write the current Timeline version as the contents of element with id el_id
4234 -Timeline.writeVersion = function(el_id) {
4235 -  document.getElementById(el_id).innerHTML = this.display_version;
4236 -};
4237 -
4238 -
4239 -
4240 -/*
4241 - *  Timeline Implementation object
4242 - *
4243 - */
4244 -Timeline._Impl = function(elmt, bandInfos, orientation, unit, timelineID) {
4245 -    SimileAjax.WindowManager.initialize();
4246 -
4247 -    this._containerDiv = elmt;
4248 -
4249 -    this._bandInfos = bandInfos;
4250 -    this._orientation = orientation == null ? Timeline.HORIZONTAL : orientation;
4251 -    this._unit = (unit != null) ? unit : SimileAjax.NativeDateUnit;
4252 -    this._starting = true; // is the Timeline being created? Used by autoWidth
4253 -                           // functions
4254 -    this._autoResizing = false;
4255 -
4256 -    // autoWidth is a "public" property of the Timeline object
4257 -    this.autoWidth = bandInfos && bandInfos[0] && bandInfos[0].theme &&
4258 -                     bandInfos[0].theme.autoWidth;
4259 -    this.autoWidthAnimationTime = bandInfos && bandInfos[0] && bandInfos[0].theme &&
4260 -                     bandInfos[0].theme.autoWidthAnimationTime;
4261 -    this.timelineID = timelineID; // also public attribute
4262 -    this.timeline_start = bandInfos && bandInfos[0] && bandInfos[0].theme &&
4263 -                     bandInfos[0].theme.timeline_start;
4264 -    this.timeline_stop  = bandInfos && bandInfos[0] && bandInfos[0].theme &&
4265 -                     bandInfos[0].theme.timeline_stop;
4266 -    this.timeline_at_start = false; // already at start or stop? Then won't
4267 -    this.timeline_at_stop = false;  // try to move further in the wrong direction
4268 -
4269 -    this._initialize();
4270 -};
4271 -
4272 -//
4273 -// Public functions used by client sw
4274 -//
4275 -Timeline._Impl.prototype.dispose = function() {
4276 -    for (var i = 0; i < this._bands.length; i++) {
4277 -        this._bands[i].dispose();
4278 -    }
4279 -    this._bands = null;
4280 -    this._bandInfos = null;
4281 -    this._containerDiv.innerHTML = "";
4282 -    // remove from array of Timelines
4283 -    Timeline.timelines[this.timelineID] = null;
4284 -};
4285 -
4286 -Timeline._Impl.prototype.getBandCount = function() {
4287 -    return this._bands.length;
4288 -};
4289 -
4290 -Timeline._Impl.prototype.getBand = function(index) {
4291 -    return this._bands[index];
4292 -};
4293 -
4294 -Timeline._Impl.prototype.finishedEventLoading = function() {
4295 -    // Called by client after events have been loaded into Timeline
4296 -    // Only used if the client has set autoWidth
4297 -    // Sets width to Timeline's requested amount and will shrink down the div if
4298 -    // need be.
4299 -    this._autoWidthCheck(true);
4300 -    this._starting = false;
4301 -};
4302 -
4303 -Timeline._Impl.prototype.layout = function() {
4304 -    // called by client when browser is resized
4305 -    this._autoWidthCheck(true);
4306 -    this._distributeWidths();
4307 -};
4308 -
4309 -Timeline._Impl.prototype.paint = function() {
4310 -    for (var i = 0; i < this._bands.length; i++) {
4311 -        this._bands[i].paint();
4312 -    }
4313 -};
4314 -
4315 -Timeline._Impl.prototype.getDocument = function() {
4316 -    return this._containerDiv.ownerDocument;
4317 -};
4318 -
4319 -Timeline._Impl.prototype.addDiv = function(div) {
4320 -    this._containerDiv.appendChild(div);
4321 -};
4322 -
4323 -Timeline._Impl.prototype.removeDiv = function(div) {
4324 -    this._containerDiv.removeChild(div);
4325 -};
4326 -
4327 -Timeline._Impl.prototype.isHorizontal = function() {
4328 -    return this._orientation == Timeline.HORIZONTAL;
4329 -};
4330 -
4331 -Timeline._Impl.prototype.isVertical = function() {
4332 -    return this._orientation == Timeline.VERTICAL;
4333 -};
4334 -
4335 -Timeline._Impl.prototype.getPixelLength = function() {
4336 -    return this._orientation == Timeline.HORIZONTAL ?
4337 -        this._containerDiv.offsetWidth : this._containerDiv.offsetHeight;
4338 -};
4339 -
4340 -Timeline._Impl.prototype.getPixelWidth = function() {
4341 -    return this._orientation == Timeline.VERTICAL ?
4342 -        this._containerDiv.offsetWidth : this._containerDiv.offsetHeight;
4343 -};
4344 -
4345 -Timeline._Impl.prototype.getUnit = function() {
4346 -    return this._unit;
4347 -};
4348 -
4349 -Timeline._Impl.prototype.getWidthStyle = function() {
4350 -    // which element.style attribute should be changed to affect Timeline's "width"
4351 -    return this._orientation == Timeline.HORIZONTAL ? 'height' : 'width';
4352 -};
4353 -
4354 -Timeline._Impl.prototype.loadXML = function(url, f) {
4355 -    var tl = this;
4356 -
4357 -
4358 -    var fError = function(statusText, status, xmlhttp) {
4359 -        alert("Failed to load data xml from " + url + "\n" + statusText);
4360 -        tl.hideLoadingMessage();
4361 -    };
4362 -    var fDone = function(xmlhttp) {
4363 -        try {
4364 -            var xml = xmlhttp.responseXML;
4365 -            if (!xml.documentElement && xmlhttp.responseStream) {
4366 -                xml.load(xmlhttp.responseStream);
4367 -            }
4368 -            f(xml, url);
4369 -        } finally {
4370 -            tl.hideLoadingMessage();
4371 -        }
4372 -    };
4373 -
4374 -    this.showLoadingMessage();
4375 -    window.setTimeout(function() { SimileAjax.XmlHttp.get(url, fError, fDone); }, 0);
4376 -};
4377 -
4378 -Timeline._Impl.prototype.loadJSON = function(url, f) {
4379 -    var tl = this;
4380 -
4381 -    var fError = function(statusText, status, xmlhttp) {
4382 -        alert("Failed to load json data from " + url + "\n" + statusText);
4383 -        tl.hideLoadingMessage();
4384 -    };
4385 -    var fDone = function(xmlhttp) {
4386 -        try {
4387 -            f(eval('(' + xmlhttp.responseText + ')'), url);
4388 -        } finally {
4389 -            tl.hideLoadingMessage();
4390 -        }
4391 -    };
4392 -
4393 -    this.showLoadingMessage();
4394 -    window.setTimeout(function() { SimileAjax.XmlHttp.get(url, fError, fDone); }, 0);
4395 -};
4396 -
4397 -
4398 -//
4399 -// Private functions used by Timeline object functions
4400 -//
4401 -
4402 -Timeline._Impl.prototype._autoWidthScrollListener = function(band) {
4403 -    band.getTimeline()._autoWidthCheck(false);
4404 -};
4405 -
4406 -// called to re-calculate auto width and adjust the overall Timeline div if needed
4407 -Timeline._Impl.prototype._autoWidthCheck = function(okToShrink) {
4408 -    var timeline = this; // this Timeline
4409 -    var immediateChange = timeline._starting;
4410 -    var newWidth = 0;
4411 -
4412 -    function changeTimelineWidth() {
4413 -        var widthStyle = timeline.getWidthStyle();
4414 -        if (immediateChange) {
4415 -            timeline._containerDiv.style[widthStyle] = newWidth + 'px';
4416 -        } else {
4417 -        	  // animate change
4418 -        	  timeline._autoResizing = true;
4419 -        	  var animateParam ={};
4420 -        	  animateParam[widthStyle] = newWidth + 'px';
4421 -
4422 -        	  SimileAjax.jQuery(timeline._containerDiv).animate(
4423 -        	      animateParam, timeline.autoWidthAnimationTime,
4424 -        	      'linear', function(){timeline._autoResizing = false;});
4425 -        }
4426 -    }
4427 -
4428 -    function checkTimelineWidth() {
4429 -        var targetWidth = 0; // the new desired width
4430 -        var currentWidth = timeline.getPixelWidth();
4431 -
4432 -        if (timeline._autoResizing) {
4433 -        	return; // early return
4434 -        }
4435 -
4436 -        // compute targetWidth
4437 -        for (var i = 0; i < timeline._bands.length; i++) {
4438 -            timeline._bands[i].checkAutoWidth();
4439 -            targetWidth += timeline._bandInfos[i].width;
4440 -        }
4441 -
4442 -        if (targetWidth > currentWidth || okToShrink) {
4443 -            // yes, let's change the size
4444 -            newWidth = targetWidth;
4445 -            changeTimelineWidth();
4446 -            timeline._distributeWidths();
4447 -        }
4448 -    }
4449 -
4450 -    // function's mainline
4451 -    if (!timeline.autoWidth) {
4452 -        return; // early return
4453 -    }
4454 -
4455 -    checkTimelineWidth();
4456 -};
4457 -
4458 -Timeline._Impl.prototype._initialize = function() {
4459 -    var containerDiv = this._containerDiv;
4460 -    var doc = containerDiv.ownerDocument;
4461 -
4462 -    containerDiv.className =
4463 -        containerDiv.className.split(" ").concat("timeline-container").join(" ");
4464 -
4465 -	/*
4466 -	 * Set css-class on container div that will define orientation
4467 -	 */
4468 -	var orientation = (this.isHorizontal()) ? 'horizontal' : 'vertical'
4469 -	containerDiv.className +=' timeline-'+orientation;
4470 -
4471 -
4472 -    while (containerDiv.firstChild) {
4473 -        containerDiv.removeChild(containerDiv.firstChild);
4474 -    }
4475 -
4476 -    /*
4477 -     *  inserting copyright and link to simile
4478 -     */
4479 -    var elmtCopyright = SimileAjax.Graphics.createTranslucentImage(Timeline.urlPrefix + (this.isHorizontal() ? "images/copyright-vertical.png" : "images/copyright.png"));
4480 -    elmtCopyright.className = "timeline-copyright";
4481 -    elmtCopyright.title = "Timeline copyright SIMILE - www.code.google.com/p/simile-widgets/";
4482 -    SimileAjax.DOM.registerEvent(elmtCopyright, "click", function() { window.location = "http://www.simile-widgets.org/"; });
4483 -    containerDiv.appendChild(elmtCopyright);
4484 -
4485 -    /*
4486 -     *  creating bands
4487 -     */
4488 -    this._bands = [];
4489 -    for (var i = 0; i < this._bandInfos.length; i++) {
4490 -        var bandInfo = this._bandInfos[i];
4491 -        var bandClass = bandInfo.bandClass || Timeline._Band;
4492 -        var band = new bandClass(this, this._bandInfos[i], i);
4493 -        this._bands.push(band);
4494 -    }
4495 -    this._distributeWidths();
4496 -
4497 -    /*
4498 -     *  sync'ing bands
4499 -     */
4500 -    for (var i = 0; i < this._bandInfos.length; i++) {
4501 -        var bandInfo = this._bandInfos[i];
4502 -        if ("syncWith" in bandInfo) {
4503 -            this._bands[i].setSyncWithBand(
4504 -                this._bands[bandInfo.syncWith],
4505 -                ("highlight" in bandInfo) ? bandInfo.highlight : false
4506 -            );
4507 -        }
4508 -    }
4509 -
4510 -
4511 -    if (this.autoWidth) {
4512 -        for (var i = 0; i < this._bands.length; i++) {
4513 -            this._bands[i].addOnScrollListener(this._autoWidthScrollListener);
4514 -        }
4515 -    }
4516 -
4517 -
4518 -    /*
4519 -     *  creating loading UI
4520 -     */
4521 -    var message = SimileAjax.Graphics.createMessageBubble(doc);
4522 -    message.containerDiv.className = "timeline-message-container";
4523 -    containerDiv.appendChild(message.containerDiv);
4524 -
4525 -    message.contentDiv.className = "timeline-message";
4526 -    message.contentDiv.innerHTML = "<img src='" + Timeline.urlPrefix + "images/progress-running.gif' /> Loading...";
4527 -
4528 -    this.showLoadingMessage = function() { message.containerDiv.style.display = "block"; };
4529 -    this.hideLoadingMessage = function() { message.containerDiv.style.display = "none"; };
4530 -};
4531 -
4532 -Timeline._Impl.prototype._distributeWidths = function() {
4533 -    var length = this.getPixelLength();
4534 -    var width = this.getPixelWidth();
4535 -    var cumulativeWidth = 0;
4536 -
4537 -    for (var i = 0; i < this._bands.length; i++) {
4538 -        var band = this._bands[i];
4539 -        var bandInfos = this._bandInfos[i];
4540 -        var widthString = bandInfos.width;
4541 -        var bandWidth;
4542 -
4543 -        if (typeof widthString == 'string') {
4544 -          var x =  widthString.indexOf("%");
4545 -          if (x > 0) {
4546 -              var percent = parseInt(widthString.substr(0, x));
4547 -              bandWidth = Math.round(percent * width / 100);
4548 -          } else {
4549 -              bandWidth = parseInt(widthString);
4550 -          }
4551 -        } else {
4552 -        	// was given an integer
4553 -        	bandWidth = widthString;
4554 -        }
4555 -
4556 -        band.setBandShiftAndWidth(cumulativeWidth, bandWidth);
4557 -        band.setViewLength(length);
4558 -
4559 -        cumulativeWidth += bandWidth;
4560 -    }
4561 -};
4562 -
4563 -Timeline._Impl.prototype.shiftOK = function(index, shift) {
4564 -    // Returns true if the proposed shift is ok
4565 -    //
4566 -    // Positive shift means going back in time
4567 -    var going_back = shift > 0,
4568 -        going_forward = shift < 0;
4569 -
4570 -    // Is there an edge?
4571 -    if ((going_back    && this.timeline_start == null) ||
4572 -        (going_forward && this.timeline_stop  == null) ||
4573 -        (shift == 0)) {
4574 -        return (true);  // early return
4575 -    }
4576 -
4577 -    // If any of the bands has noted that it is changing the others,
4578 -    // then this shift is a secondary shift in reaction to the real shift,
4579 -    // which already happened. In such cases, ignore it. (The issue is
4580 -    // that a positive original shift can cause a negative secondary shift,
4581 -    // as the bands adjust.)
4582 -    var secondary_shift = false;
4583 -    for (var i = 0; i < this._bands.length && !secondary_shift; i++) {
4584 -       secondary_shift = this._bands[i].busy();
4585 -    }
4586 -    if (secondary_shift) {
4587 -        return(true); // early return
4588 -    }
4589 -
4590 -    // If we are already at an edge, then don't even think about going any further
4591 -    if ((going_back    && this.timeline_at_start) ||
4592 -        (going_forward && this.timeline_at_stop)) {
4593 -        return (false);  // early return
4594 -    }
4595 -
4596 -    // Need to check all the bands
4597 -    var ok = false; // return value
4598 -    // If any of the bands will be or are showing an ok date, then let the shift proceed.
4599 -    for (var i = 0; i < this._bands.length && !ok; i++) {
4600 -       var band = this._bands[i];
4601 -       if (going_back) {
4602 -           ok = (i == index ? band.getMinVisibleDateAfterDelta(shift) : band.getMinVisibleDate())
4603 -                >= this.timeline_start;
4604 -       } else {
4605 -           ok = (i == index ? band.getMaxVisibleDateAfterDelta(shift) : band.getMaxVisibleDate())
4606 -                <= this.timeline_stop;
4607 -       }
4608 -    }
4609 -
4610 -    // process results
4611 -    if (going_back) {
4612 -       this.timeline_at_start = !ok;
4613 -       this.timeline_at_stop = false;
4614 -    } else {
4615 -       this.timeline_at_stop = !ok;
4616 -       this.timeline_at_start = false;
4617 -    }
4618 -    // This is where you could have an effect once per hitting an
4619 -    // edge of the Timeline. Eg jitter the Timeline
4620 -    //if (!ok) {
4621 -        //alert(going_back ? "At beginning" : "At end");
4622 -    //}
4623 -    return (ok);
4624 -};
4625 -
4626 -Timeline._Impl.prototype.zoom = function (zoomIn, x, y, target) {
4627 -  var matcher = new RegExp("^timeline-band-([0-9]+)$");
4628 -  var bandIndex = null;
4629 -
4630 -  var result = matcher.exec(target.id);
4631 -  if (result) {
4632 -    bandIndex = parseInt(result[1]);
4633 -  }
4634 -
4635 -  if (bandIndex != null) {
4636 -    this._bands[bandIndex].zoom(zoomIn, x, y, target);
4637 -  }
4638 -
4639 -  this.paint();
4640 -};
4641 -
4642 -/*
4643 - *
4644 - * Coding standards:
4645 - *
4646 - * We aim towards Douglas Crockford's Javascript conventions.
4647 - * See:  http://javascript.crockford.com/code.html
4648 - * See also: http://www.crockford.com/javascript/javascript.html
4649 - *
4650 - * That said, this JS code was written before some recent JS
4651 - * support libraries became widely used or available.
4652 - * In particular, the _ character is used to indicate a class function or
4653 - * variable that should be considered private to the class.
4654 - *
4655 - * The code mostly uses accessor methods for getting/setting the private
4656 - * class variables.
4657 - *
4658 - * Over time, we'd like to formalize the convention by using support libraries
4659 - * which enforce privacy in objects.
4660 - *
4661 - * We also want to use jslint:  http://www.jslint.com/
4662 - *
4663 - *
4664 - *
4665 - */
4666 -
4667 -
4668 -
4669 -/*
4670 - *  Band
4671 - *
4672 - */
4673 -Timeline._Band = function(timeline, bandInfo, index) {
4674 -    // hack for easier subclassing
4675 -    if (timeline !== undefined) {
4676 -        this.initialize(timeline, bandInfo, index);
4677 -    }
4678 -};
4679 -
4680 -Timeline._Band.prototype.initialize = function(timeline, bandInfo, index) {
4681 -    // Set up the band's object
4682 -
4683 -    // Munge params: If autoWidth is on for the Timeline, then ensure that
4684 -    // bandInfo.width is an integer
4685 -    if (timeline.autoWidth && typeof bandInfo.width == 'string') {
4686 -        bandInfo.width = bandInfo.width.indexOf("%") > -1 ? 0 : parseInt(bandInfo.width);
4687 -    }
4688 -
4689 -    this._timeline = timeline;
4690 -    this._bandInfo = bandInfo;
4691 -
4692 -    this._index = index;
4693 -
4694 -    this._locale = ("locale" in bandInfo) ? bandInfo.locale : Timeline.getDefaultLocale();
4695 -    this._timeZone = ("timeZone" in bandInfo) ? bandInfo.timeZone : 0;
4696 -    this._labeller = ("labeller" in bandInfo) ? bandInfo.labeller :
4697 -        (("createLabeller" in timeline.getUnit()) ?
4698 -            timeline.getUnit().createLabeller(this._locale, this._timeZone) :
4699 -            new Timeline.GregorianDateLabeller(this._locale, this._timeZone));
4700 -    this._theme = bandInfo.theme;
4701 -    this._zoomIndex = ("zoomIndex" in bandInfo) ? bandInfo.zoomIndex : 0;
4702 -    this._zoomSteps = ("zoomSteps" in bandInfo) ? bandInfo.zoomSteps : null;
4703 -
4704 -    this._dragging = false;
4705 -    this._changing = false;
4706 -    this._originalScrollSpeed = 5; // pixels
4707 -    this._scrollSpeed = this._originalScrollSpeed;
4708 -    this._viewOrthogonalOffset= 0; // vertical offset if the timeline is horizontal, and vice versa
4709 -    this._onScrollListeners = [];
4710 -
4711 -    var b = this;
4712 -    this._syncWithBand = null;
4713 -    this._syncWithBandHandler = function(band) {
4714 -        b._onHighlightBandScroll();
4715 -    };
4716 -    this._selectorListener = function(band) {
4717 -        b._onHighlightBandScroll();
4718 -    };
4719 -
4720 -    /*
4721 -     *  Install a textbox to capture keyboard events
4722 -     */
4723 -    var inputDiv = this._timeline.getDocument().createElement("div");
4724 -    inputDiv.className = "timeline-band-input";
4725 -    this._timeline.addDiv(inputDiv);
4726 -
4727 -    this._keyboardInput = document.createElement("input");
4728 -    this._keyboardInput.type = "text";
4729 -    inputDiv.appendChild(this._keyboardInput);
4730 -    SimileAjax.DOM.registerEventWithObject(this._keyboardInput, "keydown", this, "_onKeyDown");
4731 -    SimileAjax.DOM.registerEventWithObject(this._keyboardInput, "keyup", this, "_onKeyUp");
4732 -
4733 -    /*
4734 -     *  The band's outer most div that slides with respect to the timeline's div
4735 -     */
4736 -    this._div = this._timeline.getDocument().createElement("div");
4737 -    this._div.id = "timeline-band-" + index;
4738 -    this._div.className = "timeline-band timeline-band-" + index;
4739 -    this._timeline.addDiv(this._div);
4740 -
4741 -    SimileAjax.DOM.registerEventWithObject(this._div, "mousedown", this, "_onMouseDown");
4742 -    SimileAjax.DOM.registerEventWithObject(this._div, "mousemove", this, "_onMouseMove");
4743 -    SimileAjax.DOM.registerEventWithObject(this._div, "mouseup", this, "_onMouseUp");
4744 -    SimileAjax.DOM.registerEventWithObject(this._div, "mouseout", this, "_onMouseOut");
4745 -    SimileAjax.DOM.registerEventWithObject(this._div, "dblclick", this, "_onDblClick");
4746 -
4747 -    var mouseWheel = this._theme!= null ? this._theme.mouseWheel : 'scroll'; // theme is not always defined
4748 -    if (mouseWheel === 'zoom' || mouseWheel === 'scroll' || this._zoomSteps) {
4749 -        // capture mouse scroll
4750 -        if (SimileAjax.Platform.browser.isFirefox) {
4751 -            SimileAjax.DOM.registerEventWithObject(this._div, "DOMMouseScroll", this, "_onMouseScroll");
4752 -        } else {
4753 -            SimileAjax.DOM.registerEventWithObject(this._div, "mousewheel", this, "_onMouseScroll");
4754 -        }
4755 -    }
4756 -
4757 -    /*
4758 -     *  The inner div that contains layers
4759 -     */
4760 -    this._innerDiv = this._timeline.getDocument().createElement("div");
4761 -    this._innerDiv.className = "timeline-band-inner";
4762 -    this._div.appendChild(this._innerDiv);
4763 -
4764 -    /*
4765 -     *  Initialize parts of the band
4766 -     */
4767 -    this._ether = bandInfo.ether;
4768 -    bandInfo.ether.initialize(this, timeline);
4769 -
4770 -    this._etherPainter = bandInfo.etherPainter;
4771 -    bandInfo.etherPainter.initialize(this, timeline);
4772 -
4773 -    this._eventSource = bandInfo.eventSource;
4774 -    if (this._eventSource) {
4775 -        this._eventListener = {
4776 -            onAddMany: function() { b._onAddMany(); },
4777 -            onClear:   function() { b._onClear(); }
4778 -        }
4779 -        this._eventSource.addListener(this._eventListener);
4780 -    }
4781 -
4782 -    this._eventPainter = bandInfo.eventPainter;
4783 -    this._eventTracksNeeded = 0;   // set by painter via updateEventTrackInfo
4784 -    this._eventTrackIncrement = 0;
4785 -    bandInfo.eventPainter.initialize(this, timeline);
4786 -
4787 -    this._decorators = ("decorators" in bandInfo) ? bandInfo.decorators : [];
4788 -    for (var i = 0; i < this._decorators.length; i++) {
4789 -        this._decorators[i].initialize(this, timeline);
4790 -    }
4791 -};
4792 -
4793 -Timeline._Band.SCROLL_MULTIPLES = 5;
4794 -
4795 -Timeline._Band.prototype.dispose = function() {
4796 -    this.closeBubble();
4797 -
4798 -    if (this._eventSource) {
4799 -        this._eventSource.removeListener(this._eventListener);
4800 -        this._eventListener = null;
4801 -        this._eventSource = null;
4802 -    }
4803 -
4804 -    this._timeline = null;
4805 -    this._bandInfo = null;
4806 -
4807 -    this._labeller = null;
4808 -    this._ether = null;
4809 -    this._etherPainter = null;
4810 -    this._eventPainter = null;
4811 -    this._decorators = null;
4812 -
4813 -    this._onScrollListeners = null;
4814 -    this._syncWithBandHandler = null;
4815 -    this._selectorListener = null;
4816 -
4817 -    this._div = null;
4818 -    this._innerDiv = null;
4819 -    this._keyboardInput = null;
4820 -};
4821 -
4822 -Timeline._Band.prototype.addOnScrollListener = function(listener) {
4823 -    this._onScrollListeners.push(listener);
4824 -};
4825 -
4826 -Timeline._Band.prototype.removeOnScrollListener = function(listener) {
4827 -    for (var i = 0; i < this._onScrollListeners.length; i++) {
4828 -        if (this._onScrollListeners[i] == listener) {
4829 -            this._onScrollListeners.splice(i, 1);
4830 -            break;
4831 -        }
4832 -    }
4833 -};
4834 -
4835 -Timeline._Band.prototype.setSyncWithBand = function(band, highlight) {
4836 -    if (this._syncWithBand) {
4837 -        this._syncWithBand.removeOnScrollListener(this._syncWithBandHandler);
4838 -    }
4839 -
4840 -    this._syncWithBand = band;
4841 -    this._syncWithBand.addOnScrollListener(this._syncWithBandHandler);
4842 -    this._highlight = highlight;
4843 -    this._positionHighlight();
4844 -};
4845 -
4846 -Timeline._Band.prototype.getLocale = function() {
4847 -    return this._locale;
4848 -};
4849 -
4850 -Timeline._Band.prototype.getTimeZone = function() {
4851 -    return this._timeZone;
4852 -};
4853 -
4854 -Timeline._Band.prototype.getLabeller = function() {
4855 -    return this._labeller;
4856 -};
4857 -
4858 -Timeline._Band.prototype.getIndex = function() {
4859 -    return this._index;
4860 -};
4861 -
4862 -Timeline._Band.prototype.getEther = function() {
4863 -    return this._ether;
4864 -};
4865 -
4866 -Timeline._Band.prototype.getEtherPainter = function() {
4867 -    return this._etherPainter;
4868 -};
4869 -
4870 -Timeline._Band.prototype.getEventSource = function() {
4871 -    return this._eventSource;
4872 -};
4873 -
4874 -Timeline._Band.prototype.getEventPainter = function() {
4875 -    return this._eventPainter;
4876 -};
4877 -
4878 -Timeline._Band.prototype.getTimeline = function() {
4879 -    return this._timeline;
4880 -};
4881 -
4882 -// Autowidth support
4883 -Timeline._Band.prototype.updateEventTrackInfo = function(tracks, increment) {
4884 -    this._eventTrackIncrement = increment; // doesn't vary for a specific band
4885 -
4886 -    if (tracks > this._eventTracksNeeded) {
4887 -        this._eventTracksNeeded = tracks;
4888 -    }
4889 -};
4890 -
4891 -// Autowidth support
4892 -Timeline._Band.prototype.checkAutoWidth = function() {
4893 -    // if a new (larger) width is needed by the band
4894 -    // then: a) updates the band's bandInfo.width
4895 -    //
4896 -    // desiredWidth for the band is
4897 -    //   (number of tracks + margin) * track increment
4898 -    if (! this._timeline.autoWidth) {
4899 -      return; // early return
4900 -    }
4901 -
4902 -    var overviewBand = this._eventPainter.getType() == 'overview';
4903 -    var margin = overviewBand ?
4904 -       this._theme.event.overviewTrack.autoWidthMargin :
4905 -       this._theme.event.track.autoWidthMargin;
4906 -    var desiredWidth = Math.ceil((this._eventTracksNeeded + margin) *
4907 -                       this._eventTrackIncrement);
4908 -    // add offset amount (additional margin)
4909 -    desiredWidth += overviewBand ? this._theme.event.overviewTrack.offset :
4910 -                                   this._theme.event.track.offset;
4911 -    var bandInfo = this._bandInfo;
4912 -
4913 -    if (desiredWidth != bandInfo.width) {
4914 -        bandInfo.width = desiredWidth;
4915 -    }
4916 -};
4917 -
4918 -Timeline._Band.prototype.layout = function() {
4919 -    this.paint();
4920 -};
4921 -
4922 -Timeline._Band.prototype.paint = function() {
4923 -    this._etherPainter.paint();
4924 -    this._paintDecorators();
4925 -    this._paintEvents();
4926 -};
4927 -
4928 -Timeline._Band.prototype.softLayout = function() {
4929 -    this.softPaint();
4930 -};
4931 -
4932 -Timeline._Band.prototype.softPaint = function() {
4933 -    this._etherPainter.softPaint();
4934 -    this._softPaintDecorators();
4935 -    this._softPaintEvents();
4936 -};
4937 -
4938 -Timeline._Band.prototype.setBandShiftAndWidth = function(shift, width) {
4939 -    var inputDiv = this._keyboardInput.parentNode;
4940 -    var middle = shift + Math.floor(width / 2);
4941 -    if (this._timeline.isHorizontal()) {
4942 -        this._div.style.top = shift + "px";
4943 -        this._div.style.height = width + "px";
4944 -
4945 -        inputDiv.style.top = middle + "px";
4946 -        inputDiv.style.left = "-1em";
4947 -    } else {
4948 -        this._div.style.left = shift + "px";
4949 -        this._div.style.width = width + "px";
4950 -
4951 -        inputDiv.style.left = middle + "px";
4952 -        inputDiv.style.top = "-1em";
4953 -    }
4954 -};
4955 -
4956 -Timeline._Band.prototype.getViewWidth = function() {
4957 -    if (this._timeline.isHorizontal()) {
4958 -        return this._div.offsetHeight;
4959 -    } else {
4960 -        return this._div.offsetWidth;
4961 -    }
4962 -};
4963 -
4964 -Timeline._Band.prototype.setViewLength = function(length) {
4965 -    this._viewLength = length;
4966 -    this._recenterDiv();
4967 -    this._onChanging();
4968 -};
4969 -
4970 -Timeline._Band.prototype.getViewLength = function() {
4971 -    return this._viewLength;
4972 -};
4973 -
4974 -Timeline._Band.prototype.getTotalViewLength = function() {
4975 -    return Timeline._Band.SCROLL_MULTIPLES * this._viewLength;
4976 -};
4977 -
4978 -Timeline._Band.prototype.getViewOffset = function() {
4979 -    return this._viewOffset;
4980 -};
4981 -
4982 -Timeline._Band.prototype.getMinDate = function() {
4983 -    return this._ether.pixelOffsetToDate(this._viewOffset);
4984 -};
4985 -
4986 -Timeline._Band.prototype.getMaxDate = function() {
4987 -    return this._ether.pixelOffsetToDate(this._viewOffset + Timeline._Band.SCROLL_MULTIPLES * this._viewLength);
4988 -};
4989 -
4990 -Timeline._Band.prototype.getMinVisibleDate = function() {
4991 -    return this._ether.pixelOffsetToDate(0);
4992 -};
4993 -
4994 -Timeline._Band.prototype.getMinVisibleDateAfterDelta = function(delta) {
4995 -    return this._ether.pixelOffsetToDate(delta);
4996 -};
4997 -
4998 -Timeline._Band.prototype.getMaxVisibleDate = function() {
4999 -    // Max date currently visible on band
5000 -    return this._ether.pixelOffsetToDate(this._viewLength);
5001 -};
5002 -
5003 -Timeline._Band.prototype.getMaxVisibleDateAfterDelta = function(delta) {
5004 -    // Max date visible on band after delta px view change is applied
5005 -    return this._ether.pixelOffsetToDate(this._viewLength + delta);
5006 -};
5007 -
5008 -Timeline._Band.prototype.getCenterVisibleDate = function() {
5009 -    return this._ether.pixelOffsetToDate(this._viewLength / 2);
5010 -};
5011 -
5012 -Timeline._Band.prototype.setMinVisibleDate = function(date) {
5013 -    if (!this._changing) {
5014 -        this._moveEther(Math.round(-this._ether.dateToPixelOffset(date)));
5015 -    }
5016 -};
5017 -
5018 -Timeline._Band.prototype.setMaxVisibleDate = function(date) {
5019 -    if (!this._changing) {
5020 -        this._moveEther(Math.round(this._viewLength - this._ether.dateToPixelOffset(date)));
5021 -    }
5022 -};
5023 -
5024 -Timeline._Band.prototype.setCenterVisibleDate = function(date) {
5025 -    if (!this._changing) {
5026 -        this._moveEther(Math.round(this._viewLength / 2 - this._ether.dateToPixelOffset(date)));
5027 -    }
5028 -};
5029 -
5030 -Timeline._Band.prototype.dateToPixelOffset = function(date) {
5031 -    return this._ether.dateToPixelOffset(date) - this._viewOffset;
5032 -};
5033 -
5034 -Timeline._Band.prototype.pixelOffsetToDate = function(pixels) {
5035 -    return this._ether.pixelOffsetToDate(pixels + this._viewOffset);
5036 -};
5037 -
5038 -Timeline._Band.prototype.getViewOrthogonalOffset = function() {
5039 -    return this._viewOrthogonalOffset;
5040 -};
5041 -
5042 -Timeline._Band.prototype.setViewOrthogonalOffset = function(offset) {
5043 -    this._viewOrthogonalOffset = Math.max(0, offset);
5044 -};
5045 -
5046 -Timeline._Band.prototype.createLayerDiv = function(zIndex, className) {
5047 -    var div = this._timeline.getDocument().createElement("div");
5048 -    div.className = "timeline-band-layer" + (typeof className == "string" ? (" " + className) : "");
5049 -    div.style.zIndex = zIndex;
5050 -    this._innerDiv.appendChild(div);
5051 -
5052 -    var innerDiv = this._timeline.getDocument().createElement("div");
5053 -    innerDiv.className = "timeline-band-layer-inner";
5054 -    if (SimileAjax.Platform.browser.isIE) {
5055 -        innerDiv.style.cursor = "move";
5056 -    } else {
5057 -        innerDiv.style.cursor = "-moz-grab";
5058 -    }
5059 -    div.appendChild(innerDiv);
5060 -
5061 -    return innerDiv;
5062 -};
5063 -
5064 -Timeline._Band.prototype.removeLayerDiv = function(div) {
5065 -    this._innerDiv.removeChild(div.parentNode);
5066 -};
5067 -
5068 -Timeline._Band.prototype.scrollToCenter = function(date, f) {
5069 -    var pixelOffset = this._ether.dateToPixelOffset(date);
5070 -    if (pixelOffset < -this._viewLength / 2) {
5071 -        this.setCenterVisibleDate(this.pixelOffsetToDate(pixelOffset + this._viewLength));
5072 -    } else if (pixelOffset > 3 * this._viewLength / 2) {
5073 -        this.setCenterVisibleDate(this.pixelOffsetToDate(pixelOffset - this._viewLength));
5074 -    }
5075 -    this._autoScroll(Math.round(this._viewLength / 2 - this._ether.dateToPixelOffset(date)), f);
5076 -};
5077 -
5078 -Timeline._Band.prototype.showBubbleForEvent = function(eventID) {
5079 -    var evt = this.getEventSource().getEvent(eventID);
5080 -    if (evt) {
5081 -        var self = this;
5082 -        this.scrollToCenter(evt.getStart(), function() {
5083 -            self._eventPainter.showBubble(evt);
5084 -        });
5085 -    }
5086 -};
5087 -
5088 -Timeline._Band.prototype.zoom = function(zoomIn, x, y, target) {
5089 -  if (!this._zoomSteps) {
5090 -    // zoom disabled
5091 -    return;
5092 -  }
5093 -
5094 -  // shift the x value by our offset
5095 -  x += this._viewOffset;
5096 -
5097 -  var zoomDate = this._ether.pixelOffsetToDate(x);
5098 -  var netIntervalChange = this._ether.zoom(zoomIn);
5099 -  this._etherPainter.zoom(netIntervalChange);
5100 -
5101 -  // shift our zoom date to the far left
5102 -  this._moveEther(Math.round(-this._ether.dateToPixelOffset(zoomDate)));
5103 -  // then shift it back to where the mouse was
5104 -  this._moveEther(x);
5105 -};
5106 -
5107 -Timeline._Band.prototype._onMouseDown = function(innerFrame, evt, target) {
5108 -    this.closeBubble();
5109 -
5110 -    this._dragging = true;
5111 -    this._dragX = evt.clientX;
5112 -    this._dragY = evt.clientY;
5113 -};
5114 -
5115 -Timeline._Band.prototype._onMouseMove = function(innerFrame, evt, target) {
5116 -    if (this._dragging) {
5117 -        var diffX = evt.clientX - this._dragX;
5118 -        var diffY = evt.clientY - this._dragY;
5119 -
5120 -        this._dragX = evt.clientX;
5121 -        this._dragY = evt.clientY;
5122 -
5123 -        if (this._timeline.isHorizontal()) {
5124 -            this._moveEther(diffX, diffY);
5125 -        } else {
5126 -            this._moveEther(diffY, diffX);
5127 -        }
5128 -        this._positionHighlight();
5129 -    }
5130 -};
5131 -
5132 -Timeline._Band.prototype._onMouseUp = function(innerFrame, evt, target) {
5133 -    this._dragging = false;
5134 -    this._keyboardInput.focus();
5135 -};
5136 -
5137 -Timeline._Band.prototype._onMouseOut = function(innerFrame, evt, target) {
5138 -    var coords = SimileAjax.DOM.getEventRelativeCoordinates(evt, innerFrame);
5139 -    coords.x += this._viewOffset;
5140 -    if (coords.x < 0 || coords.x > innerFrame.offsetWidth ||
5141 -        coords.y < 0 || coords.y > innerFrame.offsetHeight) {
5142 -        this._dragging = false;
5143 -    }
5144 -};
5145 -
5146 -Timeline._Band.prototype._onMouseScroll = function(innerFrame, evt, target) {
5147 -  var now = new Date();
5148 -  now = now.getTime();
5149 -
5150 -  if (!this._lastScrollTime || ((now - this._lastScrollTime) > 50)) {
5151 -    // limit 1 scroll per 200ms due to FF3 sending multiple events back to back
5152 -    this._lastScrollTime = now;
5153 -
5154 -    var delta = 0;
5155 -    if (evt.wheelDelta) {
5156 -      delta = evt.wheelDelta/120;
5157 -    } else if (evt.detail) {
5158 -      delta = -evt.detail/3;
5159 -    }
5160 -
5161 -    // either scroll or zoom
5162 -    var mouseWheel = this._theme.mouseWheel;
5163 -
5164 -    if (this._zoomSteps || mouseWheel === 'zoom') {
5165 -      var loc = SimileAjax.DOM.getEventRelativeCoordinates(evt, innerFrame);
5166 -      if (delta != 0) {
5167 -        var zoomIn;
5168 -        if (delta > 0)
5169 -          zoomIn = true;
5170 -        if (delta < 0)
5171 -          zoomIn = false;
5172 -        // call zoom on the timeline so we could zoom multiple bands if desired
5173 -        this._timeline.zoom(zoomIn, loc.x, loc.y, innerFrame);
5174 -      }
5175 -    }
5176 -    else if (mouseWheel === 'scroll') {
5177 -    	var move_amt = 50 * (delta < 0 ? -1 : 1);
5178 -      this._moveEther(move_amt);
5179 -    }
5180 -  }
5181 -
5182 -  // prevent bubble
5183 -  if (evt.stopPropagation) {
5184 -    evt.stopPropagation();
5185 -  }
5186 -  evt.cancelBubble = true;
5187 -
5188 -  // prevent the default action
5189 -  if (evt.preventDefault) {
5190 -    evt.preventDefault();
5191 -  }
5192 -  evt.returnValue = false;
5193 -};
5194 -
5195 -Timeline._Band.prototype._onDblClick = function(innerFrame, evt, target) {
5196 -    var coords = SimileAjax.DOM.getEventRelativeCoordinates(evt, innerFrame);
5197 -    var distance = coords.x - (this._viewLength / 2 - this._viewOffset);
5198 -
5199 -    this._autoScroll(-distance);
5200 -};
5201 -
5202 -Timeline._Band.prototype._onKeyDown = function(keyboardInput, evt, target) {
5203 -    if (!this._dragging) {
5204 -        switch (evt.keyCode) {
5205 -        case 27: // ESC
5206 -            break;
5207 -        case 37: // left arrow
5208 -        case 38: // up arrow
5209 -            this._scrollSpeed = Math.min(50, Math.abs(this._scrollSpeed * 1.05));
5210 -            this._moveEther(this._scrollSpeed);
5211 -            break;
5212 -        case 39: // right arrow
5213 -        case 40: // down arrow
5214 -            this._scrollSpeed = -Math.min(50, Math.abs(this._scrollSpeed * 1.05));
5215 -            this._moveEther(this._scrollSpeed);
5216 -            break;
5217 -        default:
5218 -            return true;
5219 -        }
5220 -        this.closeBubble();
5221 -
5222 -        SimileAjax.DOM.cancelEvent(evt);
5223 -        return false;
5224 -    }
5225 -    return true;
5226 -};
5227 -
5228 -Timeline._Band.prototype._onKeyUp = function(keyboardInput, evt, target) {
5229 -    if (!this._dragging) {
5230 -        this._scrollSpeed = this._originalScrollSpeed;
5231 -
5232 -        switch (evt.keyCode) {
5233 -        case 35: // end
5234 -            this.setCenterVisibleDate(this._eventSource.getLatestDate());
5235 -            break;
5236 -        case 36: // home
5237 -            this.setCenterVisibleDate(this._eventSource.getEarliestDate());
5238 -            break;
5239 -        case 33: // page up
5240 -            this._autoScroll(this._timeline.getPixelLength());
5241 -            break;
5242 -        case 34: // page down
5243 -            this._autoScroll(-this._timeline.getPixelLength());
5244 -            break;
5245 -        default:
5246 -            return true;
5247 -        }
5248 -
5249 -        this.closeBubble();
5250 -
5251 -        SimileAjax.DOM.cancelEvent(evt);
5252 -        return false;
5253 -    }
5254 -    return true;
5255 -};
5256 -
5257 -Timeline._Band.prototype._autoScroll = function(distance, f) {
5258 -    var b = this;
5259 -    var a = SimileAjax.Graphics.createAnimation(
5260 -        function(abs, diff) {
5261 -            b._moveEther(diff);
5262 -        },
5263 -        0,
5264 -        distance,
5265 -        1000,
5266 -        f
5267 -    );
5268 -    a.run();
5269 -};
5270 -
5271 -Timeline._Band.prototype._moveEther = function(shift, orthogonalShift) {
5272 -    if (orthogonalShift === undefined) {
5273 -        orthogonalShift = 0;
5274 -    }
5275 -
5276 -    this.closeBubble();
5277 -
5278 -    // A positive shift means back in time
5279 -    // Check that we're not moving beyond Timeline's limits
5280 -    if (!this._timeline.shiftOK(this._index, shift)) {
5281 -        return; // early return
5282 -    }
5283 -
5284 -    this._viewOffset += shift;
5285 -    this._viewOrthogonalOffset = Math.min(0, this._viewOrthogonalOffset + orthogonalShift);
5286 -
5287 -    this._ether.shiftPixels(-shift);
5288 -    if (this._timeline.isHorizontal()) {
5289 -        this._div.style.left = this._viewOffset + "px";
5290 -    } else {
5291 -        this._div.style.top = this._viewOffset + "px";
5292 -    }
5293 -
5294 -    if (this._viewOffset > -this._viewLength * 0.5 ||
5295 -        this._viewOffset < -this._viewLength * (Timeline._Band.SCROLL_MULTIPLES - 1.5)) {
5296 -
5297 -        this._recenterDiv();
5298 -    } else {
5299 -        this.softLayout();
5300 -    }
5301 -
5302 -    this._onChanging();
5303 -}
5304 -
5305 -Timeline._Band.prototype._onChanging = function() {
5306 -    this._changing = true;
5307 -
5308 -    this._fireOnScroll();
5309 -    this._setSyncWithBandDate();
5310 -
5311 -    this._changing = false;
5312 -};
5313 -
5314 -Timeline._Band.prototype.busy = function() {
5315 -    // Is this band busy changing other bands?
5316 -    return(this._changing);
5317 -};
5318 -
5319 -Timeline._Band.prototype._fireOnScroll = function() {
5320 -    for (var i = 0; i < this._onScrollListeners.length; i++) {
5321 -        this._onScrollListeners[i](this);
5322 -    }
5323 -};
5324 -
5325 -Timeline._Band.prototype._setSyncWithBandDate = function() {
5326 -    if (this._syncWithBand) {
5327 -        var centerDate = this._ether.pixelOffsetToDate(this.getViewLength() / 2);
5328 -        this._syncWithBand.setCenterVisibleDate(centerDate);
5329 -    }
5330 -};
5331 -
5332 -Timeline._Band.prototype._onHighlightBandScroll = function() {
5333 -    if (this._syncWithBand) {
5334 -        var centerDate = this._syncWithBand.getCenterVisibleDate();
5335 -        var centerPixelOffset = this._ether.dateToPixelOffset(centerDate);
5336 -
5337 -        this._moveEther(Math.round(this._viewLength / 2 - centerPixelOffset));
5338 -
5339 -        if (this._highlight) {
5340 -            this._etherPainter.setHighlight(
5341 -                this._syncWithBand.getMinVisibleDate(),
5342 -                this._syncWithBand.getMaxVisibleDate());
5343 -        }
5344 -    }
5345 -};
5346 -
5347 -Timeline._Band.prototype._onAddMany = function() {
5348 -    this._paintEvents();
5349 -};
5350 -
5351 -Timeline._Band.prototype._onClear = function() {
5352 -    this._paintEvents();
5353 -};
5354 -
5355 -Timeline._Band.prototype._positionHighlight = function() {
5356 -    if (this._syncWithBand) {
5357 -        var startDate = this._syncWithBand.getMinVisibleDate();
5358 -        var endDate = this._syncWithBand.getMaxVisibleDate();
5359 -
5360 -        if (this._highlight) {
5361 -            this._etherPainter.setHighlight(startDate, endDate);
5362 -        }
5363 -    }
5364 -};
5365 -
5366 -Timeline._Band.prototype._recenterDiv = function() {
5367 -    this._viewOffset = -this._viewLength * (Timeline._Band.SCROLL_MULTIPLES - 1) / 2;
5368 -    if (this._timeline.isHorizontal()) {
5369 -        this._div.style.left = this._viewOffset + "px";
5370 -        this._div.style.width = (Timeline._Band.SCROLL_MULTIPLES * this._viewLength) + "px";
5371 -    } else {
5372 -        this._div.style.top = this._viewOffset + "px";
5373 -        this._div.style.height = (Timeline._Band.SCROLL_MULTIPLES * this._viewLength) + "px";
5374 -    }
5375 -    this.layout();
5376 -};
5377 -
5378 -Timeline._Band.prototype._paintEvents = function() {
5379 -    this._eventPainter.paint();
5380 -};
5381 -
5382 -Timeline._Band.prototype._softPaintEvents = function() {
5383 -    this._eventPainter.softPaint();
5384 -};
5385 -
5386 -Timeline._Band.prototype._paintDecorators = function() {
5387 -    for (var i = 0; i < this._decorators.length; i++) {
5388 -        this._decorators[i].paint();
5389 -    }
5390 -};
5391 -
5392 -Timeline._Band.prototype._softPaintDecorators = function() {
5393 -    for (var i = 0; i < this._decorators.length; i++) {
5394 -        this._decorators[i].softPaint();
5395 -    }
5396 -};
5397 -
5398 -Timeline._Band.prototype.closeBubble = function() {
5399 -    SimileAjax.WindowManager.cancelPopups();
5400 -};
5401 -/*
5402 - *  Classic Theme
5403 - *
5404 - */
5405 -
5406 -
5407 -
5408 -Timeline.ClassicTheme = new Object();
5409 -
5410 -Timeline.ClassicTheme.implementations = [];
5411 -
5412 -Timeline.ClassicTheme.create = function(locale) {
5413 -    if (locale == null) {
5414 -        locale = Timeline.getDefaultLocale();
5415 -    }
5416 -
5417 -    var f = Timeline.ClassicTheme.implementations[locale];
5418 -    if (f == null) {
5419 -        f = Timeline.ClassicTheme._Impl;
5420 -    }
5421 -    return new f();
5422 -};
5423 -
5424 -Timeline.ClassicTheme._Impl = function() {
5425 -    this.firstDayOfWeek = 0; // Sunday
5426 -
5427 -    // Note: Many styles previously set here are now set using CSS
5428 -    //       The comments indicate settings controlled by CSS, not
5429 -    //       lines to be un-commented.
5430 -    //
5431 -    //
5432 -    // Attributes autoWidth, autoWidthAnimationTime, timeline_start
5433 -    // and timeline_stop must be set on the first band's theme.
5434 -    // The other attributes can be set differently for each
5435 -    // band by using different themes for the bands.
5436 -    this.autoWidth = false; // Should the Timeline automatically grow itself, as
5437 -                            // needed when too many events for the available width
5438 -                            // are painted on the visible part of the Timeline?
5439 -    this.autoWidthAnimationTime = 500; // mSec
5440 -    this.timeline_start = null; // Setting a date, eg new Date(Date.UTC(2010,0,17,20,00,00,0)) will prevent the
5441 -                                // Timeline from being moved to anytime before the date.
5442 -    this.timeline_stop = null;  // Use for setting a maximum date. The Timeline will not be able
5443 -                                // to be moved to anytime after this date.
5444 -    this.ether = {
5445 -        backgroundColors: [
5446 -        //    "#EEE",
5447 -        //    "#DDD",
5448 -        //    "#CCC",
5449 -        //    "#AAA"
5450 -        ],
5451 -     //   highlightColor:     "white",
5452 -        highlightOpacity:   50,
5453 -        interval: {
5454 -            line: {
5455 -                show:       true,
5456 -                opacity:    25
5457 -               // color:      "#aaa",
5458 -            },
5459 -            weekend: {
5460 -                opacity:    30
5461 -              //  color:      "#FFFFE0",
5462 -            },
5463 -            marker: {
5464 -                hAlign:     "Bottom",
5465 -                vAlign:     "Right"
5466 -                                        /*
5467 -                hBottomStyler: function(elmt) {
5468 -                    elmt.className = "timeline-ether-marker-bottom";
5469 -                },
5470 -                hBottomEmphasizedStyler: function(elmt) {
5471 -                    elmt.className = "timeline-ether-marker-bottom-emphasized";
5472 -                },
5473 -                hTopStyler: function(elmt) {
5474 -                    elmt.className = "timeline-ether-marker-top";
5475 -                },
5476 -                hTopEmphasizedStyler: function(elmt) {
5477 -                    elmt.className = "timeline-ether-marker-top-emphasized";
5478 -                },
5479 -                */
5480 -
5481 -
5482 -               /*
5483 -                                  vRightStyler: function(elmt) {
5484 -                    elmt.className = "timeline-ether-marker-right";
5485 -                },
5486 -                vRightEmphasizedStyler: function(elmt) {
5487 -                    elmt.className = "timeline-ether-marker-right-emphasized";
5488 -                },
5489 -                vLeftStyler: function(elmt) {
5490 -                    elmt.className = "timeline-ether-marker-left";
5491 -                },
5492 -                vLeftEmphasizedStyler:function(elmt) {
5493 -                    elmt.className = "timeline-ether-marker-left-emphasized";
5494 -                }
5495 -                */
5496 -            }
5497 -        }
5498 -    };
5499 -
5500 -    this.event = {
5501 -        track: {
5502 -                   height: 10, // px. You will need to change the track
5503 -                               //     height if you change the tape height.
5504 -                      gap:  2, // px. Gap between tracks
5505 -                   offset:  2, // px. top margin above tapes
5506 -          autoWidthMargin:  1.5
5507 -          /* autoWidthMargin is only used if autoWidth (see above) is true.
5508 -             The autoWidthMargin setting is used to set how close the bottom of the
5509 -             lowest track is to the edge of the band's div. The units are total track
5510 -             width (tape + label + gap). A min of 0.5 is suggested. Use this setting to
5511 -             move the bottom track's tapes above the axis markers, if needed for your
5512 -             Timeline.
5513 -          */
5514 -        },
5515 -        overviewTrack: {
5516 -                  offset: 20, // px -- top margin above tapes
5517 -              tickHeight:  6, // px
5518 -                  height:  2, // px
5519 -                     gap:  1, // px
5520 -         autoWidthMargin:  5 // This attribute is only used if autoWidth (see above) is true.
5521 -        },
5522 -        tape: {
5523 -            height:         4 // px. For thicker tapes, remember to change track height too.
5524 -        },
5525 -        instant: {
5526 -                           icon: Timeline.urlPrefix + "images/dull-blue-circle.png",
5527 -                                 // default icon. Icon can also be specified per event
5528 -                      iconWidth: 10,
5529 -                     iconHeight: 10,
5530 -               impreciseOpacity: 20, // opacity of the tape when durationEvent is false
5531 -            impreciseIconMargin: 3   // A tape and an icon are painted for imprecise instant
5532 -                                     // events. This attribute is the margin between the
5533 -                                     // bottom of the tape and the top of the icon in that
5534 -                                     // case.
5535 -    //        color:             "#58A0DC",
5536 -    //        impreciseColor:    "#58A0DC",
5537 -        },
5538 -        duration: {
5539 -            impreciseOpacity: 20 // tape opacity for imprecise part of duration events
5540 -      //      color:            "#58A0DC",
5541 -      //      impreciseColor:   "#58A0DC",
5542 -        },
5543 -        label: {
5544 -            backgroundOpacity: 50,// only used in detailed painter
5545 -               offsetFromLine:  3 // px left margin amount from icon's right edge
5546 -      //      backgroundColor:   "white",
5547 -      //      lineColor:         "#58A0DC",
5548 -        },
5549 -        highlightColors: [  // Use with getEventPainter().setHighlightMatcher
5550 -                            // See webapp/examples/examples.js
5551 -            "#FFFF00",
5552 -            "#FFC000",
5553 -            "#FF0000",
5554 -            "#0000FF"
5555 -        ],
5556 -        highlightLabelBackground: false, // When highlighting an event, also change the event's label background?
5557 -        bubble: {
5558 -            width:          250, // px
5559 -            maxHeight:        0, // px Maximum height of bubbles. 0 means no max height.
5560 -                                 // scrollbar will be added for taller bubbles
5561 -            titleStyler: function(elmt) {
5562 -                elmt.className = "timeline-event-bubble-title";
5563 -            },
5564 -            bodyStyler: function(elmt) {
5565 -                elmt.className = "timeline-event-bubble-body";
5566 -            },
5567 -            imageStyler: function(elmt) {
5568 -                elmt.className = "timeline-event-bubble-image";
5569 -            },
5570 -            wikiStyler: function(elmt) {
5571 -                elmt.className = "timeline-event-bubble-wiki";
5572 -            },
5573 -            timeStyler: function(elmt) {
5574 -                elmt.className = "timeline-event-bubble-time";
5575 -            }
5576 -        }
5577 -    };
5578 -
5579 -    this.mouseWheel = 'scroll'; // 'default', 'zoom', 'scroll'
5580 -};/*
5581 - *  An "ether" is a object that maps date/time to pixel coordinates.
5582 - *
5583 - */
5584 -
5585 -/*
5586 - *  Linear Ether
5587 - *
5588 - */
5589 -
5590 -Timeline.LinearEther = function(params) {
5591 -    this._params = params;
5592 -    this._interval = params.interval;
5593 -    this._pixelsPerInterval = params.pixelsPerInterval;
5594 -};
5595 -
5596 -Timeline.LinearEther.prototype.initialize = function(band, timeline) {
5597 -    this._band = band;
5598 -    this._timeline = timeline;
5599 -    this._unit = timeline.getUnit();
5600 -
5601 -    if ("startsOn" in this._params) {
5602 -        this._start = this._unit.parseFromObject(this._params.startsOn);
5603 -    } else if ("endsOn" in this._params) {
5604 -        this._start = this._unit.parseFromObject(this._params.endsOn);
5605 -        this.shiftPixels(-this._timeline.getPixelLength());
5606 -    } else if ("centersOn" in this._params) {
5607 -        this._start = this._unit.parseFromObject(this._params.centersOn);
5608 -        this.shiftPixels(-this._timeline.getPixelLength() / 2);
5609 -    } else {
5610 -        this._start = this._unit.makeDefaultValue();
5611 -        this.shiftPixels(-this._timeline.getPixelLength() / 2);
5612 -    }
5613 -};
5614 -
5615 -Timeline.LinearEther.prototype.setDate = function(date) {
5616 -    this._start = this._unit.cloneValue(date);
5617 -};
5618 -
5619 -Timeline.LinearEther.prototype.shiftPixels = function(pixels) {
5620 -    var numeric = this._interval * pixels / this._pixelsPerInterval;
5621 -    this._start = this._unit.change(this._start, numeric);
5622 -};
5623 -
5624 -Timeline.LinearEther.prototype.dateToPixelOffset = function(date) {
5625 -    var numeric = this._unit.compare(date, this._start);
5626 -    return this._pixelsPerInterval * numeric / this._interval;
5627 -};
5628 -
5629 -Timeline.LinearEther.prototype.pixelOffsetToDate = function(pixels) {
5630 -    var numeric = pixels * this._interval / this._pixelsPerInterval;
5631 -    return this._unit.change(this._start, numeric);
5632 -};
5633 -
5634 -Timeline.LinearEther.prototype.zoom = function(zoomIn) {
5635 -  var netIntervalChange = 0;
5636 -  var currentZoomIndex = this._band._zoomIndex;
5637 -  var newZoomIndex = currentZoomIndex;
5638 -
5639 -  if (zoomIn && (currentZoomIndex > 0)) {
5640 -    newZoomIndex = currentZoomIndex - 1;
5641 -  }
5642 -
5643 -  if (!zoomIn && (currentZoomIndex < (this._band._zoomSteps.length - 1))) {
5644 -    newZoomIndex = currentZoomIndex + 1;
5645 -  }
5646 -
5647 -  this._band._zoomIndex = newZoomIndex;
5648 -  this._interval =
5649 -    SimileAjax.DateTime.gregorianUnitLengths[this._band._zoomSteps[newZoomIndex].unit];
5650 -  this._pixelsPerInterval = this._band._zoomSteps[newZoomIndex].pixelsPerInterval;
5651 -  netIntervalChange = this._band._zoomSteps[newZoomIndex].unit -
5652 -    this._band._zoomSteps[currentZoomIndex].unit;
5653 -
5654 -  return netIntervalChange;
5655 -};
5656 -
5657 -
5658 -/*
5659 - *  Hot Zone Ether
5660 - *
5661 - */
5662 -
5663 -Timeline.HotZoneEther = function(params) {
5664 -    this._params = params;
5665 -    this._interval = params.interval;
5666 -    this._pixelsPerInterval = params.pixelsPerInterval;
5667 -    this._theme = params.theme;
5668 -};
5669 -
5670 -Timeline.HotZoneEther.prototype.initialize = function(band, timeline) {
5671 -    this._band = band;
5672 -    this._timeline = timeline;
5673 -    this._unit = timeline.getUnit();
5674 -
5675 -    this._zones = [{
5676 -        startTime:  Number.NEGATIVE_INFINITY,
5677 -        endTime:    Number.POSITIVE_INFINITY,
5678 -        magnify:    1
5679 -    }];
5680 -    var params = this._params;
5681 -    for (var i = 0; i < params.zones.length; i++) {
5682 -        var zone = params.zones[i];
5683 -        var zoneStart = this._unit.parseFromObject(zone.start);
5684 -        var zoneEnd =   this._unit.parseFromObject(zone.end);
5685 -
5686 -        for (var j = 0; j < this._zones.length && this._unit.compare(zoneEnd, zoneStart) > 0; j++) {
5687 -            var zone2 = this._zones[j];
5688 -
5689 -            if (this._unit.compare(zoneStart, zone2.endTime) < 0) {
5690 -                if (this._unit.compare(zoneStart, zone2.startTime) > 0) {
5691 -                    this._zones.splice(j, 0, {
5692 -                        startTime:   zone2.startTime,
5693 -                        endTime:     zoneStart,
5694 -                        magnify:     zone2.magnify
5695 -                    });
5696 -                    j++;
5697 -
5698 -                    zone2.startTime = zoneStart;
5699 -                }
5700 -
5701 -                if (this._unit.compare(zoneEnd, zone2.endTime) < 0) {
5702 -                    this._zones.splice(j, 0, {
5703 -                        startTime:  zoneStart,
5704 -                        endTime:    zoneEnd,
5705 -                        magnify:    zone.magnify * zone2.magnify
5706 -                    });
5707 -                    j++;
5708 -
5709 -                    zone2.startTime = zoneEnd;
5710 -                    zoneStart = zoneEnd;
5711 -                } else {
5712 -                    zone2.magnify *= zone.magnify;
5713 -                    zoneStart = zone2.endTime;
5714 -                }
5715 -            } // else, try the next existing zone
5716 -        }
5717 -    }
5718 -
5719 -    if ("startsOn" in this._params) {
5720 -        this._start = this._unit.parseFromObject(this._params.startsOn);
5721 -    } else if ("endsOn" in this._params) {
5722 -        this._start = this._unit.parseFromObject(this._params.endsOn);
5723 -        this.shiftPixels(-this._timeline.getPixelLength());
5724 -    } else if ("centersOn" in this._params) {
5725 -        this._start = this._unit.parseFromObject(this._params.centersOn);
5726 -        this.shiftPixels(-this._timeline.getPixelLength() / 2);
5727 -    } else {
5728 -        this._start = this._unit.makeDefaultValue();
5729 -        this.shiftPixels(-this._timeline.getPixelLength() / 2);
5730 -    }
5731 -};
5732 -
5733 -Timeline.HotZoneEther.prototype.setDate = function(date) {
5734 -    this._start = this._unit.cloneValue(date);
5735 -};
5736 -
5737 -Timeline.HotZoneEther.prototype.shiftPixels = function(pixels) {
5738 -    this._start = this.pixelOffsetToDate(pixels);
5739 -};
5740 -
5741 -Timeline.HotZoneEther.prototype.dateToPixelOffset = function(date) {
5742 -    return this._dateDiffToPixelOffset(this._start, date);
5743 -};
5744 -
5745 -Timeline.HotZoneEther.prototype.pixelOffsetToDate = function(pixels) {
5746 -    return this._pixelOffsetToDate(pixels, this._start);
5747 -};
5748 -
5749 -Timeline.HotZoneEther.prototype.zoom = function(zoomIn) {
5750 -  var netIntervalChange = 0;
5751 -  var currentZoomIndex = this._band._zoomIndex;
5752 -  var newZoomIndex = currentZoomIndex;
5753 -
5754 -  if (zoomIn && (currentZoomIndex > 0)) {
5755 -    newZoomIndex = currentZoomIndex - 1;
5756 -  }
5757 -
5758 -  if (!zoomIn && (currentZoomIndex < (this._band._zoomSteps.length - 1))) {
5759 -    newZoomIndex = currentZoomIndex + 1;
5760 -  }
5761 -
5762 -  this._band._zoomIndex = newZoomIndex;
5763 -  this._interval =
5764 -    SimileAjax.DateTime.gregorianUnitLengths[this._band._zoomSteps[newZoomIndex].unit];
5765 -  this._pixelsPerInterval = this._band._zoomSteps[newZoomIndex].pixelsPerInterval;
5766 -  netIntervalChange = this._band._zoomSteps[newZoomIndex].unit -
5767 -    this._band._zoomSteps[currentZoomIndex].unit;
5768 -
5769 -  return netIntervalChange;
5770 -};
5771 -
5772 -Timeline.HotZoneEther.prototype._dateDiffToPixelOffset = function(fromDate, toDate) {
5773 -    var scale = this._getScale();
5774 -    var fromTime = fromDate;
5775 -    var toTime = toDate;
5776 -
5777 -    var pixels = 0;
5778 -    if (this._unit.compare(fromTime, toTime) < 0) {
5779 -        var z = 0;
5780 -        while (z < this._zones.length) {
5781 -            if (this._unit.compare(fromTime, this._zones[z].endTime) < 0) {
5782 -                break;
5783 -            }
5784 -            z++;
5785 -        }
5786 -
5787 -        while (this._unit.compare(fromTime, toTime) < 0) {
5788 -            var zone = this._zones[z];
5789 -            var toTime2 = this._unit.earlier(toTime, zone.endTime);
5790 -
5791 -            pixels += (this._unit.compare(toTime2, fromTime) / (scale / zone.magnify));
5792 -
5793 -            fromTime = toTime2;
5794 -            z++;
5795 -        }
5796 -    } else {
5797 -        var z = this._zones.length - 1;
5798 -        while (z >= 0) {
5799 -            if (this._unit.compare(fromTime, this._zones[z].startTime) > 0) {
5800 -                break;
5801 -            }
5802 -            z--;
5803 -        }
5804 -
5805 -        while (this._unit.compare(fromTime, toTime) > 0) {
5806 -            var zone = this._zones[z];
5807 -            var toTime2 = this._unit.later(toTime, zone.startTime);
5808 -
5809 -            pixels += (this._unit.compare(toTime2, fromTime) / (scale / zone.magnify));
5810 -
5811 -            fromTime = toTime2;
5812 -            z--;
5813 -        }
5814 -    }
5815 -    return pixels;
5816 -};
5817 -
5818 -Timeline.HotZoneEther.prototype._pixelOffsetToDate = function(pixels, fromDate) {
5819 -    var scale = this._getScale();
5820 -    var time = fromDate;
5821 -    if (pixels > 0) {
5822 -        var z = 0;
5823 -        while (z < this._zones.length) {
5824 -            if (this._unit.compare(time, this._zones[z].endTime) < 0) {
5825 -                break;
5826 -            }
5827 -            z++;
5828 -        }
5829 -
5830 -        while (pixels > 0) {
5831 -            var zone = this._zones[z];
5832 -            var scale2 = scale / zone.magnify;
5833 -
5834 -            if (zone.endTime == Number.POSITIVE_INFINITY) {
5835 -                time = this._unit.change(time, pixels * scale2);
5836 -                pixels = 0;
5837 -            } else {
5838 -                var pixels2 = this._unit.compare(zone.endTime, time) / scale2;
5839 -                if (pixels2 > pixels) {
5840 -                    time = this._unit.change(time, pixels * scale2);
5841 -                    pixels = 0;
5842 -                } else {
5843 -                    time = zone.endTime;
5844 -                    pixels -= pixels2;
5845 -                }
5846 -            }
5847 -            z++;
5848 -        }
5849 -    } else {
5850 -        var z = this._zones.length - 1;
5851 -        while (z >= 0) {
5852 -            if (this._unit.compare(time, this._zones[z].startTime) > 0) {
5853 -                break;
5854 -            }
5855 -            z--;
5856 -        }
5857 -
5858 -        pixels = -pixels;
5859 -        while (pixels > 0) {
5860 -            var zone = this._zones[z];
5861 -            var scale2 = scale / zone.magnify;
5862 -
5863 -            if (zone.startTime == Number.NEGATIVE_INFINITY) {
5864 -                time = this._unit.change(time, -pixels * scale2);
5865 -                pixels = 0;
5866 -            } else {
5867 -                var pixels2 = this._unit.compare(time, zone.startTime) / scale2;
5868 -                if (pixels2 > pixels) {
5869 -                    time = this._unit.change(time, -pixels * scale2);
5870 -                    pixels = 0;
5871 -                } else {
5872 -                    time = zone.startTime;
5873 -                    pixels -= pixels2;
5874 -                }
5875 -            }
5876 -            z--;
5877 -        }
5878 -    }
5879 -    return time;
5880 -};
5881 -
5882 -Timeline.HotZoneEther.prototype._getScale = function() {
5883 -    return this._interval / this._pixelsPerInterval;
5884 -};
5885 -/*
5886 - *  Gregorian Ether Painter
5887 - *
5888 - */
5889 -
5890 -Timeline.GregorianEtherPainter = function(params) {
5891 -    this._params = params;
5892 -    this._theme = params.theme;
5893 -    this._unit = params.unit;
5894 -    this._multiple = ("multiple" in params) ? params.multiple : 1;
5895 -};
5896 -
5897 -Timeline.GregorianEtherPainter.prototype.initialize = function(band, timeline) {
5898 -    this._band = band;
5899 -    this._timeline = timeline;
5900 -
5901 -    this._backgroundLayer = band.createLayerDiv(0);
5902 -    this._backgroundLayer.setAttribute("name", "ether-background"); // for debugging
5903 -    this._backgroundLayer.className = 'timeline-ether-bg';
5904 -  //  this._backgroundLayer.style.background = this._theme.ether.backgroundColors[band.getIndex()];
5905 -
5906 -
5907 -    this._markerLayer = null;
5908 -    this._lineLayer = null;
5909 -
5910 -    var align = ("align" in this._params && this._params.align != undefined) ? this._params.align :
5911 -        this._theme.ether.interval.marker[timeline.isHorizontal() ? "hAlign" : "vAlign"];
5912 -    var showLine = ("showLine" in this._params) ? this._params.showLine :
5913 -        this._theme.ether.interval.line.show;
5914 -
5915 -    this._intervalMarkerLayout = new Timeline.EtherIntervalMarkerLayout(
5916 -        this._timeline, this._band, this._theme, align, showLine);
5917 -
5918 -    this._highlight = new Timeline.EtherHighlight(
5919 -        this._timeline, this._band, this._theme, this._backgroundLayer);
5920 -}
5921 -
5922 -Timeline.GregorianEtherPainter.prototype.setHighlight = function(startDate, endDate) {
5923 -    this._highlight.position(startDate, endDate);
5924 -}
5925 -
5926 -Timeline.GregorianEtherPainter.prototype.paint = function() {
5927 -    if (this._markerLayer) {
5928 -        this._band.removeLayerDiv(this._markerLayer);
5929 -    }
5930 -    this._markerLayer = this._band.createLayerDiv(100);
5931 -    this._markerLayer.setAttribute("name", "ether-markers"); // for debugging
5932 -    this._markerLayer.style.display = "none";
5933 -
5934 -    if (this._lineLayer) {
5935 -        this._band.removeLayerDiv(this._lineLayer);
5936 -    }
5937 -    this._lineLayer = this._band.createLayerDiv(1);
5938 -    this._lineLayer.setAttribute("name", "ether-lines"); // for debugging
5939 -    this._lineLayer.style.display = "none";
5940 -
5941 -    var minDate = this._band.getMinDate();
5942 -    var maxDate = this._band.getMaxDate();
5943 -
5944 -    var timeZone = this._band.getTimeZone();
5945 -    var labeller = this._band.getLabeller();
5946 -
5947 -    SimileAjax.DateTime.roundDownToInterval(minDate, this._unit, timeZone, this._multiple, this._theme.firstDayOfWeek);
5948 -
5949 -    var p = this;
5950 -    var incrementDate = function(date) {
5951 -        for (var i = 0; i < p._multiple; i++) {
5952 -            SimileAjax.DateTime.incrementByInterval(date, p._unit);
5953 -        }
5954 -    };
5955 -
5956 -    while (minDate.getTime() < maxDate.getTime()) {
5957 -        this._intervalMarkerLayout.createIntervalMarker(
5958 -            minDate, labeller, this._unit, this._markerLayer, this._lineLayer);
5959 -
5960 -        incrementDate(minDate);
5961 -    }
5962 -    this._markerLayer.style.display = "block";
5963 -    this._lineLayer.style.display = "block";
5964 -};
5965 -
5966 -Timeline.GregorianEtherPainter.prototype.softPaint = function() {
5967 -};
5968 -
5969 -Timeline.GregorianEtherPainter.prototype.zoom = function(netIntervalChange) {
5970 -  if (netIntervalChange != 0) {
5971 -    this._unit += netIntervalChange;
5972 -  }
5973 -};
5974 -
5975 -
5976 -/*
5977 - *  Hot Zone Gregorian Ether Painter
5978 - *
5979 - */
5980 -
5981 -Timeline.HotZoneGregorianEtherPainter = function(params) {
5982 -    this._params = params;
5983 -    this._theme = params.theme;
5984 -
5985 -    this._zones = [{
5986 -        startTime:  Number.NEGATIVE_INFINITY,
5987 -        endTime:    Number.POSITIVE_INFINITY,
5988 -        unit:       params.unit,
5989 -        multiple:   1
5990 -    }];
5991 -    for (var i = 0; i < params.zones.length; i++) {
5992 -        var zone = params.zones[i];
5993 -        var zoneStart = SimileAjax.DateTime.parseGregorianDateTime(zone.start).getTime();
5994 -        var zoneEnd = SimileAjax.DateTime.parseGregorianDateTime(zone.end).getTime();
5995 -
5996 -        for (var j = 0; j < this._zones.length && zoneEnd > zoneStart; j++) {
5997 -            var zone2 = this._zones[j];
5998 -
5999 -            if (zoneStart < zone2.endTime) {
6000 -                if (zoneStart > zone2.startTime) {
6001 -                    this._zones.splice(j, 0, {
6002 -                        startTime:   zone2.startTime,
6003 -                        endTime:     zoneStart,
6004 -                        unit:        zone2.unit,
6005 -                        multiple:    zone2.multiple
6006 -                    });
6007 -                    j++;
6008 -
6009 -                    zone2.startTime = zoneStart;
6010 -                }
6011 -
6012 -                if (zoneEnd < zone2.endTime) {
6013 -                    this._zones.splice(j, 0, {
6014 -                        startTime:  zoneStart,
6015 -                        endTime:    zoneEnd,
6016 -                        unit:       zone.unit,
6017 -                        multiple:   (zone.multiple) ? zone.multiple : 1
6018 -                    });
6019 -                    j++;
6020 -
6021 -                    zone2.startTime = zoneEnd;
6022 -                    zoneStart = zoneEnd;
6023 -                } else {
6024 -                    zone2.multiple = zone.multiple;
6025 -                    zone2.unit = zone.unit;
6026 -                    zoneStart = zone2.endTime;
6027 -                }
6028 -            } // else, try the next existing zone
6029 -        }
6030 -    }
6031 -};
6032 -
6033 -Timeline.HotZoneGregorianEtherPainter.prototype.initialize = function(band, timeline) {
6034 -    this._band = band;
6035 -    this._timeline = timeline;
6036 -
6037 -    this._backgroundLayer = band.createLayerDiv(0);
6038 -    this._backgroundLayer.setAttribute("name", "ether-background"); // for debugging
6039 -    this._backgroundLayer.className ='timeline-ether-bg';
6040 -    //this._backgroundLayer.style.background = this._theme.ether.backgroundColors[band.getIndex()];
6041 -
6042 -    this._markerLayer = null;
6043 -    this._lineLayer = null;
6044 -
6045 -    var align = ("align" in this._params && this._params.align != undefined) ? this._params.align :
6046 -        this._theme.ether.interval.marker[timeline.isHorizontal() ? "hAlign" : "vAlign"];
6047 -    var showLine = ("showLine" in this._params) ? this._params.showLine :
6048 -        this._theme.ether.interval.line.show;
6049 -
6050 -    this._intervalMarkerLayout = new Timeline.EtherIntervalMarkerLayout(
6051 -        this._timeline, this._band, this._theme, align, showLine);
6052 -
6053 -    this._highlight = new Timeline.EtherHighlight(
6054 -        this._timeline, this._band, this._theme, this._backgroundLayer);
6055 -}
6056 -
6057 -Timeline.HotZoneGregorianEtherPainter.prototype.setHighlight = function(startDate, endDate) {
6058 -    this._highlight.position(startDate, endDate);
6059 -}
6060 -
6061 -Timeline.HotZoneGregorianEtherPainter.prototype.paint = function() {
6062 -    if (this._markerLayer) {
6063 -        this._band.removeLayerDiv(this._markerLayer);
6064 -    }
6065 -    this._markerLayer = this._band.createLayerDiv(100);
6066 -    this._markerLayer.setAttribute("name", "ether-markers"); // for debugging
6067 -    this._markerLayer.style.display = "none";
6068 -
6069 -    if (this._lineLayer) {
6070 -        this._band.removeLayerDiv(this._lineLayer);
6071 -    }
6072 -    this._lineLayer = this._band.createLayerDiv(1);
6073 -    this._lineLayer.setAttribute("name", "ether-lines"); // for debugging
6074 -    this._lineLayer.style.display = "none";
6075 -
6076 -    var minDate = this._band.getMinDate();
6077 -    var maxDate = this._band.getMaxDate();
6078 -
6079 -    var timeZone = this._band.getTimeZone();
6080 -    var labeller = this._band.getLabeller();
6081 -
6082 -    var p = this;
6083 -    var incrementDate = function(date, zone) {
6084 -        for (var i = 0; i < zone.multiple; i++) {
6085 -            SimileAjax.DateTime.incrementByInterval(date, zone.unit);
6086 -        }
6087 -    };
6088 -
6089 -    var zStart = 0;
6090 -    while (zStart < this._zones.length) {
6091 -        if (minDate.getTime() < this._zones[zStart].endTime) {
6092 -            break;
6093 -        }
6094 -        zStart++;
6095 -    }
6096 -    var zEnd = this._zones.length - 1;
6097 -    while (zEnd >= 0) {
6098 -        if (maxDate.getTime() > this._zones[zEnd].startTime) {
6099 -            break;
6100 -        }
6101 -        zEnd--;
6102 -    }
6103 -
6104 -    for (var z = zStart; z <= zEnd; z++) {
6105 -        var zone = this._zones[z];
6106 -
6107 -        var minDate2 = new Date(Math.max(minDate.getTime(), zone.startTime));
6108 -        var maxDate2 = new Date(Math.min(maxDate.getTime(), zone.endTime));
6109 -
6110 -        SimileAjax.DateTime.roundDownToInterval(minDate2, zone.unit, timeZone, zone.multiple, this._theme.firstDayOfWeek);
6111 -        SimileAjax.DateTime.roundUpToInterval(maxDate2, zone.unit, timeZone, zone.multiple, this._theme.firstDayOfWeek);
6112 -
6113 -        while (minDate2.getTime() < maxDate2.getTime()) {
6114 -            this._intervalMarkerLayout.createIntervalMarker(
6115 -                minDate2, labeller, zone.unit, this._markerLayer, this._lineLayer);
6116 -
6117 -            incrementDate(minDate2, zone);
6118 -        }
6119 -    }
6120 -    this._markerLayer.style.display = "block";
6121 -    this._lineLayer.style.display = "block";
6122 -};
6123 -
6124 -Timeline.HotZoneGregorianEtherPainter.prototype.softPaint = function() {
6125 -};
6126 -
6127 -Timeline.HotZoneGregorianEtherPainter.prototype.zoom = function(netIntervalChange) {
6128 -  if (netIntervalChange != 0) {
6129 -    for (var i = 0; i < this._zones.length; ++i) {
6130 -      if (this._zones[i]) {
6131 -        this._zones[i].unit += netIntervalChange;
6132 -      }
6133 -    }
6134 -  }
6135 -};
6136 -
6137 -/*
6138 - *  Year Count Ether Painter
6139 - *
6140 - */
6141 -
6142 -Timeline.YearCountEtherPainter = function(params) {
6143 -    this._params = params;
6144 -    this._theme = params.theme;
6145 -    this._startDate = SimileAjax.DateTime.parseGregorianDateTime(params.startDate);
6146 -    this._multiple = ("multiple" in params) ? params.multiple : 1;
6147 -};
6148 -
6149 -Timeline.YearCountEtherPainter.prototype.initialize = function(band, timeline) {
6150 -    this._band = band;
6151 -    this._timeline = timeline;
6152 -
6153 -    this._backgroundLayer = band.createLayerDiv(0);
6154 -    this._backgroundLayer.setAttribute("name", "ether-background"); // for debugging
6155 -    this._backgroundLayer.className = 'timeline-ether-bg';
6156 -   // this._backgroundLayer.style.background = this._theme.ether.backgroundColors[band.getIndex()];
6157 -
6158 -    this._markerLayer = null;
6159 -    this._lineLayer = null;
6160 -
6161 -    var align = ("align" in this._params) ? this._params.align :
6162 -        this._theme.ether.interval.marker[timeline.isHorizontal() ? "hAlign" : "vAlign"];
6163 -    var showLine = ("showLine" in this._params) ? this._params.showLine :
6164 -        this._theme.ether.interval.line.show;
6165 -
6166 -    this._intervalMarkerLayout = new Timeline.EtherIntervalMarkerLayout(
6167 -        this._timeline, this._band, this._theme, align, showLine);
6168 -
6169 -    this._highlight = new Timeline.EtherHighlight(
6170 -        this._timeline, this._band, this._theme, this._backgroundLayer);
6171 -};
6172 -
6173 -Timeline.YearCountEtherPainter.prototype.setHighlight = function(startDate, endDate) {
6174 -    this._highlight.position(startDate, endDate);
6175 -};
6176 -
6177 -Timeline.YearCountEtherPainter.prototype.paint = function() {
6178 -    if (this._markerLayer) {
6179 -        this._band.removeLayerDiv(this._markerLayer);
6180 -    }
6181 -    this._markerLayer = this._band.createLayerDiv(100);
6182 -    this._markerLayer.setAttribute("name", "ether-markers"); // for debugging
6183 -    this._markerLayer.style.display = "none";
6184 -
6185 -    if (this._lineLayer) {
6186 -        this._band.removeLayerDiv(this._lineLayer);
6187 -    }
6188 -    this._lineLayer = this._band.createLayerDiv(1);
6189 -    this._lineLayer.setAttribute("name", "ether-lines"); // for debugging
6190 -    this._lineLayer.style.display = "none";
6191 -
6192 -    var minDate = new Date(this._startDate.getTime());
6193 -    var maxDate = this._band.getMaxDate();
6194 -    var yearDiff = this._band.getMinDate().getUTCFullYear() - this._startDate.getUTCFullYear();
6195 -    minDate.setUTCFullYear(this._band.getMinDate().getUTCFullYear() - yearDiff % this._multiple);
6196 -
6197 -    var p = this;
6198 -    var incrementDate = function(date) {
6199 -        for (var i = 0; i < p._multiple; i++) {
6200 -            SimileAjax.DateTime.incrementByInterval(date, SimileAjax.DateTime.YEAR);
6201 -        }
6202 -    };
6203 -    var labeller = {
6204 -        labelInterval: function(date, intervalUnit) {
6205 -            var diff = date.getUTCFullYear() - p._startDate.getUTCFullYear();
6206 -            return {
6207 -                text: diff,
6208 -                emphasized: diff == 0
6209 -            };
6210 -        }
6211 -    };
6212 -
6213 -    while (minDate.getTime() < maxDate.getTime()) {
6214 -        this._intervalMarkerLayout.createIntervalMarker(
6215 -            minDate, labeller, SimileAjax.DateTime.YEAR, this._markerLayer, this._lineLayer);
6216 -
6217 -        incrementDate(minDate);
6218 -    }
6219 -    this._markerLayer.style.display = "block";
6220 -    this._lineLayer.style.display = "block";
6221 -};
6222 -
6223 -Timeline.YearCountEtherPainter.prototype.softPaint = function() {
6224 -};
6225 -
6226 -/*
6227 - *  Quarterly Ether Painter
6228 - *
6229 - */
6230 -
6231 -Timeline.QuarterlyEtherPainter = function(params) {
6232 -    this._params = params;
6233 -    this._theme = params.theme;
6234 -    this._startDate = SimileAjax.DateTime.parseGregorianDateTime(params.startDate);
6235 -};
6236 -
6237 -Timeline.QuarterlyEtherPainter.prototype.initialize = function(band, timeline) {
6238 -    this._band = band;
6239 -    this._timeline = timeline;
6240 -
6241 -    this._backgroundLayer = band.createLayerDiv(0);
6242 -    this._backgroundLayer.setAttribute("name", "ether-background"); // for debugging
6243 -    this._backgroundLayer.className = 'timeline-ether-bg';
6244 - //   this._backgroundLayer.style.background = this._theme.ether.backgroundColors[band.getIndex()];
6245 -
6246 -    this._markerLayer = null;
6247 -    this._lineLayer = null;
6248 -
6249 -    var align = ("align" in this._params) ? this._params.align :
6250 -        this._theme.ether.interval.marker[timeline.isHorizontal() ? "hAlign" : "vAlign"];
6251 -    var showLine = ("showLine" in this._params) ? this._params.showLine :
6252 -        this._theme.ether.interval.line.show;
6253 -
6254 -    this._intervalMarkerLayout = new Timeline.EtherIntervalMarkerLayout(
6255 -        this._timeline, this._band, this._theme, align, showLine);
6256 -
6257 -    this._highlight = new Timeline.EtherHighlight(
6258 -        this._timeline, this._band, this._theme, this._backgroundLayer);
6259 -};
6260 -
6261 -Timeline.QuarterlyEtherPainter.prototype.setHighlight = function(startDate, endDate) {
6262 -    this._highlight.position(startDate, endDate);
6263 -};
6264 -
6265 -Timeline.QuarterlyEtherPainter.prototype.paint = function() {
6266 -    if (this._markerLayer) {
6267 -        this._band.removeLayerDiv(this._markerLayer);
6268 -    }
6269 -    this._markerLayer = this._band.createLayerDiv(100);
6270 -    this._markerLayer.setAttribute("name", "ether-markers"); // for debugging
6271 -    this._markerLayer.style.display = "none";
6272 -
6273 -    if (this._lineLayer) {
6274 -        this._band.removeLayerDiv(this._lineLayer);
6275 -    }
6276 -    this._lineLayer = this._band.createLayerDiv(1);
6277 -    this._lineLayer.setAttribute("name", "ether-lines"); // for debugging
6278 -    this._lineLayer.style.display = "none";
6279 -
6280 -    var minDate = new Date(0);
6281 -    var maxDate = this._band.getMaxDate();
6282 -
6283 -    minDate.setUTCFullYear(Math.max(this._startDate.getUTCFullYear(), this._band.getMinDate().getUTCFullYear()));
6284 -    minDate.setUTCMonth(this._startDate.getUTCMonth());
6285 -
6286 -    var p = this;
6287 -    var incrementDate = function(date) {
6288 -        date.setUTCMonth(date.getUTCMonth() + 3);
6289 -    };
6290 -    var labeller = {
6291 -        labelInterval: function(date, intervalUnit) {
6292 -            var quarters = (4 + (date.getUTCMonth() - p._startDate.getUTCMonth()) / 3) % 4;
6293 -            if (quarters != 0) {
6294 -                return { text: "Q" + (quarters + 1), emphasized: false };
6295 -            } else {
6296 -                return { text: "Y" + (date.getUTCFullYear() - p._startDate.getUTCFullYear() + 1), emphasized: true };
6297 -            }
6298 -        }
6299 -    };
6300 -
6301 -    while (minDate.getTime() < maxDate.getTime()) {
6302 -        this._intervalMarkerLayout.createIntervalMarker(
6303 -            minDate, labeller, SimileAjax.DateTime.YEAR, this._markerLayer, this._lineLayer);
6304 -
6305 -        incrementDate(minDate);
6306 -    }
6307 -    this._markerLayer.style.display = "block";
6308 -    this._lineLayer.style.display = "block";
6309 -};
6310 -
6311 -Timeline.QuarterlyEtherPainter.prototype.softPaint = function() {
6312 -};
6313 -
6314 -/*
6315 - *  Ether Interval Marker Layout
6316 - *
6317 - */
6318 -
6319 -Timeline.EtherIntervalMarkerLayout = function(timeline, band, theme, align, showLine) {
6320 -    var horizontal = timeline.isHorizontal();
6321 -    if (horizontal) {
6322 -        if (align == "Top") {
6323 -            this.positionDiv = function(div, offset) {
6324 -                div.style.left = offset + "px";
6325 -                div.style.top = "0px";
6326 -            };
6327 -        } else {
6328 -            this.positionDiv = function(div, offset) {
6329 -                div.style.left = offset + "px";
6330 -                div.style.bottom = "0px";
6331 -            };
6332 -        }
6333 -    } else {
6334 -        if (align == "Left") {
6335 -            this.positionDiv = function(div, offset) {
6336 -                div.style.top = offset + "px";
6337 -                div.style.left = "0px";
6338 -            };
6339 -        } else {
6340 -            this.positionDiv = function(div, offset) {
6341 -                div.style.top = offset + "px";
6342 -                div.style.right = "0px";
6343 -            };
6344 -        }
6345 -    }
6346 -
6347 -    var markerTheme = theme.ether.interval.marker;
6348 -    var lineTheme = theme.ether.interval.line;
6349 -    var weekendTheme = theme.ether.interval.weekend;
6350 -
6351 -    var stylePrefix = (horizontal ? "h" : "v") + align;
6352 -    var labelStyler = markerTheme[stylePrefix + "Styler"];
6353 -    var emphasizedLabelStyler = markerTheme[stylePrefix + "EmphasizedStyler"];
6354 -    var day = SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.DAY];
6355 -
6356 -    this.createIntervalMarker = function(date, labeller, unit, markerDiv, lineDiv) {
6357 -        var offset = Math.round(band.dateToPixelOffset(date));
6358 -
6359 -        if (showLine && unit != SimileAjax.DateTime.WEEK) {
6360 -            var divLine = timeline.getDocument().createElement("div");
6361 -            divLine.className = "timeline-ether-lines";
6362 -
6363 -            if (lineTheme.opacity < 100) {
6364 -                SimileAjax.Graphics.setOpacity(divLine, lineTheme.opacity);
6365 -            }
6366 -
6367 -            if (horizontal) {
6368 -				//divLine.className += " timeline-ether-lines-vertical";
6369 -				divLine.style.left = offset + "px";
6370 -            } else {
6371 -				//divLine.className += " timeline-ether-lines-horizontal";
6372 -                divLine.style.top = offset + "px";
6373 -            }
6374 -            lineDiv.appendChild(divLine);
6375 -        }
6376 -        if (unit == SimileAjax.DateTime.WEEK) {
6377 -            var firstDayOfWeek = theme.firstDayOfWeek;
6378 -
6379 -            var saturday = new Date(date.getTime() + (6 - firstDayOfWeek - 7) * day);
6380 -            var monday = new Date(saturday.getTime() + 2 * day);
6381 -
6382 -            var saturdayPixel = Math.round(band.dateToPixelOffset(saturday));
6383 -            var mondayPixel = Math.round(band.dateToPixelOffset(monday));
6384 -            var length = Math.max(1, mondayPixel - saturdayPixel);
6385 -
6386 -            var divWeekend = timeline.getDocument().createElement("div");
6387 -			divWeekend.className = 'timeline-ether-weekends'
6388 -
6389 -            if (weekendTheme.opacity < 100) {
6390 -                SimileAjax.Graphics.setOpacity(divWeekend, weekendTheme.opacity);
6391 -            }
6392 -
6393 -            if (horizontal) {
6394 -                divWeekend.style.left = saturdayPixel + "px";
6395 -                divWeekend.style.width = length + "px";
6396 -            } else {
6397 -                divWeekend.style.top = saturdayPixel + "px";
6398 -                divWeekend.style.height = length + "px";
6399 -            }
6400 -            lineDiv.appendChild(divWeekend);
6401 -        }
6402 -
6403 -        var label = labeller.labelInterval(date, unit);
6404 -
6405 -        var div = timeline.getDocument().createElement("div");
6406 -        div.innerHTML = label.text;
6407 -
6408 -
6409 -
6410 -		div.className = 'timeline-date-label'
6411 -		if(label.emphasized) div.className += ' timeline-date-label-em'
6412 -
6413 -        this.positionDiv(div, offset);
6414 -        markerDiv.appendChild(div);
6415 -
6416 -        return div;
6417 -    };
6418 -};
6419 -
6420 -/*
6421 - *  Ether Highlight Layout
6422 - *
6423 - */
6424 -
6425 -Timeline.EtherHighlight = function(timeline, band, theme, backgroundLayer) {
6426 -    var horizontal = timeline.isHorizontal();
6427 -
6428 -    this._highlightDiv = null;
6429 -    this._createHighlightDiv = function() {
6430 -        if (this._highlightDiv == null) {
6431 -            this._highlightDiv = timeline.getDocument().createElement("div");
6432 -            this._highlightDiv.setAttribute("name", "ether-highlight"); // for debugging
6433 -            this._highlightDiv.className = 'timeline-ether-highlight'
6434 -
6435 -            var opacity = theme.ether.highlightOpacity;
6436 -            if (opacity < 100) {
6437 -                SimileAjax.Graphics.setOpacity(this._highlightDiv, opacity);
6438 -            }
6439 -
6440 -            backgroundLayer.appendChild(this._highlightDiv);
6441 -        }
6442 -    }
6443 -
6444 -    this.position = function(startDate, endDate) {
6445 -        this._createHighlightDiv();
6446 -
6447 -        var startPixel = Math.round(band.dateToPixelOffset(startDate));
6448 -        var endPixel = Math.round(band.dateToPixelOffset(endDate));
6449 -        var length = Math.max(endPixel - startPixel, 3);
6450 -        if (horizontal) {
6451 -            this._highlightDiv.style.left = startPixel + "px";
6452 -            this._highlightDiv.style.width = length + "px";
6453 -            this._highlightDiv.style.height = (band.getViewWidth() - 4) + "px";
6454 -        } else {
6455 -            this._highlightDiv.style.top = startPixel + "px";
6456 -            this._highlightDiv.style.height = length + "px";
6457 -            this._highlightDiv.style.width = (band.getViewWidth() - 4) + "px";
6458 -        }
6459 -    }
6460 -};
6461 -/*
6462 - *  Event Utils
6463 - *
6464 - */
6465 -Timeline.EventUtils = {};
6466 -
6467 -Timeline.EventUtils.getNewEventID = function() {
6468 -    // global across page
6469 -    if (this._lastEventID == null) {
6470 -        this._lastEventID = 0;
6471 -    }
6472 -
6473 -    this._lastEventID += 1;
6474 -    return "e" + this._lastEventID;
6475 -};
6476 -
6477 -Timeline.EventUtils.decodeEventElID = function(elementID) {
6478 -    /*
6479 -     *
6480 -     * Use this function to decode an event element's id on a band (label div,
6481 -     * tape div or icon img).
6482 -     *
6483 -     * Returns {band: <bandObj>, evt: <eventObj>}
6484 -     *
6485 -     * To enable a single event listener to monitor everything
6486 -     * on a Timeline, a set format is used for the id's of the
6487 -     * elements on the Timeline--
6488 -     *
6489 -     * element id format for labels, icons, tapes:
6490 -     *   labels: label-tl-<timelineID>-<band_index>-<evt.id>
6491 -     *    icons: icon-tl-<timelineID>-<band_index>-<evt.id>
6492 -     *    tapes: tape1-tl-<timelineID>-<band_index>-<evt.id>
6493 -     *           tape2-tl-<timelineID>-<band_index>-<evt.id>
6494 -     *           // some events have more than one tape
6495 -     *    highlight: highlight1-tl-<timelineID>-<band_index>-<evt.id>
6496 -     *               highlight2-tl-<timelineID>-<band_index>-<evt.id>
6497 -     *           // some events have more than one highlight div (future)
6498 -     * Note: use split('-') to get array of the format's parts
6499 -     *
6500 -     * You can then retrieve the timeline object and event object
6501 -     * by using Timeline.getTimeline, Timeline.getBand, or
6502 -     * Timeline.getEvent and passing in the element's id
6503 -     *
6504 -     *
6505 -     */
6506 -
6507 -    var parts = elementID.split('-');
6508 -    if (parts[1] != 'tl') {
6509 -        alert("Internal Timeline problem 101, please consult support");
6510 -        return {band: null, evt: null}; // early return
6511 -    }
6512 -
6513 -    var timeline = Timeline.getTimelineFromID(parts[2]);
6514 -    var band = timeline.getBand(parts[3]);
6515 -    var evt = band.getEventSource.getEvent(parts[4]);
6516 -
6517 -    return {band: band, evt: evt};
6518 -};
6519 -
6520 -Timeline.EventUtils.encodeEventElID = function(timeline, band, elType, evt) {
6521 -    // elType should be one of {label | icon | tapeN | highlightN}
6522 -    return elType + "-tl-" + timeline.timelineID +
6523 -       "-" + band.getIndex() + "-" + evt.getID();
6524 -};/*
6525 - *  Gregorian Date Labeller
6526 - *
6527 - */
6528 -
6529 -Timeline.GregorianDateLabeller = function(locale, timeZone) {
6530 -    this._locale = locale;
6531 -    this._timeZone = timeZone;
6532 -};
6533 -
6534 -Timeline.GregorianDateLabeller.monthNames = [];
6535 -Timeline.GregorianDateLabeller.dayNames = [];
6536 -Timeline.GregorianDateLabeller.labelIntervalFunctions = [];
6537 -
6538 -Timeline.GregorianDateLabeller.getMonthName = function(month, locale) {
6539 -    return Timeline.GregorianDateLabeller.monthNames[locale][month];
6540 -};
6541 -
6542 -Timeline.GregorianDateLabeller.prototype.labelInterval = function(date, intervalUnit) {
6543 -    var f = Timeline.GregorianDateLabeller.labelIntervalFunctions[this._locale];
6544 -    if (f == null) {
6545 -        f = Timeline.GregorianDateLabeller.prototype.defaultLabelInterval;
6546 -    }
6547 -    return f.call(this, date, intervalUnit);
6548 -};
6549 -
6550 -Timeline.GregorianDateLabeller.prototype.labelPrecise = function(date) {
6551 -    return SimileAjax.DateTime.removeTimeZoneOffset(
6552 -        date,
6553 -        this._timeZone //+ (new Date().getTimezoneOffset() / 60)
6554 -    ).toUTCString();
6555 -};
6556 -
6557 -Timeline.GregorianDateLabeller.prototype.defaultLabelInterval = function(date, intervalUnit) {
6558 -    var text;
6559 -    var emphasized = false;
6560 -
6561 -    date = SimileAjax.DateTime.removeTimeZoneOffset(date, this._timeZone);
6562 -
6563 -    switch(intervalUnit) {
6564 -    case SimileAjax.DateTime.MILLISECOND:
6565 -        text = date.getUTCMilliseconds();
6566 -        break;
6567 -    case SimileAjax.DateTime.SECOND:
6568 -        text = date.getUTCSeconds();
6569 -        break;
6570 -    case SimileAjax.DateTime.MINUTE:
6571 -        var m = date.getUTCMinutes();
6572 -        if (m == 0) {
6573 -            text = date.getUTCHours() + ":00";
6574 -            emphasized = true;
6575 -        } else {
6576 -            text = m;
6577 -        }
6578 -        break;
6579 -    case SimileAjax.DateTime.HOUR:
6580 -        text = date.getUTCHours() + "hr";
6581 -        break;
6582 -    case SimileAjax.DateTime.DAY:
6583 -        text = Timeline.GregorianDateLabeller.getMonthName(date.getUTCMonth(), this._locale) + " " + date.getUTCDate();
6584 -        break;
6585 -    case SimileAjax.DateTime.WEEK:
6586 -        text = Timeline.GregorianDateLabeller.getMonthName(date.getUTCMonth(), this._locale) + " " + date.getUTCDate();
6587 -        break;
6588 -    case SimileAjax.DateTime.MONTH:
6589 -        var m = date.getUTCMonth();
6590 -        if (m != 0) {
6591 -            text = Timeline.GregorianDateLabeller.getMonthName(m, this._locale);
6592 -            break;
6593 -        } // else, fall through
6594 -    case SimileAjax.DateTime.YEAR:
6595 -    case SimileAjax.DateTime.DECADE:
6596 -    case SimileAjax.DateTime.CENTURY:
6597 -    case SimileAjax.DateTime.MILLENNIUM:
6598 -        var y = date.getUTCFullYear();
6599 -        if (y > 0) {
6600 -            text = date.getUTCFullYear();
6601 -        } else {
6602 -            text = (1 - y) + "BC";
6603 -        }
6604 -        emphasized =
6605 -            (intervalUnit == SimileAjax.DateTime.MONTH) ||
6606 -            (intervalUnit == SimileAjax.DateTime.DECADE && y % 100 == 0) ||
6607 -            (intervalUnit == SimileAjax.DateTime.CENTURY && y % 1000 == 0);
6608 -        break;
6609 -    default:
6610 -        text = date.toUTCString();
6611 -    }
6612 -    return { text: text, emphasized: emphasized };
6613 -}
6614 -
6615 -/*
6616 - *  Default Event Source
6617 - *
6618 - */
6619 -
6620 -
6621 -Timeline.DefaultEventSource = function(eventIndex) {
6622 -    this._events = (eventIndex instanceof Object) ? eventIndex : new SimileAjax.EventIndex();
6623 -    this._listeners = [];
6624 -};
6625 -
6626 -Timeline.DefaultEventSource.prototype.addListener = function(listener) {
6627 -    this._listeners.push(listener);
6628 -};
6629 -
6630 -Timeline.DefaultEventSource.prototype.removeListener = function(listener) {
6631 -    for (var i = 0; i < this._listeners.length; i++) {
6632 -        if (this._listeners[i] == listener) {
6633 -            this._listeners.splice(i, 1);
6634 -            break;
6635 -        }
6636 -    }
6637 -};
6638 -
6639 -Timeline.DefaultEventSource.prototype.loadXML = function(xml, url) {
6640 -    var base = this._getBaseURL(url);
6641 -
6642 -    var wikiURL = xml.documentElement.getAttribute("wiki-url");
6643 -    var wikiSection = xml.documentElement.getAttribute("wiki-section");
6644 -
6645 -    var dateTimeFormat = xml.documentElement.getAttribute("date-time-format");
6646 -    var parseDateTimeFunction = this._events.getUnit().getParser(dateTimeFormat);
6647 -
6648 -    var node = xml.documentElement.firstChild;
6649 -    var added = false;
6650 -    while (node != null) {
6651 -        if (node.nodeType == 1) {
6652 -            var description = "";
6653 -            if (node.firstChild != null && node.firstChild.nodeType == 3) {
6654 -                description = node.firstChild.nodeValue;
6655 -            }
6656 -            // instant event: default is true. Or use values from isDuration or durationEvent
6657 -            var instant = (node.getAttribute("isDuration")    === null &&
6658 -                           node.getAttribute("durationEvent") === null) ||
6659 -                          node.getAttribute("isDuration") == "false" ||
6660 -                          node.getAttribute("durationEvent") == "false";
6661 -
6662 -            var evt = new Timeline.DefaultEventSource.Event( {
6663 -                          id: node.getAttribute("id"),
6664 -                       start: parseDateTimeFunction(node.getAttribute("start")),
6665 -                         end: parseDateTimeFunction(node.getAttribute("end")),
6666 -                 latestStart: parseDateTimeFunction(node.getAttribute("latestStart")),
6667 -                 earliestEnd: parseDateTimeFunction(node.getAttribute("earliestEnd")),
6668 -                     instant: instant,
6669 -                        text: node.getAttribute("title"),
6670 -                 description: description,
6671 -                       image: this._resolveRelativeURL(node.getAttribute("image"), base),
6672 -                        link: this._resolveRelativeURL(node.getAttribute("link") , base),
6673 -                        icon: this._resolveRelativeURL(node.getAttribute("icon") , base),
6674 -                       color: node.getAttribute("color"),
6675 -                   textColor: node.getAttribute("textColor"),
6676 -                   hoverText: node.getAttribute("hoverText"),
6677 -                   classname: node.getAttribute("classname"),
6678 -                   tapeImage: node.getAttribute("tapeImage"),
6679 -                  tapeRepeat: node.getAttribute("tapeRepeat"),
6680 -                     caption: node.getAttribute("caption"),
6681 -                     eventID: node.getAttribute("eventID"),
6682 -                    trackNum: node.getAttribute("trackNum")
6683 -            });
6684 -
6685 -            evt._node = node;
6686 -            evt.getProperty = function(name) {
6687 -                return this._node.getAttribute(name);
6688 -            };
6689 -            evt.setWikiInfo(wikiURL, wikiSection);
6690 -
6691 -            this._events.add(evt);
6692 -
6693 -            added = true;
6694 -        }
6695 -        node = node.nextSibling;
6696 -    }
6697 -
6698 -    if (added) {
6699 -        this._fire("onAddMany", []);
6700 -    }
6701 -};
6702 -
6703 -
6704 -Timeline.DefaultEventSource.prototype.loadJSON = function(data, url) {
6705 -    var base = this._getBaseURL(url);
6706 -    var added = false;
6707 -    if (data && data.events){
6708 -        var wikiURL = ("wikiURL" in data) ? data.wikiURL : null;
6709 -        var wikiSection = ("wikiSection" in data) ? data.wikiSection : null;
6710 -
6711 -        var dateTimeFormat = ("dateTimeFormat" in data) ? data.dateTimeFormat : null;
6712 -        var parseDateTimeFunction = this._events.getUnit().getParser(dateTimeFormat);
6713 -
6714 -        for (var i=0; i < data.events.length; i++){
6715 -            var event = data.events[i];
6716 -            // Fixing issue 33:
6717 -            // instant event: default (for JSON only) is false. Or use values from isDuration or durationEvent
6718 -            // isDuration was negated (see issue 33, so keep that interpretation
6719 -            var instant = event.isDuration || (event.durationEvent != null && !event.durationEvent);
6720 -
6721 -            var evt = new Timeline.DefaultEventSource.Event({
6722 -                          id: ("id" in event) ? event.id : undefined,
6723 -                       start: parseDateTimeFunction(event.start),
6724 -                         end: parseDateTimeFunction(event.end),
6725 -                 latestStart: parseDateTimeFunction(event.latestStart),
6726 -                 earliestEnd: parseDateTimeFunction(event.earliestEnd),
6727 -                     instant: instant,
6728 -                        text: event.title,
6729 -                 description: event.description,
6730 -                       image: this._resolveRelativeURL(event.image, base),
6731 -                        link: this._resolveRelativeURL(event.link , base),
6732 -                        icon: this._resolveRelativeURL(event.icon , base),
6733 -                       color: event.color,
6734 -                   textColor: event.textColor,
6735 -                   hoverText: event.hoverText,
6736 -                   classname: event.classname,
6737 -                   tapeImage: event.tapeImage,
6738 -                  tapeRepeat: event.tapeRepeat,
6739 -                     caption: event.caption,
6740 -                     eventID: event.eventID,
6741 -                    trackNum: event.trackNum
6742 -            });
6743 -            evt._obj = event;
6744 -            evt.getProperty = function(name) {
6745 -                return this._obj[name];
6746 -            };
6747 -            evt.setWikiInfo(wikiURL, wikiSection);
6748 -
6749 -            this._events.add(evt);
6750 -            added = true;
6751 -        }
6752 -    }
6753 -
6754 -    if (added) {
6755 -        this._fire("onAddMany", []);
6756 -    }
6757 -};
6758 -
6759 -/*
6760 - *  Contributed by Morten Frederiksen, http://www.wasab.dk/morten/
6761 - */
6762 -Timeline.DefaultEventSource.prototype.loadSPARQL = function(xml, url) {
6763 -    var base = this._getBaseURL(url);
6764 -
6765 -    var dateTimeFormat = 'iso8601';
6766 -    var parseDateTimeFunction = this._events.getUnit().getParser(dateTimeFormat);
6767 -
6768 -    if (xml == null) {
6769 -        return;
6770 -    }
6771 -
6772 -    /*
6773 -     *  Find <results> tag
6774 -     */
6775 -    var node = xml.documentElement.firstChild;
6776 -    while (node != null && (node.nodeType != 1 || node.nodeName != 'results')) {
6777 -        node = node.nextSibling;
6778 -    }
6779 -
6780 -    var wikiURL = null;
6781 -    var wikiSection = null;
6782 -    if (node != null) {
6783 -        wikiURL = node.getAttribute("wiki-url");
6784 -        wikiSection = node.getAttribute("wiki-section");
6785 -
6786 -        node = node.firstChild;
6787 -    }
6788 -
6789 -    var added = false;
6790 -    while (node != null) {
6791 -        if (node.nodeType == 1) {
6792 -            var bindings = { };
6793 -            var binding = node.firstChild;
6794 -            while (binding != null) {
6795 -                if (binding.nodeType == 1 &&
6796 -                    binding.firstChild != null &&
6797 -                    binding.firstChild.nodeType == 1 &&
6798 -                    binding.firstChild.firstChild != null &&
6799 -                    binding.firstChild.firstChild.nodeType == 3) {
6800 -                    bindings[binding.getAttribute('name')] = binding.firstChild.firstChild.nodeValue;
6801 -                }
6802 -                binding = binding.nextSibling;
6803 -            }
6804 -
6805 -            if (bindings["start"] == null && bindings["date"] != null) {
6806 -                bindings["start"] = bindings["date"];
6807 -            }
6808 -
6809 -            // instant event: default is true. Or use values from isDuration or durationEvent
6810 -            var instant = (bindings["isDuration"]    === null &&
6811 -                           bindings["durationEvent"] === null) ||
6812 -                          bindings["isDuration"] == "false" ||
6813 -                          bindings["durationEvent"] == "false";
6814 -
6815 -            var evt = new Timeline.DefaultEventSource.Event({
6816 -                          id: bindings["id"],
6817 -                       start: parseDateTimeFunction(bindings["start"]),
6818 -                         end: parseDateTimeFunction(bindings["end"]),
6819 -                 latestStart: parseDateTimeFunction(bindings["latestStart"]),
6820 -                 earliestEnd: parseDateTimeFunction(bindings["earliestEnd"]),
6821 -                     instant: instant, // instant
6822 -                        text: bindings["title"], // text
6823 -                 description: bindings["description"],
6824 -                       image: this._resolveRelativeURL(bindings["image"], base),
6825 -                        link: this._resolveRelativeURL(bindings["link"] , base),
6826 -                        icon: this._resolveRelativeURL(bindings["icon"] , base),
6827 -                       color: bindings["color"],
6828 -                   textColor: bindings["textColor"],
6829 -                   hoverText: bindings["hoverText"],
6830 -                     caption: bindings["caption"],
6831 -                   classname: bindings["classname"],
6832 -                   tapeImage: bindings["tapeImage"],
6833 -                  tapeRepeat: bindings["tapeRepeat"],
6834 -                     eventID: bindings["eventID"],
6835 -                    trackNum: bindings["trackNum"]
6836 -            });
6837 -            evt._bindings = bindings;
6838 -            evt.getProperty = function(name) {
6839 -                return this._bindings[name];
6840 -            };
6841 -            evt.setWikiInfo(wikiURL, wikiSection);
6842 -
6843 -            this._events.add(evt);
6844 -            added = true;
6845 -        }
6846 -        node = node.nextSibling;
6847 -    }
6848 -
6849 -    if (added) {
6850 -        this._fire("onAddMany", []);
6851 -    }
6852 -};
6853 -
6854 -Timeline.DefaultEventSource.prototype.add = function(evt) {
6855 -    this._events.add(evt);
6856 -    this._fire("onAddOne", [evt]);
6857 -};
6858 -
6859 -Timeline.DefaultEventSource.prototype.addMany = function(events) {
6860 -    for (var i = 0; i < events.length; i++) {
6861 -        this._events.add(events[i]);
6862 -    }
6863 -    this._fire("onAddMany", []);
6864 -};
6865 -
6866 -Timeline.DefaultEventSource.prototype.clear = function() {
6867 -    this._events.removeAll();
6868 -    this._fire("onClear", []);
6869 -};
6870 -
6871 -Timeline.DefaultEventSource.prototype.getEvent = function(id) {
6872 -    return this._events.getEvent(id);
6873 -};
6874 -
6875 -Timeline.DefaultEventSource.prototype.getEventIterator = function(startDate, endDate) {
6876 -    return this._events.getIterator(startDate, endDate);
6877 -};
6878 -
6879 -Timeline.DefaultEventSource.prototype.getEventReverseIterator = function(startDate, endDate) {
6880 -    return this._events.getReverseIterator(startDate, endDate);
6881 -};
6882 -
6883 -Timeline.DefaultEventSource.prototype.getAllEventIterator = function() {
6884 -    return this._events.getAllIterator();
6885 -};
6886 -
6887 -Timeline.DefaultEventSource.prototype.getCount = function() {
6888 -    return this._events.getCount();
6889 -};
6890 -
6891 -Timeline.DefaultEventSource.prototype.getEarliestDate = function() {
6892 -    return this._events.getEarliestDate();
6893 -};
6894 -
6895 -Timeline.DefaultEventSource.prototype.getLatestDate = function() {
6896 -    return this._events.getLatestDate();
6897 -};
6898 -
6899 -Timeline.DefaultEventSource.prototype._fire = function(handlerName, args) {
6900 -    for (var i = 0; i < this._listeners.length; i++) {
6901 -        var listener = this._listeners[i];
6902 -        if (handlerName in listener) {
6903 -            try {
6904 -                listener[handlerName].apply(listener, args);
6905 -            } catch (e) {
6906 -                SimileAjax.Debug.exception(e);
6907 -            }
6908 -        }
6909 -    }
6910 -};
6911 -
6912 -Timeline.DefaultEventSource.prototype._getBaseURL = function(url) {
6913 -    if (url.indexOf("://") < 0) {
6914 -        var url2 = this._getBaseURL(document.location.href);
6915 -        if (url.substr(0,1) == "/") {
6916 -            url = url2.substr(0, url2.indexOf("/", url2.indexOf("://") + 3)) + url;
6917 -        } else {
6918 -            url = url2 + url;
6919 -        }
6920 -    }
6921 -
6922 -    var i = url.lastIndexOf("/");
6923 -    if (i < 0) {
6924 -        return "";
6925 -    } else {
6926 -        return url.substr(0, i+1);
6927 -    }
6928 -};
6929 -
6930 -Timeline.DefaultEventSource.prototype._resolveRelativeURL = function(url, base) {
6931 -    if (url == null || url == "") {
6932 -        return url;
6933 -    } else if (url.indexOf("://") > 0) {
6934 -        return url;
6935 -    } else if (url.substr(0,1) == "/") {
6936 -        return base.substr(0, base.indexOf("/", base.indexOf("://") + 3)) + url;
6937 -    } else {
6938 -        return base + url;
6939 -    }
6940 -};
6941 -
6942 -
6943 -Timeline.DefaultEventSource.Event = function(args) {
6944 -  //
6945 -  // Attention developers!
6946 -  // If you add a new event attribute, please be sure to add it to
6947 -  // all three load functions: loadXML, loadSPARCL, loadJSON.
6948 -  // Thanks!
6949 -  //
6950 -  // args is a hash/object. It supports the following keys. Most are optional
6951 -  //   id            -- an internal id. Really shouldn't be used by events.
6952 -  //                    Timeline library clients should use eventID
6953 -  //   eventID       -- For use by library client when writing custom painters or
6954 -  //                    custom fillInfoBubble
6955 -  //   start
6956 -  //   end
6957 -  //   latestStart
6958 -  //   earliestEnd
6959 -  //   instant      -- boolean. Controls precise/non-precise logic & duration/instant issues
6960 -  //   text         -- event source attribute 'title' -- used as the label on Timelines and in bubbles.
6961 -  //   description  -- used in bubbles
6962 -  //   image        -- used in bubbles
6963 -  //   link         -- used in bubbles
6964 -  //   icon         -- on the Timeline
6965 -  //   color        -- Timeline label and tape color
6966 -  //   textColor    -- Timeline label color, overrides color attribute
6967 -  //   hoverText    -- deprecated, here for backwards compatibility.
6968 -  //                   Superceeded by caption
6969 -  //   caption      -- tooltip-like caption on the Timeline. Uses HTML title attribute
6970 -  //   classname    -- used to set classname in Timeline. Enables better CSS selector rules
6971 -  //   tapeImage    -- background image of the duration event's tape div on the Timeline
6972 -  //   tapeRepeat   -- repeat attribute for tapeImage. {repeat | repeat-x | repeat-y }
6973 -
6974 -  function cleanArg(arg) {
6975 -      // clean up an arg
6976 -      return (args[arg] != null && args[arg] != "") ? args[arg] : null;
6977 -  }
6978 -
6979 -  var id = args.id ? args.id.trim() : "";
6980 -  this._id = id.length > 0 ? id : Timeline.EventUtils.getNewEventID();
6981 -
6982 -  this._instant = args.instant || (args.end == null);
6983 -
6984 -  this._start = args.start;
6985 -  this._end = (args.end != null) ? args.end : args.start;
6986 -
6987 -  this._latestStart = (args.latestStart != null) ?
6988 -                       args.latestStart : (args.instant ? this._end : this._start);
6989 -  this._earliestEnd = (args.earliestEnd != null) ? args.earliestEnd : this._end;
6990 -
6991 -  // check sanity of dates since incorrect dates will later cause calculation errors
6992 -  // when painting
6993 -  var err=[];
6994 -  if (this._start > this._latestStart) {
6995 -          this._latestStart = this._start;
6996 -          err.push("start is > latestStart");}
6997 -  if (this._start > this._earliestEnd) {
6998 -          this._earliestEnd = this._latestStart;
6999 -          err.push("start is > earliestEnd");}
7000 -  if (this._start > this._end) {
7001 -          this._end = this._earliestEnd;
7002 -          err.push("start is > end");}
7003 -  if (this._latestStart > this._earliestEnd) {
7004 -          this._earliestEnd = this._latestStart;
7005 -          err.push("latestStart is > earliestEnd");}
7006 -  if (this._latestStart > this._end) {
7007 -          this._end = this._earliestEnd;
7008 -          err.push("latestStart is > end");}
7009 -  if (this._earliestEnd > this._end) {
7010 -          this._end = this._earliestEnd;
7011 -          err.push("earliestEnd is > end");}
7012 -
7013 -  this._eventID = cleanArg('eventID');
7014 -  this._text = (args.text != null) ? SimileAjax.HTML.deEntify(args.text) : ""; // Change blank titles to ""
7015 -  if (err.length > 0) {
7016 -          this._text += " PROBLEM: " + err.join(", ");
7017 -  }
7018 -
7019 -  this._description = SimileAjax.HTML.deEntify(args.description);
7020 -  this._image = cleanArg('image');
7021 -  this._link =  cleanArg('link');
7022 -  this._title = cleanArg('hoverText');
7023 -  this._title = cleanArg('caption');
7024 -
7025 -  this._icon = cleanArg('icon');
7026 -  this._color = cleanArg('color');
7027 -  this._textColor = cleanArg('textColor');
7028 -  this._classname = cleanArg('classname');
7029 -  this._tapeImage = cleanArg('tapeImage');
7030 -  this._tapeRepeat = cleanArg('tapeRepeat');
7031 -  this._trackNum = cleanArg('trackNum');
7032 -  if (this._trackNum != null) {
7033 -      this._trackNum = parseInt(this._trackNum);
7034 -  }
7035 -
7036 -  this._wikiURL = null;
7037 -  this._wikiSection = null;
7038 -};
7039 -
7040 -Timeline.DefaultEventSource.Event.prototype = {
7041 -    getID:          function() { return this._id; },
7042 -
7043 -    isInstant:      function() { return this._instant; },
7044 -    isImprecise:    function() { return this._start != this._latestStart || this._end != this._earliestEnd; },
7045 -
7046 -    getStart:       function() { return this._start; },
7047 -    getEnd:         function() { return this._end; },
7048 -    getLatestStart: function() { return this._latestStart; },
7049 -    getEarliestEnd: function() { return this._earliestEnd; },
7050 -
7051 -    getEventID:     function() { return this._eventID; },
7052 -    getText:        function() { return this._text; }, // title
7053 -    getDescription: function() { return this._description; },
7054 -    getImage:       function() { return this._image; },
7055 -    getLink:        function() { return this._link; },
7056 -
7057 -    getIcon:        function() { return this._icon; },
7058 -    getColor:       function() { return this._color; },
7059 -    getTextColor:   function() { return this._textColor; },
7060 -    getClassName:   function() { return this._classname; },
7061 -    getTapeImage:   function() { return this._tapeImage; },
7062 -    getTapeRepeat:  function() { return this._tapeRepeat; },
7063 -    getTrackNum:    function() { return this._trackNum; },
7064 -
7065 -    getProperty:    function(name) { return null; },
7066 -
7067 -    getWikiURL:     function() { return this._wikiURL; },
7068 -    getWikiSection: function() { return this._wikiSection; },
7069 -    setWikiInfo: function(wikiURL, wikiSection) {
7070 -        this._wikiURL = wikiURL;
7071 -        this._wikiSection = wikiSection;
7072 -    },
7073 -
7074 -    fillDescription: function(elmt) {
7075 -        elmt.innerHTML = this._description;
7076 -    },
7077 -    fillWikiInfo: function(elmt) {
7078 -        // Many bubbles will not support a wiki link.
7079 -        //
7080 -        // Strategy: assume no wiki link. If we do have
7081 -        // enough parameters for one, then create it.
7082 -        elmt.style.display = "none"; // default
7083 -
7084 -        if (this._wikiURL == null || this._wikiSection == null) {
7085 -          return; // EARLY RETURN
7086 -        }
7087 -
7088 -        // create the wikiID from the property or from the event text (the title)
7089 -        var wikiID = this.getProperty("wikiID");
7090 -        if (wikiID == null || wikiID.length == 0) {
7091 -            wikiID = this.getText(); // use the title as the backup wiki id
7092 -        }
7093 -
7094 -        if (wikiID == null || wikiID.length == 0) {
7095 -          return; // No wikiID. Thus EARLY RETURN
7096 -        }
7097 -
7098 -        // ready to go...
7099 -        elmt.style.display = "inline";
7100 -        wikiID = wikiID.replace(/\s/g, "_");
7101 -        var url = this._wikiURL + this._wikiSection.replace(/\s/g, "_") + "/" + wikiID;
7102 -        var a = document.createElement("a");
7103 -        a.href = url;
7104 -        a.target = "new";
7105 -        a.innerHTML = Timeline.strings[Timeline.clientLocale].wikiLinkLabel;
7106 -
7107 -        elmt.appendChild(document.createTextNode("["));
7108 -        elmt.appendChild(a);
7109 -        elmt.appendChild(document.createTextNode("]"));
7110 -    },
7111 -
7112 -    fillTime: function(elmt, labeller) {
7113 -        if (this._instant) {
7114 -            if (this.isImprecise()) {
7115 -                elmt.appendChild(elmt.ownerDocument.createTextNode(labeller.labelPrecise(this._start)));
7116 -                elmt.appendChild(elmt.ownerDocument.createElement("br"));
7117 -                elmt.appendChild(elmt.ownerDocument.createTextNode(labeller.labelPrecise(this._end)));
7118 -            } else {
7119 -                elmt.appendChild(elmt.ownerDocument.createTextNode(labeller.labelPrecise(this._start)));
7120 -            }
7121 -        } else {
7122 -            if (this.isImprecise()) {
7123 -                elmt.appendChild(elmt.ownerDocument.createTextNode(
7124 -                    labeller.labelPrecise(this._start) + " ~ " + labeller.labelPrecise(this._latestStart)));
7125 -                elmt.appendChild(elmt.ownerDocument.createElement("br"));
7126 -                elmt.appendChild(elmt.ownerDocument.createTextNode(
7127 -                    labeller.labelPrecise(this._earliestEnd) + " ~ " + labeller.labelPrecise(this._end)));
7128 -            } else {
7129 -                elmt.appendChild(elmt.ownerDocument.createTextNode(labeller.labelPrecise(this._start)));
7130 -                elmt.appendChild(elmt.ownerDocument.createElement("br"));
7131 -                elmt.appendChild(elmt.ownerDocument.createTextNode(labeller.labelPrecise(this._end)));
7132 -            }
7133 -        }
7134 -    },
7135 -
7136 -    fillInfoBubble: function(elmt, theme, labeller) {
7137 -        var doc = elmt.ownerDocument;
7138 -
7139 -        var title = this.getText();
7140 -        var link = this.getLink();
7141 -        var image = this.getImage();
7142 -
7143 -        if (image != null) {
7144 -            var img = doc.createElement("img");
7145 -            img.src = image;
7146 -
7147 -            theme.event.bubble.imageStyler(img);
7148 -            elmt.appendChild(img);
7149 -        }
7150 -
7151 -        var divTitle = doc.createElement("div");
7152 -        var textTitle = doc.createTextNode(title);
7153 -        if (link != null) {
7154 -            var a = doc.createElement("a");
7155 -            a.href = link;
7156 -            a.appendChild(textTitle);
7157 -            divTitle.appendChild(a);
7158 -        } else {
7159 -            divTitle.appendChild(textTitle);
7160 -        }
7161 -        theme.event.bubble.titleStyler(divTitle);
7162 -        elmt.appendChild(divTitle);
7163 -
7164 -        var divBody = doc.createElement("div");
7165 -        this.fillDescription(divBody);
7166 -        theme.event.bubble.bodyStyler(divBody);
7167 -        elmt.appendChild(divBody);
7168 -
7169 -        var divTime = doc.createElement("div");
7170 -        this.fillTime(divTime, labeller);
7171 -        theme.event.bubble.timeStyler(divTime);
7172 -        elmt.appendChild(divTime);
7173 -
7174 -        var divWiki = doc.createElement("div");
7175 -        this.fillWikiInfo(divWiki);
7176 -        theme.event.bubble.wikiStyler(divWiki);
7177 -        elmt.appendChild(divWiki);
7178 -    }
7179 -};
7180 -
7181 -
7182 -/*
7183 - *  Original Event Painter
7184 - *
7185 - */
7186 -
7187 -/*
7188 - *
7189 - * To enable a single event listener to monitor everything
7190 - * on a Timeline, we need a way to map from an event's icon,
7191 - * label or tape element to the associated timeline, band and
7192 - * specific event.
7193 - *
7194 - * Thus a set format is used for the id's of the
7195 - * events' elements on the Timeline--
7196 - *
7197 - * element id format for labels, icons, tapes:
7198 - *   labels: label-tl-<timelineID>-<band_index>-<evt.id>
7199 - *    icons: icon-tl-<timelineID>-<band_index>-<evt.id>
7200 - *    tapes: tape1-tl-<timelineID>-<band_index>-<evt.id>
7201 - *           tape2-tl-<timelineID>-<band_index>-<evt.id>
7202 - *           // some events have more than one tape
7203 - *    highlight: highlight1-tl-<timelineID>-<band_index>-<evt.id>
7204 - *               highlight2-tl-<timelineID>-<band_index>-<evt.id>
7205 - *           // some events have more than one highlight div (future)
7206 - * You can then retrieve the band/timeline objects and event object
7207 - * by using Timeline.EventUtils.decodeEventElID
7208 - *
7209 - *
7210 - */
7211 -
7212 -/*
7213 - *    eventPaintListener functions receive calls about painting.
7214 - *    function(band, op, evt, els)
7215 - *       context: 'this' will be an OriginalEventPainter object.
7216 - *                It has properties and methods for obtaining
7217 - *                the relevant band, timeline, etc
7218 - *       band = the band being painted
7219 - *       op = 'paintStarting' // the painter is about to remove
7220 - *            all previously painted events, if any. It will
7221 - *            then start painting all of the visible events that
7222 - *            pass the filter.
7223 - *            evt = null, els = null
7224 - *       op = 'paintEnded' // the painter has finished painting
7225 - *            all of the visible events that passed the filter
7226 - *            evt = null, els = null
7227 - *       op = 'paintedEvent' // the painter just finished painting an event
7228 - *            evt = event just painted
7229 - *            els = array of painted elements' divs. Depending on the event,
7230 - *                  the array could be just a tape or icon (if no label).
7231 - *                  Or could include label, multiple tape divs (imprecise event),
7232 - *                  highlight divs. The array is not ordered. The meaning of
7233 - *                  each el is available by decoding the el's id
7234 - *      Note that there may be no paintedEvent calls if no events were visible
7235 - *      or passed the filter.
7236 - */
7237 -
7238 -Timeline.OriginalEventPainter = function(params) {
7239 -    this._params = params;
7240 -    this._onSelectListeners = [];
7241 -    this._eventPaintListeners = [];
7242 -
7243 -    this._filterMatcher = null;
7244 -    this._highlightMatcher = null;
7245 -    this._frc = null;
7246 -
7247 -    this._eventIdToElmt = {};
7248 -};
7249 -
7250 -Timeline.OriginalEventPainter.prototype.initialize = function(band, timeline) {
7251 -    this._band = band;
7252 -    this._timeline = timeline;
7253 -
7254 -    this._backLayer = null;
7255 -    this._eventLayer = null;
7256 -    this._lineLayer = null;
7257 -    this._highlightLayer = null;
7258 -
7259 -    this._eventIdToElmt = null;
7260 -};
7261 -
7262 -Timeline.OriginalEventPainter.prototype.getType = function() {
7263 -    return 'original';
7264 -};
7265 -
7266 -Timeline.OriginalEventPainter.prototype.addOnSelectListener = function(listener) {
7267 -    this._onSelectListeners.push(listener);
7268 -};
7269 -
7270 -Timeline.OriginalEventPainter.prototype.removeOnSelectListener = function(listener) {
7271 -    for (var i = 0; i < this._onSelectListeners.length; i++) {
7272 -        if (this._onSelectListeners[i] == listener) {
7273 -            this._onSelectListeners.splice(i, 1);
7274 -            break;
7275 -        }
7276 -    }
7277 -};
7278 -
7279 -Timeline.OriginalEventPainter.prototype.addEventPaintListener = function(listener) {
7280 -    this._eventPaintListeners.push(listener);
7281 -};
7282 -
7283 -Timeline.OriginalEventPainter.prototype.removeEventPaintListener = function(listener) {
7284 -    for (var i = 0; i < this._eventPaintListeners.length; i++) {
7285 -        if (this._eventPaintListeners[i] == listener) {
7286 -            this._eventPaintListeners.splice(i, 1);
7287 -            break;
7288 -        }
7289 -    }
7290 -};
7291 -
7292 -Timeline.OriginalEventPainter.prototype.getFilterMatcher = function() {
7293 -    return this._filterMatcher;
7294 -};
7295 -
7296 -Timeline.OriginalEventPainter.prototype.setFilterMatcher = function(filterMatcher) {
7297 -    this._filterMatcher = filterMatcher;
7298 -};
7299 -
7300 -Timeline.OriginalEventPainter.prototype.getHighlightMatcher = function() {
7301 -    return this._highlightMatcher;
7302 -};
7303 -
7304 -Timeline.OriginalEventPainter.prototype.setHighlightMatcher = function(highlightMatcher) {
7305 -    this._highlightMatcher = highlightMatcher;
7306 -};
7307 -
7308 -Timeline.OriginalEventPainter.prototype.paint = function() {
7309 -    // Paints the events for a given section of the band--what is
7310 -    // visible on screen and some extra.
7311 -    var eventSource = this._band.getEventSource();
7312 -    if (eventSource == null) {
7313 -        return;
7314 -    }
7315 -
7316 -    this._eventIdToElmt = {};
7317 -    this._fireEventPaintListeners('paintStarting', null, null);
7318 -    this._prepareForPainting();
7319 -
7320 -    var metrics = this._computeMetrics();
7321 -    var minDate = this._band.getMinDate();
7322 -    var maxDate = this._band.getMaxDate();
7323 -
7324 -    var filterMatcher = (this._filterMatcher != null) ?
7325 -        this._filterMatcher :
7326 -        function(evt) { return true; };
7327 -    var highlightMatcher = (this._highlightMatcher != null) ?
7328 -        this._highlightMatcher :
7329 -        function(evt) { return -1; };
7330 -
7331 -    var iterator = eventSource.getEventReverseIterator(minDate, maxDate);
7332 -    while (iterator.hasNext()) {
7333 -        var evt = iterator.next();
7334 -        if (filterMatcher(evt)) {
7335 -            this.paintEvent(evt, metrics, this._params.theme, highlightMatcher(evt));
7336 -        }
7337 -    }
7338 -
7339 -    this._highlightLayer.style.display = "block";
7340 -    this._lineLayer.style.display = "block";
7341 -    this._eventLayer.style.display = "block";
7342 -    // update the band object for max number of tracks in this section of the ether
7343 -    this._band.updateEventTrackInfo(this._tracks.length, metrics.trackIncrement);
7344 -    this._fireEventPaintListeners('paintEnded', null, null);
7345 -
7346 -    this._setOrthogonalOffset(metrics);
7347 -};
7348 -
7349 -Timeline.OriginalEventPainter.prototype.softPaint = function() {
7350 -    this._setOrthogonalOffset(this._computeMetrics());
7351 -};
7352 -
7353 -Timeline.OriginalEventPainter.prototype._setOrthogonalOffset = function(metrics) {
7354 -    var actualViewWidth = 2 * metrics.trackOffset + this._tracks.length * metrics.trackIncrement;
7355 -    var minOrthogonalOffset = Math.min(0, this._band.getViewWidth() - actualViewWidth);
7356 -    var orthogonalOffset = Math.max(minOrthogonalOffset, this._band.getViewOrthogonalOffset());
7357 -
7358 -    this._highlightLayer.style.top =
7359 -        this._lineLayer.style.top =
7360 -            this._eventLayer.style.top =
7361 -                orthogonalOffset + "px";
7362 -};
7363 -
7364 -Timeline.OriginalEventPainter.prototype._computeMetrics = function() {
7365 -     var eventTheme = this._params.theme.event;
7366 -     var trackHeight = Math.max(eventTheme.track.height, eventTheme.tape.height +
7367 -                         this._frc.getLineHeight());
7368 -     var metrics = {
7369 -            trackOffset: eventTheme.track.offset,
7370 -            trackHeight: trackHeight,
7371 -               trackGap: eventTheme.track.gap,
7372 -         trackIncrement: trackHeight + eventTheme.track.gap,
7373 -                   icon: eventTheme.instant.icon,
7374 -              iconWidth: eventTheme.instant.iconWidth,
7375 -             iconHeight: eventTheme.instant.iconHeight,
7376 -             labelWidth: eventTheme.label.width,
7377 -           maxLabelChar: eventTheme.label.maxLabelChar,
7378 -    impreciseIconMargin: eventTheme.instant.impreciseIconMargin
7379 -     };
7380 -
7381 -     return metrics;
7382 -};
7383 -
7384 -Timeline.OriginalEventPainter.prototype._prepareForPainting = function() {
7385 -    // Remove everything previously painted: highlight, line and event layers.
7386 -    // Prepare blank layers for painting.
7387 -    var band = this._band;
7388 -
7389 -    if (this._backLayer == null) {
7390 -        this._backLayer = this._band.createLayerDiv(0, "timeline-band-events");
7391 -        this._backLayer.style.visibility = "hidden";
7392 -
7393 -        var eventLabelPrototype = document.createElement("span");
7394 -        eventLabelPrototype.className = "timeline-event-label";
7395 -        this._backLayer.appendChild(eventLabelPrototype);
7396 -        this._frc = SimileAjax.Graphics.getFontRenderingContext(eventLabelPrototype);
7397 -    }
7398 -    this._frc.update();
7399 -    this._tracks = [];
7400 -
7401 -    if (this._highlightLayer != null) {
7402 -        band.removeLayerDiv(this._highlightLayer);
7403 -    }
7404 -    this._highlightLayer = band.createLayerDiv(105, "timeline-band-highlights");
7405 -    this._highlightLayer.style.display = "none";
7406 -
7407 -    if (this._lineLayer != null) {
7408 -        band.removeLayerDiv(this._lineLayer);
7409 -    }
7410 -    this._lineLayer = band.createLayerDiv(110, "timeline-band-lines");
7411 -    this._lineLayer.style.display = "none";
7412 -
7413 -    if (this._eventLayer != null) {
7414 -        band.removeLayerDiv(this._eventLayer);
7415 -    }
7416 -    this._eventLayer = band.createLayerDiv(115, "timeline-band-events");
7417 -    this._eventLayer.style.display = "none";
7418 -};
7419 -
7420 -Timeline.OriginalEventPainter.prototype.paintEvent = function(evt, metrics, theme, highlightIndex) {
7421 -    if (evt.isInstant()) {
7422 -        this.paintInstantEvent(evt, metrics, theme, highlightIndex);
7423 -    } else {
7424 -        this.paintDurationEvent(evt, metrics, theme, highlightIndex);
7425 -    }
7426 -};
7427 -
7428 -Timeline.OriginalEventPainter.prototype.paintInstantEvent = function(evt, metrics, theme, highlightIndex) {
7429 -    if (evt.isImprecise()) {
7430 -        this.paintImpreciseInstantEvent(evt, metrics, theme, highlightIndex);
7431 -    } else {
7432 -        this.paintPreciseInstantEvent(evt, metrics, theme, highlightIndex);
7433 -    }
7434 -}
7435 -
7436 -Timeline.OriginalEventPainter.prototype.paintDurationEvent = function(evt, metrics, theme, highlightIndex) {
7437 -    if (evt.isImprecise()) {
7438 -        this.paintImpreciseDurationEvent(evt, metrics, theme, highlightIndex);
7439 -    } else {
7440 -        this.paintPreciseDurationEvent(evt, metrics, theme, highlightIndex);
7441 -    }
7442 -}
7443 -
7444 -Timeline.OriginalEventPainter.prototype.paintPreciseInstantEvent = function(evt, metrics, theme, highlightIndex) {
7445 -    var doc = this._timeline.getDocument();
7446 -    var text = evt.getText();
7447 -
7448 -    var startDate = evt.getStart();
7449 -    var startPixel = Math.round(this._band.dateToPixelOffset(startDate));
7450 -    var iconRightEdge = Math.round(startPixel + metrics.iconWidth / 2);
7451 -    var iconLeftEdge = Math.round(startPixel - metrics.iconWidth / 2);
7452 -
7453 -    var labelDivClassName = this._getLabelDivClassName(evt);
7454 -    var labelSize = this._frc.computeSize(text, labelDivClassName);
7455 -    var labelLeft = iconRightEdge + theme.event.label.offsetFromLine;
7456 -    var labelRight = labelLeft + labelSize.width;
7457 -
7458 -    var rightEdge = labelRight;
7459 -    var track = this._findFreeTrack(evt, rightEdge);
7460 -
7461 -    var labelTop = Math.round(
7462 -        metrics.trackOffset + track * metrics.trackIncrement +
7463 -        metrics.trackHeight / 2 - labelSize.height / 2);
7464 -
7465 -    var iconElmtData = this._paintEventIcon(evt, track, iconLeftEdge, metrics, theme, 0);
7466 -    var labelElmtData = this._paintEventLabel(evt, text, labelLeft, labelTop, labelSize.width,
7467 -        labelSize.height, theme, labelDivClassName, highlightIndex);
7468 -    var els = [iconElmtData.elmt, labelElmtData.elmt];
7469 -
7470 -    var self = this;
7471 -    var clickHandler = function(elmt, domEvt, target) {
7472 -        return self._onClickInstantEvent(iconElmtData.elmt, domEvt, evt);
7473 -    };
7474 -    SimileAjax.DOM.registerEvent(iconElmtData.elmt, "mousedown", clickHandler);
7475 -    SimileAjax.DOM.registerEvent(labelElmtData.elmt, "mousedown", clickHandler);
7476 -
7477 -    var hDiv = this._createHighlightDiv(highlightIndex, iconElmtData, theme, evt);
7478 -    if (hDiv != null) {els.push(hDiv);}
7479 -    this._fireEventPaintListeners('paintedEvent', evt, els);
7480 -
7481 -
7482 -    this._eventIdToElmt[evt.getID()] = iconElmtData.elmt;
7483 -    this._tracks[track] = iconLeftEdge;
7484 -};
7485 -
7486 -Timeline.OriginalEventPainter.prototype.paintImpreciseInstantEvent = function(evt, metrics, theme, highlightIndex) {
7487 -    var doc = this._timeline.getDocument();
7488 -    var text = evt.getText();
7489 -
7490 -    var startDate = evt.getStart();
7491 -    var endDate = evt.getEnd();
7492 -    var startPixel = Math.round(this._band.dateToPixelOffset(startDate));
7493 -    var endPixel = Math.round(this._band.dateToPixelOffset(endDate));
7494 -
7495 -    var iconRightEdge = Math.round(startPixel + metrics.iconWidth / 2);
7496 -    var iconLeftEdge = Math.round(startPixel - metrics.iconWidth / 2);
7497 -
7498 -    var labelDivClassName = this._getLabelDivClassName(evt);
7499 -    var labelSize = this._frc.computeSize(text, labelDivClassName);
7500 -    var labelLeft = iconRightEdge + theme.event.label.offsetFromLine;
7501 -    var labelRight = labelLeft + labelSize.width;
7502 -
7503 -    var rightEdge = Math.max(labelRight, endPixel);
7504 -    var track = this._findFreeTrack(evt, rightEdge);
7505 -    var tapeHeight = theme.event.tape.height;
7506 -    var labelTop = Math.round(
7507 -        metrics.trackOffset + track * metrics.trackIncrement + tapeHeight);
7508 -
7509 -    var iconElmtData = this._paintEventIcon(evt, track, iconLeftEdge, metrics, theme, tapeHeight);
7510 -    var labelElmtData = this._paintEventLabel(evt, text, labelLeft, labelTop, labelSize.width,
7511 -                        labelSize.height, theme, labelDivClassName, highlightIndex);
7512 -
7513 -    var color = evt.getColor();
7514 -    color = color != null ? color : theme.event.instant.impreciseColor;
7515 -
7516 -    var tapeElmtData = this._paintEventTape(evt, track, startPixel, endPixel,
7517 -        color, theme.event.instant.impreciseOpacity, metrics, theme, 0);
7518 -    var els = [iconElmtData.elmt, labelElmtData.elmt, tapeElmtData.elmt];
7519 -
7520 -    var self = this;
7521 -    var clickHandler = function(elmt, domEvt, target) {
7522 -        return self._onClickInstantEvent(iconElmtData.elmt, domEvt, evt);
7523 -    };
7524 -    SimileAjax.DOM.registerEvent(iconElmtData.elmt, "mousedown", clickHandler);
7525 -    SimileAjax.DOM.registerEvent(tapeElmtData.elmt, "mousedown", clickHandler);
7526 -    SimileAjax.DOM.registerEvent(labelElmtData.elmt, "mousedown", clickHandler);
7527 -
7528 -    var hDiv = this._createHighlightDiv(highlightIndex, iconElmtData, theme, evt);
7529 -    if (hDiv != null) {els.push(hDiv);}
7530 -    this._fireEventPaintListeners('paintedEvent', evt, els);
7531 -
7532 -    this._eventIdToElmt[evt.getID()] = iconElmtData.elmt;
7533 -    this._tracks[track] = iconLeftEdge;
7534 -};
7535 -
7536 -Timeline.OriginalEventPainter.prototype.paintPreciseDurationEvent = function(evt, metrics, theme, highlightIndex) {
7537 -    var doc = this._timeline.getDocument();
7538 -    var text = evt.getText();
7539 -
7540 -    var startDate = evt.getStart();
7541 -    var endDate = evt.getEnd();
7542 -    var startPixel = Math.round(this._band.dateToPixelOffset(startDate));
7543 -    var endPixel = Math.round(this._band.dateToPixelOffset(endDate));
7544 -
7545 -    var labelDivClassName = this._getLabelDivClassName(evt);
7546 -    var labelSize = this._frc.computeSize(text, labelDivClassName);
7547 -    var labelLeft = startPixel;
7548 -    var labelRight = labelLeft + labelSize.width;
7549 -
7550 -    var rightEdge = Math.max(labelRight, endPixel);
7551 -    var track = this._findFreeTrack(evt, rightEdge);
7552 -    var labelTop = Math.round(
7553 -        metrics.trackOffset + track * metrics.trackIncrement + theme.event.tape.height);
7554 -
7555 -    var color = evt.getColor();
7556 -    color = color != null ? color : theme.event.duration.color;
7557 -
7558 -    var tapeElmtData = this._paintEventTape(evt, track, startPixel, endPixel, color, 100, metrics, theme, 0);
7559 -    var labelElmtData = this._paintEventLabel(evt, text, labelLeft, labelTop, labelSize.width,
7560 -      labelSize.height, theme, labelDivClassName, highlightIndex);
7561 -    var els = [tapeElmtData.elmt, labelElmtData.elmt];
7562 -
7563 -    var self = this;
7564 -    var clickHandler = function(elmt, domEvt, target) {
7565 -        return self._onClickDurationEvent(tapeElmtData.elmt, domEvt, evt);
7566 -    };
7567 -    SimileAjax.DOM.registerEvent(tapeElmtData.elmt, "mousedown", clickHandler);
7568 -    SimileAjax.DOM.registerEvent(labelElmtData.elmt, "mousedown", clickHandler);
7569 -
7570 -    var hDiv = this._createHighlightDiv(highlightIndex, tapeElmtData, theme, evt);
7571 -    if (hDiv != null) {els.push(hDiv);}
7572 -    this._fireEventPaintListeners('paintedEvent', evt, els);
7573 -
7574 -    this._eventIdToElmt[evt.getID()] = tapeElmtData.elmt;
7575 -    this._tracks[track] = startPixel;
7576 -};
7577 -
7578 -Timeline.OriginalEventPainter.prototype.paintImpreciseDurationEvent = function(evt, metrics, theme, highlightIndex) {
7579 -    var doc = this._timeline.getDocument();
7580 -    var text = evt.getText();
7581 -
7582 -    var startDate = evt.getStart();
7583 -    var latestStartDate = evt.getLatestStart();
7584 -    var endDate = evt.getEnd();
7585 -    var earliestEndDate = evt.getEarliestEnd();
7586 -
7587 -    var startPixel = Math.round(this._band.dateToPixelOffset(startDate));
7588 -    var latestStartPixel = Math.round(this._band.dateToPixelOffset(latestStartDate));
7589 -    var endPixel = Math.round(this._band.dateToPixelOffset(endDate));
7590 -    var earliestEndPixel = Math.round(this._band.dateToPixelOffset(earliestEndDate));
7591 -
7592 -    var labelDivClassName = this._getLabelDivClassName(evt);
7593 -    var labelSize = this._frc.computeSize(text, labelDivClassName);
7594 -    var labelLeft = latestStartPixel;
7595 -    var labelRight = labelLeft + labelSize.width;
7596 -
7597 -    var rightEdge = Math.max(labelRight, endPixel);
7598 -    var track = this._findFreeTrack(evt, rightEdge);
7599 -    var labelTop = Math.round(
7600 -        metrics.trackOffset + track * metrics.trackIncrement + theme.event.tape.height);
7601 -
7602 -    var color = evt.getColor();
7603 -    color = color != null ? color : theme.event.duration.color;
7604 -
7605 -    // Imprecise events can have two event tapes
7606 -    // The imprecise dates tape, uses opacity to be dimmer than precise dates
7607 -    var impreciseTapeElmtData = this._paintEventTape(evt, track, startPixel, endPixel,
7608 -        theme.event.duration.impreciseColor,
7609 -        theme.event.duration.impreciseOpacity, metrics, theme, 0);
7610 -    // The precise dates tape, regular (100%) opacity
7611 -    var tapeElmtData = this._paintEventTape(evt, track, latestStartPixel,
7612 -        earliestEndPixel, color, 100, metrics, theme, 1);
7613 -
7614 -    var labelElmtData = this._paintEventLabel(evt, text, labelLeft, labelTop,
7615 -        labelSize.width, labelSize.height, theme, labelDivClassName, highlightIndex);
7616 -    var els = [impreciseTapeElmtData.elmt, tapeElmtData.elmt, labelElmtData.elmt];
7617 -
7618 -    var self = this;
7619 -    var clickHandler = function(elmt, domEvt, target) {
7620 -        return self._onClickDurationEvent(tapeElmtData.elmt, domEvt, evt);
7621 -    };
7622 -    SimileAjax.DOM.registerEvent(tapeElmtData.elmt, "mousedown", clickHandler);
7623 -    SimileAjax.DOM.registerEvent(labelElmtData.elmt, "mousedown", clickHandler);
7624 -
7625 -    var hDiv = this._createHighlightDiv(highlightIndex, tapeElmtData, theme, evt);
7626 -    if (hDiv != null) {els.push(hDiv);}
7627 -    this._fireEventPaintListeners('paintedEvent', evt, els);
7628 -
7629 -    this._eventIdToElmt[evt.getID()] = tapeElmtData.elmt;
7630 -    this._tracks[track] = startPixel;
7631 -};
7632 -
7633 -Timeline.OriginalEventPainter.prototype._encodeEventElID = function(elType, evt) {
7634 -    return Timeline.EventUtils.encodeEventElID(this._timeline, this._band, elType, evt);
7635 -};
7636 -
7637 -Timeline.OriginalEventPainter.prototype._findFreeTrack = function(event, rightEdge) {
7638 -    var trackAttribute = event.getTrackNum();
7639 -    if (trackAttribute != null) {
7640 -        return trackAttribute; // early return since event includes track number
7641 -    }
7642 -
7643 -    // normal case: find an open track
7644 -    for (var i = 0; i < this._tracks.length; i++) {
7645 -        var t = this._tracks[i];
7646 -        if (t > rightEdge) {
7647 -            break;
7648 -        }
7649 -    }
7650 -    return i;
7651 -};
7652 -
7653 -Timeline.OriginalEventPainter.prototype._paintEventIcon = function(evt, iconTrack, left, metrics, theme, tapeHeight) {
7654 -    // If no tape, then paint the icon in the middle of the track.
7655 -    // If there is a tape, paint the icon below the tape + impreciseIconMargin
7656 -    var icon = evt.getIcon();
7657 -    icon = icon != null ? icon : metrics.icon;
7658 -
7659 -    var top; // top of the icon
7660 -    if (tapeHeight > 0) {
7661 -        top = metrics.trackOffset + iconTrack * metrics.trackIncrement +
7662 -              tapeHeight + metrics.impreciseIconMargin;
7663 -    } else {
7664 -        var middle = metrics.trackOffset + iconTrack * metrics.trackIncrement +
7665 -                     metrics.trackHeight / 2;
7666 -        top = Math.round(middle - metrics.iconHeight / 2);
7667 -    }
7668 -    var img = SimileAjax.Graphics.createTranslucentImage(icon);
7669 -    var iconDiv = this._timeline.getDocument().createElement("div");
7670 -    iconDiv.className = this._getElClassName('timeline-event-icon', evt, 'icon');
7671 -    iconDiv.id = this._encodeEventElID('icon', evt);
7672 -    iconDiv.style.left = left + "px";
7673 -    iconDiv.style.top = top + "px";
7674 -    iconDiv.appendChild(img);
7675 -
7676 -    if(evt._title != null)
7677 -        iconDiv.title = evt._title;
7678 -
7679 -    this._eventLayer.appendChild(iconDiv);
7680 -
7681 -    return {
7682 -        left:   left,
7683 -        top:    top,
7684 -        width:  metrics.iconWidth,
7685 -        height: metrics.iconHeight,
7686 -        elmt:   iconDiv
7687 -    };
7688 -};
7689 -
7690 -Timeline.OriginalEventPainter.prototype._paintEventLabel = function(evt, text, left, top, width,
7691 -    height, theme, labelDivClassName, highlightIndex) {
7692 -    var doc = this._timeline.getDocument();
7693 -
7694 -    var labelDiv = doc.createElement("div");
7695 -    labelDiv.className = labelDivClassName;
7696 -    labelDiv.id = this._encodeEventElID('label', evt);
7697 -    labelDiv.style.left = left + "px";
7698 -    labelDiv.style.width = width + "px";
7699 -    labelDiv.style.top = top + "px";
7700 -    labelDiv.innerHTML = text;
7701 -
7702 -    if(evt._title != null)
7703 -        labelDiv.title = evt._title;
7704 -
7705 -    var color = evt.getTextColor();
7706 -    if (color == null) {
7707 -        color = evt.getColor();
7708 -    }
7709 -    if (color != null) {
7710 -        labelDiv.style.color = color;
7711 -    }
7712 -    if (theme.event.highlightLabelBackground && highlightIndex >= 0) {
7713 -        labelDiv.style.background = this._getHighlightColor(highlightIndex, theme);
7714 -    }
7715 -
7716 -    this._eventLayer.appendChild(labelDiv);
7717 -
7718 -    return {
7719 -        left:   left,
7720 -        top:    top,
7721 -        width:  width,
7722 -        height: height,
7723 -        elmt:   labelDiv
7724 -    };
7725 -};
7726 -
7727 -Timeline.OriginalEventPainter.prototype._paintEventTape = function(
7728 -    evt, iconTrack, startPixel, endPixel, color, opacity, metrics, theme, tape_index) {
7729 -
7730 -    var tapeWidth = endPixel - startPixel;
7731 -    var tapeHeight = theme.event.tape.height;
7732 -    var top = metrics.trackOffset + iconTrack * metrics.trackIncrement;
7733 -
7734 -    var tapeDiv = this._timeline.getDocument().createElement("div");
7735 -    tapeDiv.className = this._getElClassName('timeline-event-tape', evt, 'tape');
7736 -    tapeDiv.id = this._encodeEventElID('tape' + tape_index, evt);
7737 -    tapeDiv.style.left = startPixel + "px";
7738 -    tapeDiv.style.width = tapeWidth + "px";
7739 -    tapeDiv.style.height = tapeHeight + "px";
7740 -    tapeDiv.style.top = top + "px";
7741 -
7742 -    if(evt._title != null)
7743 -        tapeDiv.title = evt._title;
7744 -
7745 -    if(color != null) {
7746 -        tapeDiv.style.backgroundColor = color;
7747 -    }
7748 -
7749 -    var backgroundImage = evt.getTapeImage();
7750 -    var backgroundRepeat = evt.getTapeRepeat();
7751 -    backgroundRepeat = backgroundRepeat != null ? backgroundRepeat : 'repeat';
7752 -    if(backgroundImage != null) {
7753 -      tapeDiv.style.backgroundImage = "url(" + backgroundImage + ")";
7754 -      tapeDiv.style.backgroundRepeat = backgroundRepeat;
7755 -    }
7756 -
7757 -    SimileAjax.Graphics.setOpacity(tapeDiv, opacity);
7758 -
7759 -    this._eventLayer.appendChild(tapeDiv);
7760 -
7761 -    return {
7762 -        left:   startPixel,
7763 -        top:    top,
7764 -        width:  tapeWidth,
7765 -        height: tapeHeight,
7766 -        elmt:   tapeDiv
7767 -    };
7768 -}
7769 -
7770 -Timeline.OriginalEventPainter.prototype._getLabelDivClassName = function(evt) {
7771 -    return this._getElClassName('timeline-event-label', evt, 'label');
7772 -};
7773 -
7774 -Timeline.OriginalEventPainter.prototype._getElClassName = function(elClassName, evt, prefix) {
7775 -    // Prefix and '_' is added to the event's classname. Set to null for no prefix
7776 -    var evt_classname = evt.getClassName(),
7777 -        pieces = [];
7778 -
7779 -    if (evt_classname) {
7780 -      if (prefix) {pieces.push(prefix + '-' + evt_classname + ' ');}
7781 -      pieces.push(evt_classname + ' ');
7782 -    }
7783 -    pieces.push(elClassName);
7784 -    return(pieces.join(''));
7785 -};
7786 -
7787 -Timeline.OriginalEventPainter.prototype._getHighlightColor = function(highlightIndex, theme) {
7788 -    var highlightColors = theme.event.highlightColors;
7789 -    return highlightColors[Math.min(highlightIndex, highlightColors.length - 1)];
7790 -};
7791 -
7792 -Timeline.OriginalEventPainter.prototype._createHighlightDiv = function(highlightIndex, dimensions, theme, evt) {
7793 -    var div = null;
7794 -    if (highlightIndex >= 0) {
7795 -        var doc = this._timeline.getDocument();
7796 -        var color = this._getHighlightColor(highlightIndex, theme);
7797 -
7798 -        div = doc.createElement("div");
7799 -        div.className = this._getElClassName('timeline-event-highlight', evt, 'highlight');
7800 -        div.id = this._encodeEventElID('highlight0', evt); // in future will have other
7801 -                                                           // highlight divs for tapes + icons
7802 -        div.style.position = "absolute";
7803 -        div.style.overflow = "hidden";
7804 -        div.style.left =    (dimensions.left - 2) + "px";
7805 -        div.style.width =   (dimensions.width + 4) + "px";
7806 -        div.style.top =     (dimensions.top - 2) + "px";
7807 -        div.style.height =  (dimensions.height + 4) + "px";
7808 -        div.style.background = color;
7809 -
7810 -        this._highlightLayer.appendChild(div);
7811 -    }
7812 -    return div;
7813 -};
7814 -
7815 -Timeline.OriginalEventPainter.prototype._onClickInstantEvent = function(icon, domEvt, evt) {
7816 -    var c = SimileAjax.DOM.getPageCoordinates(icon);
7817 -    this._showBubble(
7818 -        c.left + Math.ceil(icon.offsetWidth / 2),
7819 -        c.top + Math.ceil(icon.offsetHeight / 2),
7820 -        evt
7821 -    );
7822 -    this._fireOnSelect(evt.getID());
7823 -
7824 -    domEvt.cancelBubble = true;
7825 -    SimileAjax.DOM.cancelEvent(domEvt);
7826 -    return false;
7827 -};
7828 -
7829 -Timeline.OriginalEventPainter.prototype._onClickDurationEvent = function(target, domEvt, evt) {
7830 -    if ("pageX" in domEvt) {
7831 -        var x = domEvt.pageX;
7832 -        var y = domEvt.pageY;
7833 -    } else {
7834 -        var c = SimileAjax.DOM.getPageCoordinates(target);
7835 -        var x = domEvt.offsetX + c.left;
7836 -        var y = domEvt.offsetY + c.top;
7837 -    }
7838 -    this._showBubble(x, y, evt);
7839 -    this._fireOnSelect(evt.getID());
7840 -
7841 -    domEvt.cancelBubble = true;
7842 -    SimileAjax.DOM.cancelEvent(domEvt);
7843 -    return false;
7844 -};
7845 -
7846 -Timeline.OriginalEventPainter.prototype.showBubble = function(evt) {
7847 -    var elmt = this._eventIdToElmt[evt.getID()];
7848 -    if (elmt) {
7849 -        var c = SimileAjax.DOM.getPageCoordinates(elmt);
7850 -        this._showBubble(c.left + elmt.offsetWidth / 2, c.top + elmt.offsetHeight / 2, evt);
7851 -    }
7852 -};
7853 -
7854 -Timeline.OriginalEventPainter.prototype._showBubble = function(x, y, evt) {
7855 -    var div = document.createElement("div");
7856 -    var themeBubble = this._params.theme.event.bubble;
7857 -    evt.fillInfoBubble(div, this._params.theme, this._band.getLabeller());
7858 -
7859 -    SimileAjax.WindowManager.cancelPopups();
7860 -    SimileAjax.Graphics.createBubbleForContentAndPoint(div, x, y,
7861 -        themeBubble.width, null, themeBubble.maxHeight);
7862 -};
7863 -
7864 -Timeline.OriginalEventPainter.prototype._fireOnSelect = function(eventID) {
7865 -    for (var i = 0; i < this._onSelectListeners.length; i++) {
7866 -        this._onSelectListeners[i](eventID);
7867 -    }
7868 -};
7869 -
7870 -Timeline.OriginalEventPainter.prototype._fireEventPaintListeners = function(op, evt, els) {
7871 -    for (var i = 0; i < this._eventPaintListeners.length; i++) {
7872 -        this._eventPaintListeners[i](this._band, op, evt, els);
7873 -    }
7874 -};
7875 -/*
7876 - *  Detailed Event Painter
7877 - *
7878 - */
7879 -
7880 -// Note: a number of features from original-painter
7881 -//       are not yet implemented in detailed painter.
7882 -//       Eg classname, id attributes for icons, labels, tapes
7883 -
7884 -Timeline.DetailedEventPainter = function(params) {
7885 -    this._params = params;
7886 -    this._onSelectListeners = [];
7887 -
7888 -    this._filterMatcher = null;
7889 -    this._highlightMatcher = null;
7890 -    this._frc = null;
7891 -
7892 -    this._eventIdToElmt = {};
7893 -};
7894 -
7895 -Timeline.DetailedEventPainter.prototype.initialize = function(band, timeline) {
7896 -    this._band = band;
7897 -    this._timeline = timeline;
7898 -
7899 -    this._backLayer = null;
7900 -    this._eventLayer = null;
7901 -    this._lineLayer = null;
7902 -    this._highlightLayer = null;
7903 -
7904 -    this._eventIdToElmt = null;
7905 -};
7906 -
7907 -Timeline.DetailedEventPainter.prototype.getType = function() {
7908 -    return 'detailed';
7909 -};
7910 -
7911 -Timeline.DetailedEventPainter.prototype.addOnSelectListener = function(listener) {
7912 -    this._onSelectListeners.push(listener);
7913 -};
7914 -
7915 -Timeline.DetailedEventPainter.prototype.removeOnSelectListener = function(listener) {
7916 -    for (var i = 0; i < this._onSelectListeners.length; i++) {
7917 -        if (this._onSelectListeners[i] == listener) {
7918 -            this._onSelectListeners.splice(i, 1);
7919 -            break;
7920 -        }
7921 -    }
7922 -};
7923 -
7924 -Timeline.DetailedEventPainter.prototype.getFilterMatcher = function() {
7925 -    return this._filterMatcher;
7926 -};
7927 -
7928 -Timeline.DetailedEventPainter.prototype.setFilterMatcher = function(filterMatcher) {
7929 -    this._filterMatcher = filterMatcher;
7930 -};
7931 -
7932 -Timeline.DetailedEventPainter.prototype.getHighlightMatcher = function() {
7933 -    return this._highlightMatcher;
7934 -};
7935 -
7936 -Timeline.DetailedEventPainter.prototype.setHighlightMatcher = function(highlightMatcher) {
7937 -    this._highlightMatcher = highlightMatcher;
7938 -};
7939 -
7940 -Timeline.DetailedEventPainter.prototype.paint = function() {
7941 -    var eventSource = this._band.getEventSource();
7942 -    if (eventSource == null) {
7943 -        return;
7944 -    }
7945 -
7946 -    this._eventIdToElmt = {};
7947 -    this._prepareForPainting();
7948 -
7949 -    var eventTheme = this._params.theme.event;
7950 -    var trackHeight = Math.max(eventTheme.track.height, this._frc.getLineHeight());
7951 -    var metrics = {
7952 -        trackOffset:    Math.round(this._band.getViewWidth() / 2 - trackHeight / 2),
7953 -        trackHeight:    trackHeight,
7954 -        trackGap:       eventTheme.track.gap,
7955 -        trackIncrement: trackHeight + eventTheme.track.gap,
7956 -        icon:           eventTheme.instant.icon,
7957 -        iconWidth:      eventTheme.instant.iconWidth,
7958 -        iconHeight:     eventTheme.instant.iconHeight,
7959 -        labelWidth:     eventTheme.label.width
7960 -    }
7961 -
7962 -    var minDate = this._band.getMinDate();
7963 -    var maxDate = this._band.getMaxDate();
7964 -
7965 -    var filterMatcher = (this._filterMatcher != null) ?
7966 -        this._filterMatcher :
7967 -        function(evt) { return true; };
7968 -    var highlightMatcher = (this._highlightMatcher != null) ?
7969 -        this._highlightMatcher :
7970 -        function(evt) { return -1; };
7971 -
7972 -    var iterator = eventSource.getEventReverseIterator(minDate, maxDate);
7973 -    while (iterator.hasNext()) {
7974 -        var evt = iterator.next();
7975 -        if (filterMatcher(evt)) {
7976 -            this.paintEvent(evt, metrics, this._params.theme, highlightMatcher(evt));
7977 -        }
7978 -    }
7979 -
7980 -    this._highlightLayer.style.display = "block";
7981 -    this._lineLayer.style.display = "block";
7982 -    this._eventLayer.style.display = "block";
7983 -    // update the band object for max number of tracks in this section of the ether
7984 -    this._band.updateEventTrackInfo(this._lowerTracks.length + this._upperTracks.length,
7985 -                                 metrics.trackIncrement);
7986 -};
7987 -
7988 -Timeline.DetailedEventPainter.prototype.softPaint = function() {
7989 -};
7990 -
7991 -Timeline.DetailedEventPainter.prototype._prepareForPainting = function() {
7992 -    var band = this._band;
7993 -
7994 -    if (this._backLayer == null) {
7995 -        this._backLayer = this._band.createLayerDiv(0, "timeline-band-events");
7996 -        this._backLayer.style.visibility = "hidden";
7997 -
7998 -        var eventLabelPrototype = document.createElement("span");
7999 -        eventLabelPrototype.className = "timeline-event-label";
8000 -        this._backLayer.appendChild(eventLabelPrototype);
8001 -        this._frc = SimileAjax.Graphics.getFontRenderingContext(eventLabelPrototype);
8002 -    }
8003 -    this._frc.update();
8004 -    this._lowerTracks = [];
8005 -    this._upperTracks = [];
8006 -
8007 -    if (this._highlightLayer != null) {
8008 -        band.removeLayerDiv(this._highlightLayer);
8009 -    }
8010 -    this._highlightLayer = band.createLayerDiv(105, "timeline-band-highlights");
8011 -    this._highlightLayer.style.display = "none";
8012 -
8013 -    if (this._lineLayer != null) {
8014 -        band.removeLayerDiv(this._lineLayer);
8015 -    }
8016 -    this._lineLayer = band.createLayerDiv(110, "timeline-band-lines");
8017 -    this._lineLayer.style.display = "none";
8018 -
8019 -    if (this._eventLayer != null) {
8020 -        band.removeLayerDiv(this._eventLayer);
8021 -    }
8022 -    this._eventLayer = band.createLayerDiv(110, "timeline-band-events");
8023 -    this._eventLayer.style.display = "none";
8024 -};
8025 -
8026 -Timeline.DetailedEventPainter.prototype.paintEvent = function(evt, metrics, theme, highlightIndex) {
8027 -    if (evt.isInstant()) {
8028 -        this.paintInstantEvent(evt, metrics, theme, highlightIndex);
8029 -    } else {
8030 -        this.paintDurationEvent(evt, metrics, theme, highlightIndex);
8031 -    }
8032 -};
8033 -
8034 -Timeline.DetailedEventPainter.prototype.paintInstantEvent = function(evt, metrics, theme, highlightIndex) {
8035 -    if (evt.isImprecise()) {
8036 -        this.paintImpreciseInstantEvent(evt, metrics, theme, highlightIndex);
8037 -    } else {
8038 -        this.paintPreciseInstantEvent(evt, metrics, theme, highlightIndex);
8039 -    }
8040 -}
8041 -
8042 -Timeline.DetailedEventPainter.prototype.paintDurationEvent = function(evt, metrics, theme, highlightIndex) {
8043 -    if (evt.isImprecise()) {
8044 -        this.paintImpreciseDurationEvent(evt, metrics, theme, highlightIndex);
8045 -    } else {
8046 -        this.paintPreciseDurationEvent(evt, metrics, theme, highlightIndex);
8047 -    }
8048 -}
8049 -
8050 -Timeline.DetailedEventPainter.prototype.paintPreciseInstantEvent = function(evt, metrics, theme, highlightIndex) {
8051 -    var doc = this._timeline.getDocument();
8052 -    var text = evt.getText();
8053 -
8054 -    var startDate = evt.getStart();
8055 -    var startPixel = Math.round(this._band.dateToPixelOffset(startDate));
8056 -    var iconRightEdge = Math.round(startPixel + metrics.iconWidth / 2);
8057 -    var iconLeftEdge = Math.round(startPixel - metrics.iconWidth / 2);
8058 -
8059 -    var labelSize = this._frc.computeSize(text);
8060 -    var iconTrack = this._findFreeTrackForSolid(iconRightEdge, startPixel);
8061 -    var iconElmtData = this._paintEventIcon(evt, iconTrack, iconLeftEdge, metrics, theme);
8062 -
8063 -    var labelLeft = iconRightEdge + theme.event.label.offsetFromLine;
8064 -    var labelTrack = iconTrack;
8065 -
8066 -    var iconTrackData = this._getTrackData(iconTrack);
8067 -    if (Math.min(iconTrackData.solid, iconTrackData.text) >= labelLeft + labelSize.width) { // label on the same track, to the right of icon
8068 -        iconTrackData.solid = iconLeftEdge;
8069 -        iconTrackData.text = labelLeft;
8070 -    } else { // label on a different track, below icon
8071 -        iconTrackData.solid = iconLeftEdge;
8072 -
8073 -        labelLeft = startPixel + theme.event.label.offsetFromLine;
8074 -        labelTrack = this._findFreeTrackForText(iconTrack, labelLeft + labelSize.width, function(t) { t.line = startPixel - 2; });
8075 -        this._getTrackData(labelTrack).text = iconLeftEdge;
8076 -
8077 -        this._paintEventLine(evt, startPixel, iconTrack, labelTrack, metrics, theme);
8078 -    }
8079 -
8080 -    var labelTop = Math.round(
8081 -        metrics.trackOffset + labelTrack * metrics.trackIncrement +
8082 -        metrics.trackHeight / 2 - labelSize.height / 2);
8083 -
8084 -    var labelElmtData = this._paintEventLabel(evt, text, labelLeft, labelTop, labelSize.width, labelSize.height, theme);
8085 -
8086 -    var self = this;
8087 -    var clickHandler = function(elmt, domEvt, target) {
8088 -        return self._onClickInstantEvent(iconElmtData.elmt, domEvt, evt);
8089 -    };
8090 -    SimileAjax.DOM.registerEvent(iconElmtData.elmt, "mousedown", clickHandler);
8091 -    SimileAjax.DOM.registerEvent(labelElmtData.elmt, "mousedown", clickHandler);
8092 -
8093 -    this._createHighlightDiv(highlightIndex, iconElmtData, theme);
8094 -
8095 -    this._eventIdToElmt[evt.getID()] = iconElmtData.elmt;
8096 -};
8097 -
8098 -Timeline.DetailedEventPainter.prototype.paintImpreciseInstantEvent = function(evt, metrics, theme, highlightIndex) {
8099 -    var doc = this._timeline.getDocument();
8100 -    var text = evt.getText();
8101 -
8102 -    var startDate = evt.getStart();
8103 -    var endDate = evt.getEnd();
8104 -    var startPixel = Math.round(this._band.dateToPixelOffset(startDate));
8105 -    var endPixel = Math.round(this._band.dateToPixelOffset(endDate));
8106 -
8107 -    var iconRightEdge = Math.round(startPixel + metrics.iconWidth / 2);
8108 -    var iconLeftEdge = Math.round(startPixel - metrics.iconWidth / 2);
8109 -
8110 -    var labelSize = this._frc.computeSize(text);
8111 -    var iconTrack = this._findFreeTrackForSolid(endPixel, startPixel);
8112 -
8113 -    var tapeElmtData = this._paintEventTape(evt, iconTrack, startPixel, endPixel,
8114 -        theme.event.instant.impreciseColor, theme.event.instant.impreciseOpacity, metrics, theme);
8115 -    var iconElmtData = this._paintEventIcon(evt, iconTrack, iconLeftEdge, metrics, theme);
8116 -
8117 -    var iconTrackData = this._getTrackData(iconTrack);
8118 -    iconTrackData.solid = iconLeftEdge;
8119 -
8120 -    var labelLeft = iconRightEdge + theme.event.label.offsetFromLine;
8121 -    var labelRight = labelLeft + labelSize.width;
8122 -    var labelTrack;
8123 -    if (labelRight < endPixel) {
8124 -        labelTrack = iconTrack;
8125 -    } else {
8126 -        labelLeft = startPixel + theme.event.label.offsetFromLine;
8127 -        labelRight = labelLeft + labelSize.width;
8128 -
8129 -        labelTrack = this._findFreeTrackForText(iconTrack, labelRight, function(t) { t.line = startPixel - 2; });
8130 -        this._getTrackData(labelTrack).text = iconLeftEdge;
8131 -
8132 -        this._paintEventLine(evt, startPixel, iconTrack, labelTrack, metrics, theme);
8133 -    }
8134 -    var labelTop = Math.round(
8135 -        metrics.trackOffset + labelTrack * metrics.trackIncrement +
8136 -        metrics.trackHeight / 2 - labelSize.height / 2);
8137 -
8138 -    var labelElmtData = this._paintEventLabel(evt, text, labelLeft, labelTop, labelSize.width, labelSize.height, theme);
8139 -
8140 -    var self = this;
8141 -    var clickHandler = function(elmt, domEvt, target) {
8142 -        return self._onClickInstantEvent(iconElmtData.elmt, domEvt, evt);
8143 -    };
8144 -    SimileAjax.DOM.registerEvent(iconElmtData.elmt, "mousedown", clickHandler);
8145 -    SimileAjax.DOM.registerEvent(tapeElmtData.elmt, "mousedown", clickHandler);
8146 -    SimileAjax.DOM.registerEvent(labelElmtData.elmt, "mousedown", clickHandler);
8147 -
8148 -    this._createHighlightDiv(highlightIndex, iconElmtData, theme);
8149 -
8150 -    this._eventIdToElmt[evt.getID()] = iconElmtData.elmt;
8151 -};
8152 -
8153 -Timeline.DetailedEventPainter.prototype.paintPreciseDurationEvent = function(evt, metrics, theme, highlightIndex) {
8154 -    var doc = this._timeline.getDocument();
8155 -    var text = evt.getText();
8156 -
8157 -    var startDate = evt.getStart();
8158 -    var endDate = evt.getEnd();
8159 -    var startPixel = Math.round(this._band.dateToPixelOffset(startDate));
8160 -    var endPixel = Math.round(this._band.dateToPixelOffset(endDate));
8161 -
8162 -    var labelSize = this._frc.computeSize(text);
8163 -    var tapeTrack = this._findFreeTrackForSolid(endPixel);
8164 -    var color = evt.getColor();
8165 -    color = color != null ? color : theme.event.duration.color;
8166 -
8167 -    var tapeElmtData = this._paintEventTape(evt, tapeTrack, startPixel, endPixel, color, 100, metrics, theme);
8168 -
8169 -    var tapeTrackData = this._getTrackData(tapeTrack);
8170 -    tapeTrackData.solid = startPixel;
8171 -
8172 -    var labelLeft = startPixel + theme.event.label.offsetFromLine;
8173 -    var labelTrack = this._findFreeTrackForText(tapeTrack, labelLeft + labelSize.width, function(t) { t.line = startPixel - 2; });
8174 -    this._getTrackData(labelTrack).text = startPixel - 2;
8175 -
8176 -    this._paintEventLine(evt, startPixel, tapeTrack, labelTrack, metrics, theme);
8177 -
8178 -    var labelTop = Math.round(
8179 -        metrics.trackOffset + labelTrack * metrics.trackIncrement +
8180 -        metrics.trackHeight / 2 - labelSize.height / 2);
8181 -
8182 -    var labelElmtData = this._paintEventLabel(evt, text, labelLeft, labelTop, labelSize.width, labelSize.height, theme);
8183 -
8184 -    var self = this;
8185 -    var clickHandler = function(elmt, domEvt, target) {
8186 -        return self._onClickDurationEvent(tapeElmtData.elmt, domEvt, evt);
8187 -    };
8188 -    SimileAjax.DOM.registerEvent(tapeElmtData.elmt, "mousedown", clickHandler);
8189 -    SimileAjax.DOM.registerEvent(labelElmtData.elmt, "mousedown", clickHandler);
8190 -
8191 -    this._createHighlightDiv(highlightIndex, tapeElmtData, theme);
8192 -
8193 -    this._eventIdToElmt[evt.getID()] = tapeElmtData.elmt;
8194 -};
8195 -
8196 -Timeline.DetailedEventPainter.prototype.paintImpreciseDurationEvent = function(evt, metrics, theme, highlightIndex) {
8197 -    var doc = this._timeline.getDocument();
8198 -    var text = evt.getText();
8199 -
8200 -    var startDate = evt.getStart();
8201 -    var latestStartDate = evt.getLatestStart();
8202 -    var endDate = evt.getEnd();
8203 -    var earliestEndDate = evt.getEarliestEnd();
8204 -
8205 -    var startPixel = Math.round(this._band.dateToPixelOffset(startDate));
8206 -    var latestStartPixel = Math.round(this._band.dateToPixelOffset(latestStartDate));
8207 -    var endPixel = Math.round(this._band.dateToPixelOffset(endDate));
8208 -    var earliestEndPixel = Math.round(this._band.dateToPixelOffset(earliestEndDate));
8209 -
8210 -    var labelSize = this._frc.computeSize(text);
8211 -    var tapeTrack = this._findFreeTrackForSolid(endPixel);
8212 -    var color = evt.getColor();
8213 -    color = color != null ? color : theme.event.duration.color;
8214 -
8215 -    var impreciseTapeElmtData = this._paintEventTape(evt, tapeTrack, startPixel, endPixel,
8216 -        theme.event.duration.impreciseColor, theme.event.duration.impreciseOpacity, metrics, theme);
8217 -    var tapeElmtData = this._paintEventTape(evt, tapeTrack, latestStartPixel, earliestEndPixel, color, 100, metrics, theme);
8218 -
8219 -    var tapeTrackData = this._getTrackData(tapeTrack);
8220 -    tapeTrackData.solid = startPixel;
8221 -
8222 -    var labelLeft = latestStartPixel + theme.event.label.offsetFromLine;
8223 -    var labelTrack = this._findFreeTrackForText(tapeTrack, labelLeft + labelSize.width, function(t) { t.line = latestStartPixel - 2; });
8224 -    this._getTrackData(labelTrack).text = latestStartPixel - 2;
8225 -
8226 -    this._paintEventLine(evt, latestStartPixel, tapeTrack, labelTrack, metrics, theme);
8227 -
8228 -    var labelTop = Math.round(
8229 -        metrics.trackOffset + labelTrack * metrics.trackIncrement +
8230 -        metrics.trackHeight / 2 - labelSize.height / 2);
8231 -
8232 -    var labelElmtData = this._paintEventLabel(evt, text, labelLeft, labelTop, labelSize.width, labelSize.height, theme);
8233 -
8234 -    var self = this;
8235 -    var clickHandler = function(elmt, domEvt, target) {
8236 -        return self._onClickDurationEvent(tapeElmtData.elmt, domEvt, evt);
8237 -    };
8238 -    SimileAjax.DOM.registerEvent(tapeElmtData.elmt, "mousedown", clickHandler);
8239 -    SimileAjax.DOM.registerEvent(labelElmtData.elmt, "mousedown", clickHandler);
8240 -
8241 -    this._createHighlightDiv(highlightIndex, tapeElmtData, theme);
8242 -
8243 -    this._eventIdToElmt[evt.getID()] = tapeElmtData.elmt;
8244 -};
8245 -
8246 -Timeline.DetailedEventPainter.prototype._findFreeTrackForSolid = function(solidEdge, softEdge) {
8247 -    for (var i = 0; true; i++) {
8248 -        if (i < this._lowerTracks.length) {
8249 -            var t = this._lowerTracks[i];
8250 -            if (Math.min(t.solid, t.text) > solidEdge && (!(softEdge) || t.line > softEdge)) {
8251 -                return i;
8252 -            }
8253 -        } else {
8254 -            this._lowerTracks.push({
8255 -                solid:  Number.POSITIVE_INFINITY,
8256 -                text:   Number.POSITIVE_INFINITY,
8257 -                line:   Number.POSITIVE_INFINITY
8258 -            });
8259 -
8260 -            return i;
8261 -        }
8262 -
8263 -        if (i < this._upperTracks.length) {
8264 -            var t = this._upperTracks[i];
8265 -            if (Math.min(t.solid, t.text) > solidEdge && (!(softEdge) || t.line > softEdge)) {
8266 -                return -1 - i;
8267 -            }
8268 -        } else {
8269 -            this._upperTracks.push({
8270 -                solid:  Number.POSITIVE_INFINITY,
8271 -                text:   Number.POSITIVE_INFINITY,
8272 -                line:   Number.POSITIVE_INFINITY
8273 -            });
8274 -
8275 -            return -1 - i;
8276 -        }
8277 -    }
8278 -};
8279 -
8280 -Timeline.DetailedEventPainter.prototype._findFreeTrackForText = function(fromTrack, edge, occupiedTrackVisitor) {
8281 -    var extendUp;
8282 -    var index;
8283 -    var firstIndex;
8284 -    var result;
8285 -
8286 -    if (fromTrack < 0) {
8287 -        extendUp = true;
8288 -        firstIndex = -fromTrack;
8289 -
8290 -        index = this._findFreeUpperTrackForText(firstIndex, edge);
8291 -        result = -1 - index;
8292 -    } else if (fromTrack > 0) {
8293 -        extendUp = false;
8294 -        firstIndex = fromTrack + 1;
8295 -
8296 -        index = this._findFreeLowerTrackForText(firstIndex, edge);
8297 -        result = index;
8298 -    } else {
8299 -        var upIndex = this._findFreeUpperTrackForText(0, edge);
8300 -        var downIndex = this._findFreeLowerTrackForText(1, edge);
8301 -
8302 -        if (downIndex - 1 <= upIndex) {
8303 -            extendUp = false;
8304 -            firstIndex = 1;
8305 -            index = downIndex;
8306 -            result = index;
8307 -        } else {
8308 -            extendUp = true;
8309 -            firstIndex = 0;
8310 -            index = upIndex;
8311 -            result = -1 - index;
8312 -        }
8313 -    }
8314 -
8315 -    if (extendUp) {
8316 -        if (index == this._upperTracks.length) {
8317 -            this._upperTracks.push({
8318 -                solid:  Number.POSITIVE_INFINITY,
8319 -                text:   Number.POSITIVE_INFINITY,
8320 -                line:   Number.POSITIVE_INFINITY
8321 -            });
8322 -        }
8323 -        for (var i = firstIndex; i < index; i++) {
8324 -            occupiedTrackVisitor(this._upperTracks[i]);
8325 -        }
8326 -    } else {
8327 -        if (index == this._lowerTracks.length) {
8328 -            this._lowerTracks.push({
8329 -                solid:  Number.POSITIVE_INFINITY,
8330 -                text:   Number.POSITIVE_INFINITY,
8331 -                line:   Number.POSITIVE_INFINITY
8332 -            });
8333 -        }
8334 -        for (var i = firstIndex; i < index; i++) {
8335 -            occupiedTrackVisitor(this._lowerTracks[i]);
8336 -        }
8337 -    }
8338 -    return result;
8339 -};
8340 -
8341 -Timeline.DetailedEventPainter.prototype._findFreeLowerTrackForText = function(index, edge) {
8342 -    for (; index < this._lowerTracks.length; index++) {
8343 -        var t = this._lowerTracks[index];
8344 -        if (Math.min(t.solid, t.text) >= edge) {
8345 -            break;
8346 -        }
8347 -    }
8348 -    return index;
8349 -};
8350 -
8351 -Timeline.DetailedEventPainter.prototype._findFreeUpperTrackForText = function(index, edge) {
8352 -    for (; index < this._upperTracks.length; index++) {
8353 -        var t = this._upperTracks[index];
8354 -        if (Math.min(t.solid, t.text) >= edge) {
8355 -            break;
8356 -        }
8357 -    }
8358 -    return index;
8359 -};
8360 -
8361 -Timeline.DetailedEventPainter.prototype._getTrackData = function(index) {
8362 -    return (index < 0) ? this._upperTracks[-index - 1] : this._lowerTracks[index];
8363 -};
8364 -
8365 -Timeline.DetailedEventPainter.prototype._paintEventLine = function(evt, left, startTrack, endTrack, metrics, theme) {
8366 -    var top = Math.round(metrics.trackOffset + startTrack * metrics.trackIncrement + metrics.trackHeight / 2);
8367 -    var height = Math.round(Math.abs(endTrack - startTrack) * metrics.trackIncrement);
8368 -
8369 -    var lineStyle = "1px solid " + theme.event.label.lineColor;
8370 -    var lineDiv = this._timeline.getDocument().createElement("div");
8371 -	lineDiv.style.position = "absolute";
8372 -    lineDiv.style.left = left + "px";
8373 -    lineDiv.style.width = theme.event.label.offsetFromLine + "px";
8374 -    lineDiv.style.height = height + "px";
8375 -    if (startTrack > endTrack) {
8376 -        lineDiv.style.top = (top - height) + "px";
8377 -        lineDiv.style.borderTop = lineStyle;
8378 -    } else {
8379 -        lineDiv.style.top = top + "px";
8380 -        lineDiv.style.borderBottom = lineStyle;
8381 -    }
8382 -    lineDiv.style.borderLeft = lineStyle;
8383 -    this._lineLayer.appendChild(lineDiv);
8384 -};
8385 -
8386 -Timeline.DetailedEventPainter.prototype._paintEventIcon = function(evt, iconTrack, left, metrics, theme) {
8387 -    var icon = evt.getIcon();
8388 -    icon = icon != null ? icon : metrics.icon;
8389 -
8390 -    var middle = metrics.trackOffset + iconTrack * metrics.trackIncrement + metrics.trackHeight / 2;
8391 -    var top = Math.round(middle - metrics.iconHeight / 2);
8392 -
8393 -    var img = SimileAjax.Graphics.createTranslucentImage(icon);
8394 -    var iconDiv = this._timeline.getDocument().createElement("div");
8395 -    iconDiv.style.position = "absolute";
8396 -    iconDiv.style.left = left + "px";
8397 -    iconDiv.style.top = top + "px";
8398 -    iconDiv.appendChild(img);
8399 -    iconDiv.style.cursor = "pointer";
8400 -
8401 -    if(evt._title != null)
8402 -        iconDiv.title = evt._title
8403 -
8404 -    this._eventLayer.appendChild(iconDiv);
8405 -
8406 -    return {
8407 -        left:   left,
8408 -        top:    top,
8409 -        width:  metrics.iconWidth,
8410 -        height: metrics.iconHeight,
8411 -        elmt:   iconDiv
8412 -    };
8413 -};
8414 -
8415 -Timeline.DetailedEventPainter.prototype._paintEventLabel = function(evt, text, left, top, width, height, theme) {
8416 -    var doc = this._timeline.getDocument();
8417 -
8418 -    var labelBackgroundDiv = doc.createElement("div");
8419 -    labelBackgroundDiv.style.position = "absolute";
8420 -    labelBackgroundDiv.style.left = left + "px";
8421 -    labelBackgroundDiv.style.width = width + "px";
8422 -    labelBackgroundDiv.style.top = top + "px";
8423 -    labelBackgroundDiv.style.height = height + "px";
8424 -    labelBackgroundDiv.style.backgroundColor = theme.event.label.backgroundColor;
8425 -    SimileAjax.Graphics.setOpacity(labelBackgroundDiv, theme.event.label.backgroundOpacity);
8426 -    this._eventLayer.appendChild(labelBackgroundDiv);
8427 -
8428 -    var labelDiv = doc.createElement("div");
8429 -    labelDiv.style.position = "absolute";
8430 -    labelDiv.style.left = left + "px";
8431 -    labelDiv.style.width = width + "px";
8432 -    labelDiv.style.top = top + "px";
8433 -    labelDiv.innerHTML = text;
8434 -    labelDiv.style.cursor = "pointer";
8435 -
8436 -    if(evt._title != null)
8437 -        labelDiv.title = evt._title;
8438 -
8439 -    var color = evt.getTextColor();
8440 -    if (color == null) {
8441 -        color = evt.getColor();
8442 -    }
8443 -    if (color != null) {
8444 -        labelDiv.style.color = color;
8445 -    }
8446 -
8447 -    this._eventLayer.appendChild(labelDiv);
8448 -
8449 -    return {
8450 -        left:   left,
8451 -        top:    top,
8452 -        width:  width,
8453 -        height: height,
8454 -        elmt:   labelDiv
8455 -    };
8456 -};
8457 -
8458 -Timeline.DetailedEventPainter.prototype._paintEventTape = function(
8459 -    evt, iconTrack, startPixel, endPixel, color, opacity, metrics, theme) {
8460 -
8461 -    var tapeWidth = endPixel - startPixel;
8462 -    var tapeHeight = theme.event.tape.height;
8463 -    var middle = metrics.trackOffset + iconTrack * metrics.trackIncrement + metrics.trackHeight / 2;
8464 -    var top = Math.round(middle - tapeHeight / 2);
8465 -
8466 -    var tapeDiv = this._timeline.getDocument().createElement("div");
8467 -    tapeDiv.style.position = "absolute";
8468 -    tapeDiv.style.left = startPixel + "px";
8469 -    tapeDiv.style.width = tapeWidth + "px";
8470 -    tapeDiv.style.top = top + "px";
8471 -    tapeDiv.style.height = tapeHeight + "px";
8472 -    tapeDiv.style.backgroundColor = color;
8473 -    tapeDiv.style.overflow = "hidden";
8474 -    tapeDiv.style.cursor = "pointer";
8475 -
8476 -    if(evt._title != null)
8477 -        tapeDiv.title = evt._title;
8478 -
8479 -    SimileAjax.Graphics.setOpacity(tapeDiv, opacity);
8480 -
8481 -    this._eventLayer.appendChild(tapeDiv);
8482 -
8483 -    return {
8484 -        left:   startPixel,
8485 -        top:    top,
8486 -        width:  tapeWidth,
8487 -        height: tapeHeight,
8488 -        elmt:   tapeDiv
8489 -    };
8490 -}
8491 -
8492 -Timeline.DetailedEventPainter.prototype._createHighlightDiv = function(highlightIndex, dimensions, theme) {
8493 -    if (highlightIndex >= 0) {
8494 -        var doc = this._timeline.getDocument();
8495 -        var eventTheme = theme.event;
8496 -
8497 -        var color = eventTheme.highlightColors[Math.min(highlightIndex, eventTheme.highlightColors.length - 1)];
8498 -
8499 -        var div = doc.createElement("div");
8500 -        div.style.position = "absolute";
8501 -        div.style.overflow = "hidden";
8502 -        div.style.left =    (dimensions.left - 2) + "px";
8503 -        div.style.width =   (dimensions.width + 4) + "px";
8504 -        div.style.top =     (dimensions.top - 2) + "px";
8505 -        div.style.height =  (dimensions.height + 4) + "px";
8506 -        div.style.background = color;
8507 -
8508 -        this._highlightLayer.appendChild(div);
8509 -    }
8510 -};
8511 -
8512 -Timeline.DetailedEventPainter.prototype._onClickInstantEvent = function(icon, domEvt, evt) {
8513 -    var c = SimileAjax.DOM.getPageCoordinates(icon);
8514 -    this._showBubble(
8515 -        c.left + Math.ceil(icon.offsetWidth / 2),
8516 -        c.top + Math.ceil(icon.offsetHeight / 2),
8517 -        evt
8518 -    );
8519 -    this._fireOnSelect(evt.getID());
8520 -
8521 -    domEvt.cancelBubble = true;
8522 -    SimileAjax.DOM.cancelEvent(domEvt);
8523 -    return false;
8524 -};
8525 -
8526 -Timeline.DetailedEventPainter.prototype._onClickDurationEvent = function(target, domEvt, evt) {
8527 -    if ("pageX" in domEvt) {
8528 -        var x = domEvt.pageX;
8529 -        var y = domEvt.pageY;
8530 -    } else {
8531 -        var c = SimileAjax.DOM.getPageCoordinates(target);
8532 -        var x = domEvt.offsetX + c.left;
8533 -        var y = domEvt.offsetY + c.top;
8534 -    }
8535 -    this._showBubble(x, y, evt);
8536 -    this._fireOnSelect(evt.getID());
8537 -
8538 -    domEvt.cancelBubble = true;
8539 -    SimileAjax.DOM.cancelEvent(domEvt);
8540 -    return false;
8541 -};
8542 -
8543 -Timeline.DetailedEventPainter.prototype.showBubble = function(evt) {
8544 -    var elmt = this._eventIdToElmt[evt.getID()];
8545 -    if (elmt) {
8546 -        var c = SimileAjax.DOM.getPageCoordinates(elmt);
8547 -        this._showBubble(c.left + elmt.offsetWidth / 2, c.top + elmt.offsetHeight / 2, evt);
8548 -    }
8549 -};
8550 -
8551 -Timeline.DetailedEventPainter.prototype._showBubble = function(x, y, evt) {
8552 -    var div = document.createElement("div");
8553 -    var themeBubble = this._params.theme.event.bubble;
8554 -    evt.fillInfoBubble(div, this._params.theme, this._band.getLabeller());
8555 -
8556 -    SimileAjax.WindowManager.cancelPopups();
8557 -    SimileAjax.Graphics.createBubbleForContentAndPoint(div, x, y,
8558 -       themeBubble.width, null, themeBubble.maxHeight);
8559 -};
8560 -
8561 -Timeline.DetailedEventPainter.prototype._fireOnSelect = function(eventID) {
8562 -    for (var i = 0; i < this._onSelectListeners.length; i++) {
8563 -        this._onSelectListeners[i](eventID);
8564 -    }
8565 -};
8566 -/*
8567 - *  Overview Event Painter
8568 - *
8569 - */
8570 -
8571 -Timeline.OverviewEventPainter = function(params) {
8572 -    this._params = params;
8573 -    this._onSelectListeners = [];
8574 -
8575 -    this._filterMatcher = null;
8576 -    this._highlightMatcher = null;
8577 -};
8578 -
8579 -Timeline.OverviewEventPainter.prototype.initialize = function(band, timeline) {
8580 -    this._band = band;
8581 -    this._timeline = timeline;
8582 -
8583 -    this._eventLayer = null;
8584 -    this._highlightLayer = null;
8585 -};
8586 -
8587 -Timeline.OverviewEventPainter.prototype.getType = function() {
8588 -    return 'overview';
8589 -};
8590 -
8591 -Timeline.OverviewEventPainter.prototype.addOnSelectListener = function(listener) {
8592 -    this._onSelectListeners.push(listener);
8593 -};
8594 -
8595 -Timeline.OverviewEventPainter.prototype.removeOnSelectListener = function(listener) {
8596 -    for (var i = 0; i < this._onSelectListeners.length; i++) {
8597 -        if (this._onSelectListeners[i] == listener) {
8598 -            this._onSelectListeners.splice(i, 1);
8599 -            break;
8600 -        }
8601 -    }
8602 -};
8603 -
8604 -Timeline.OverviewEventPainter.prototype.getFilterMatcher = function() {
8605 -    return this._filterMatcher;
8606 -};
8607 -
8608 -Timeline.OverviewEventPainter.prototype.setFilterMatcher = function(filterMatcher) {
8609 -    this._filterMatcher = filterMatcher;
8610 -};
8611 -
8612 -Timeline.OverviewEventPainter.prototype.getHighlightMatcher = function() {
8613 -    return this._highlightMatcher;
8614 -};
8615 -
8616 -Timeline.OverviewEventPainter.prototype.setHighlightMatcher = function(highlightMatcher) {
8617 -    this._highlightMatcher = highlightMatcher;
8618 -};
8619 -
8620 -Timeline.OverviewEventPainter.prototype.paint = function() {
8621 -    var eventSource = this._band.getEventSource();
8622 -    if (eventSource == null) {
8623 -        return;
8624 -    }
8625 -
8626 -    this._prepareForPainting();
8627 -
8628 -    var eventTheme = this._params.theme.event;
8629 -    var metrics = {
8630 -        trackOffset:    eventTheme.overviewTrack.offset,
8631 -        trackHeight:    eventTheme.overviewTrack.height,
8632 -        trackGap:       eventTheme.overviewTrack.gap,
8633 -        trackIncrement: eventTheme.overviewTrack.height + eventTheme.overviewTrack.gap
8634 -    }
8635 -
8636 -    var minDate = this._band.getMinDate();
8637 -    var maxDate = this._band.getMaxDate();
8638 -
8639 -    var filterMatcher = (this._filterMatcher != null) ?
8640 -        this._filterMatcher :
8641 -        function(evt) { return true; };
8642 -    var highlightMatcher = (this._highlightMatcher != null) ?
8643 -        this._highlightMatcher :
8644 -        function(evt) { return -1; };
8645 -
8646 -    var iterator = eventSource.getEventReverseIterator(minDate, maxDate);
8647 -    while (iterator.hasNext()) {
8648 -        var evt = iterator.next();
8649 -        if (filterMatcher(evt)) {
8650 -            this.paintEvent(evt, metrics, this._params.theme, highlightMatcher(evt));
8651 -        }
8652 -    }
8653 -
8654 -    this._highlightLayer.style.display = "block";
8655 -    this._eventLayer.style.display = "block";
8656 -    // update the band object for max number of tracks in this section of the ether
8657 -    this._band.updateEventTrackInfo(this._tracks.length, metrics.trackIncrement);
8658 -};
8659 -
8660 -Timeline.OverviewEventPainter.prototype.softPaint = function() {
8661 -};
8662 -
8663 -Timeline.OverviewEventPainter.prototype._prepareForPainting = function() {
8664 -    var band = this._band;
8665 -
8666 -    this._tracks = [];
8667 -
8668 -    if (this._highlightLayer != null) {
8669 -        band.removeLayerDiv(this._highlightLayer);
8670 -    }
8671 -    this._highlightLayer = band.createLayerDiv(105, "timeline-band-highlights");
8672 -    this._highlightLayer.style.display = "none";
8673 -
8674 -    if (this._eventLayer != null) {
8675 -        band.removeLayerDiv(this._eventLayer);
8676 -    }
8677 -    this._eventLayer = band.createLayerDiv(110, "timeline-band-events");
8678 -    this._eventLayer.style.display = "none";
8679 -};
8680 -
8681 -Timeline.OverviewEventPainter.prototype.paintEvent = function(evt, metrics, theme, highlightIndex) {
8682 -    if (evt.isInstant()) {
8683 -        this.paintInstantEvent(evt, metrics, theme, highlightIndex);
8684 -    } else {
8685 -        this.paintDurationEvent(evt, metrics, theme, highlightIndex);
8686 -    }
8687 -};
8688 -
8689 -Timeline.OverviewEventPainter.prototype.paintInstantEvent = function(evt, metrics, theme, highlightIndex) {
8690 -    var startDate = evt.getStart();
8691 -    var startPixel = Math.round(this._band.dateToPixelOffset(startDate));
8692 -
8693 -    var color = evt.getColor(),
8694 -        klassName = evt.getClassName();
8695 -    if (klassName) {
8696 -      color = null;
8697 -    } else {
8698 -      color = color != null ? color : theme.event.duration.color;
8699 -    }
8700 -
8701 -    var tickElmtData = this._paintEventTick(evt, startPixel, color, 100, metrics, theme);
8702 -
8703 -    this._createHighlightDiv(highlightIndex, tickElmtData, theme);
8704 -};
8705 -
8706 -Timeline.OverviewEventPainter.prototype.paintDurationEvent = function(evt, metrics, theme, highlightIndex) {
8707 -    var latestStartDate = evt.getLatestStart();
8708 -    var earliestEndDate = evt.getEarliestEnd();
8709 -
8710 -    var latestStartPixel = Math.round(this._band.dateToPixelOffset(latestStartDate));
8711 -    var earliestEndPixel = Math.round(this._band.dateToPixelOffset(earliestEndDate));
8712 -
8713 -    var tapeTrack = 0;
8714 -    for (; tapeTrack < this._tracks.length; tapeTrack++) {
8715 -        if (earliestEndPixel < this._tracks[tapeTrack]) {
8716 -            break;
8717 -        }
8718 -    }
8719 -    this._tracks[tapeTrack] = earliestEndPixel;
8720 -
8721 -    var color = evt.getColor(),
8722 -        klassName = evt.getClassName();
8723 -    if (klassName) {
8724 -      color = null;
8725 -    } else {
8726 -      color = color != null ? color : theme.event.duration.color;
8727 -    }
8728 -
8729 -    var tapeElmtData = this._paintEventTape(evt, tapeTrack, latestStartPixel, earliestEndPixel,
8730 -      color, 100, metrics, theme, klassName);
8731 -
8732 -    this._createHighlightDiv(highlightIndex, tapeElmtData, theme);
8733 -};
8734 -
8735 -Timeline.OverviewEventPainter.prototype._paintEventTape = function(
8736 -    evt, track, left, right, color, opacity, metrics, theme, klassName) {
8737 -
8738 -    var top = metrics.trackOffset + track * metrics.trackIncrement;
8739 -    var width = right - left;
8740 -    var height = metrics.trackHeight;
8741 -
8742 -    var tapeDiv = this._timeline.getDocument().createElement("div");
8743 -    tapeDiv.className = 'timeline-small-event-tape'
8744 -    if (klassName) {tapeDiv.className += ' small-' + klassName;}
8745 -    tapeDiv.style.left = left + "px";
8746 -    tapeDiv.style.width = width + "px";
8747 -    tapeDiv.style.top = top + "px";
8748 -    tapeDiv.style.height = height + "px";
8749 -
8750 -    if (color) {
8751 -      tapeDiv.style.backgroundColor = color; // set color here if defined by event. Else use css
8752 -    }
8753 - //   tapeDiv.style.overflow = "hidden";   // now set in css
8754 - //   tapeDiv.style.position = "absolute";
8755 -    if(opacity<100) SimileAjax.Graphics.setOpacity(tapeDiv, opacity);
8756 -
8757 -    this._eventLayer.appendChild(tapeDiv);
8758 -
8759 -    return {
8760 -        left:   left,
8761 -        top:    top,
8762 -        width:  width,
8763 -        height: height,
8764 -        elmt:   tapeDiv
8765 -    };
8766 -}
8767 -
8768 -Timeline.OverviewEventPainter.prototype._paintEventTick = function(
8769 -    evt, left, color, opacity, metrics, theme) {
8770 -
8771 -    var height = theme.event.overviewTrack.tickHeight;
8772 -    var top = metrics.trackOffset - height;
8773 -    var width = 1;
8774 -
8775 -    var tickDiv = this._timeline.getDocument().createElement("div");
8776 -	  tickDiv.className = 'timeline-small-event-icon'
8777 -    tickDiv.style.left = left + "px";
8778 -    tickDiv.style.top = top + "px";
8779 -  //  tickDiv.style.width = width + "px";
8780 -  //  tickDiv.style.position = "absolute";
8781 -  //  tickDiv.style.height = height + "px";
8782 -  //  tickDiv.style.backgroundColor = color;
8783 -  //  tickDiv.style.overflow = "hidden";
8784 -
8785 -    var klassName = evt.getClassName()
8786 -    if (klassName) {tickDiv.className +=' small-' + klassName};
8787 -
8788 -    if(opacity<100) {SimileAjax.Graphics.setOpacity(tickDiv, opacity)};
8789 -
8790 -    this._eventLayer.appendChild(tickDiv);
8791 -
8792 -    return {
8793 -        left:   left,
8794 -        top:    top,
8795 -        width:  width,
8796 -        height: height,
8797 -        elmt:   tickDiv
8798 -    };
8799 -}
8800 -
8801 -Timeline.OverviewEventPainter.prototype._createHighlightDiv = function(highlightIndex, dimensions, theme) {
8802 -    if (highlightIndex >= 0) {
8803 -        var doc = this._timeline.getDocument();
8804 -        var eventTheme = theme.event;
8805 -
8806 -        var color = eventTheme.highlightColors[Math.min(highlightIndex, eventTheme.highlightColors.length - 1)];
8807 -
8808 -        var div = doc.createElement("div");
8809 -        div.style.position = "absolute";
8810 -        div.style.overflow = "hidden";
8811 -        div.style.left =    (dimensions.left - 1) + "px";
8812 -        div.style.width =   (dimensions.width + 2) + "px";
8813 -        div.style.top =     (dimensions.top - 1) + "px";
8814 -        div.style.height =  (dimensions.height + 2) + "px";
8815 -        div.style.background = color;
8816 -
8817 -        this._highlightLayer.appendChild(div);
8818 -    }
8819 -};
8820 -
8821 -Timeline.OverviewEventPainter.prototype.showBubble = function(evt) {
8822 -    // not implemented
8823 -};
8824 -/*
8825 - *  Compact Event Painter
8826 - *
8827 - */
8828 -
8829 -Timeline.CompactEventPainter = function(params) {
8830 -    this._params = params;
8831 -    this._onSelectListeners = [];
8832 -
8833 -    this._filterMatcher = null;
8834 -    this._highlightMatcher = null;
8835 -    this._frc = null;
8836 -
8837 -    this._eventIdToElmt = {};
8838 -};
8839 -
8840 -Timeline.CompactEventPainter.prototype.getType = function() {
8841 -    return 'compact';
8842 -};
8843 -
8844 -Timeline.CompactEventPainter.prototype.initialize = function(band, timeline) {
8845 -    this._band = band;
8846 -    this._timeline = timeline;
8847 -
8848 -    this._backLayer = null;
8849 -    this._eventLayer = null;
8850 -    this._lineLayer = null;
8851 -    this._highlightLayer = null;
8852 -
8853 -    this._eventIdToElmt = null;
8854 -};
8855 -
8856 -Timeline.CompactEventPainter.prototype.addOnSelectListener = function(listener) {
8857 -    this._onSelectListeners.push(listener);
8858 -};
8859 -
8860 -Timeline.CompactEventPainter.prototype.removeOnSelectListener = function(listener) {
8861 -    for (var i = 0; i < this._onSelectListeners.length; i++) {
8862 -        if (this._onSelectListeners[i] == listener) {
8863 -            this._onSelectListeners.splice(i, 1);
8864 -            break;
8865 -        }
8866 -    }
8867 -};
8868 -
8869 -Timeline.CompactEventPainter.prototype.getFilterMatcher = function() {
8870 -    return this._filterMatcher;
8871 -};
8872 -
8873 -Timeline.CompactEventPainter.prototype.setFilterMatcher = function(filterMatcher) {
8874 -    this._filterMatcher = filterMatcher;
8875 -};
8876 -
8877 -Timeline.CompactEventPainter.prototype.getHighlightMatcher = function() {
8878 -    return this._highlightMatcher;
8879 -};
8880 -
8881 -Timeline.CompactEventPainter.prototype.setHighlightMatcher = function(highlightMatcher) {
8882 -    this._highlightMatcher = highlightMatcher;
8883 -};
8884 -
8885 -Timeline.CompactEventPainter.prototype.paint = function() {
8886 -    var eventSource = this._band.getEventSource();
8887 -    if (eventSource == null) {
8888 -        return;
8889 -    }
8890 -
8891 -    this._eventIdToElmt = {};
8892 -    this._prepareForPainting();
8893 -
8894 -    var metrics = this._computeMetrics();
8895 -    var minDate = this._band.getMinDate();
8896 -    var maxDate = this._band.getMaxDate();
8897 -
8898 -    var filterMatcher = (this._filterMatcher != null) ?
8899 -        this._filterMatcher :
8900 -        function(evt) { return true; };
8901 -
8902 -    var highlightMatcher = (this._highlightMatcher != null) ?
8903 -        this._highlightMatcher :
8904 -        function(evt) { return -1; };
8905 -
8906 -    var iterator = eventSource.getEventIterator(minDate, maxDate);
8907 -
8908 -    var stackConcurrentPreciseInstantEvents = "stackConcurrentPreciseInstantEvents" in this._params && typeof this._params.stackConcurrentPreciseInstantEvents == "object";
8909 -    var collapseConcurrentPreciseInstantEvents = "collapseConcurrentPreciseInstantEvents" in this._params && this._params.collapseConcurrentPreciseInstantEvents;
8910 -    if (collapseConcurrentPreciseInstantEvents || stackConcurrentPreciseInstantEvents) {
8911 -        var bufferedEvents = [];
8912 -        var previousInstantEvent = null;
8913 -
8914 -        while (iterator.hasNext()) {
8915 -            var evt = iterator.next();
8916 -            if (filterMatcher(evt)) {
8917 -                if (!evt.isInstant() || evt.isImprecise()) {
8918 -                    this.paintEvent(evt, metrics, this._params.theme, highlightMatcher(evt));
8919 -                } else if (previousInstantEvent != null &&
8920 -                        previousInstantEvent.getStart().getTime() == evt.getStart().getTime()) {
8921 -                    bufferedEvents[bufferedEvents.length - 1].push(evt);
8922 -                } else {
8923 -                    bufferedEvents.push([ evt ]);
8924 -                    previousInstantEvent = evt;
8925 -                }
8926 -            }
8927 -        }
8928 -
8929 -        for (var i = 0; i < bufferedEvents.length; i++) {
8930 -            var compositeEvents = bufferedEvents[i];
8931 -            if (compositeEvents.length == 1) {
8932 -                this.paintEvent(compositeEvents[0], metrics, this._params.theme, highlightMatcher(evt));
8933 -            } else {
8934 -                var match = -1;
8935 -                for (var j = 0; match < 0 && j < compositeEvents.length; j++) {
8936 -                    match = highlightMatcher(compositeEvents[j]);
8937 -                }
8938 -
8939 -                if (stackConcurrentPreciseInstantEvents) {
8940 -                    this.paintStackedPreciseInstantEvents(compositeEvents, metrics, this._params.theme, match);
8941 -                } else {
8942 -                    this.paintCompositePreciseInstantEvents(compositeEvents, metrics, this._params.theme, match);
8943 -                }
8944 -            }
8945 -        }
8946 -    } else {
8947 -        while (iterator.hasNext()) {
8948 -            var evt = iterator.next();
8949 -            if (filterMatcher(evt)) {
8950 -                this.paintEvent(evt, metrics, this._params.theme, highlightMatcher(evt));
8951 -            }
8952 -        }
8953 -    }
8954 -
8955 -    this._highlightLayer.style.display = "block";
8956 -    this._lineLayer.style.display = "block";
8957 -    this._eventLayer.style.display = "block";
8958 -
8959 -    this._setOrthogonalOffset(metrics);
8960 -};
8961 -
8962 -Timeline.CompactEventPainter.prototype.softPaint = function() {
8963 -    this._setOrthogonalOffset(this._computeMetrics());
8964 -};
8965 -
8966 -Timeline.CompactEventPainter.prototype._setOrthogonalOffset = function(metrics) {
8967 -    var actualViewWidth = 2 * metrics.trackOffset + this._tracks.length * metrics.trackHeight;
8968 -    var minOrthogonalOffset = Math.min(0, this._band.getViewWidth() - actualViewWidth);
8969 -    var orthogonalOffset = Math.max(minOrthogonalOffset, this._band.getViewOrthogonalOffset());
8970 -
8971 -    this._highlightLayer.style.top =
8972 -        this._lineLayer.style.top =
8973 -            this._eventLayer.style.top =
8974 -                orthogonalOffset + "px";
8975 -};
8976 -
8977 -Timeline.CompactEventPainter.prototype._computeMetrics = function() {
8978 -    var theme = this._params.theme;
8979 -    var eventTheme = theme.event;
8980 -
8981 -    var metrics = {
8982 -        trackOffset:            "trackOffset" in this._params ? this._params.trackOffset : 10,
8983 -        trackHeight:            "trackHeight" in this._params ? this._params.trackHeight : 10,
8984 -
8985 -        tapeHeight:             theme.event.tape.height,
8986 -        tapeBottomMargin:       "tapeBottomMargin" in this._params ? this._params.tapeBottomMargin : 2,
8987 -
8988 -        labelBottomMargin:      "labelBottomMargin" in this._params ? this._params.labelBottomMargin : 5,
8989 -        labelRightMargin:       "labelRightMargin" in this._params ? this._params.labelRightMargin : 5,
8990 -
8991 -        defaultIcon:            eventTheme.instant.icon,
8992 -        defaultIconWidth:       eventTheme.instant.iconWidth,
8993 -        defaultIconHeight:      eventTheme.instant.iconHeight,
8994 -
8995 -        customIconWidth:        "iconWidth" in this._params ? this._params.iconWidth : eventTheme.instant.iconWidth,
8996 -        customIconHeight:       "iconHeight" in this._params ? this._params.iconHeight : eventTheme.instant.iconHeight,
8997 -
8998 -        iconLabelGap:           "iconLabelGap" in this._params ? this._params.iconLabelGap : 2,
8999 -        iconBottomMargin:       "iconBottomMargin" in this._params ? this._params.iconBottomMargin : 2
9000 -    };
9001 -    if ("compositeIcon" in this._params) {
9002 -        metrics.compositeIcon = this._params.compositeIcon;
9003 -        metrics.compositeIconWidth = this._params.compositeIconWidth || metrics.customIconWidth;
9004 -        metrics.compositeIconHeight = this._params.compositeIconHeight || metrics.customIconHeight;
9005 -    } else {
9006 -        metrics.compositeIcon = metrics.defaultIcon;
9007 -        metrics.compositeIconWidth = metrics.defaultIconWidth;
9008 -        metrics.compositeIconHeight = metrics.defaultIconHeight;
9009 -    }
9010 -    metrics.defaultStackIcon = "icon" in this._params.stackConcurrentPreciseInstantEvents ?
9011 -        this._params.stackConcurrentPreciseInstantEvents.icon : metrics.defaultIcon;
9012 -    metrics.defaultStackIconWidth = "iconWidth" in this._params.stackConcurrentPreciseInstantEvents ?
9013 -        this._params.stackConcurrentPreciseInstantEvents.iconWidth : metrics.defaultIconWidth;
9014 -    metrics.defaultStackIconHeight = "iconHeight" in this._params.stackConcurrentPreciseInstantEvents ?
9015 -        this._params.stackConcurrentPreciseInstantEvents.iconHeight : metrics.defaultIconHeight;
9016 -
9017 -    return metrics;
9018 -};
9019 -
9020 -Timeline.CompactEventPainter.prototype._prepareForPainting = function() {
9021 -    var band = this._band;
9022 -
9023 -    if (this._backLayer == null) {
9024 -        this._backLayer = this._band.createLayerDiv(0, "timeline-band-events");
9025 -        this._backLayer.style.visibility = "hidden";
9026 -
9027 -        var eventLabelPrototype = document.createElement("span");
9028 -        eventLabelPrototype.className = "timeline-event-label";
9029 -        this._backLayer.appendChild(eventLabelPrototype);
9030 -        this._frc = SimileAjax.Graphics.getFontRenderingContext(eventLabelPrototype);
9031 -    }
9032 -    this._frc.update();
9033 -    this._tracks = [];
9034 -
9035 -    if (this._highlightLayer != null) {
9036 -        band.removeLayerDiv(this._highlightLayer);
9037 -    }
9038 -    this._highlightLayer = band.createLayerDiv(105, "timeline-band-highlights");
9039 -    this._highlightLayer.style.display = "none";
9040 -
9041 -    if (this._lineLayer != null) {
9042 -        band.removeLayerDiv(this._lineLayer);
9043 -    }
9044 -    this._lineLayer = band.createLayerDiv(110, "timeline-band-lines");
9045 -    this._lineLayer.style.display = "none";
9046 -
9047 -    if (this._eventLayer != null) {
9048 -        band.removeLayerDiv(this._eventLayer);
9049 -    }
9050 -    this._eventLayer = band.createLayerDiv(115, "timeline-band-events");
9051 -    this._eventLayer.style.display = "none";
9052 -};
9053 -
9054 -Timeline.CompactEventPainter.prototype.paintEvent = function(evt, metrics, theme, highlightIndex) {
9055 -    if (evt.isInstant()) {
9056 -        this.paintInstantEvent(evt, metrics, theme, highlightIndex);
9057 -    } else {
9058 -        this.paintDurationEvent(evt, metrics, theme, highlightIndex);
9059 -    }
9060 -};
9061 -
9062 -Timeline.CompactEventPainter.prototype.paintInstantEvent = function(evt, metrics, theme, highlightIndex) {
9063 -    if (evt.isImprecise()) {
9064 -        this.paintImpreciseInstantEvent(evt, metrics, theme, highlightIndex);
9065 -    } else {
9066 -        this.paintPreciseInstantEvent(evt, metrics, theme, highlightIndex);
9067 -    }
9068 -}
9069 -
9070 -Timeline.CompactEventPainter.prototype.paintDurationEvent = function(evt, metrics, theme, highlightIndex) {
9071 -    if (evt.isImprecise()) {
9072 -        this.paintImpreciseDurationEvent(evt, metrics, theme, highlightIndex);
9073 -    } else {
9074 -        this.paintPreciseDurationEvent(evt, metrics, theme, highlightIndex);
9075 -    }
9076 -}
9077 -
9078 -Timeline.CompactEventPainter.prototype.paintPreciseInstantEvent = function(evt, metrics, theme, highlightIndex) {
9079 -    var commonData = {
9080 -        tooltip: evt.getProperty("tooltip") || evt.getText()
9081 -    };
9082 -
9083 -    var iconData = {
9084 -        url: evt.getIcon()
9085 -    };
9086 -    if (iconData.url == null) {
9087 -        iconData.url = metrics.defaultIcon;
9088 -        iconData.width = metrics.defaultIconWidth;
9089 -        iconData.height = metrics.defaultIconHeight;
9090 -        iconData.className = "timeline-event-icon-default";
9091 -    } else {
9092 -        iconData.width = evt.getProperty("iconWidth") || metrics.customIconWidth;
9093 -        iconData.height = evt.getProperty("iconHeight") || metrics.customIconHeight;
9094 -    }
9095 -
9096 -    var labelData = {
9097 -        text:       evt.getText(),
9098 -        color:      evt.getTextColor() || evt.getColor(),
9099 -        className:  evt.getClassName()
9100 -    };
9101 -
9102 -    var result = this.paintTapeIconLabel(
9103 -        evt.getStart(),
9104 -        commonData,
9105 -        null, // no tape data
9106 -        iconData,
9107 -        labelData,
9108 -        metrics,
9109 -        theme,
9110 -        highlightIndex
9111 -    );
9112 -
9113 -    var self = this;
9114 -    var clickHandler = function(elmt, domEvt, target) {
9115 -        return self._onClickInstantEvent(result.iconElmtData.elmt, domEvt, evt);
9116 -    };
9117 -    SimileAjax.DOM.registerEvent(result.iconElmtData.elmt, "mousedown", clickHandler);
9118 -    SimileAjax.DOM.registerEvent(result.labelElmtData.elmt, "mousedown", clickHandler);
9119 -
9120 -    this._eventIdToElmt[evt.getID()] = result.iconElmtData.elmt;
9121 -};
9122 -
9123 -Timeline.CompactEventPainter.prototype.paintCompositePreciseInstantEvents = function(events, metrics, theme, highlightIndex) {
9124 -    var evt = events[0];
9125 -
9126 -    var tooltips = [];
9127 -    for (var i = 0; i < events.length; i++) {
9128 -        tooltips.push(events[i].getProperty("tooltip") || events[i].getText());
9129 -    }
9130 -    var commonData = {
9131 -        tooltip: tooltips.join("; ")
9132 -    };
9133 -
9134 -    var iconData = {
9135 -        url: metrics.compositeIcon,
9136 -        width: metrics.compositeIconWidth,
9137 -        height: metrics.compositeIconHeight,
9138 -        className: "timeline-event-icon-composite"
9139 -    };
9140 -
9141 -    var labelData = {
9142 -        text: String.substitute(this._params.compositeEventLabelTemplate, [ events.length ])
9143 -    };
9144 -
9145 -    var result = this.paintTapeIconLabel(
9146 -        evt.getStart(),
9147 -        commonData,
9148 -        null, // no tape data
9149 -        iconData,
9150 -        labelData,
9151 -        metrics,
9152 -        theme,
9153 -        highlightIndex
9154 -    );
9155 -
9156 -    var self = this;
9157 -    var clickHandler = function(elmt, domEvt, target) {
9158 -        return self._onClickMultiplePreciseInstantEvent(result.iconElmtData.elmt, domEvt, events);
9159 -    };
9160 -
9161 -    SimileAjax.DOM.registerEvent(result.iconElmtData.elmt, "mousedown", clickHandler);
9162 -    SimileAjax.DOM.registerEvent(result.labelElmtData.elmt, "mousedown", clickHandler);
9163 -
9164 -    for (var i = 0; i < events.length; i++) {
9165 -        this._eventIdToElmt[events[i].getID()] = result.iconElmtData.elmt;
9166 -    }
9167 -};
9168 -
9169 -Timeline.CompactEventPainter.prototype.paintStackedPreciseInstantEvents = function(events, metrics, theme, highlightIndex) {
9170 -    var limit = "limit" in this._params.stackConcurrentPreciseInstantEvents ?
9171 -        this._params.stackConcurrentPreciseInstantEvents.limit : 10;
9172 -    var moreMessageTemplate = "moreMessageTemplate" in this._params.stackConcurrentPreciseInstantEvents ?
9173 -        this._params.stackConcurrentPreciseInstantEvents.moreMessageTemplate : "%0 More Events";
9174 -    var showMoreMessage = limit <= events.length - 2; // We want at least 2 more events above the limit.
9175 -                                                      // Otherwise we'd need the singular case of "1 More Event"
9176 -
9177 -    var band = this._band;
9178 -    var getPixelOffset = function(date) {
9179 -        return Math.round(band.dateToPixelOffset(date));
9180 -    };
9181 -    var getIconData = function(evt) {
9182 -        var iconData = {
9183 -            url: evt.getIcon()
9184 -        };
9185 -        if (iconData.url == null) {
9186 -            iconData.url = metrics.defaultStackIcon;
9187 -            iconData.width = metrics.defaultStackIconWidth;
9188 -            iconData.height = metrics.defaultStackIconHeight;
9189 -            iconData.className = "timeline-event-icon-stack timeline-event-icon-default";
9190 -        } else {
9191 -            iconData.width = evt.getProperty("iconWidth") || metrics.customIconWidth;
9192 -            iconData.height = evt.getProperty("iconHeight") || metrics.customIconHeight;
9193 -            iconData.className = "timeline-event-icon-stack";
9194 -        }
9195 -        return iconData;
9196 -    };
9197 -
9198 -    var firstIconData = getIconData(events[0]);
9199 -    var horizontalIncrement = 5;
9200 -    var leftIconEdge = 0;
9201 -    var totalLabelWidth = 0;
9202 -    var totalLabelHeight = 0;
9203 -    var totalIconHeight = 0;
9204 -
9205 -    var records = [];
9206 -    for (var i = 0; i < events.length && (!showMoreMessage || i < limit); i++) {
9207 -        var evt = events[i];
9208 -        var text = evt.getText();
9209 -        var iconData = getIconData(evt);
9210 -        var labelSize = this._frc.computeSize(text);
9211 -        var record = {
9212 -            text:       text,
9213 -            iconData:   iconData,
9214 -            labelSize:  labelSize,
9215 -            iconLeft:   firstIconData.width + i * horizontalIncrement - iconData.width
9216 -        };
9217 -        record.labelLeft = firstIconData.width + i * horizontalIncrement + metrics.iconLabelGap;
9218 -        record.top = totalLabelHeight;
9219 -        records.push(record);
9220 -
9221 -        leftIconEdge = Math.min(leftIconEdge, record.iconLeft);
9222 -        totalLabelHeight += labelSize.height;
9223 -        totalLabelWidth = Math.max(totalLabelWidth, record.labelLeft + labelSize.width);
9224 -        totalIconHeight = Math.max(totalIconHeight, record.top + iconData.height);
9225 -    }
9226 -    if (showMoreMessage) {
9227 -        var moreMessage = String.substitute(moreMessageTemplate, [ events.length - limit ]);
9228 -
9229 -        var moreMessageLabelSize = this._frc.computeSize(moreMessage);
9230 -        var moreMessageLabelLeft = firstIconData.width + (limit - 1) * horizontalIncrement + metrics.iconLabelGap;
9231 -        var moreMessageLabelTop = totalLabelHeight;
9232 -
9233 -        totalLabelHeight += moreMessageLabelSize.height;
9234 -        totalLabelWidth = Math.max(totalLabelWidth, moreMessageLabelLeft + moreMessageLabelSize.width);
9235 -    }
9236 -    totalLabelWidth += metrics.labelRightMargin;
9237 -    totalLabelHeight += metrics.labelBottomMargin;
9238 -    totalIconHeight += metrics.iconBottomMargin;
9239 -
9240 -    var anchorPixel = getPixelOffset(events[0].getStart());
9241 -    var newTracks = [];
9242 -
9243 -    var trackCount = Math.ceil(Math.max(totalIconHeight, totalLabelHeight) / metrics.trackHeight);
9244 -    var rightIconEdge = firstIconData.width + (events.length - 1) * horizontalIncrement;
9245 -    for (var i = 0; i < trackCount; i++) {
9246 -        newTracks.push({ start: leftIconEdge, end: rightIconEdge });
9247 -    }
9248 -    var labelTrackCount = Math.ceil(totalLabelHeight / metrics.trackHeight);
9249 -    for (var i = 0; i < labelTrackCount; i++) {
9250 -        var track = newTracks[i];
9251 -        track.end = Math.max(track.end, totalLabelWidth);
9252 -    }
9253 -
9254 -    var firstTrack = this._fitTracks(anchorPixel, newTracks);
9255 -    var verticalPixelOffset = firstTrack * metrics.trackHeight + metrics.trackOffset;
9256 -
9257 -    var iconStackDiv = this._timeline.getDocument().createElement("div");
9258 -    iconStackDiv.className = 'timeline-event-icon-stack';
9259 -    iconStackDiv.style.position = "absolute";
9260 -    iconStackDiv.style.overflow = "visible";
9261 -    iconStackDiv.style.left = anchorPixel + "px";
9262 -    iconStackDiv.style.top = verticalPixelOffset + "px";
9263 -    iconStackDiv.style.width = rightIconEdge + "px";
9264 -    iconStackDiv.style.height = totalIconHeight + "px";
9265 -    iconStackDiv.innerHTML = "<div style='position: relative'></div>";
9266 -    this._eventLayer.appendChild(iconStackDiv);
9267 -
9268 -    var self = this;
9269 -    var onMouseOver = function(domEvt) {
9270 -        try {
9271 -            var n = parseInt(this.getAttribute("index"));
9272 -            var childNodes = iconStackDiv.firstChild.childNodes;
9273 -            for (var i = 0; i < childNodes.length; i++) {
9274 -                var child = childNodes[i];
9275 -                if (i == n) {
9276 -                    child.style.zIndex = childNodes.length;
9277 -                } else {
9278 -                    child.style.zIndex = childNodes.length - i;
9279 -                }
9280 -            }
9281 -        } catch (e) {
9282 -        }
9283 -    };
9284 -    var paintEvent = function(index) {
9285 -        var record = records[index];
9286 -        var evt = events[index];
9287 -        var tooltip = evt.getProperty("tooltip") || evt.getText();
9288 -
9289 -        var labelElmtData = self._paintEventLabel(
9290 -            { tooltip: tooltip },
9291 -            { text: record.text },
9292 -            anchorPixel + record.labelLeft,
9293 -            verticalPixelOffset + record.top,
9294 -            record.labelSize.width,
9295 -            record.labelSize.height,
9296 -            theme
9297 -        );
9298 -        labelElmtData.elmt.setAttribute("index", index);
9299 -        labelElmtData.elmt.onmouseover = onMouseOver;
9300 -
9301 -        var img = SimileAjax.Graphics.createTranslucentImage(record.iconData.url);
9302 -        var iconDiv = self._timeline.getDocument().createElement("div");
9303 -        iconDiv.className = 'timeline-event-icon' + ("className" in record.iconData ? (" " + record.iconData.className) : "");
9304 -        iconDiv.style.left = record.iconLeft + "px";
9305 -        iconDiv.style.top = record.top + "px";
9306 -        iconDiv.style.zIndex = (records.length - index);
9307 -        iconDiv.appendChild(img);
9308 -        iconDiv.setAttribute("index", index);
9309 -        iconDiv.onmouseover = onMouseOver;
9310 -
9311 -        iconStackDiv.firstChild.appendChild(iconDiv);
9312 -
9313 -        var clickHandler = function(elmt, domEvt, target) {
9314 -            return self._onClickInstantEvent(labelElmtData.elmt, domEvt, evt);
9315 -        };
9316 -
9317 -        SimileAjax.DOM.registerEvent(iconDiv, "mousedown", clickHandler);
9318 -        SimileAjax.DOM.registerEvent(labelElmtData.elmt, "mousedown", clickHandler);
9319 -
9320 -        self._eventIdToElmt[evt.getID()] = iconDiv;
9321 -    };
9322 -    for (var i = 0; i < records.length; i++) {
9323 -        paintEvent(i);
9324 -    }
9325 -
9326 -    if (showMoreMessage) {
9327 -        var moreEvents = events.slice(limit);
9328 -        var moreMessageLabelElmtData = this._paintEventLabel(
9329 -            { tooltip: moreMessage },
9330 -            { text: moreMessage },
9331 -            anchorPixel + moreMessageLabelLeft,
9332 -            verticalPixelOffset + moreMessageLabelTop,
9333 -            moreMessageLabelSize.width,
9334 -            moreMessageLabelSize.height,
9335 -            theme
9336 -        );
9337 -
9338 -        var moreMessageClickHandler = function(elmt, domEvt, target) {
9339 -            return self._onClickMultiplePreciseInstantEvent(moreMessageLabelElmtData.elmt, domEvt, moreEvents);
9340 -        };
9341 -        SimileAjax.DOM.registerEvent(moreMessageLabelElmtData.elmt, "mousedown", moreMessageClickHandler);
9342 -
9343 -        for (var i = 0; i < moreEvents.length; i++) {
9344 -            this._eventIdToElmt[moreEvents[i].getID()] = moreMessageLabelElmtData.elmt;
9345 -        }
9346 -    }
9347 -    //this._createHighlightDiv(highlightIndex, iconElmtData, theme);
9348 -};
9349 -
9350 -Timeline.CompactEventPainter.prototype.paintImpreciseInstantEvent = function(evt, metrics, theme, highlightIndex) {
9351 -    var commonData = {
9352 -        tooltip: evt.getProperty("tooltip") || evt.getText()
9353 -    };
9354 -
9355 -    var tapeData = {
9356 -        start:          evt.getStart(),
9357 -        end:            evt.getEnd(),
9358 -        latestStart:    evt.getLatestStart(),
9359 -        earliestEnd:    evt.getEarliestEnd(),
9360 -        isInstant:      true
9361 -    };
9362 -
9363 -    var iconData = {
9364 -        url: evt.getIcon()
9365 -    };
9366 -    if (iconData.url == null) {
9367 -        iconData = null;
9368 -    } else {
9369 -        iconData.width = evt.getProperty("iconWidth") || metrics.customIconWidth;
9370 -        iconData.height = evt.getProperty("iconHeight") || metrics.customIconHeight;
9371 -    }
9372 -
9373 -    var labelData = {
9374 -        text:       evt.getText(),
9375 -        color:      evt.getTextColor() || evt.getColor(),
9376 -        className:  evt.getClassName()
9377 -    };
9378 -
9379 -    var result = this.paintTapeIconLabel(
9380 -        evt.getStart(),
9381 -        commonData,
9382 -        tapeData, // no tape data
9383 -        iconData,
9384 -        labelData,
9385 -        metrics,
9386 -        theme,
9387 -        highlightIndex
9388 -    );
9389 -
9390 -    var self = this;
9391 -    var clickHandler = iconData != null ?
9392 -        function(elmt, domEvt, target) {
9393 -            return self._onClickInstantEvent(result.iconElmtData.elmt, domEvt, evt);
9394 -        } :
9395 -        function(elmt, domEvt, target) {
9396 -            return self._onClickInstantEvent(result.labelElmtData.elmt, domEvt, evt);
9397 -        };
9398 -
9399 -    SimileAjax.DOM.registerEvent(result.labelElmtData.elmt, "mousedown", clickHandler);
9400 -    SimileAjax.DOM.registerEvent(result.impreciseTapeElmtData.elmt, "mousedown", clickHandler);
9401 -
9402 -    if (iconData != null) {
9403 -        SimileAjax.DOM.registerEvent(result.iconElmtData.elmt, "mousedown", clickHandler);
9404 -        this._eventIdToElmt[evt.getID()] = result.iconElmtData.elmt;
9405 -    } else {
9406 -        this._eventIdToElmt[evt.getID()] = result.labelElmtData.elmt;
9407 -    }
9408 -};
9409 -
9410 -Timeline.CompactEventPainter.prototype.paintPreciseDurationEvent = function(evt, metrics, theme, highlightIndex) {
9411 -    var commonData = {
9412 -        tooltip: evt.getProperty("tooltip") || evt.getText()
9413 -    };
9414 -
9415 -    var tapeData = {
9416 -        start:          evt.getStart(),
9417 -        end:            evt.getEnd(),
9418 -        isInstant:      false
9419 -    };
9420 -
9421 -    var iconData = {
9422 -        url: evt.getIcon()
9423 -    };
9424 -    if (iconData.url == null) {
9425 -        iconData = null;
9426 -    } else {
9427 -        iconData.width = evt.getProperty("iconWidth") || metrics.customIconWidth;
9428 -        iconData.height = evt.getProperty("iconHeight") || metrics.customIconHeight;
9429 -    }
9430 -
9431 -    var labelData = {
9432 -        text:       evt.getText(),
9433 -        color:      evt.getTextColor() || evt.getColor(),
9434 -        className:  evt.getClassName()
9435 -    };
9436 -
9437 -    var result = this.paintTapeIconLabel(
9438 -        evt.getLatestStart(),
9439 -        commonData,
9440 -        tapeData, // no tape data
9441 -        iconData,
9442 -        labelData,
9443 -        metrics,
9444 -        theme,
9445 -        highlightIndex
9446 -    );
9447 -
9448 -    var self = this;
9449 -    var clickHandler = iconData != null ?
9450 -        function(elmt, domEvt, target) {
9451 -            return self._onClickInstantEvent(result.iconElmtData.elmt, domEvt, evt);
9452 -        } :
9453 -        function(elmt, domEvt, target) {
9454 -            return self._onClickInstantEvent(result.labelElmtData.elmt, domEvt, evt);
9455 -        };
9456 -
9457 -    SimileAjax.DOM.registerEvent(result.labelElmtData.elmt, "mousedown", clickHandler);
9458 -    SimileAjax.DOM.registerEvent(result.tapeElmtData.elmt, "mousedown", clickHandler);
9459 -
9460 -    if (iconData != null) {
9461 -        SimileAjax.DOM.registerEvent(result.iconElmtData.elmt, "mousedown", clickHandler);
9462 -        this._eventIdToElmt[evt.getID()] = result.iconElmtData.elmt;
9463 -    } else {
9464 -        this._eventIdToElmt[evt.getID()] = result.labelElmtData.elmt;
9465 -    }
9466 -};
9467 -
9468 -Timeline.CompactEventPainter.prototype.paintImpreciseDurationEvent = function(evt, metrics, theme, highlightIndex) {
9469 -    var commonData = {
9470 -        tooltip: evt.getProperty("tooltip") || evt.getText()
9471 -    };
9472 -
9473 -    var tapeData = {
9474 -        start:          evt.getStart(),
9475 -        end:            evt.getEnd(),
9476 -        latestStart:    evt.getLatestStart(),
9477 -        earliestEnd:    evt.getEarliestEnd(),
9478 -        isInstant:      false
9479 -    };
9480 -
9481 -    var iconData = {
9482 -        url: evt.getIcon()
9483 -    };
9484 -    if (iconData.url == null) {
9485 -        iconData = null;
9486 -    } else {
9487 -        iconData.width = evt.getProperty("iconWidth") || metrics.customIconWidth;
9488 -        iconData.height = evt.getProperty("iconHeight") || metrics.customIconHeight;
9489 -    }
9490 -
9491 -    var labelData = {
9492 -        text:       evt.getText(),
9493 -        color:      evt.getTextColor() || evt.getColor(),
9494 -        className:  evt.getClassName()
9495 -    };
9496 -
9497 -    var result = this.paintTapeIconLabel(
9498 -        evt.getLatestStart(),
9499 -        commonData,
9500 -        tapeData, // no tape data
9501 -        iconData,
9502 -        labelData,
9503 -        metrics,
9504 -        theme,
9505 -        highlightIndex
9506 -    );
9507 -
9508 -    var self = this;
9509 -    var clickHandler = iconData != null ?
9510 -        function(elmt, domEvt, target) {
9511 -            return self._onClickInstantEvent(result.iconElmtData.elmt, domEvt, evt);
9512 -        } :
9513 -        function(elmt, domEvt, target) {
9514 -            return self._onClickInstantEvent(result.labelElmtData.elmt, domEvt, evt);
9515 -        };
9516 -
9517 -    SimileAjax.DOM.registerEvent(result.labelElmtData.elmt, "mousedown", clickHandler);
9518 -    SimileAjax.DOM.registerEvent(result.tapeElmtData.elmt, "mousedown", clickHandler);
9519 -
9520 -    if (iconData != null) {
9521 -        SimileAjax.DOM.registerEvent(result.iconElmtData.elmt, "mousedown", clickHandler);
9522 -        this._eventIdToElmt[evt.getID()] = result.iconElmtData.elmt;
9523 -    } else {
9524 -        this._eventIdToElmt[evt.getID()] = result.labelElmtData.elmt;
9525 -    }
9526 -};
9527 -
9528 -Timeline.CompactEventPainter.prototype.paintTapeIconLabel = function(
9529 -    anchorDate,
9530 -    commonData,
9531 -    tapeData,
9532 -    iconData,
9533 -    labelData,
9534 -    metrics,
9535 -    theme,
9536 -    highlightIndex
9537 -) {
9538 -    var band = this._band;
9539 -    var getPixelOffset = function(date) {
9540 -        return Math.round(band.dateToPixelOffset(date));
9541 -    };
9542 -
9543 -    var anchorPixel = getPixelOffset(anchorDate);
9544 -    var newTracks = [];
9545 -
9546 -    var tapeHeightOccupied = 0;         // how many pixels (vertically) the tape occupies, including bottom margin
9547 -    var tapeTrackCount = 0;             // how many tracks the tape takes up, usually just 1
9548 -    var tapeLastTrackExtraSpace = 0;    // on the last track that the tape occupies, how many pixels are left (for icon and label to occupy as well)
9549 -    if (tapeData != null) {
9550 -        tapeHeightOccupied = metrics.tapeHeight + metrics.tapeBottomMargin;
9551 -        tapeTrackCount = Math.ceil(metrics.tapeHeight / metrics.trackHeight);
9552 -
9553 -        var tapeEndPixelOffset = getPixelOffset(tapeData.end) - anchorPixel;
9554 -        var tapeStartPixelOffset = getPixelOffset(tapeData.start) - anchorPixel;
9555 -
9556 -        for (var t = 0; t < tapeTrackCount; t++) {
9557 -            newTracks.push({ start: tapeStartPixelOffset, end: tapeEndPixelOffset });
9558 -        }
9559 -
9560 -        tapeLastTrackExtraSpace = metrics.trackHeight - (tapeHeightOccupied % metrics.tapeHeight);
9561 -    }
9562 -
9563 -    var iconStartPixelOffset = 0;        // where the icon starts compared to the anchor pixel;
9564 -                                         // this can be negative if the icon is center-aligned around the anchor
9565 -    var iconHorizontalSpaceOccupied = 0; // how many pixels the icon take up from the anchor pixel,
9566 -                                         // including the gap between the icon and the label
9567 -    if (iconData != null) {
9568 -        if ("iconAlign" in iconData && iconData.iconAlign == "center") {
9569 -            iconStartPixelOffset = -Math.floor(iconData.width / 2);
9570 -        }
9571 -        iconHorizontalSpaceOccupied = iconStartPixelOffset + iconData.width + metrics.iconLabelGap;
9572 -
9573 -        if (tapeTrackCount > 0) {
9574 -            newTracks[tapeTrackCount - 1].end = Math.max(newTracks[tapeTrackCount - 1].end, iconHorizontalSpaceOccupied);
9575 -        }
9576 -
9577 -        var iconHeight = iconData.height + metrics.iconBottomMargin + tapeLastTrackExtraSpace;
9578 -        while (iconHeight > 0) {
9579 -            newTracks.push({ start: iconStartPixelOffset, end: iconHorizontalSpaceOccupied });
9580 -            iconHeight -= metrics.trackHeight;
9581 -        }
9582 -    }
9583 -
9584 -    var text = labelData.text;
9585 -    var labelSize = this._frc.computeSize(text);
9586 -    var labelHeight = labelSize.height + metrics.labelBottomMargin + tapeLastTrackExtraSpace;
9587 -    var labelEndPixelOffset = iconHorizontalSpaceOccupied + labelSize.width + metrics.labelRightMargin;
9588 -    if (tapeTrackCount > 0) {
9589 -        newTracks[tapeTrackCount - 1].end = Math.max(newTracks[tapeTrackCount - 1].end, labelEndPixelOffset);
9590 -    }
9591 -    for (var i = 0; labelHeight > 0; i++) {
9592 -        if (tapeTrackCount + i < newTracks.length) {
9593 -            var track = newTracks[tapeTrackCount + i];
9594 -            track.end = labelEndPixelOffset;
9595 -        } else {
9596 -            newTracks.push({ start: 0, end: labelEndPixelOffset });
9597 -        }
9598 -        labelHeight -= metrics.trackHeight;
9599 -    }
9600 -
9601 -    /*
9602 -     *  Try to fit the new track on top of the existing tracks, then
9603 -     *  render the various elements.
9604 -     */
9605 -    var firstTrack = this._fitTracks(anchorPixel, newTracks);
9606 -    var verticalPixelOffset = firstTrack * metrics.trackHeight + metrics.trackOffset;
9607 -    var result = {};
9608 -
9609 -    result.labelElmtData = this._paintEventLabel(
9610 -        commonData,
9611 -        labelData,
9612 -        anchorPixel + iconHorizontalSpaceOccupied,
9613 -        verticalPixelOffset + tapeHeightOccupied,
9614 -        labelSize.width,
9615 -        labelSize.height,
9616 -        theme
9617 -    );
9618 -
9619 -    if (tapeData != null) {
9620 -        if ("latestStart" in tapeData || "earliestEnd" in tapeData) {
9621 -            result.impreciseTapeElmtData = this._paintEventTape(
9622 -                commonData,
9623 -                tapeData,
9624 -                metrics.tapeHeight,
9625 -                verticalPixelOffset,
9626 -                getPixelOffset(tapeData.start),
9627 -                getPixelOffset(tapeData.end),
9628 -                theme.event.duration.impreciseColor,
9629 -                theme.event.duration.impreciseOpacity,
9630 -                metrics,
9631 -                theme
9632 -            );
9633 -        }
9634 -        if (!tapeData.isInstant && "start" in tapeData && "end" in tapeData) {
9635 -            result.tapeElmtData = this._paintEventTape(
9636 -                commonData,
9637 -                tapeData,
9638 -                metrics.tapeHeight,
9639 -                verticalPixelOffset,
9640 -                anchorPixel,
9641 -                getPixelOffset("earliestEnd" in tapeData ? tapeData.earliestEnd : tapeData.end),
9642 -                tapeData.color,
9643 -                100,
9644 -                metrics,
9645 -                theme
9646 -            );
9647 -        }
9648 -    }
9649 -
9650 -    if (iconData != null) {
9651 -        result.iconElmtData = this._paintEventIcon(
9652 -            com